Add IconButton composable

This commit is contained in:
vfsfitvnm 2022-10-04 14:31:59 +02:00
parent 185c5ec726
commit 7fe9c1dee8
7 changed files with 299 additions and 366 deletions

View file

@ -23,17 +23,12 @@ fun HeaderIconButton(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enabled: Boolean = true enabled: Boolean = true
) { ) {
Image( IconButton(
painter = painterResource(icon), icon = icon,
contentDescription = null, color = color,
colorFilter = ColorFilter.tint(color), onClick = onClick,
enabled = enabled,
modifier = modifier modifier = modifier
.clickable(
indication = rememberRipple(bounded = false),
interactionSource = remember { MutableInteractionSource() },
enabled = enabled,
onClick = onClick
)
.padding(all = 4.dp) .padding(all = 4.dp)
.size(18.dp) .size(18.dp)
) )
@ -51,12 +46,13 @@ fun IconButton(
painter = painterResource(icon), painter = painterResource(icon),
contentDescription = null, contentDescription = null,
colorFilter = ColorFilter.tint(color), colorFilter = ColorFilter.tint(color),
modifier = modifier modifier = Modifier
.clickable( .clickable(
indication = rememberRipple(bounded = false), indication = rememberRipple(bounded = false),
interactionSource = remember { MutableInteractionSource() }, interactionSource = remember { MutableInteractionSource() },
enabled = enabled, enabled = enabled,
onClick = onClick onClick = onClick
) )
.then(modifier)
) )
} }

View file

@ -3,7 +3,6 @@ package it.vfsfitvnm.vimusic.ui.screens.album
import android.content.Intent import android.content.Intent
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.BoxWithConstraints
@ -21,9 +20,7 @@ import androidx.compose.runtime.saveable.rememberSaveableStateHolder
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.draw.clip
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.valentinilk.shimmer.shimmer import com.valentinilk.shimmer.shimmer
@ -39,6 +36,7 @@ import it.vfsfitvnm.vimusic.savers.InnertubePlaylistOrAlbumPageSaver
import it.vfsfitvnm.vimusic.savers.innertubeItemsPageSaver import it.vfsfitvnm.vimusic.savers.innertubeItemsPageSaver
import it.vfsfitvnm.vimusic.savers.nullableSaver import it.vfsfitvnm.vimusic.savers.nullableSaver
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
import it.vfsfitvnm.vimusic.ui.items.AlbumItem import it.vfsfitvnm.vimusic.ui.items.AlbumItem
@ -143,49 +141,39 @@ fun AlbumScreen(browseId: String) {
.weight(1f) .weight(1f)
) )
Image( HeaderIconButton(
painter = painterResource( icon = if (album?.bookmarkedAt == null) {
if (album?.bookmarkedAt == null) { R.drawable.bookmark_outline
R.drawable.bookmark_outline } else {
} else { R.drawable.bookmark
R.drawable.bookmark },
} color = colorPalette.accent,
), onClick = {
contentDescription = null, val bookmarkedAt =
colorFilter = ColorFilter.tint(colorPalette.accent), if (album?.bookmarkedAt == null) System.currentTimeMillis() else null
modifier = Modifier
.clickable {
val bookmarkedAt =
if (album?.bookmarkedAt == null) System.currentTimeMillis() else null
query { query {
album album
?.copy(bookmarkedAt = bookmarkedAt) ?.copy(bookmarkedAt = bookmarkedAt)
?.let(Database::update) ?.let(Database::update)
}
} }
.padding(all = 4.dp) }
.size(18.dp)
) )
Image( HeaderIconButton(
painter = painterResource(R.drawable.share_social), icon = R.drawable.share_social,
contentDescription = null, color = colorPalette.text,
colorFilter = ColorFilter.tint(colorPalette.text), onClick = {
modifier = Modifier album?.shareUrl?.let { url ->
.clickable { val sendIntent = Intent().apply {
album?.shareUrl?.let { url -> action = Intent.ACTION_SEND
val sendIntent = Intent().apply { type = "text/plain"
action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, url)
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, url)
}
context.startActivity(Intent.createChooser(sendIntent, null))
} }
context.startActivity(Intent.createChooser(sendIntent, null))
} }
.padding(all = 4.dp) }
.size(18.dp)
) )
} }
} }

