Tweak PlaylistScreen, LocalPlaylistScreen and BuiltInPlaylistScreen UI

This commit is contained in:
vfsfitvnm
2022-07-27 11:55:29 +02:00
parent 910a17e3d0
commit 4ec94589ca
4 changed files with 242 additions and 336 deletions

View File

@@ -192,7 +192,6 @@ fun AlbumScreen(browseId: String) {
.fillMaxWidth() .fillMaxWidth()
.zIndex(1f) .zIndex(1f)
.padding(horizontal = 8.dp) .padding(horizontal = 8.dp)
// .padding(top = 8.dp)
) { ) {
Image( Image(
painter = painterResource(R.drawable.shuffle), painter = painterResource(R.drawable.shuffle),

View File

@@ -9,13 +9,13 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
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.CircleShape
import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -23,10 +23,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
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.shadow
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
@@ -102,6 +102,55 @@ fun BuiltInPlaylistScreen(builtInPlaylist: BuiltInPlaylist) {
.padding(vertical = 8.dp, horizontal = 16.dp) .padding(vertical = 8.dp, horizontal = 16.dp)
.size(24.dp) .size(24.dp)
) )
}
}
item {
Column(
modifier = Modifier
.padding(top = 16.dp, bottom = 8.dp)
.padding(horizontal = 16.dp)
) {
BasicText(
text = when (builtInPlaylist) {
BuiltInPlaylist.Favorites -> "Favorites"
BuiltInPlaylist.Cached -> "Offline"
},
style = typography.m.semiBold
)
BasicText(
text = "${songs.size} songs",
style = typography.xxs.semiBold.secondary
)
}
}
item {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
.zIndex(1f)
.padding(horizontal = 8.dp)
) {
Image(
painter = painterResource(R.drawable.shuffle),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable(enabled = songs.isNotEmpty()) {
binder?.stopRadio()
binder?.player?.forcePlayFromBeginning(
songs
.shuffled()
.map(DetailedSong::asMediaItem)
)
}
.padding(horizontal = 8.dp, vertical = 8.dp)
.size(20.dp)
)
Image( Image(
painter = painterResource(R.drawable.ellipsis_horizontal), painter = painterResource(R.drawable.ellipsis_horizontal),
@@ -123,90 +172,12 @@ fun BuiltInPlaylistScreen(builtInPlaylist: BuiltInPlaylist) {
} }
} }
} }
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(horizontal = 8.dp, vertical = 8.dp)
.size(24.dp) .size(20.dp)
) )
} }
} }
item {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.padding(top = 16.dp, bottom = 32.dp)
) {
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 16.dp)
) {
BasicText(
text = when (builtInPlaylist) {
BuiltInPlaylist.Favorites -> "Favorites"
BuiltInPlaylist.Cached -> "Offline"
},
style = typography.m.semiBold
)
BasicText(
text = "${songs.size} songs",
style = typography.xxs.semiBold.secondary
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.padding(horizontal = 16.dp)
) {
Image(
painter = painterResource(R.drawable.shuffle),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
binder?.stopRadio()
binder?.player?.forcePlayFromBeginning(
songs
.map(DetailedSong::asMediaItem)
.shuffled()
)
}
.shadow(elevation = 2.dp, shape = CircleShape)
.background(
color = colorPalette.elevatedBackground,
shape = CircleShape
)
.padding(horizontal = 16.dp, vertical = 16.dp)
.size(20.dp)
)
Image(
painter = painterResource(R.drawable.play),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
binder?.stopRadio()
binder?.player?.forcePlayFromBeginning(
songs.map(
DetailedSong::asMediaItem
)
)
}
.shadow(elevation = 2.dp, shape = CircleShape)
.background(
color = colorPalette.elevatedBackground,
shape = CircleShape
)
.padding(horizontal = 16.dp, vertical = 16.dp)
.size(20.dp)
)
}
}
}
itemsIndexed( itemsIndexed(
items = songs, items = songs,
key = { _, song -> song.song.id }, key = { _, song -> song.song.id },

View File

@@ -9,13 +9,13 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
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.CircleShape
import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -26,12 +26,12 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
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.shadow
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import it.vfsfitvnm.reordering.rememberReorderingState import it.vfsfitvnm.reordering.rememberReorderingState
import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder import it.vfsfitvnm.reordering.verticalDragAfterLongPressToReorder
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
@@ -145,6 +145,52 @@ fun LocalPlaylistScreen(playlistId: Long) {
.padding(vertical = 8.dp, horizontal = 16.dp) .padding(vertical = 8.dp, horizontal = 16.dp)
.size(24.dp) .size(24.dp)
) )
}
}
item {
Column(
modifier = Modifier
.padding(top = 16.dp, bottom = 8.dp)
.padding(horizontal = 16.dp)
) {
BasicText(
text = playlistWithSongs.playlist.name,
style = typography.m.semiBold
)
BasicText(
text = "${playlistWithSongs.songs.size} songs",
style = typography.xxs.semiBold.secondary
)
}
}
item {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
.zIndex(1f)
.padding(horizontal = 8.dp)
) {
Image(
painter = painterResource(R.drawable.shuffle),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable(enabled = playlistWithSongs.songs.isNotEmpty()) {
binder?.stopRadio()
binder?.player?.forcePlayFromBeginning(
playlistWithSongs.songs
.shuffled()
.map(DetailedSong::asMediaItem)
)
}
.padding(horizontal = 8.dp, vertical = 8.dp)
.size(20.dp)
)
Image( Image(
painter = painterResource(R.drawable.ellipsis_horizontal), painter = painterResource(R.drawable.ellipsis_horizontal),
@@ -188,89 +234,12 @@ fun LocalPlaylistScreen(playlistId: Long) {
} }
} }
} }
.padding(horizontal = 16.dp, vertical = 8.dp) .padding(horizontal = 8.dp, vertical = 8.dp)
.size(24.dp) .size(20.dp)
) )
} }
} }
item {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.padding(top = 16.dp, bottom = 32.dp)
) {
Column(
modifier = Modifier
.weight(1f)
.padding(horizontal = 16.dp)
) {
BasicText(
text = playlistWithSongs.playlist.name,
style = typography.m.semiBold
)
BasicText(
text = "${playlistWithSongs.songs.size} songs",
style = typography.xxs.semiBold.secondary
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.padding(horizontal = 16.dp)
) {
Image(
painter = painterResource(R.drawable.shuffle),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
binder?.stopRadio()
binder?.player?.forcePlayFromBeginning(
playlistWithSongs.songs
.map(
DetailedSong::asMediaItem
)
.shuffled()
)
}
.shadow(elevation = 2.dp, shape = CircleShape)
.background(
color = colorPalette.elevatedBackground,
shape = CircleShape
)
.padding(horizontal = 16.dp, vertical = 16.dp)
.size(20.dp)
)
Image(
painter = painterResource(R.drawable.play),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
binder?.stopRadio()
binder?.player?.forcePlayFromBeginning(
playlistWithSongs.songs.map(
DetailedSong::asMediaItem
)
)
}
.shadow(elevation = 2.dp, shape = CircleShape)
.background(
color = colorPalette.elevatedBackground,
shape = CircleShape
)
.padding(horizontal = 16.dp, vertical = 16.dp)
.size(20.dp)
)
}
}
}
itemsIndexed( itemsIndexed(
items = playlistWithSongs.songs, items = playlistWithSongs.songs,
key = { _, song -> song.song.id }, key = { _, song -> song.song.id },

View File

@@ -33,13 +33,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
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.zIndex
import coil.compose.AsyncImage import coil.compose.AsyncImage
import it.vfsfitvnm.route.RouteHandler import it.vfsfitvnm.route.RouteHandler
import it.vfsfitvnm.vimusic.Database import it.vfsfitvnm.vimusic.Database
@@ -125,28 +125,109 @@ fun PlaylistScreen(browseId: String) {
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
.size(24.dp) .size(24.dp)
) )
}
}
Image( item {
painter = painterResource(R.drawable.ellipsis_horizontal), playlist?.getOrNull()?.let { playlist ->
contentDescription = null, Column {
colorFilter = ColorFilter.tint(colorPalette.text), Row(
modifier = Modifier horizontalArrangement = Arrangement.spacedBy(16.dp),
.clickable { modifier = Modifier
menuState.display { .fillMaxWidth()
Menu { .height(IntrinsicSize.Max)
MenuEntry( .padding(vertical = 8.dp, horizontal = 16.dp)
icon = R.drawable.enqueue, .padding(bottom = 8.dp)
text = "Enqueue", ) {
onClick = { AsyncImage(
menuState.hide() model = playlist.thumbnail?.size(thumbnailSizePx),
playlist contentDescription = null,
?.getOrNull() contentScale = ContentScale.Crop,
?.let { album -> modifier = Modifier
album.items .clip(ThumbnailRoundness.shape)
.size(Dimensions.thumbnails.playlist)
)
Column(
verticalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxSize()
) {
Column {
BasicText(
text = playlist.title ?: "Unknown",
style = typography.m.semiBold
)
BasicText(
text = playlist.authors?.joinToString("") { it.name }
?: "",
style = typography.xs.secondary.semiBold,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
playlist.year?.let { year ->
BasicText(
text = year,
style = typography.xs.secondary,
maxLines = 1,
modifier = Modifier
.padding(top = 8.dp)
)
}
}
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
.zIndex(1f)
.padding(horizontal = 8.dp)
) {
Image(
painter = painterResource(R.drawable.shuffle),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
binder?.stopRadio()
playlist.items
?.shuffled()
?.mapNotNull { song ->
song.toMediaItem(browseId, playlist)
}
?.let { mediaItems ->
binder?.player?.forcePlayFromBeginning(
mediaItems
)
}
}
.padding(horizontal = 8.dp, vertical = 8.dp)
.size(20.dp)
)
Image(
painter = painterResource(R.drawable.ellipsis_horizontal),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
menuState.display {
Menu {
MenuEntry(
icon = R.drawable.enqueue,
text = "Enqueue",
onClick = {
menuState.hide()
playlist.items
?.mapNotNull { song -> ?.mapNotNull { song ->
song.toMediaItem( song.toMediaItem(
browseId, browseId,
album playlist
) )
} }
?.let { mediaItems -> ?.let { mediaItems ->
@@ -155,32 +236,27 @@ fun PlaylistScreen(browseId: String) {
) )
} }
} }
} )
)
MenuEntry( MenuEntry(
icon = R.drawable.playlist, icon = R.drawable.playlist,
text = "Import", text = "Import",
onClick = { onClick = {
menuState.hide() menuState.hide()
playlist
?.getOrNull()
?.let { album ->
transaction { transaction {
val playlistId = val playlistId =
Database.insert( Database.insert(
Playlist( Playlist(
name = album.title name = playlist.title
?: "Unknown" ?: "Unknown"
) )
) )
album.items?.forEachIndexed { index, song -> playlist.items?.forEachIndexed { index, song ->
song song
.toMediaItem( .toMediaItem(
browseId, browseId,
album playlist
) )
?.let { mediaItem -> ?.let { mediaItem ->
Database.insert( Database.insert(
@@ -198,150 +274,41 @@ fun PlaylistScreen(browseId: String) {
} }
} }
} }
} )
)
MenuEntry( MenuEntry(
icon = R.drawable.share_social, icon = R.drawable.share_social,
text = "Share", text = "Share",
onClick = { onClick = {
menuState.hide() menuState.hide()
(playlist?.getOrNull()?.url (playlist.url
?: "https://music.youtube.com/playlist?list=${ ?: "https://music.youtube.com/playlist?list=${
browseId.removePrefix( browseId.removePrefix(
"VL" "VL"
) )
}").let { url -> }").let { url ->
val sendIntent = Intent().apply { val sendIntent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
type = "text/plain" type = "text/plain"
putExtra(Intent.EXTRA_TEXT, url) putExtra(Intent.EXTRA_TEXT, url)
}
context.startActivity(
Intent.createChooser(
sendIntent,
null
)
)
}
} }
)
context.startActivity(
Intent.createChooser(
sendIntent,
null
)
)
}
} }
) }
} }
} .padding(horizontal = 8.dp, vertical = 8.dp)
} .size(20.dp)
.padding(horizontal = 16.dp, vertical = 8.dp) )
.size(24.dp)
)
}
}
item {
playlist?.getOrNull()?.let { playlist ->
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.fillMaxWidth()
.height(IntrinsicSize.Max)
.padding(vertical = 8.dp, horizontal = 16.dp)
.padding(bottom = 16.dp)
) {
AsyncImage(
model = playlist.thumbnail?.size(thumbnailSizePx),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.clip(ThumbnailRoundness.shape)
.size(Dimensions.thumbnails.playlist)
)
Column(
verticalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxSize()
) {
Column {
BasicText(
text = playlist.title ?: "Unknown",
style = typography.m.semiBold
)
BasicText(
text = buildString {
val authors =
playlist.authors?.joinToString("") { it.name }
append(authors)
if (authors?.isNotEmpty() == true && playlist.year != null) {
append("")
}
append(playlist.year)
},
style = typography.xs.secondary.semiBold,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.align(Alignment.End)
.padding(horizontal = 16.dp)
) {
Image(
painter = painterResource(R.drawable.shuffle),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
binder?.stopRadio()
playlist.items
?.shuffled()
?.mapNotNull { song ->
song.toMediaItem(browseId, playlist)
}
?.let { mediaItems ->
binder?.player?.forcePlayFromBeginning(
mediaItems
)
}
}
.shadow(elevation = 2.dp, shape = CircleShape)
.background(
color = colorPalette.elevatedBackground,
shape = CircleShape
)
.padding(horizontal = 16.dp, vertical = 16.dp)
.size(20.dp)
)
Image(
painter = painterResource(R.drawable.play),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
binder?.stopRadio()
playlist.items
?.mapNotNull { song ->
song.toMediaItem(browseId, playlist)
}
?.let { mediaItems ->
binder?.player?.forcePlayFromBeginning(
mediaItems
)
}
}
.shadow(elevation = 2.dp, shape = CircleShape)
.background(
color = colorPalette.elevatedBackground,
shape = CircleShape
)
.padding(horizontal = 16.dp, vertical = 16.dp)
.size(20.dp)
)
}
} }
} }
} ?: playlist?.exceptionOrNull()?.let { throwable -> } ?: playlist?.exceptionOrNull()?.let { throwable ->