Drop home page song collections in favor of a sort feature (#11)

This commit is contained in:
vfsfitvnm 2022-07-06 19:30:40 +02:00
parent ae00f8ea3d
commit 59b6c61bb2
9 changed files with 418 additions and 74 deletions

View file

@ -8,9 +8,11 @@ import android.os.Parcel
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.room.* import androidx.room.*
import androidx.room.migration.AutoMigrationSpec import androidx.room.migration.AutoMigrationSpec
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.room.migration.Migration import androidx.room.migration.Migration
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import it.vfsfitvnm.vimusic.enums.SongSortBy
import it.vfsfitvnm.vimusic.enums.SortOrder
import it.vfsfitvnm.vimusic.models.* import it.vfsfitvnm.vimusic.models.*
import it.vfsfitvnm.vimusic.utils.getFloatOrNull import it.vfsfitvnm.vimusic.utils.getFloatOrNull
import it.vfsfitvnm.vimusic.utils.getLongOrNull import it.vfsfitvnm.vimusic.utils.getLongOrNull
@ -21,6 +23,35 @@ import kotlinx.coroutines.flow.Flow
interface Database { interface Database {
companion object : Database by DatabaseInitializer.Instance.database companion object : Database by DatabaseInitializer.Instance.database
@Transaction
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID ASC")
fun songsByRowIdAsc(): Flow<List<DetailedSong>>
@Transaction
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID DESC")
fun songsByRowIdDesc(): Flow<List<DetailedSong>>
@Transaction
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY totalPlayTimeMs ASC")
fun songsByPlayTimeAsc(): Flow<List<DetailedSong>>
@Transaction
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY totalPlayTimeMs DESC")
fun songsByPlayTimeDesc(): Flow<List<DetailedSong>>
fun songs(sortBy: SongSortBy, sortOrder: SortOrder): Flow<List<DetailedSong>> {
return when (sortBy) {
SongSortBy.PlayTime -> when (sortOrder) {
SortOrder.Ascending -> songsByPlayTimeAsc()
SortOrder.Descending -> songsByPlayTimeDesc()
}
SongSortBy.DateAdded -> when (sortOrder) {
SortOrder.Ascending -> songsByRowIdAsc()
SortOrder.Descending -> songsByRowIdDesc()
}
}
}
@Transaction @Transaction
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID DESC") @Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID DESC")
fun history(): Flow<List<DetailedSong>> fun history(): Flow<List<DetailedSong>>
@ -29,10 +60,6 @@ interface Database {
@Query("SELECT * FROM Song WHERE likedAt IS NOT NULL ORDER BY likedAt DESC") @Query("SELECT * FROM Song WHERE likedAt IS NOT NULL ORDER BY likedAt DESC")
fun favorites(): Flow<List<DetailedSong>> fun favorites(): Flow<List<DetailedSong>>
@Transaction
@Query("SELECT * FROM Song WHERE totalPlayTimeMs >= 60000 ORDER BY totalPlayTimeMs DESC LIMIT 20")
fun mostPlayed(): Flow<List<DetailedSong>>
@Query("SELECT * FROM QueuedMediaItem") @Query("SELECT * FROM QueuedMediaItem")
fun queue(): List<QueuedMediaItem> fun queue(): List<QueuedMediaItem>

View file

@ -1,7 +0,0 @@
package it.vfsfitvnm.vimusic.enums
enum class SongCollection {
MostPlayed,
Favorites,
History
}

View file

@ -0,0 +1,6 @@
package it.vfsfitvnm.vimusic.enums
enum class SongSortBy {
PlayTime,
DateAdded
}

View file

@ -0,0 +1,11 @@
package it.vfsfitvnm.vimusic.enums
enum class SortOrder {
Ascending,
Descending;
operator fun not() = when (this) {
Ascending -> Descending
Descending -> Ascending
}
}

View file

@ -0,0 +1,191 @@
package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.compose.animation.core.*
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.*
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.window.PopupProperties
import kotlin.math.max
import kotlin.math.min
@Composable
fun DropdownMenu(
isDisplayed: Boolean,
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
offset: DpOffset = DpOffset(0.dp, 0.dp),
properties: PopupProperties = PopupProperties(focusable = true),
content: @Composable ColumnScope.() -> Unit
) {
val expandedStates = remember {
MutableTransitionState(false)
}.apply { targetState = isDisplayed }
if (expandedStates.currentState || expandedStates.targetState) {
val density = LocalDensity.current
var transformOrigin by remember {
mutableStateOf(TransformOrigin.Center)
}
val popupPositionProvider =
DropdownMenuPositionProvider(offset, density) { parentBounds, menuBounds ->
transformOrigin = calculateTransformOrigin(parentBounds, menuBounds)
}
Popup(
onDismissRequest = onDismissRequest,
popupPositionProvider = popupPositionProvider,
properties = properties
) {
DropdownMenuContent(
expandedStates = expandedStates,
transformOrigin = transformOrigin,
modifier = modifier,
content = content
)
}
}
}
@Composable
internal fun DropdownMenuContent(
expandedStates: MutableTransitionState<Boolean>,
transformOrigin: TransformOrigin,
modifier: Modifier = Modifier,
content: @Composable ColumnScope.() -> Unit
) {
val transition = updateTransition(expandedStates, "DropDownMenu")
val scale by transition.animateFloat(
transitionSpec = {
if (false isTransitioningTo true) {
// Dismissed to expanded
tween(
durationMillis = 128,
easing = LinearOutSlowInEasing
)
} else {
// Expanded to dismissed.
tween(
durationMillis = 64,
delayMillis = 64
)
}
}, label = ""
) { isDisplayed ->
if (isDisplayed) 1f else 0.9f
}
Column(
modifier = modifier
.graphicsLayer {
scaleX = scale
scaleY = scale
this.transformOrigin = transformOrigin
},
content = content,
)
}
@Immutable
private data class DropdownMenuPositionProvider(
val contentOffset: DpOffset,
val density: Density,
val onPositionCalculated: (IntRect, IntRect) -> Unit = { _, _ -> }
) : PopupPositionProvider {
override fun calculatePosition(
anchorBounds: IntRect,
windowSize: IntSize,
layoutDirection: LayoutDirection,
popupContentSize: IntSize
): IntOffset {
// The min margin above and below the menu, relative to the screen.
val verticalMargin = with(density) { 48.dp.roundToPx() }
// The content offset specified using the dropdown offset parameter.
val contentOffsetX = with(density) { contentOffset.x.roundToPx() }
val contentOffsetY = with(density) { contentOffset.y.roundToPx() }
// Compute horizontal position.
val toRight = anchorBounds.left + contentOffsetX
val toLeft = anchorBounds.right - contentOffsetX - popupContentSize.width
val toDisplayRight = windowSize.width - popupContentSize.width
val toDisplayLeft = 0
val x = if (layoutDirection == LayoutDirection.Ltr) {
sequenceOf(
toRight,
toLeft,
// If the anchor gets outside of the window on the left, we want to position
// toDisplayLeft for proximity to the anchor. Otherwise, toDisplayRight.
if (anchorBounds.left >= 0) toDisplayRight else toDisplayLeft
)
} else {
sequenceOf(
toLeft,
toRight,
// If the anchor gets outside of the window on the right, we want to position
// toDisplayRight for proximity to the anchor. Otherwise, toDisplayLeft.
if (anchorBounds.right <= windowSize.width) toDisplayLeft else toDisplayRight
)
}.firstOrNull {
it >= 0 && it + popupContentSize.width <= windowSize.width
} ?: toLeft
// Compute vertical position.
val toBottom = maxOf(anchorBounds.bottom + contentOffsetY, verticalMargin)
val toTop = anchorBounds.top - contentOffsetY - popupContentSize.height
val toCenter = anchorBounds.top - popupContentSize.height / 2
val toDisplayBottom = windowSize.height - popupContentSize.height - verticalMargin
val y = sequenceOf(toBottom, toTop, toCenter, toDisplayBottom).firstOrNull {
it >= verticalMargin &&
it + popupContentSize.height <= windowSize.height - verticalMargin
} ?: toTop
onPositionCalculated(
anchorBounds,
IntRect(x, y, x + popupContentSize.width, y + popupContentSize.height)
)
return IntOffset(x, y)
}
}
fun calculateTransformOrigin(
parentBounds: IntRect,
menuBounds: IntRect
): TransformOrigin {
val pivotX = when {
menuBounds.left >= parentBounds.right -> 0f
menuBounds.right <= parentBounds.left -> 1f
menuBounds.width == 0 -> 0f
else -> {
val intersectionCenter =
(
max(parentBounds.left, menuBounds.left) +
min(parentBounds.right, menuBounds.right)
) / 2
(intersectionCenter - menuBounds.left).toFloat() / menuBounds.width
}
}
val pivotY = when {
menuBounds.top >= parentBounds.bottom -> 0f
menuBounds.bottom <= parentBounds.top -> 1f
menuBounds.height == 0 -> 0f
else -> {
val intersectionCenter =
(
max(parentBounds.top, menuBounds.top) +
min(parentBounds.bottom, menuBounds.bottom)
) / 2
(intersectionCenter - menuBounds.top).toFloat() / menuBounds.height
}
}
return TransformOrigin(pivotX, pivotY)
}

View file

@ -14,12 +14,14 @@ import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
@ -27,20 +29,22 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.route.fastFade import it.vfsfitvnm.route.fastFade
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.enums.SongCollection import it.vfsfitvnm.vimusic.enums.SongSortBy
import it.vfsfitvnm.vimusic.enums.SortOrder
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.models.Playlist import it.vfsfitvnm.vimusic.models.Playlist
import it.vfsfitvnm.vimusic.models.SearchQuery import it.vfsfitvnm.vimusic.models.SearchQuery
import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.query import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.InFavoritesMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.DropdownMenu
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
@ -75,12 +79,8 @@ fun HomeScreen() {
val preferences = LocalPreferences.current val preferences = LocalPreferences.current
val songCollection by remember(preferences.homePageSongCollection) { val songCollection by remember(preferences.songSortBy, preferences.songSortOrder) {
when (preferences.homePageSongCollection) { Database.songs(preferences.songSortBy, preferences.songSortOrder)
SongCollection.MostPlayed -> Database.mostPlayed()
SongCollection.Favorites -> Database.favorites()
SongCollection.History -> Database.history()
}
}.collectAsState(initial = emptyList(), context = Dispatchers.IO) }.collectAsState(initial = emptyList(), context = Dispatchers.IO)
RouteHandler( RouteHandler(
@ -313,48 +313,13 @@ fun HomeScreen() {
.padding(horizontal = 8.dp) .padding(horizontal = 8.dp)
.padding(top = 32.dp) .padding(top = 32.dp)
) { ) {
Row( BasicText(
verticalAlignment = Alignment.Bottom, text = "Songs",
style = typography.m.semiBold,
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.padding(horizontal = 8.dp) .padding(horizontal = 8.dp)
) { )
BasicText(
text = when (preferences.homePageSongCollection) {
SongCollection.MostPlayed -> "Most played"
SongCollection.Favorites -> "Favorites"
SongCollection.History -> "History"
},
style = typography.m.semiBold,
modifier = Modifier
.alignByBaseline()
.animateContentSize()
)
val songCollections = enumValues<SongCollection>()
val nextSongCollection =
songCollections[(preferences.homePageSongCollection.ordinal + 1) % songCollections.size]
BasicText(
text = when (nextSongCollection) {
SongCollection.MostPlayed -> "Most played"
SongCollection.Favorites -> "Favorites"
SongCollection.History -> "History"
},
style = typography.xxs.secondary.bold,
modifier = Modifier
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = {
preferences.homePageSongCollection = nextSongCollection
}
)
.alignByBaseline()
.padding(horizontal = 16.dp)
.animateContentSize()
)
}
Image( Image(
painter = painterResource(R.drawable.shuffle), painter = painterResource(R.drawable.shuffle),
@ -372,6 +337,126 @@ fun HomeScreen() {
.padding(horizontal = 8.dp, vertical = 8.dp) .padding(horizontal = 8.dp, vertical = 8.dp)
.size(20.dp) .size(20.dp)
) )
Box {
var isSortMenuDisplayed by remember {
mutableStateOf(false)
}
Image(
painter = painterResource(R.drawable.sort),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
isSortMenuDisplayed = true
}
.padding(horizontal = 8.dp, vertical = 8.dp)
.size(20.dp)
)
DropdownMenu(
isDisplayed = isSortMenuDisplayed,
onDismissRequest = {
isSortMenuDisplayed = false
}
) {
@Composable
fun Item(
text: String,
textColor: Color,
backgroundColor: Color,
onClick: () -> Unit
) {
BasicText(
text = text,
style = typography.xxs.copy(color = textColor, letterSpacing = 1.sp),
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.clickable(
indication = rememberRipple(bounded = true),
interactionSource = remember { MutableInteractionSource() },
onClick = {
isSortMenuDisplayed = false
onClick()
}
)
.background(backgroundColor)
.fillMaxWidth()
.widthIn(min = 124.dp, max = 248.dp)
.padding(horizontal = 16.dp, vertical = 8.dp)
)
}
@Composable
fun Item(
text: String,
isSelected: Boolean,
onClick: () -> Unit
) {
Item(
text = text,
textColor = if (isSelected) {
colorPalette.onPrimaryContainer
} else {
colorPalette.textSecondary
},
backgroundColor = if (isSelected) {
colorPalette.primaryContainer
} else {
colorPalette.elevatedBackground
},
onClick = onClick
)
}
Column(
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.background(colorPalette.elevatedBackground)
.width(IntrinsicSize.Max),
) {
Item(
text = "PLAY TIME",
isSelected = preferences.songSortBy == SongSortBy.PlayTime,
onClick = {
preferences.songSortBy = SongSortBy.PlayTime
}
)
Item(
text = "DATE ADDED",
isSelected = preferences.songSortBy == SongSortBy.DateAdded,
onClick = {
preferences.songSortBy = SongSortBy.DateAdded
}
)
}
Spacer(
modifier = Modifier
.height(4.dp)
)
Column(
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.background(colorPalette.elevatedBackground)
.width(IntrinsicSize.Max),
) {
Item(
text = when (preferences.songSortOrder) {
SortOrder.Ascending -> "ASCENDING"
SortOrder.Descending -> "DESCENDING"
},
textColor = colorPalette.text,
backgroundColor = colorPalette.elevatedBackground,
onClick = {
preferences.songSortOrder = !preferences.songSortOrder
}
)
}
}
}
} }
} }
@ -393,15 +478,14 @@ fun HomeScreen() {
) )
}, },
menuContent = { menuContent = {
when (preferences.homePageSongCollection) { when (preferences.songSortBy) {
SongCollection.MostPlayed -> NonQueuedMediaItemMenu(mediaItem = song.asMediaItem) SongSortBy.PlayTime -> NonQueuedMediaItemMenu(mediaItem = song.asMediaItem)
SongCollection.Favorites -> InFavoritesMediaItemMenu(song = song) SongSortBy.DateAdded -> InHistoryMediaItemMenu(song = song)
SongCollection.History -> InHistoryMediaItemMenu(song = song)
} }
}, },
onThumbnailContent = { onThumbnailContent = {
AnimatedVisibility( AnimatedVisibility(
visible = preferences.homePageSongCollection == SongCollection.MostPlayed, visible = preferences.songSortBy == SongSortBy.PlayTime,
enter = fadeIn(), enter = fadeIn(),
exit = fadeOut(), exit = fadeOut(),
modifier = Modifier modifier = Modifier

View file

@ -6,19 +6,18 @@ import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.core.content.edit import androidx.core.content.edit
import androidx.media3.common.Player import androidx.media3.common.Player
import it.vfsfitvnm.vimusic.enums.ColorPaletteMode import it.vfsfitvnm.vimusic.enums.*
import it.vfsfitvnm.vimusic.enums.SongCollection
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
import it.vfsfitvnm.youtubemusic.YouTube import it.vfsfitvnm.youtubemusic.YouTube
@Stable @Stable
class Preferences( class Preferences(
private val edit: (action: SharedPreferences.Editor.() -> Unit) -> Unit, private val edit: (action: SharedPreferences.Editor.() -> Unit) -> Unit,
initialSongSortBy: SongSortBy,
initialSongSortOrder: SortOrder,
initialColorPaletteMode: ColorPaletteMode, initialColorPaletteMode: ColorPaletteMode,
initialSearchFilter: String, initialSearchFilter: String,
initialRepeatMode: Int, initialRepeatMode: Int,
initialHomePageSongCollection: SongCollection,
initialThumbnailRoundness: ThumbnailRoundness, initialThumbnailRoundness: ThumbnailRoundness,
initialCoilDiskCacheMaxSizeBytes: Long, initialCoilDiskCacheMaxSizeBytes: Long,
initialExoPlayerDiskCacheMaxSizeBytes: Long, initialExoPlayerDiskCacheMaxSizeBytes: Long,
@ -30,10 +29,11 @@ class Preferences(
edit = { action: SharedPreferences.Editor.() -> Unit -> edit = { action: SharedPreferences.Editor.() -> Unit ->
preferences.edit(action = action) preferences.edit(action = action)
}, },
initialSongSortBy = preferences.getEnum(Keys.songSortBy, SongSortBy.DateAdded),
initialSongSortOrder = preferences.getEnum(Keys.songSortOrder, SortOrder.Descending),
initialColorPaletteMode = preferences.getEnum(Keys.colorPaletteMode, ColorPaletteMode.System), initialColorPaletteMode = preferences.getEnum(Keys.colorPaletteMode, ColorPaletteMode.System),
initialSearchFilter = preferences.getString(Keys.searchFilter, YouTube.Item.Song.Filter.value)!!, initialSearchFilter = preferences.getString(Keys.searchFilter, YouTube.Item.Song.Filter.value)!!,
initialRepeatMode = preferences.getInt(Keys.repeatMode, Player.REPEAT_MODE_OFF), initialRepeatMode = preferences.getInt(Keys.repeatMode, Player.REPEAT_MODE_OFF),
initialHomePageSongCollection = preferences.getEnum(Keys.homePageSongCollection, SongCollection.History),
initialThumbnailRoundness = preferences.getEnum(Keys.thumbnailRoundness, ThumbnailRoundness.Light), initialThumbnailRoundness = preferences.getEnum(Keys.thumbnailRoundness, ThumbnailRoundness.Light),
initialCoilDiskCacheMaxSizeBytes = preferences.getLong(Keys.coilDiskCacheMaxSizeBytes, 512L * 1024 * 1024), initialCoilDiskCacheMaxSizeBytes = preferences.getLong(Keys.coilDiskCacheMaxSizeBytes, 512L * 1024 * 1024),
initialExoPlayerDiskCacheMaxSizeBytes = preferences.getLong(Keys.exoPlayerDiskCacheMaxSizeBytes, 512L * 1024 * 1024), initialExoPlayerDiskCacheMaxSizeBytes = preferences.getLong(Keys.exoPlayerDiskCacheMaxSizeBytes, 512L * 1024 * 1024),
@ -42,6 +42,12 @@ class Preferences(
initialPersistentQueue = preferences.getBoolean(Keys.persistentQueue, false) initialPersistentQueue = preferences.getBoolean(Keys.persistentQueue, false)
) )
var songSortBy = initialSongSortBy
set(value) = edit { putEnum(Keys.songSortBy, value) }
var songSortOrder = initialSongSortOrder
set(value) = edit { putEnum(Keys.songSortOrder, value) }
var colorPaletteMode = initialColorPaletteMode var colorPaletteMode = initialColorPaletteMode
set(value) = edit { putEnum(Keys.colorPaletteMode, value) } set(value) = edit { putEnum(Keys.colorPaletteMode, value) }
@ -51,9 +57,6 @@ class Preferences(
var repeatMode = initialRepeatMode var repeatMode = initialRepeatMode
set(value) = edit { putInt(Keys.repeatMode, value) } set(value) = edit { putInt(Keys.repeatMode, value) }
var homePageSongCollection = initialHomePageSongCollection
set(value) = edit { putEnum(Keys.homePageSongCollection, value) }
var thumbnailRoundness = initialThumbnailRoundness var thumbnailRoundness = initialThumbnailRoundness
set(value) = edit { putEnum(Keys.thumbnailRoundness, value) } set(value) = edit { putEnum(Keys.thumbnailRoundness, value) }
@ -73,10 +76,11 @@ class Preferences(
set(value) = edit { putBoolean(Keys.persistentQueue, value) } set(value) = edit { putBoolean(Keys.persistentQueue, value) }
object Keys { object Keys {
const val songSortOrder = "songSortOrder"
const val songSortBy = "songSortBy"
const val colorPaletteMode = "colorPaletteMode" const val colorPaletteMode = "colorPaletteMode"
const val searchFilter = "searchFilter" const val searchFilter = "searchFilter"
const val repeatMode = "repeatMode" const val repeatMode = "repeatMode"
const val homePageSongCollection = "homePageSongCollection"
const val thumbnailRoundness = "thumbnailRoundness" const val thumbnailRoundness = "thumbnailRoundness"
const val coilDiskCacheMaxSizeBytes = "coilDiskCacheMaxSizeBytes" const val coilDiskCacheMaxSizeBytes = "coilDiskCacheMaxSizeBytes"
const val exoPlayerDiskCacheMaxSizeBytes = "exoPlayerDiskCacheMaxSizeBytes" const val exoPlayerDiskCacheMaxSizeBytes = "exoPlayerDiskCacheMaxSizeBytes"

View file

@ -0,0 +1,13 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M416,128l-224,256l-96,-96"
android:strokeLineJoin="round"
android:strokeWidth="32"
android:fillColor="#00000000"
android:strokeColor="#000"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:fillColor="#FF000000"
android:pathData="M472,168H40a24,24 0,0 1,0 -48H472a24,24 0,0 1,0 48Z"/>
<path
android:fillColor="#FF000000"
android:pathData="M312,280H40a24,24 0,0 1,0 -48h272a24,24 0,0 1,0 48z"/>
<path
android:fillColor="#FF000000"
android:pathData="M120,392H40a24,24 0,0 1,0 -48h80a24,24 0,0 1,0 48z"/>
</vector>