Sync with master
This commit is contained in:
parent
1e719b33ed
commit
5319c4094b
12 changed files with 1492 additions and 457 deletions
|
@ -11,12 +11,12 @@ import androidx.room.migration.AutoMigrationSpec
|
|||
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.enums.*
|
||||
import it.vfsfitvnm.vimusic.models.*
|
||||
import it.vfsfitvnm.vimusic.utils.getFloatOrNull
|
||||
import it.vfsfitvnm.vimusic.utils.getLongOrNull
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
|
||||
@Dao
|
||||
|
@ -31,6 +31,14 @@ interface Database {
|
|||
@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 title ASC")
|
||||
fun songsByTitleAsc(): Flow<List<DetailedSong>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY title DESC")
|
||||
fun songsByTitleDesc(): Flow<List<DetailedSong>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM Song WHERE totalPlayTimeMs > 0 ORDER BY totalPlayTimeMs ASC")
|
||||
fun songsByPlayTimeAsc(): Flow<List<DetailedSong>>
|
||||
|
@ -45,6 +53,10 @@ interface Database {
|
|||
SortOrder.Ascending -> songsByPlayTimeAsc()
|
||||
SortOrder.Descending -> songsByPlayTimeDesc()
|
||||
}
|
||||
SongSortBy.Title -> when (sortOrder) {
|
||||
SortOrder.Ascending -> songsByTitleAsc()
|
||||
SortOrder.Descending -> songsByTitleDesc()
|
||||
}
|
||||
SongSortBy.DateAdded -> when (sortOrder) {
|
||||
SortOrder.Ascending -> songsByRowIdAsc()
|
||||
SortOrder.Descending -> songsByRowIdDesc()
|
||||
|
@ -71,9 +83,72 @@ interface Database {
|
|||
@Query("SELECT * FROM Artist WHERE id = :id")
|
||||
fun artist(id: String): Flow<Artist?>
|
||||
|
||||
@Query("SELECT * FROM Artist ORDER BY name DESC")
|
||||
fun artistsByNameDesc(): Flow<List<Artist>>
|
||||
|
||||
@Query("SELECT * FROM Artist ORDER BY name ASC")
|
||||
fun artistsByNameAsc(): Flow<List<Artist>>
|
||||
|
||||
@Query("SELECT * FROM Artist ORDER BY ROWID DESC")
|
||||
fun artistsByRowIdDesc(): Flow<List<Artist>>
|
||||
|
||||
@Query("SELECT * FROM Artist ORDER BY ROWID ASC")
|
||||
fun artistsByRowIdAsc(): Flow<List<Artist>>
|
||||
|
||||
fun artists(sortBy: ArtistSortBy, sortOrder: SortOrder): Flow<List<Artist>> {
|
||||
return when (sortBy) {
|
||||
ArtistSortBy.Name -> when (sortOrder) {
|
||||
SortOrder.Ascending -> artistsByNameAsc()
|
||||
SortOrder.Descending -> artistsByNameDesc()
|
||||
}
|
||||
ArtistSortBy.DateAdded -> when (sortOrder) {
|
||||
SortOrder.Ascending -> artistsByRowIdAsc()
|
||||
SortOrder.Descending -> artistsByRowIdDesc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Query("SELECT * FROM Album WHERE id = :id")
|
||||
fun album(id: String): Flow<Album?>
|
||||
|
||||
@Query("SELECT * FROM Album ORDER BY ROWID ASC")
|
||||
fun albums(): Flow<List<Album>>
|
||||
|
||||
@Query("SELECT * FROM Album ORDER BY title ASC")
|
||||
fun albumsByTitleAsc(): Flow<List<Album>>
|
||||
|
||||
@Query("SELECT * FROM Album ORDER BY year ASC")
|
||||
fun albumsByYearAsc(): Flow<List<Album>>
|
||||
|
||||
@Query("SELECT * FROM Album ORDER BY ROWID ASC")
|
||||
fun albumsByRowIdAsc(): Flow<List<Album>>
|
||||
|
||||
@Query("SELECT * FROM Album ORDER BY title DESC")
|
||||
fun albumsByTitleDesc(): Flow<List<Album>>
|
||||
|
||||
@Query("SELECT * FROM Album ORDER BY year DESC")
|
||||
fun albumsByYearDesc(): Flow<List<Album>>
|
||||
|
||||
@Query("SELECT * FROM Album ORDER BY ROWID DESC")
|
||||
fun albumsByRowIdDesc(): Flow<List<Album>>
|
||||
|
||||
fun albums(sortBy: AlbumSortBy, sortOrder: SortOrder): Flow<List<Album>> {
|
||||
return when (sortBy) {
|
||||
AlbumSortBy.Title -> when (sortOrder) {
|
||||
SortOrder.Ascending -> albumsByTitleAsc()
|
||||
SortOrder.Descending -> albumsByTitleDesc()
|
||||
}
|
||||
AlbumSortBy.Year -> when (sortOrder) {
|
||||
SortOrder.Ascending -> albumsByYearAsc()
|
||||
SortOrder.Descending -> albumsByYearDesc()
|
||||
}
|
||||
AlbumSortBy.DateAdded -> when (sortOrder) {
|
||||
SortOrder.Ascending -> albumsByRowIdAsc()
|
||||
SortOrder.Descending -> albumsByRowIdDesc()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Query("UPDATE Song SET totalPlayTimeMs = totalPlayTimeMs + :addition WHERE id = :id")
|
||||
fun incrementTotalPlayTimeMs(id: String, addition: Long)
|
||||
|
||||
|
@ -82,8 +157,29 @@ interface Database {
|
|||
fun playlistWithSongs(id: Long): Flow<PlaylistWithSongs?>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT id, name, (SELECT COUNT(*) FROM SongPlaylistMap WHERE playlistId = id) as songCount FROM Playlist")
|
||||
fun playlistPreviews(): Flow<List<PlaylistPreview>>
|
||||
@Query("SELECT id, name, (SELECT COUNT(*) FROM SongPlaylistMap WHERE playlistId = id) as songCount FROM Playlist ORDER BY name ASC")
|
||||
fun playlistPreviewsByName(): Flow<List<PlaylistPreview>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT id, name, (SELECT COUNT(*) FROM SongPlaylistMap WHERE playlistId = id) as songCount FROM Playlist ORDER BY ROWID ASC")
|
||||
fun playlistPreviewsByDateAdded(): Flow<List<PlaylistPreview>>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT id, name, (SELECT COUNT(*) FROM SongPlaylistMap WHERE playlistId = id) as songCount FROM Playlist ORDER BY songCount ASC")
|
||||
fun playlistPreviewsByDateSongCount(): Flow<List<PlaylistPreview>>
|
||||
|
||||
fun playlistPreviews(sortBy: PlaylistSortBy, sortOrder: SortOrder): Flow<List<PlaylistPreview>> {
|
||||
return when (sortBy) {
|
||||
PlaylistSortBy.Name -> playlistPreviewsByName()
|
||||
PlaylistSortBy.DateAdded -> playlistPreviewsByDateAdded()
|
||||
PlaylistSortBy.SongCount -> playlistPreviewsByDateSongCount()
|
||||
}.map {
|
||||
when (sortOrder) {
|
||||
SortOrder.Ascending -> it
|
||||
SortOrder.Descending -> it.reversed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Query("SELECT thumbnailUrl FROM Song JOIN SongPlaylistMap ON id = songId WHERE playlistId = :id ORDER BY position LIMIT 4")
|
||||
fun playlistThumbnailUrls(id: Long): Flow<List<String?>>
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
enum class AlbumSortBy {
|
||||
Title,
|
||||
Year,
|
||||
DateAdded
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
enum class ArtistSortBy {
|
||||
Name,
|
||||
DateAdded
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package it.vfsfitvnm.vimusic.enums
|
||||
|
||||
enum class PlaylistSortBy {
|
||||
Name,
|
||||
DateAdded,
|
||||
SongCount
|
||||
}
|
|
@ -2,5 +2,6 @@ package it.vfsfitvnm.vimusic.enums
|
|||
|
||||
enum class SongSortBy {
|
||||
PlayTime,
|
||||
Title,
|
||||
DateAdded
|
||||
}
|
||||
|
|
|
@ -23,10 +23,11 @@ import kotlin.math.absoluteValue
|
|||
class TabPagerState(
|
||||
val pageCount: Int,
|
||||
val initialPageIndex: Int,
|
||||
val onPageChanged: ((Int) -> Unit)?
|
||||
) {
|
||||
var pageIndex by mutableStateOf(initialPageIndex)
|
||||
|
||||
var tempPageIndex: Int? = null
|
||||
var tempPageIndex by mutableStateOf<Int?>(null)
|
||||
|
||||
val animatable = Animatable(0f)
|
||||
|
||||
|
@ -57,15 +58,17 @@ class TabPagerState(
|
|||
pageIndex = newPageIndex
|
||||
animatable.snapTo(0f)
|
||||
tempPageIndex = null
|
||||
onPageChanged?.invoke(newPageIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberTabPagerState(initialPageIndex: Int, pageCount: Int): TabPagerState {
|
||||
fun rememberTabPagerState(initialPageIndex: Int, pageCount: Int, onPageChanged: ((Int) -> Unit)? = null): TabPagerState {
|
||||
return remember {
|
||||
TabPagerState(
|
||||
pageCount = pageCount,
|
||||
initialPageIndex = initialPageIndex,
|
||||
onPageChanged = onPageChanged
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +125,7 @@ fun HorizontalTabPager(
|
|||
.plus(1)
|
||||
.coerceAtMost(state.pageCount - 1)
|
||||
state.animatable.snapTo(0f)
|
||||
state.onPageChanged?.invoke(state.pageIndex)
|
||||
}
|
||||
} else {
|
||||
state.animatable.animateTo(
|
||||
|
@ -133,6 +137,7 @@ fun HorizontalTabPager(
|
|||
.minus(1)
|
||||
.coerceAtLeast(0)
|
||||
state.animatable.snapTo(0f)
|
||||
state.onPageChanged?.invoke(state.pageIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +147,7 @@ fun HorizontalTabPager(
|
|||
) { constraints ->
|
||||
val previousPlaceable = state.offset.takeIf { it < 0 }?.let {
|
||||
(state.tempPageIndex ?: (state.pageIndex - 1)).takeIf { it >= 0 }?.let { index ->
|
||||
measure(index, constraints).first()
|
||||
measure(index, constraints).firstOrNull()
|
||||
}
|
||||
}
|
||||
val placeable = measure(state.pageIndex, constraints).first()
|
||||
|
@ -150,7 +155,7 @@ fun HorizontalTabPager(
|
|||
val nextPlaceable = state.offset.takeIf { it > 0 }?.let {
|
||||
(state.tempPageIndex ?: (state.pageIndex + 1)).takeIf { it < state.pageCount }
|
||||
?.let { index ->
|
||||
measure(index, constraints).first()
|
||||
measure(index, constraints).firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
package it.vfsfitvnm.vimusic.ui.components
|
||||
|
||||
import androidx.compose.animation.core.animateIntAsState
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||
|
||||
data class TabPosition(
|
||||
val left: Int,
|
||||
val width: Int
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun TabRow(
|
||||
tabPagerState: TabPagerState,
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val (colorPalette) = LocalAppearance.current
|
||||
|
||||
var tabPositions by remember {
|
||||
mutableStateOf<List<TabPosition>?>(null)
|
||||
}
|
||||
|
||||
val indicatorWidth by animateIntAsState(
|
||||
targetValue = (tabPositions?.getOrNull(tabPagerState.transitioningIndex)?.width ?: 0)
|
||||
)
|
||||
|
||||
val indicatorStart by animateIntAsState(
|
||||
targetValue = (tabPositions?.getOrNull(tabPagerState.transitioningIndex)?.left ?: 0)
|
||||
)
|
||||
|
||||
Layout(
|
||||
modifier = modifier
|
||||
.drawBehind {
|
||||
if (indicatorWidth == 0) return@drawBehind
|
||||
|
||||
drawLine(
|
||||
color = colorPalette.primaryContainer,
|
||||
start = Offset(x = indicatorStart + 16.dp.toPx(), y = size.height),
|
||||
end = Offset(
|
||||
x = indicatorStart + indicatorWidth - 16.dp.toPx(),
|
||||
y = size.height
|
||||
),
|
||||
cap = StrokeCap.Round,
|
||||
strokeWidth = 3.dp.toPx()
|
||||
)
|
||||
},
|
||||
content = content
|
||||
) { measurables, constraints ->
|
||||
val placeables = measurables.map {
|
||||
it.measure(constraints)
|
||||
}
|
||||
|
||||
if (tabPositions == null) {
|
||||
var x = 0
|
||||
|
||||
tabPositions = placeables.map { placeable ->
|
||||
TabPosition(
|
||||
left = x,
|
||||
width = placeable.width
|
||||
).also {
|
||||
x += placeable.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout(constraints.maxWidth, placeables.maxOf(Placeable::height)) {
|
||||
var x = 0
|
||||
placeables.fastForEach { placeable ->
|
||||
placeable.place(x = x, y = constraints.minHeight / 2)
|
||||
x += placeable.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline val TabPagerState.transitioningIndex: Int
|
||||
get() = tempPageIndex ?: pageIndex
|
|
@ -22,6 +22,8 @@ import it.vfsfitvnm.route.RouteHandler
|
|||
import it.vfsfitvnm.route.empty
|
||||
import it.vfsfitvnm.vimusic.*
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.enums.PlaylistSortBy
|
||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||
import it.vfsfitvnm.vimusic.models.Playlist
|
||||
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
||||
|
@ -276,7 +278,7 @@ fun MediaItemMenu(
|
|||
onGlobalRouteEmitted: (() -> Unit)? = null,
|
||||
) {
|
||||
val playlistPreviews by remember {
|
||||
Database.playlistPreviews()
|
||||
Database.playlistPreviews(PlaylistSortBy.DateAdded, SortOrder.Descending)
|
||||
}.collectAsState(initial = emptyList(), context = Dispatchers.IO)
|
||||
|
||||
val viewPlaylistsRoute = rememberCreatePlaylistRoute()
|
||||
|
|
|
@ -71,7 +71,7 @@ fun BuiltInPlaylistScreen(
|
|||
when (builtInPlaylist) {
|
||||
BuiltInPlaylist.Favorites -> Database.favorites()
|
||||
BuiltInPlaylist.Cached -> Database.songsByRowIdDesc().map { songs ->
|
||||
songs.filter { song ->
|
||||
songs.reversed().filter { song ->
|
||||
song.song.contentLength?.let { contentLength ->
|
||||
binder?.cache?.isCached(song.song.id, 0, contentLength)
|
||||
} ?: false
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,9 @@
|
|||
package it.vfsfitvnm.vimusic.ui.views
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
|
@ -12,23 +11,27 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
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.dp
|
||||
import coil.compose.AsyncImage
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
||||
import it.vfsfitvnm.vimusic.models.PlaylistPreview
|
||||
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||
import it.vfsfitvnm.vimusic.utils.color
|
||||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import it.vfsfitvnm.vimusic.utils.thumbnail
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
@Composable
|
||||
|
@ -36,66 +39,131 @@ fun PlaylistPreviewItem(
|
|||
playlistPreview: PlaylistPreview,
|
||||
modifier: Modifier = Modifier,
|
||||
thumbnailSize: Dp = Dimensions.thumbnails.song,
|
||||
trailingContent: (@Composable () -> Unit)? = null
|
||||
) {
|
||||
val (colorPalette, typography) = LocalAppearance.current
|
||||
val density = LocalDensity.current
|
||||
|
||||
val thumbnailSizePx = density.run {
|
||||
thumbnailSize.toPx().toInt()
|
||||
thumbnailSize.toPx().roundToInt()
|
||||
}
|
||||
|
||||
val thumbnails by remember(playlistPreview.playlist.id) {
|
||||
Database.playlistThumbnailUrls(playlistPreview.playlist.id).distinctUntilChanged()
|
||||
}.collectAsState(initial = emptyList(), context = Dispatchers.IO)
|
||||
|
||||
Box(
|
||||
modifier = modifier
|
||||
.background(colorPalette.lightBackground)
|
||||
.size(thumbnailSize * 2)
|
||||
) {
|
||||
if (thumbnails.toSet().size == 1) {
|
||||
AsyncImage(
|
||||
model = thumbnails.first().thumbnail(thumbnailSizePx * 2),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.size(thumbnailSize * 2)
|
||||
)
|
||||
} else {
|
||||
listOf(
|
||||
Alignment.TopStart,
|
||||
Alignment.TopEnd,
|
||||
Alignment.BottomStart,
|
||||
Alignment.BottomEnd
|
||||
).forEachIndexed { index, alignment ->
|
||||
PlaylistItem(
|
||||
name = playlistPreview.playlist.name,
|
||||
modifier = modifier,
|
||||
thumbnailSize = thumbnailSize,
|
||||
trailingContent = trailingContent,
|
||||
songCount = playlistPreview.songCount,
|
||||
imageContent = {
|
||||
if (thumbnails.toSet().size == 1) {
|
||||
AsyncImage(
|
||||
model = thumbnails.getOrNull(index).thumbnail(thumbnailSizePx),
|
||||
model = thumbnails.first().thumbnail(thumbnailSizePx),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.align(alignment)
|
||||
.size(thumbnailSize)
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(thumbnailSize)
|
||||
) {
|
||||
listOf(
|
||||
Alignment.TopStart,
|
||||
Alignment.TopEnd,
|
||||
Alignment.BottomStart,
|
||||
Alignment.BottomEnd
|
||||
).forEachIndexed { index, alignment ->
|
||||
AsyncImage(
|
||||
model = thumbnails.getOrNull(index).thumbnail(thumbnailSizePx),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.align(alignment)
|
||||
.size(thumbnailSize / 2)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BuiltInPlaylistItem(
|
||||
@DrawableRes icon: Int,
|
||||
colorTint: Color,
|
||||
name: String,
|
||||
modifier: Modifier = Modifier,
|
||||
thumbnailSize: Dp = Dimensions.thumbnails.song
|
||||
) {
|
||||
PlaylistItem(
|
||||
name = name,
|
||||
modifier = modifier,
|
||||
thumbnailSize = thumbnailSize,
|
||||
songCount = null,
|
||||
imageContent = {
|
||||
Image(
|
||||
painter = painterResource(icon),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(colorTint),
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(18.dp)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlaylistItem(
|
||||
name: String,
|
||||
songCount: Int?,
|
||||
modifier: Modifier = Modifier,
|
||||
thumbnailSize: Dp = Dimensions.thumbnails.song,
|
||||
trailingContent: (@Composable () -> Unit)? = null,
|
||||
imageContent: @Composable BoxScope.() -> Unit
|
||||
) {
|
||||
val (colorPalette, typography) = LocalAppearance.current
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 5.dp)
|
||||
.padding(start = 16.dp, end = if (trailingContent == null) 16.dp else 8.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(ThumbnailRoundness.shape)
|
||||
.background(colorPalette.lightBackground)
|
||||
.size(thumbnailSize),
|
||||
content = imageContent
|
||||
)
|
||||
|
||||
BasicText(
|
||||
text = playlistPreview.playlist.name,
|
||||
style = typography.xxs.semiBold.color(Color.White),
|
||||
text = name,
|
||||
style = typography.xs.semiBold,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.BottomStart)
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
Color.Black.copy(alpha = 0.75f)
|
||||
)
|
||||
)
|
||||
)
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
.weight(1f)
|
||||
)
|
||||
|
||||
songCount?.let {
|
||||
BasicText(
|
||||
text = "$songCount song${if (songCount == 1) "" else "s"}",
|
||||
style = typography.xxs.secondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
trailingContent?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import androidx.core.content.edit
|
|||
|
||||
|
||||
const val colorPaletteModeKey = "colorPaletteMode"
|
||||
const val homeScreenPageIndexKey = "homeScreenPageIndex"
|
||||
const val thumbnailRoundnessKey = "thumbnailRoundness"
|
||||
const val isCachedPlaylistShownKey = "isCachedPlaylistShown"
|
||||
const val coilDiskCacheMaxSizeKey = "coilDiskCacheMaxSize"
|
||||
|
@ -52,6 +53,16 @@ fun rememberPreference(key: String, defaultValue: Boolean): MutableState<Boolean
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberPreference(key: String, defaultValue: Int): MutableState<Int> {
|
||||
val context = LocalContext.current
|
||||
return remember {
|
||||
mutableStatePreferenceOf(context.preferences.getInt(key, defaultValue)) {
|
||||
context.preferences.edit { putInt(key, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberPreference(key: String, defaultValue: String): MutableState<String> {
|
||||
val context = LocalContext.current
|
||||
|
|
Loading…
Add table
Reference in a new issue