View file

@ -3,7 +3,6 @@ package it.vfsfitvnm.vimusic.ui.screens.artist
import android.content.Intent import android.content.Intent
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
@ -21,9 +20,7 @@ import androidx.compose.runtime.saveable.rememberSaveableStateHolder
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.draw.clip
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.valentinilk.shimmer.shimmer import com.valentinilk.shimmer.shimmer
@ -40,6 +37,7 @@ import it.vfsfitvnm.vimusic.savers.InnertubeSongsPageSaver
import it.vfsfitvnm.vimusic.savers.nullableSaver import it.vfsfitvnm.vimusic.savers.nullableSaver
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
@ -96,7 +94,11 @@ fun ArtistScreen(browseId: String) {
stateSaver = nullableSaver(InnertubeArtistPageSaver), stateSaver = nullableSaver(InnertubeArtistPageSaver),
tabIndex < 4 tabIndex < 4
) { ) {
if (value != null || (tabIndex == 4 && withContext(Dispatchers.IO) { Database.artistTimestamp(browseId) } != null)) return@produceSaveableState if (value != null || (tabIndex == 4 && withContext(Dispatchers.IO) {
Database.artistTimestamp(
browseId
)
} != null)) return@produceSaveableState
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
Innertube.artistPage(BrowseBody(browseId = browseId)) Innertube.artistPage(BrowseBody(browseId = browseId))
@ -154,35 +156,33 @@ fun ArtistScreen(browseId: String) {
} }
} }
val headerContent: @Composable (textButton: (@Composable () -> Unit)?) -> Unit = { textButton -> val headerContent: @Composable (textButton: (@Composable () -> Unit)?) -> Unit =
if (artist?.timestamp == null) { { textButton ->
HeaderPlaceholder( if (artist?.timestamp == null) {
modifier = Modifier HeaderPlaceholder(
.shimmer()
)
} else {
val context = LocalContext.current
Header(title = artist?.name ?: "Unknown") {
textButton?.invoke()
Spacer(
modifier = Modifier modifier = Modifier
.weight(1f) .shimmer()
) )
} else {
val (colorPalette) = LocalAppearance.current
val context = LocalContext.current
Image( Header(title = artist?.name ?: "Unknown") {
painter = painterResource( textButton?.invoke()
if (artist?.bookmarkedAt == null) {
Spacer(
modifier = Modifier
.weight(1f)
)
HeaderIconButton(
icon = if (artist?.bookmarkedAt == null) {
R.drawable.bookmark_outline R.drawable.bookmark_outline
} else { } else {
R.drawable.bookmark R.drawable.bookmark
} },
), color = colorPalette.accent,
contentDescription = null, onClick = {
colorFilter = ColorFilter.tint(LocalAppearance.current.colorPalette.accent),
modifier = Modifier
.clickable {
val bookmarkedAt = val bookmarkedAt =
if (artist?.bookmarkedAt == null) System.currentTimeMillis() else null if (artist?.bookmarkedAt == null) System.currentTimeMillis() else null
@ -192,16 +192,12 @@ fun ArtistScreen(browseId: String) {
?.let(Database::update) ?.let(Database::update)
} }
} }
.padding(all = 4.dp) )
.size(18.dp)
)
Image( HeaderIconButton(
painter = painterResource(R.drawable.share_social), icon = R.drawable.share_social,
contentDescription = null, color = colorPalette.text,
colorFilter = ColorFilter.tint(LocalAppearance.current.colorPalette.text), onClick = {
modifier = Modifier
.clickable {
val sendIntent = Intent().apply { val sendIntent = Intent().apply {
action = Intent.ACTION_SEND action = Intent.ACTION_SEND
type = "text/plain" type = "text/plain"
@ -211,19 +207,12 @@ fun ArtistScreen(browseId: String) {
) )
} }
context.startActivity( context.startActivity(Intent.createChooser(sendIntent, null))
Intent.createChooser(
sendIntent,
null
)
)
} }
.padding(all = 4.dp) )
.size(18.dp) }
)
} }
} }
}
Scaffold( Scaffold(
topIconButtonId = R.drawable.chevron_back, topIconButtonId = R.drawable.chevron_back,
@ -256,36 +245,36 @@ fun ArtistScreen(browseId: String) {
val thumbnailSizeDp = Dimensions.thumbnails.song val thumbnailSizeDp = Dimensions.thumbnails.song
val thumbnailSizePx = thumbnailSizeDp.px val thumbnailSizePx = thumbnailSizeDp.px
ItemsPage( ItemsPage(
stateSaver = InnertubeSongsPageSaver, stateSaver = InnertubeSongsPageSaver,
headerContent = headerContent, headerContent = headerContent,
itemsPageProvider = youtubeArtist?.let {({ continuation -> itemsPageProvider = youtubeArtist?.let {
continuation?.let { ({ continuation ->
Innertube.itemsPage( continuation?.let {
body = ContinuationBody(continuation = continuation),
fromMusicResponsiveListItemRenderer = Innertube.SongItem::from,
)
} ?: youtubeArtist
?.songsEndpoint
?.takeIf { it.browseId != null }
?.let { endpoint ->
Innertube.itemsPage( Innertube.itemsPage(
body = BrowseBody( body = ContinuationBody(continuation = continuation),
browseId = endpoint.browseId!!,
params = endpoint.params,
),
fromMusicResponsiveListItemRenderer = Innertube.SongItem::from, fromMusicResponsiveListItemRenderer = Innertube.SongItem::from,
) )
} } ?: youtubeArtist
?: Result.success( ?.songsEndpoint
Innertube.ItemsPage( ?.takeIf { it.browseId != null }
items = youtubeArtist?.songs, ?.let { endpoint ->
continuation = null Innertube.itemsPage(
body = BrowseBody(
browseId = endpoint.browseId!!,
params = endpoint.params,
),
fromMusicResponsiveListItemRenderer = Innertube.SongItem::from,
)
}
?: Result.success(
Innertube.ItemsPage(
items = youtubeArtist?.songs,
continuation = null
)
) )
) })
})}, },
itemContent = { song -> itemContent = { song ->
SongItem( SongItem(
song = song, song = song,
@ -320,31 +309,33 @@ fun ArtistScreen(browseId: String) {
stateSaver = InnertubeAlbumsPageSaver, stateSaver = InnertubeAlbumsPageSaver,
headerContent = headerContent, headerContent = headerContent,
emptyItemsText = "This artist didn't release any album", emptyItemsText = "This artist didn't release any album",
itemsPageProvider = youtubeArtist?.let {({ continuation -> itemsPageProvider = youtubeArtist?.let {
continuation?.let { ({ continuation ->
Innertube.itemsPage( continuation?.let {
body = ContinuationBody(continuation = continuation),
fromMusicTwoRowItemRenderer = Innertube.AlbumItem::from,
)
} ?: youtubeArtist
?.albumsEndpoint
?.takeIf { it.browseId != null }
?.let { endpoint ->
Innertube.itemsPage( Innertube.itemsPage(
body = BrowseBody( body = ContinuationBody(continuation = continuation),
browseId = endpoint.browseId!!,
params = endpoint.params,
),
fromMusicTwoRowItemRenderer = Innertube.AlbumItem::from, fromMusicTwoRowItemRenderer = Innertube.AlbumItem::from,
) )
} } ?: youtubeArtist
?: Result.success( ?.albumsEndpoint
Innertube.ItemsPage( ?.takeIf { it.browseId != null }
items = youtubeArtist?.albums, ?.let { endpoint ->
continuation = null Innertube.itemsPage(
body = BrowseBody(
browseId = endpoint.browseId!!,
params = endpoint.params,
),
fromMusicTwoRowItemRenderer = Innertube.AlbumItem::from,
)
}
?: Result.success(
Innertube.ItemsPage(
items = youtubeArtist?.albums,
continuation = null
)
) )
) })
})}, },
itemContent = { album -> itemContent = { album ->
AlbumItem( AlbumItem(
album = album, album = album,
@ -368,31 +359,33 @@ fun ArtistScreen(browseId: String) {
stateSaver = InnertubeAlbumsPageSaver, stateSaver = InnertubeAlbumsPageSaver,
headerContent = headerContent, headerContent = headerContent,
emptyItemsText = "This artist didn't release any single", emptyItemsText = "This artist didn't release any single",
itemsPageProvider = youtubeArtist?.let {({ continuation -> itemsPageProvider = youtubeArtist?.let {
continuation?.let { ({ continuation ->
Innertube.itemsPage( continuation?.let {
body = ContinuationBody(continuation = continuation),
fromMusicTwoRowItemRenderer = Innertube.AlbumItem::from,
)
} ?: youtubeArtist
?.singlesEndpoint
?.takeIf { it.browseId != null }
?.let { endpoint ->
Innertube.itemsPage( Innertube.itemsPage(
body = BrowseBody( body = ContinuationBody(continuation = continuation),
browseId = endpoint.browseId!!,
params = endpoint.params,
),
fromMusicTwoRowItemRenderer = Innertube.AlbumItem::from, fromMusicTwoRowItemRenderer = Innertube.AlbumItem::from,
) )
} } ?: youtubeArtist
?: Result.success( ?.singlesEndpoint
Innertube.ItemsPage( ?.takeIf { it.browseId != null }
items = youtubeArtist?.singles, ?.let { endpoint ->
continuation = null Innertube.itemsPage(
body = BrowseBody(
browseId = endpoint.browseId!!,
params = endpoint.params,
),
fromMusicTwoRowItemRenderer = Innertube.AlbumItem::from,
)
}
?: Result.success(
Innertube.ItemsPage(
items = youtubeArtist?.singles,
continuation = null
)
) )
) })
})}, },
itemContent = { album -> itemContent = { album ->
AlbumItem( AlbumItem(
album = album, album = album,

View file

@ -40,6 +40,7 @@ import it.vfsfitvnm.vimusic.transaction
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
import it.vfsfitvnm.vimusic.ui.components.themed.InPlaylistMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.InPlaylistMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.PrimaryButton import it.vfsfitvnm.vimusic.ui.components.themed.PrimaryButton
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
@ -164,59 +165,46 @@ fun LocalPlaylistSongs(
) )
playlistWithSongs?.playlist?.browseId?.let { browseId -> playlistWithSongs?.playlist?.browseId?.let { browseId ->
Image( HeaderIconButton(
painter = painterResource(R.drawable.sync), icon = R.drawable.sync,
contentDescription = null, color = colorPalette.text,
colorFilter = ColorFilter.tint(colorPalette.text), onClick = {
modifier = Modifier transaction {
.clickable { runBlocking(Dispatchers.IO) {
transaction { withContext(Dispatchers.IO) {
runBlocking(Dispatchers.IO) { Innertube.playlistPage(BrowseBody(browseId = browseId))?.completed()
withContext(Dispatchers.IO) {
Innertube.playlistPage(BrowseBody(browseId = browseId))?.completed()
}
}?.getOrNull()?.let { remotePlaylist ->
Database.clearPlaylist(playlistId)
remotePlaylist.
songsPage
?.items
?.map(Innertube.SongItem::asMediaItem)
?.onEach(Database::insert)
?.mapIndexed { position, mediaItem ->
SongPlaylistMap(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = position
)
}?.let(Database::insertSongPlaylistMaps)
} }
}?.getOrNull()?.let { remotePlaylist ->
Database.clearPlaylist(playlistId)
remotePlaylist.
songsPage
?.items
?.map(Innertube.SongItem::asMediaItem)
?.onEach(Database::insert)
?.mapIndexed { position, mediaItem ->
SongPlaylistMap(
songId = mediaItem.mediaId,
playlistId = playlistId,
position = position
)
}?.let(Database::insertSongPlaylistMaps)
} }
} }
.padding(all = 4.dp) }
.size(18.dp)
) )
} }
Image( HeaderIconButton(
painter = painterResource(R.drawable.pencil), icon = R.drawable.pencil,
contentDescription = null, color = colorPalette.text,
colorFilter = ColorFilter.tint(colorPalette.text), onClick = { isRenaming = true }
modifier = Modifier
.clickable { isRenaming = true }
.padding(all = 4.dp)
.size(18.dp)
) )
HeaderIconButton(
Image( icon = R.drawable.trash,
painter = painterResource(R.drawable.trash), color = colorPalette.text,
contentDescription = null, onClick = { isDeleting = true }
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.clickable { isDeleting = true }
.padding(all = 4.dp)
.size(18.dp)
) )
} }
} }

View file

@ -52,6 +52,7 @@ import it.vfsfitvnm.vimusic.ui.components.BottomSheet
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.MusicBars import it.vfsfitvnm.vimusic.ui.components.MusicBars
import it.vfsfitvnm.vimusic.ui.components.themed.IconButton
import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.QueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.items.SongItem import it.vfsfitvnm.vimusic.ui.items.SongItem
import it.vfsfitvnm.vimusic.ui.items.SongItemPlaceholder import it.vfsfitvnm.vimusic.ui.items.SongItemPlaceholder
@ -227,7 +228,10 @@ fun PlayerBottomSheet(
} }
) )
.animateItemPlacement(reorderingState = reorderingState) .animateItemPlacement(reorderingState = reorderingState)
.draggedItem(reorderingState = reorderingState, index = window.firstPeriodIndex) .draggedItem(
reorderingState = reorderingState,
index = window.firstPeriodIndex
)
) )
} }
@ -257,14 +261,13 @@ fun PlayerBottomSheet(
.height(64.dp + bottomPadding) .height(64.dp + bottomPadding)
.background(colorPalette.background2) .background(colorPalette.background2)
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 8.dp) .padding(horizontal = 12.dp)
.padding(bottom = bottomPadding) .padding(bottom = bottomPadding)
) { ) {
BasicText( BasicText(
text = "${windows.size} songs", text = "${windows.size} songs",
style = typography.xxs.medium, style = typography.xxs.medium,
modifier = Modifier modifier = Modifier
.padding(start = 4.dp)
.background(color = colorPalette.background1, shape = RoundedCornerShape(16.dp)) .background(color = colorPalette.background1, shape = RoundedCornerShape(16.dp))
.align(Alignment.CenterStart) .align(Alignment.CenterStart)
.padding(all = 8.dp) .padding(all = 8.dp)
@ -279,22 +282,20 @@ fun PlayerBottomSheet(
.size(18.dp) .size(18.dp)
) )
Image( IconButton(
painter = painterResource(R.drawable.shuffle), icon = R.drawable.shuffle,
contentDescription = null, color = colorPalette.text,
colorFilter = ColorFilter.tint(colorPalette.text), onClick = {
modifier = Modifier reorderingState.coroutineScope.launch {
.padding(end = 2.dp) reorderingState.lazyListState.smoothScrollToTop()
.clickable { }.invokeOnCompletion {
reorderingState.coroutineScope.launch { binder.player.shuffleQueue()
reorderingState.lazyListState.smoothScrollToTop()
}.invokeOnCompletion {
binder.player.shuffleQueue()
}
} }
.align(Alignment.CenterEnd) },
.padding(all = 8.dp) modifier = Modifier
.padding(horizontal = 4.dp, vertical = 8.dp)
.size(20.dp) .size(20.dp)
.align(Alignment.CenterEnd)
) )
} }
} }

View file

@ -8,9 +8,7 @@ import androidx.activity.compose.LocalActivityResultRegistryOwner
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -40,12 +38,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
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.media3.common.Player import androidx.media3.common.Player
@ -58,6 +54,7 @@ import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.rememberBottomSheetState import it.vfsfitvnm.vimusic.ui.components.rememberBottomSheetState
import it.vfsfitvnm.vimusic.ui.components.themed.BaseMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.BaseMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.IconButton
import it.vfsfitvnm.vimusic.ui.styling.Dimensions import it.vfsfitvnm.vimusic.ui.styling.Dimensions
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
import it.vfsfitvnm.vimusic.ui.styling.collapsedPlayerProgressBar import it.vfsfitvnm.vimusic.ui.styling.collapsedPlayerProgressBar
@ -178,44 +175,32 @@ fun PlayerView(
modifier = Modifier modifier = Modifier
.height(Dimensions.collapsedPlayer) .height(Dimensions.collapsedPlayer)
) { ) {
Box( IconButton(
modifier = Modifier icon = if (shouldBePlaying) R.drawable.pause else R.drawable.play,
.clickable { color = colorPalette.text,
if (shouldBePlaying) { onClick = {
binder.player.pause() if (shouldBePlaying) {
} else { binder.player.pause()
if (binder.player.playbackState == Player.STATE_IDLE) { } else {
binder.player.prepare() if (binder.player.playbackState == Player.STATE_IDLE) {
} binder.player.prepare()
binder.player.play()
} }
binder.player.play()
} }
.padding(horizontal = 4.dp, vertical = 8.dp) },
) {
Image(
painter = painterResource(if (shouldBePlaying) R.drawable.pause else R.drawable.play),
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.text),
modifier = Modifier
.align(Alignment.Center)
.size(20.dp)
)
}
Box(
modifier = Modifier modifier = Modifier
.clickable(onClick = binder.player::seekToNext)
.padding(horizontal = 4.dp, vertical = 8.dp) .padding(horizontal = 4.dp, vertical = 8.dp)
) { .size(20.dp)
Image( )
painter = painterResource(R.drawable.play_skip_forward),
contentDescription = null, IconButton(
colorFilter = ColorFilter.tint(colorPalette.text), icon = R.drawable.play_skip_forward,
modifier = Modifier color = colorPalette.text,
.align(Alignment.Center) onClick = binder.player::seekToNext,
.size(20.dp) modifier = Modifier
) .padding(horizontal = 4.dp, vertical = 8.dp)
} .size(20.dp)
)
} }
Spacer( Spacer(
@ -335,65 +320,60 @@ fun PlayerView(
.padding(horizontal = 8.dp) .padding(horizontal = 8.dp)
.fillMaxHeight() .fillMaxHeight()
) { ) {
Image( IconButton(
painter = painterResource(R.drawable.ellipsis_horizontal), icon = R.drawable.ellipsis_horizontal,
contentDescription = null, color = colorPalette.text,
colorFilter = ColorFilter.tint(colorPalette.text), onClick = {
modifier = Modifier menuState.display {
.clickable { val resultRegistryOwner =
menuState.display { LocalActivityResultRegistryOwner.current
val resultRegistryOwner =
LocalActivityResultRegistryOwner.current
BaseMediaItemMenu( BaseMediaItemMenu(
mediaItem = mediaItem, mediaItem = mediaItem,
onStartRadio = { onStartRadio = {
binder.stopRadio() binder.stopRadio()
binder.player.seamlessPlay(mediaItem) binder.player.seamlessPlay(mediaItem)
binder.setupRadio( binder.setupRadio(
NavigationEndpoint.Endpoint.Watch(videoId = mediaItem.mediaId) NavigationEndpoint.Endpoint.Watch(videoId = mediaItem.mediaId)
) )
}, },
onGoToEqualizer = { onGoToEqualizer = {
val intent = val intent =
Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL).apply { Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL).apply {
putExtra( putExtra(
AudioEffect.EXTRA_AUDIO_SESSION, AudioEffect.EXTRA_AUDIO_SESSION,
binder.player.audioSessionId binder.player.audioSessionId
) )
putExtra( putExtra(
AudioEffect.EXTRA_PACKAGE_NAME, AudioEffect.EXTRA_PACKAGE_NAME,
context.packageName context.packageName
) )
putExtra( putExtra(
AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.EXTRA_CONTENT_TYPE,
AudioEffect.CONTENT_TYPE_MUSIC AudioEffect.CONTENT_TYPE_MUSIC
) )
}
if (intent.resolveActivity(context.packageManager) != null) {
val contract =
ActivityResultContracts.StartActivityForResult()
resultRegistryOwner?.activityResultRegistry
?.register("", contract) {}
?.launch(intent)
} else {
Toast
.makeText(
context,
"No equalizer app found!",
Toast.LENGTH_SHORT
)
.show()
} }
},
onSetSleepTimer = {}, if (intent.resolveActivity(context.packageManager) != null) {
onDismiss = menuState::hide val contract =
) ActivityResultContracts.StartActivityForResult()
}
resultRegistryOwner?.activityResultRegistry
?.register("", contract) {}
?.launch(intent)
} else {
Toast
.makeText(context, "No equalizer app found!", Toast.LENGTH_SHORT)
.show()
}
},
onSetSleepTimer = {},
onDismiss = menuState::hide
)
} }
.padding(all = 8.dp) },
modifier = Modifier
.padding(horizontal = 4.dp, vertical = 8.dp)
.size(20.dp) .size(20.dp)
) )

View file

@ -3,9 +3,7 @@ package it.vfsfitvnm.vimusic.ui.screens.playlist
import android.content.Intent import android.content.Intent
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -28,9 +26,7 @@ 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.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.valentinilk.shimmer.shimmer import com.valentinilk.shimmer.shimmer
@ -45,6 +41,7 @@ import it.vfsfitvnm.vimusic.savers.resultSaver
import it.vfsfitvnm.vimusic.transaction import it.vfsfitvnm.vimusic.transaction
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
import it.vfsfitvnm.vimusic.ui.components.themed.Header import it.vfsfitvnm.vimusic.ui.components.themed.Header
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.PrimaryButton import it.vfsfitvnm.vimusic.ui.components.themed.PrimaryButton
@ -137,57 +134,47 @@ fun PlaylistSongList(
.weight(1f) .weight(1f)
) )
Image( HeaderIconButton(
painter = painterResource( icon = if (isImported == true) R.drawable.bookmark else R.drawable.bookmark_outline,
if (isImported == true) R.drawable.bookmark else R.drawable.bookmark_outline color = colorPalette.accent,
), onClick = {
contentDescription = null, transaction {
colorFilter = ColorFilter.tint(colorPalette.accent), val playlistId =
modifier = Modifier Database.insert(
.clickable(enabled = isImported == false) { Playlist(
transaction { name = playlist.title ?: "Unknown",
val playlistId = browseId = browseId
Database.insert(
Playlist(
name = playlist.title ?: "Unknown",
browseId = browseId
)
) )
)
playlist.songsPage?.items playlist.songsPage?.items
?.map(Innertube.SongItem::asMediaItem) ?.map(Innertube.SongItem::asMediaItem)
?.onEach(Database::insert) ?.onEach(Database::insert)
?.mapIndexed { index, mediaItem -> ?.mapIndexed { index, mediaItem ->
SongPlaylistMap( SongPlaylistMap(
songId = mediaItem.mediaId, songId = mediaItem.mediaId,
playlistId = playlistId, playlistId = playlistId,
position = index position = index
) )
}?.let(Database::insertSongPlaylistMaps) }?.let(Database::insertSongPlaylistMaps)
}
} }
.padding(all = 4.dp) }
.size(18.dp)
) )
Image( HeaderIconButton(
painter = painterResource(R.drawable.share_social), icon = R.drawable.share_social,
contentDescription = null, color = colorPalette.text,
colorFilter = ColorFilter.tint(colorPalette.text), onClick = {
modifier = Modifier (playlist.url ?: "https://music.youtube.com/playlist?list=${browseId.removePrefix("VL")}").let { url ->
.clickable { val sendIntent = Intent().apply {
(playlist.url ?: "https://music.youtube.com/playlist?list=${browseId.removePrefix("VL")}").let { url -> action = Intent.ACTION_SEND
val sendIntent = Intent().apply { type = "text/plain"
action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, url)
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, url)
}
context.startActivity(Intent.createChooser(sendIntent, null))
} }
context.startActivity(Intent.createChooser(sendIntent, null))
} }
.padding(all = 4.dp) }
.size(18.dp)
) )
} }