Drop home page song collections in favor of a sort feature (#11)
This commit is contained in:
parent
ae00f8ea3d
commit
59b6c61bb2
9 changed files with 418 additions and 74 deletions
|
@ -8,9 +8,11 @@ import android.os.Parcel
|
|||
import androidx.media3.common.MediaItem
|
||||
import androidx.room.*
|
||||
import androidx.room.migration.AutoMigrationSpec
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
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.utils.getFloatOrNull
|
||||
import it.vfsfitvnm.vimusic.utils.getLongOrNull
|
||||
|
@ -21,6 +23,35 @@ import kotlinx.coroutines.flow.Flow
|
|||
interface 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
|
||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY ROWID DESC")
|
||||
fun history(): Flow<List<DetailedSong>>
|
||||
|
@ -29,10 +60,6 @@ interface Database {
|
|||
@Query("SELECT * FROM Song WHERE likedAt IS NOT NULL ORDER BY likedAt DESC")
|
||||
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")
|
||||
fun queue(): List<QueuedMediaItem>
|
||||
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
enum class SongCollection {
|
||||
MostPlayed,
|
||||
Favorites,
|
||||
History
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
enum class SongSortBy {
|
||||
PlayTime,
|
||||
DateAdded
|
||||
}
|
11
app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/SortOrder.kt
Normal file
11
app/src/main/kotlin/it/vfsfitvnm/vimusic/enums/SortOrder.kt
Normal file
|
@ -0,0 +1,11 @@
|
|||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
enum class SortOrder {
|
||||
Ascending,
|
||||
Descending;
|
||||
|
||||
operator fun not() = when (this) {
|
||||
Ascending -> Descending
|
||||
Descending -> Ascending
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -14,12 +14,14 @@ import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
|
|||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.compose.ui.zIndex
|
||||
import it.vfsfitvnm.route.RouteHandler
|
||||
import it.vfsfitvnm.route.fastFade
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||
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.models.DetailedSong
|
||||
import it.vfsfitvnm.vimusic.models.Playlist
|
||||
import it.vfsfitvnm.vimusic.models.SearchQuery
|
||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||
import it.vfsfitvnm.vimusic.query
|
||||
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.NonQueuedMediaItemMenu
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog
|
||||
|
@ -75,12 +79,8 @@ fun HomeScreen() {
|
|||
|
||||
val preferences = LocalPreferences.current
|
||||
|
||||
val songCollection by remember(preferences.homePageSongCollection) {
|
||||
when (preferences.homePageSongCollection) {
|
||||
SongCollection.MostPlayed -> Database.mostPlayed()
|
||||
SongCollection.Favorites -> Database.favorites()
|
||||
SongCollection.History -> Database.history()
|
||||
}
|
||||
val songCollection by remember(preferences.songSortBy, preferences.songSortOrder) {
|
||||
Database.songs(preferences.songSortBy, preferences.songSortOrder)
|
||||
}.collectAsState(initial = emptyList(), context = Dispatchers.IO)
|
||||
|
||||
RouteHandler(
|
||||
|
@ -313,48 +313,13 @@ fun HomeScreen() {
|
|||
.padding(horizontal = 8.dp)
|
||||
.padding(top = 32.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
BasicText(
|
||||
text = "Songs",
|
||||
style = typography.m.semiBold,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.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(
|
||||
painter = painterResource(R.drawable.shuffle),
|
||||
|
@ -372,6 +337,126 @@ fun HomeScreen() {
|
|||
.padding(horizontal = 8.dp, vertical = 8.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 = {
|
||||
when (preferences.homePageSongCollection) {
|
||||
SongCollection.MostPlayed -> NonQueuedMediaItemMenu(mediaItem = song.asMediaItem)
|
||||
SongCollection.Favorites -> InFavoritesMediaItemMenu(song = song)
|
||||
SongCollection.History -> InHistoryMediaItemMenu(song = song)
|
||||
when (preferences.songSortBy) {
|
||||
SongSortBy.PlayTime -> NonQueuedMediaItemMenu(mediaItem = song.asMediaItem)
|
||||
SongSortBy.DateAdded -> InHistoryMediaItemMenu(song = song)
|
||||
}
|
||||
},
|
||||
onThumbnailContent = {
|
||||
AnimatedVisibility(
|
||||
visible = preferences.homePageSongCollection == SongCollection.MostPlayed,
|
||||
visible = preferences.songSortBy == SongSortBy.PlayTime,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut(),
|
||||
modifier = Modifier
|
||||
|
|
|
@ -6,19 +6,18 @@ import androidx.compose.runtime.*
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.core.content.edit
|
||||
import androidx.media3.common.Player
|
||||
import it.vfsfitvnm.vimusic.enums.ColorPaletteMode
|
||||
import it.vfsfitvnm.vimusic.enums.SongCollection
|
||||
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
||||
import it.vfsfitvnm.vimusic.enums.*
|
||||
import it.vfsfitvnm.youtubemusic.YouTube
|
||||
|
||||
|
||||
@Stable
|
||||
class Preferences(
|
||||
private val edit: (action: SharedPreferences.Editor.() -> Unit) -> Unit,
|
||||
initialSongSortBy: SongSortBy,
|
||||
initialSongSortOrder: SortOrder,
|
||||
initialColorPaletteMode: ColorPaletteMode,
|
||||
initialSearchFilter: String,
|
||||
initialRepeatMode: Int,
|
||||
initialHomePageSongCollection: SongCollection,
|
||||
initialThumbnailRoundness: ThumbnailRoundness,
|
||||
initialCoilDiskCacheMaxSizeBytes: Long,
|
||||
initialExoPlayerDiskCacheMaxSizeBytes: Long,
|
||||
|
@ -30,10 +29,11 @@ class Preferences(
|
|||
edit = { action: SharedPreferences.Editor.() -> Unit ->
|
||||
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),
|
||||
initialSearchFilter = preferences.getString(Keys.searchFilter, YouTube.Item.Song.Filter.value)!!,
|
||||
initialRepeatMode = preferences.getInt(Keys.repeatMode, Player.REPEAT_MODE_OFF),
|
||||
initialHomePageSongCollection = preferences.getEnum(Keys.homePageSongCollection, SongCollection.History),
|
||||
initialThumbnailRoundness = preferences.getEnum(Keys.thumbnailRoundness, ThumbnailRoundness.Light),
|
||||
initialCoilDiskCacheMaxSizeBytes = preferences.getLong(Keys.coilDiskCacheMaxSizeBytes, 512L * 1024 * 1024),
|
||||
initialExoPlayerDiskCacheMaxSizeBytes = preferences.getLong(Keys.exoPlayerDiskCacheMaxSizeBytes, 512L * 1024 * 1024),
|
||||
|
@ -42,6 +42,12 @@ class Preferences(
|
|||
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
|
||||
set(value) = edit { putEnum(Keys.colorPaletteMode, value) }
|
||||
|
||||
|
@ -51,9 +57,6 @@ class Preferences(
|
|||
var repeatMode = initialRepeatMode
|
||||
set(value) = edit { putInt(Keys.repeatMode, value) }
|
||||
|
||||
var homePageSongCollection = initialHomePageSongCollection
|
||||
set(value) = edit { putEnum(Keys.homePageSongCollection, value) }
|
||||
|
||||
var thumbnailRoundness = initialThumbnailRoundness
|
||||
set(value) = edit { putEnum(Keys.thumbnailRoundness, value) }
|
||||
|
||||
|
@ -73,10 +76,11 @@ class Preferences(
|
|||
set(value) = edit { putBoolean(Keys.persistentQueue, value) }
|
||||
|
||||
object Keys {
|
||||
const val songSortOrder = "songSortOrder"
|
||||
const val songSortBy = "songSortBy"
|
||||
const val colorPaletteMode = "colorPaletteMode"
|
||||
const val searchFilter = "searchFilter"
|
||||
const val repeatMode = "repeatMode"
|
||||
const val homePageSongCollection = "homePageSongCollection"
|
||||
const val thumbnailRoundness = "thumbnailRoundness"
|
||||
const val coilDiskCacheMaxSizeBytes = "coilDiskCacheMaxSizeBytes"
|
||||
const val exoPlayerDiskCacheMaxSizeBytes = "exoPlayerDiskCacheMaxSizeBytes"
|
||||
|
|
13
app/src/main/res/drawable/checkmark.xml
Normal file
13
app/src/main/res/drawable/checkmark.xml
Normal 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>
|
15
app/src/main/res/drawable/sort.xml
Normal file
15
app/src/main/res/drawable/sort.xml
Normal 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>
|
Loading…
Add table
Reference in a new issue