Use LazyColumn in PlaylistOrAlbumScreen

This commit is contained in:
vfsfitvnm
2022-06-12 19:54:48 +02:00
parent ec052c7636
commit 6737a7b003

View File

@@ -3,8 +3,13 @@ package it.vfsfitvnm.vimusic.ui.screens
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.* import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.* import androidx.compose.runtime.*
@@ -51,7 +56,7 @@ import kotlinx.coroutines.withContext
fun PlaylistOrAlbumScreen( fun PlaylistOrAlbumScreen(
browseId: String, browseId: String,
) { ) {
val scrollState = rememberScrollState() val lazyListState = rememberLazyListState()
var playlistOrAlbum by remember { var playlistOrAlbum by remember {
mutableStateOf<Outcome<YouTube.PlaylistOrAlbum>>(Outcome.Loading) mutableStateOf<Outcome<YouTube.PlaylistOrAlbum>>(Outcome.Loading)
@@ -101,103 +106,104 @@ fun PlaylistOrAlbumScreen(
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
Column( LazyColumn(
state = lazyListState,
contentPadding = PaddingValues(bottom = 72.dp),
modifier = Modifier modifier = Modifier
.background(colorPalette.background) .background(colorPalette.background)
.fillMaxSize() .fillMaxSize()
.verticalScroll(scrollState)
.padding(bottom = 72.dp)
) { ) {
TopAppBar( item {
modifier = Modifier TopAppBar(
.height(52.dp)
) {
Image(
painter = painterResource(R.drawable.chevron_back),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier modifier = Modifier
.clickable(onClick = pop) .height(52.dp)
.padding(vertical = 8.dp) ) {
.padding(horizontal = 16.dp) Image(
.size(24.dp) painter = painterResource(R.drawable.chevron_back),
) contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable(onClick = pop)
.padding(vertical = 8.dp)
.padding(horizontal = 16.dp)
.size(24.dp)
)
Image( Image(
painter = painterResource(R.drawable.ellipsis_horizontal), painter = painterResource(R.drawable.ellipsis_horizontal),
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text), colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier modifier = Modifier
.clickable { .clickable {
menuState.display { menuState.display {
Menu { Menu {
MenuCloseButton(onClick = menuState::hide) MenuCloseButton(onClick = menuState::hide)
MenuEntry( MenuEntry(
icon = R.drawable.time, icon = R.drawable.time,
text = "Enqueue", text = "Enqueue",
enabled = player?.playbackState == Player.STATE_READY, enabled = player?.playbackState == Player.STATE_READY,
onClick = { onClick = {
menuState.hide() menuState.hide()
playlistOrAlbum.valueOrNull?.let { album -> playlistOrAlbum.valueOrNull?.let { album ->
album.items album.items
?.mapNotNull { song -> ?.mapNotNull { song ->
song.toMediaItem(browseId, album) song.toMediaItem(browseId, album)
} }
?.let { mediaItems -> ?.let { mediaItems ->
player?.mediaController?.enqueue( player?.mediaController?.enqueue(
mediaItems mediaItems
) )
} }
}
} }
} )
)
MenuEntry( MenuEntry(
icon = R.drawable.list, icon = R.drawable.list,
text = "Import as playlist", text = "Import as playlist",
onClick = { onClick = {
menuState.hide() menuState.hide()
playlistOrAlbum.valueOrNull?.let { album -> playlistOrAlbum.valueOrNull?.let { album ->
coroutineScope.launch(Dispatchers.IO) { coroutineScope.launch(Dispatchers.IO) {
Database.internal.runInTransaction { Database.internal.runInTransaction {
val playlistId = val playlistId =
Database.insert(Playlist(name = album.title ?: "Unknown")) Database.insert(Playlist(name = album.title ?: "Unknown"))
album.items?.forEachIndexed { index, song ->
song
.toMediaItem(browseId, album)
?.let { mediaItem ->
if (Database.song(mediaItem.mediaId) == null) {
Database.insert(
mediaItem
)
}
album.items?.forEachIndexed { index, song ->
song
.toMediaItem(browseId, album)
?.let { mediaItem ->
if (Database.song(mediaItem.mediaId) == null) {
Database.insert( Database.insert(
mediaItem SongInPlaylist(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = index
)
) )
} }
}
Database.insert(
SongInPlaylist(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = index
)
)
}
} }
} }
} }
} }
} )
)
MenuEntry( MenuEntry(
icon = R.drawable.share_social, icon = R.drawable.share_social,
text = "Share", text = "Share",
onClick = { onClick = {
menuState.hide() menuState.hide()
(playlistOrAlbum.valueOrNull?.url (playlistOrAlbum.valueOrNull?.url
?: "https://music.youtube.com/playlist?list=${browseId.removePrefix("VL")}").let { url -> ?: "https://music.youtube.com/playlist?list=${browseId.removePrefix("VL")}").let { url ->
val sendIntent = Intent().apply { val sendIntent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
type = "text/plain" type = "text/plain"
@@ -206,170 +212,176 @@ fun PlaylistOrAlbumScreen(
context.startActivity(Intent.createChooser(sendIntent, null)) context.startActivity(Intent.createChooser(sendIntent, null))
} }
} }
) )
}
} }
} }
} .padding(horizontal = 16.dp, vertical = 8.dp)
.padding(horizontal = 16.dp, vertical = 8.dp) .size(24.dp)
.size(24.dp) )
) }
} }
OutcomeItem( item {
outcome = playlistOrAlbum, OutcomeItem(
onRetry = onLoad, outcome = playlistOrAlbum,
onLoading = { onRetry = onLoad,
Loading() onLoading = {
} Loading()
) { playlistOrAlbum -> }
Row( ) { playlistOrAlbum ->
horizontalArrangement = Arrangement.spacedBy(16.dp), Row(
modifier = Modifier horizontalArrangement = Arrangement.spacedBy(16.dp),
.fillMaxWidth()
.height(IntrinsicSize.Max)
.padding(vertical = 8.dp, horizontal = 16.dp)
.padding(bottom = 16.dp)
) {
AsyncImage(
model = playlistOrAlbum.thumbnail?.size(thumbnailSizePx),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier modifier = Modifier
.clip(ThumbnailRoundness.shape) .fillMaxWidth()
.size(thumbnailSizeDp) .height(IntrinsicSize.Max)
) .padding(vertical = 8.dp, horizontal = 16.dp)
.padding(bottom = 16.dp)
Column(
verticalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxSize()
) { ) {
Column { AsyncImage(
BasicText( model = playlistOrAlbum.thumbnail?.size(thumbnailSizePx),
text = playlistOrAlbum.title ?: "Unknown", contentDescription = null,
style = typography.m.semiBold contentScale = ContentScale.Crop,
)
BasicText(
text = buildString {
val authors = playlistOrAlbum.authors?.joinToString("") { it.name }
append(authors)
if (authors?.isNotEmpty() == true && playlistOrAlbum.year != null) {
append("")
}
append(playlistOrAlbum.year)
},
style = typography.xs.secondary.semiBold,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier modifier = Modifier
.align(Alignment.End) .clip(ThumbnailRoundness.shape)
.padding(horizontal = 16.dp) .size(thumbnailSizeDp)
)
Column(
verticalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier
.fillMaxSize()
) { ) {
Image( Column {
painter = painterResource(R.drawable.shuffle), BasicText(
contentDescription = null, text = playlistOrAlbum.title ?: "Unknown",
colorFilter = ColorFilter.tint(colorPalette.text), style = typography.m.semiBold
)
BasicText(
text = buildString {
val authors = playlistOrAlbum.authors?.joinToString("") { it.name }
append(authors)
if (authors?.isNotEmpty() == true && playlistOrAlbum.year != null) {
append("")
}
append(playlistOrAlbum.year)
},
style = typography.xs.secondary.semiBold,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier modifier = Modifier
.clickable { .align(Alignment.End)
player?.mediaController?.let { .padding(horizontal = 16.dp)
it.sendCustomCommand(StopRadioCommand, Bundle.EMPTY) ) {
playlistOrAlbum.items Image(
?.shuffled() painter = painterResource(R.drawable.shuffle),
?.mapNotNull { song -> contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable {
player?.mediaController?.let {
it.sendCustomCommand(StopRadioCommand, Bundle.EMPTY)
playlistOrAlbum.items
?.shuffled()
?.mapNotNull { song ->
song.toMediaItem(browseId, playlistOrAlbum)
}?.let { mediaItems ->
it.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 {
player?.mediaController?.let {
it.sendCustomCommand(StopRadioCommand, Bundle.EMPTY)
playlistOrAlbum.items?.mapNotNull { song ->
song.toMediaItem(browseId, playlistOrAlbum) song.toMediaItem(browseId, playlistOrAlbum)
}?.let { mediaItems -> }?.let { mediaItems ->
it.forcePlayFromBeginning(mediaItems) it.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 {
player?.mediaController?.let {
it.sendCustomCommand(StopRadioCommand, Bundle.EMPTY)
playlistOrAlbum.items?.mapNotNull { song ->
song.toMediaItem(browseId, playlistOrAlbum)
}?.let { mediaItems ->
it.forcePlayFromBeginning(mediaItems)
} }
} }
} .shadow(elevation = 2.dp, shape = CircleShape)
.shadow(elevation = 2.dp, shape = CircleShape) .background(
.background( color = colorPalette.elevatedBackground,
color = colorPalette.elevatedBackground, shape = CircleShape
shape = CircleShape )
) .padding(horizontal = 16.dp, vertical = 16.dp)
.padding(horizontal = 16.dp, vertical = 16.dp) .size(20.dp)
.size(20.dp) )
) }
} }
} }
} }
}
playlistOrAlbum.items?.forEachIndexed { index, song -> itemsIndexed(
SongItem( items = playlistOrAlbum.valueOrNull?.items ?: emptyList(),
title = song.info.name, contentType = { _, song -> song }
authors = (song.authors ?: playlistOrAlbum.authors)?.joinToString("") { it.name }, ) { index, song ->
durationText = song.durationText, SongItem(
onClick = { title = song.info.name,
player?.mediaController?.let { authors = (song.authors ?: playlistOrAlbum.valueOrNull?.authors)?.joinToString("") { it.name },
it.sendCustomCommand(StopRadioCommand, Bundle.EMPTY) durationText = song.durationText,
playlistOrAlbum.items?.mapNotNull { song -> onClick = {
song.toMediaItem(browseId, playlistOrAlbum) player?.mediaController?.let {
}?.let { mediaItems -> it.sendCustomCommand(StopRadioCommand, Bundle.EMPTY)
it.forcePlayAtIndex(mediaItems, index) playlistOrAlbum.valueOrNull?.items?.mapNotNull { song ->
} song.toMediaItem(browseId, playlistOrAlbum.valueOrNull!!)
}?.let { mediaItems ->
it.forcePlayAtIndex(mediaItems, index)
} }
}, }
startContent = { },
if (song.thumbnail == null) { startContent = {
BasicText( if (song.thumbnail == null) {
text = "${index + 1}", BasicText(
style = typography.xs.secondary.bold.center, text = "${index + 1}",
maxLines = 1, style = typography.xs.secondary.bold.center,
overflow = TextOverflow.Ellipsis, maxLines = 1,
modifier = Modifier overflow = TextOverflow.Ellipsis,
.width(36.dp) modifier = Modifier
) .width(36.dp)
} else { )
AsyncImage( } else {
model = song.thumbnail!!.size(songThumbnailSizePx), AsyncImage(
contentDescription = null, model = song.thumbnail!!.size(songThumbnailSizePx),
contentScale = ContentScale.Crop, contentDescription = null,
modifier = Modifier contentScale = ContentScale.Crop,
.clip(ThumbnailRoundness.shape) modifier = Modifier
.size(songThumbnailSizeDp) .clip(ThumbnailRoundness.shape)
) .size(songThumbnailSizeDp)
}
},
menuContent = {
NonQueuedMediaItemMenu(
mediaItem = song.toMediaItem(browseId, playlistOrAlbum)
?: return@SongItem,
onDismiss = menuState::hide,
) )
} }
) },
} menuContent = {
NonQueuedMediaItemMenu(
mediaItem = song.toMediaItem(browseId, playlistOrAlbum.valueOrNull!!)
?: return@SongItem,
onDismiss = menuState::hide,
)
}
)
} }
} }
} }