From 61be05094791e1d78a701d4cd3cd4a480af10d93 Mon Sep 17 00:00:00 2001 From: mattcarter11 <38189440+mattcarter11@users.noreply.github.com> Date: Wed, 11 Dec 2024 00:20:05 +0100 Subject: [PATCH 1/6] songs: centralize SongListItem logic + select header also consider connection --- .../dd3boh/outertune/extensions/SongsExt.kt | 10 + .../outertune/ui/component/AnchorDraggable.kt | 10 +- .../dd3boh/outertune/ui/component/Items.kt | 235 ++++++++++++++---- .../com/dd3boh/outertune/ui/menu/SongMenu.kt | 22 +- .../outertune/ui/screens/AlbumScreen.kt | 128 +++------- .../outertune/ui/screens/HistoryScreen.kt | 187 +++++--------- .../dd3boh/outertune/ui/screens/HomeScreen.kt | 101 ++------ .../outertune/ui/screens/SetupWizzard.kt | 40 +-- .../outertune/ui/screens/StatsScreen.kt | 81 +----- .../ui/screens/artist/ArtistItemsScreen.kt | 44 ++-- .../ui/screens/artist/ArtistScreen.kt | 63 ++--- .../ui/screens/artist/ArtistSongsScreen.kt | 123 ++------- .../screens/library/LibraryFoldersScreen.kt | 97 ++------ .../ui/screens/library/LibrarySongsScreen.kt | 110 ++------ .../ui/screens/playlist/AutoPlaylistScreen.kt | 110 ++------ .../screens/playlist/LocalPlaylistScreen.kt | 126 ++-------- .../screens/playlist/OnlinePlaylistScreen.kt | 4 +- .../ui/screens/search/LocalSearchScreen.kt | 79 ++---- .../ui/screens/search/OnlineSearchResult.kt | 66 ++--- .../ui/screens/search/OnlineSearchScreen.kt | 82 +++--- 20 files changed, 603 insertions(+), 1115 deletions(-) create mode 100644 app/src/main/java/com/dd3boh/outertune/extensions/SongsExt.kt diff --git a/app/src/main/java/com/dd3boh/outertune/extensions/SongsExt.kt b/app/src/main/java/com/dd3boh/outertune/extensions/SongsExt.kt new file mode 100644 index 000000000..71021ea0f --- /dev/null +++ b/app/src/main/java/com/dd3boh/outertune/extensions/SongsExt.kt @@ -0,0 +1,10 @@ +package com.dd3boh.outertune.extensions + +import com.dd3boh.outertune.db.entities.Song + +fun List.getAvailableSongs(isInternetConnected: Boolean): List { + if (isInternetConnected){ + return this + } + return filter { it.song.isAvailableOffline() } +} \ No newline at end of file diff --git a/app/src/main/java/com/dd3boh/outertune/ui/component/AnchorDraggable.kt b/app/src/main/java/com/dd3boh/outertune/ui/component/AnchorDraggable.kt index 204551459..13f3c2629 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/component/AnchorDraggable.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/component/AnchorDraggable.kt @@ -166,7 +166,10 @@ fun SwipeToQueueBox( coroutineScope.launch { snackbarHostState.showSnackbar( - message = context.getString(R.string.song_added_to_queue, item.mediaMetadata.title), + message = context.getString( + R.string.song_added_to_queue, + item.mediaMetadata.title + ), duration = SnackbarDuration.Short ) } @@ -178,9 +181,9 @@ fun SwipeToQueueBox( ) } - if (!enabled){ + if (!enabled) { Box { content() } - } + } else { LaunchedEffect(state.currentValue) { if (state.currentValue == DragAnchors.Start && !state.isAnimationRunning) { @@ -215,5 +218,4 @@ fun SwipeToQueueBox( } ) } - } \ No newline at end of file diff --git a/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt b/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt index 2f4e097f5..4331be38e 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt @@ -8,8 +8,10 @@ import androidx.compose.animation.expandIn import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkOut +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxWithConstraints @@ -30,6 +32,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.QueueMusic import androidx.compose.material.icons.rounded.CloudOff +import androidx.compose.material.icons.rounded.DragHandle import androidx.compose.material.icons.rounded.Edit import androidx.compose.material.icons.rounded.EditOff import androidx.compose.material.icons.rounded.Explicit @@ -39,10 +42,13 @@ import androidx.compose.material.icons.rounded.FolderCopy import androidx.compose.material.icons.rounded.LibraryAddCheck import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.OfflinePin +import androidx.compose.material3.Checkbox import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable @@ -60,9 +66,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource @@ -92,8 +100,11 @@ import com.dd3boh.outertune.db.entities.Album import com.dd3boh.outertune.db.entities.Artist import com.dd3boh.outertune.db.entities.Playlist import com.dd3boh.outertune.db.entities.PlaylistEntity +import com.dd3boh.outertune.db.entities.PlaylistSong import com.dd3boh.outertune.db.entities.Song import com.dd3boh.outertune.extensions.isAvailableOffline +import com.dd3boh.outertune.extensions.toMediaItem +import com.dd3boh.outertune.extensions.togglePlayPause import com.dd3boh.outertune.models.DirectoryTree import com.dd3boh.outertune.models.MediaMetadata import com.dd3boh.outertune.models.MultiQueueObject @@ -101,6 +112,7 @@ import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.queues.ListQueue import com.dd3boh.outertune.ui.component.Icon.FolderCopy import com.dd3boh.outertune.ui.menu.FolderMenu +import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.ui.utils.getLocalThumbnail import com.dd3boh.outertune.ui.utils.getNSongsString import com.dd3boh.outertune.utils.joinByBullet @@ -130,11 +142,11 @@ inline fun ListItem( trailingContent: @Composable RowScope.() -> Unit = {}, isSelected: Boolean? = false, isActive: Boolean = false, - enabled: Boolean = true, + available: Boolean = true, ) { Row( verticalAlignment = Alignment.CenterVertically, - modifier = if(!enabled) { + modifier = if(!available) { modifier .height(ListItemHeight) .padding(horizontal = 8.dp) @@ -167,7 +179,7 @@ inline fun ListItem( contentAlignment = Alignment.Center ) { thumbnailContent() - if (!enabled) { + if (!available) { Box( modifier = Modifier .size(ListThumbnailSize) // Adjust size as needed @@ -225,7 +237,7 @@ fun ListItem( isSelected: Boolean? = false, isActive: Boolean = false, isLocalSong: Boolean? = null, - enabled: Boolean = true, + available: Boolean = true, ) = ListItem( title = title, subtitle = { @@ -251,7 +263,7 @@ fun ListItem( modifier = modifier, isSelected = isSelected, isActive = isActive, - enabled = enabled + available = available ) @Composable @@ -338,62 +350,175 @@ fun GridItem( fillMaxWidth = fillMaxWidth ) +@OptIn(ExperimentalFoundationApi::class) @Composable fun SongListItem( song: Song, + onPlay: () -> Unit, + onSelectModeActivation : () -> Unit, + inSelectMode : Boolean, + selectionIds: MutableList?, + navController: NavController, modifier: Modifier = Modifier, + enableSwipeToQueue: Boolean = true, albumIndex: Int? = null, showLikedIcon: Boolean = true, - showInLibraryIcon: Boolean = false, + showInLibraryIcon: Boolean = true, showDownloadIcon: Boolean = true, - isSelected: Boolean = false, - badges: @Composable RowScope.() -> Unit = { - if (showLikedIcon && song.song.liked) { - Icon.Favorite() - } - if (showInLibraryIcon && song.song.inLibrary != null) { - Icon.Library() - } - if (showDownloadIcon) { - val download by LocalDownloadUtil.current.getDownload(song.id) - .collectAsState(initial = null) - Icon.Download(download?.state) - } - - // local song indicator - if (song.song.isLocal) { - FolderCopy() - } - }, - isActive: Boolean = false, - isPlaying: Boolean = false, - trailingContent: @Composable RowScope.() -> Unit = {}, + showLocalIcon: Boolean = true, + playlistSong : PlaylistSong? = null, + playlistBrowseId : String? = null, + showDragHandle: Boolean = false, + dragHandleModifier: Modifier? = null, + disableShowMenu: Boolean = false, ) { + val menuState = LocalMenuState.current + val haptic = LocalHapticFeedback.current val isNetworkConnected = LocalIsInternetConnected.current + val available = song.song.isAvailableOffline() || isNetworkConnected - ListItem( - title = song.song.title, - subtitle = joinByBullet( - song.artists.joinToString { it.name }, - makeTimeString(song.song.duration * 1000L) - ), - badges = badges, - thumbnailContent = { - ItemThumbnail( - thumbnailUrl = if (song.song.isLocal) song.song.localPath else song.song.thumbnailUrl, - albumIndex = albumIndex, - isActive = isActive, - isPlaying = isPlaying, - shape = RoundedCornerShape(ThumbnailCornerRadius), - modifier = Modifier.size(ListThumbnailSize) + val playerConnection = LocalPlayerConnection.current ?: return + val isPlaying by playerConnection.isPlaying.collectAsState() + val mediaMetadata by playerConnection.mediaMetadata.collectAsState() + + val isActive = song.id == mediaMetadata?.id + + val snackbarHostState = remember { SnackbarHostState() } + + val isSelected = selectionIds?.contains(song.id) == true + val onCheckedChange: (Boolean) -> Unit = { + if (it) { + selectionIds?.add(song.id) + } else { + selectionIds?.remove(song.id) + } + } + + val listItem: @Composable () -> Unit = { + ListItem( + title = song.song.title, + subtitle = joinByBullet( + song.artists.joinToString { it.name }, + makeTimeString(song.song.duration * 1000L) + ), + badges = { + if (showLikedIcon && song.song.liked) { + Icon.Favorite() + } + if (showInLibraryIcon && song.song.inLibrary != null) { + Icon.Library() + } + if (showDownloadIcon) { + val download by LocalDownloadUtil.current.getDownload(song.id) + .collectAsState(initial = null) + Icon.Download(download?.state) + } + if (showLocalIcon && song.song.isLocal) { + FolderCopy() + } + }, + thumbnailContent = { + ItemThumbnail( + thumbnailUrl = if (song.song.isLocal) song.song.localPath else song.song.thumbnailUrl, + albumIndex = albumIndex, + isActive = isActive, + isPlaying = isPlaying, + shape = RoundedCornerShape(ThumbnailCornerRadius), + modifier = Modifier.size(ListThumbnailSize) + ) + }, + trailingContent = { + if (available){ + if (inSelectMode) { + Checkbox( + checked = isSelected, + onCheckedChange = onCheckedChange + ) + } else { + IconButton( + onClick = { + if (!disableShowMenu) + { + menuState.show { + SongMenu( + originalSong = song, + playlistSong = playlistSong, + playlistBrowseId = playlistBrowseId, + navController = navController, + onDismiss = menuState::dismiss + ) + } + } + } + ) { + Icon( + Icons.Rounded.MoreVert, + contentDescription = null + ) + } + } + } + + if (showDragHandle && dragHandleModifier != null) { + IconButton( + onClick = { }, + modifier = dragHandleModifier + ) { + Icon( + Icons.Rounded.DragHandle, + contentDescription = null + ) + } + } + }, + isSelected = inSelectMode && isSelected, + isActive = isActive, + available = available, + modifier = modifier.combinedClickable( + onClick = { + if (available){ + if (inSelectMode) { + onCheckedChange(!isSelected) + } else if (song.id == mediaMetadata?.id) { + playerConnection.player.togglePlayPause() + } else { + onPlay() + } + } + }, + onLongClick = { + if (available){ + if (selectionIds == null) + { + menuState.show { + SongMenu( + originalSong = song, + navController = navController, + onDismiss = menuState::dismiss + ) + } + } + if (!inSelectMode) { + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + onSelectModeActivation() + onCheckedChange(true) + } + } + } ) - }, - trailingContent = trailingContent, - modifier = modifier, - isSelected = isSelected, - isActive = isActive, - enabled = song.song.isAvailableOffline() || isNetworkConnected - ) + ) + } + + if (enableSwipeToQueue && available){ + SwipeToQueueBox( + item = song.toMediaItem(), + content = { listItem() }, + snackbarHostState = snackbarHostState + ) + } + else { + listItem() + } } @Composable @@ -445,7 +570,7 @@ fun SongFolderItem( ) }, trailingContent = { - androidx.compose.material3.IconButton( + IconButton( onClick = { menuState.show { FolderMenu( @@ -734,7 +859,7 @@ fun AlbumGridItem( } var downloadState by remember { - mutableStateOf(Download.STATE_STOPPED) + mutableIntStateOf(Download.STATE_STOPPED) } LaunchedEffect(songs) { @@ -1048,8 +1173,8 @@ fun YouTubeListItem( val isNetworkConnected = LocalIsInternetConnected.current val downloads by LocalDownloadUtil.current.downloads.collectAsState() - var enabled = true - if (item is SongItem) { enabled = downloads[item.id]?.isAvailableOffline() ?: false || isNetworkConnected } + var available = true + if (item is SongItem) { available = downloads[item.id]?.isAvailableOffline() ?: false || isNetworkConnected } ListItem( title = item.title, @@ -1084,7 +1209,7 @@ fun YouTubeListItem( modifier = modifier, isSelected = isSelected, isActive = isActive, - enabled = enabled + available = available ) } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/SongMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/SongMenu.kt index d7f06e1e3..c144a67c1 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/SongMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/SongMenu.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.PlaylistAdd import androidx.compose.material.icons.automirrored.rounded.PlaylistPlay @@ -63,6 +64,7 @@ import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R import com.dd3boh.outertune.constants.ListItemHeight import com.dd3boh.outertune.constants.ListThumbnailSize +import com.dd3boh.outertune.constants.ThumbnailCornerRadius import com.dd3boh.outertune.db.entities.Event import com.dd3boh.outertune.db.entities.PlaylistSong import com.dd3boh.outertune.db.entities.Song @@ -76,8 +78,10 @@ import com.dd3boh.outertune.ui.component.DownloadGridMenu import com.dd3boh.outertune.ui.component.GridMenu import com.dd3boh.outertune.ui.component.GridMenuItem import com.dd3boh.outertune.ui.component.ListDialog -import com.dd3boh.outertune.ui.component.SongListItem +import com.dd3boh.outertune.ui.component.ListItem import com.dd3boh.outertune.ui.component.TextFieldDialog +import com.dd3boh.outertune.utils.joinByBullet +import com.dd3boh.outertune.utils.makeTimeString import com.zionhuang.innertube.YouTube import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -227,9 +231,19 @@ fun SongMenu( ) } - SongListItem( - song = song, - badges = {}, + ListItem( + title = song.song.title, + subtitle = joinByBullet( + song.artists.joinToString { it.name }, + makeTimeString(song.song.duration * 1000L) + ), + thumbnailContent = { + AsyncImage( + model = if (song.song.isLocal) song.song.localPath else song.song.thumbnailUrl, + contentDescription = null, + modifier = Modifier.size(ListThumbnailSize).clip(RoundedCornerShape(ThumbnailCornerRadius)) + ) + }, trailingContent = { IconButton( onClick = { diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt index b55857a22..01d3f538a 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt @@ -30,7 +30,6 @@ import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.OfflinePin import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Checkbox import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon @@ -89,8 +88,7 @@ import com.dd3boh.outertune.constants.CONTENT_TYPE_HEADER import com.dd3boh.outertune.constants.ThumbnailCornerRadius import com.dd3boh.outertune.db.entities.Album import com.dd3boh.outertune.db.entities.Song -import com.dd3boh.outertune.extensions.toMediaItem -import com.dd3boh.outertune.extensions.togglePlayPause +import com.dd3boh.outertune.extensions.getAvailableSongs import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.ExoDownloadService import com.dd3boh.outertune.playback.queues.ListQueue @@ -101,14 +99,12 @@ import com.dd3boh.outertune.ui.component.LocalMenuState import com.dd3boh.outertune.ui.component.NavigationTitle import com.dd3boh.outertune.ui.component.SelectHeader import com.dd3boh.outertune.ui.component.SongListItem -import com.dd3boh.outertune.ui.component.SwipeToQueueBox import com.dd3boh.outertune.ui.component.YouTubeGridItem import com.dd3boh.outertune.ui.component.shimmer.ButtonPlaceholder import com.dd3boh.outertune.ui.component.shimmer.ListItemPlaceHolder import com.dd3boh.outertune.ui.component.shimmer.ShimmerHost import com.dd3boh.outertune.ui.component.shimmer.TextPlaceholder import com.dd3boh.outertune.ui.menu.AlbumMenu -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.ui.menu.YouTubeAlbumMenu import com.dd3boh.outertune.ui.utils.backToMain import com.dd3boh.outertune.ui.utils.getNSongsString @@ -141,7 +137,7 @@ fun AlbumScreen( // multiselect var inSelectMode by rememberSaveable { mutableStateOf(false) } val selection = rememberSaveable( - saver = listSaver, Int>( + saver = listSaver, String>( save = { it.toList() }, restore = { it.toMutableStateList() } ) @@ -395,15 +391,15 @@ fun AlbumScreen( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(start = 16.dp) ) { - if (inSelectMode && albumWithSongs?.songs != null) { + if (inSelectMode) { SelectHeader( - selectedItems = selection.mapNotNull { index -> - albumWithSongs?.songs?.getOrNull(index) - }.map { it.toMediaMetadata()}, - totalItemCount = albumWithSongs!!.songs.size, + selectedItems = selection.mapNotNull { id -> + albumWithSongsLocal.songs.find { it.song.id == id } + }.map { it.toMediaMetadata() }, + totalItemCount = albumWithSongsLocal.songs.getAvailableSongs(isNetworkConnected).size, onSelectAll = { selection.clear() - selection.addAll(albumWithSongs!!.songs.indices) + selection.addAll(albumWithSongsLocal.songs.getAvailableSongs(isNetworkConnected).map { it.id }) }, onDeselectAll = { selection.clear() }, menuState = menuState, @@ -414,90 +410,29 @@ fun AlbumScreen( } - if (albumWithSongs?.songs != null) { - itemsIndexed( - items = albumWithSongs!!.songs, - key = { _, song -> song.id } - ) { index, song -> - val onCheckedChange: (Boolean) -> Unit = { - if (it) { - selection.add(index) - } else { - selection.remove(index) - } - } - - val enabled = song.song.isAvailableOffline() || isNetworkConnected - SwipeToQueueBox( - enabled = enabled, - item = song.toMediaItem(), - content = { - SongListItem( - song = song, - albumIndex = index + 1, - isActive = song.id == mediaMetadata?.id, - isPlaying = isPlaying, - showInLibraryIcon = true, - trailingContent = { - if (inSelectMode) { - Checkbox( - checked = index in selection, - onCheckedChange = onCheckedChange - ) - } else { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = song, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - } - }, - isSelected = inSelectMode && index in selection, - modifier = Modifier - .fillMaxWidth() - .combinedClickable( - onClick = { - if (inSelectMode) { - onCheckedChange(index !in selection) - } else if (enabled) { - if (song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue( - ListQueue( - title = albumWithSongsLocal.album.title, - items = albumWithSongsLocal.songs.map { it.toMediaMetadata() }, - startIndex = index, - playlistId = albumWithSongsLocal.album.playlistId - ) - ) - } - } - }, - onLongClick = { - if (!inSelectMode) { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - inSelectMode = true - onCheckedChange(true) - } - } - ) + itemsIndexed( + items = albumWithSongs!!.songs, + key = { _, song -> song.id } + ) { index, song -> + SongListItem( + song = song, + albumIndex = index + 1, + onPlay = { + playerConnection.playQueue( + ListQueue( + title = albumWithSongsLocal.album.title, + items = albumWithSongsLocal.songs.map { it.toMediaMetadata() }, + startIndex = index, + playlistId = albumWithSongsLocal.album.playlistId ) - }, - snackbarHostState = snackbarHostState - ) - } + ) + }, + onSelectModeActivation = { inSelectMode = true }, + inSelectMode = inSelectMode, + selectionIds = selection, + navController = navController, + modifier = Modifier.fillMaxWidth().animateItem() + ) } if (otherVersions.isNotEmpty()) { @@ -517,8 +452,7 @@ fun AlbumScreen( isActive = mediaMetadata?.album?.id == item.id, isPlaying = isPlaying, coroutineScope = scope, - modifier = - Modifier + modifier = Modifier .combinedClickable( onClick = { navController.navigate("album/${item.id}") }, onLongClick = { diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt index 6efa27f45..095d619b9 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt @@ -26,7 +26,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.Search -import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -53,9 +52,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color -import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.TextFieldValue @@ -73,6 +70,7 @@ import com.dd3boh.outertune.R import com.dd3boh.outertune.constants.HistorySource import com.dd3boh.outertune.constants.InnerTubeCookieKey import com.dd3boh.outertune.db.entities.EventWithSong +import com.dd3boh.outertune.extensions.getAvailableSongs import com.dd3boh.outertune.extensions.isAvailableOffline import com.dd3boh.outertune.extensions.toMediaItem import com.dd3boh.outertune.extensions.togglePlayPause @@ -88,7 +86,6 @@ import com.dd3boh.outertune.ui.component.SelectHeader import com.dd3boh.outertune.ui.component.SongListItem import com.dd3boh.outertune.ui.component.SwipeToQueueBox import com.dd3boh.outertune.ui.component.YouTubeListItem -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.ui.menu.YouTubeSongMenu import com.dd3boh.outertune.ui.utils.backToMain import com.dd3boh.outertune.utils.rememberPreference @@ -103,7 +100,6 @@ fun HistoryScreen( navController: NavController, viewModel: HistoryViewModel = hiltViewModel(), ) { - val haptic = LocalHapticFeedback.current val database = LocalDatabase.current val context = LocalContext.current val menuState = LocalMenuState.current @@ -136,7 +132,7 @@ fun HistoryScreen( var inSelectMode by rememberSaveable { mutableStateOf(false) } val selection = rememberSaveable( - saver = listSaver, Long>( + saver = listSaver, String>( save = { it.toList() }, restore = { it.toMutableStateList() } ) @@ -179,15 +175,15 @@ fun HistoryScreen( } .filterValues { it.isNotEmpty() } } - val filteredEventIndex: Map by remember(filteredEventsMap) { + val filteredEventIndex: Map by remember(filteredEventsMap) { derivedStateOf { - filteredEventsMap.flatMap { it.value }.associateBy { it.event.id } + filteredEventsMap.flatMap { it.value }.associateBy { it.song.song.id } } } LaunchedEffect(filteredEventsMap) { - selection.fastForEachReversed { eventId -> - if (filteredEventIndex[eventId] == null) { - selection.remove(eventId) + selection.fastForEachReversed { songId -> + if (filteredEventIndex[songId] == null) { + selection.remove(songId) } } } @@ -282,7 +278,7 @@ fun HistoryScreen( items = section.songs, key = { it.id } ) { song -> - val enabled = downloads[song.id]?.isAvailableOffline() ?: false || isNetworkConnected + val available = downloads[song.id]?.isAvailableOffline() ?: false || isNetworkConnected val content: @Composable () -> Unit = { YouTubeListItem( @@ -290,28 +286,30 @@ fun HistoryScreen( isActive = song.id == mediaMetadata?.id, isPlaying = isPlaying, trailingContent = { - IconButton( - onClick = { - menuState.show { - YouTubeSongMenu( - song = song, - navController = navController, - onDismiss = menuState::dismiss - ) + if (available){ + IconButton( + onClick = { + menuState.show { + YouTubeSongMenu( + song = song, + navController = navController, + onDismiss = menuState::dismiss + ) + } } + ) { + Icon( + Icons.Rounded.MoreVert, + contentDescription = null + ) } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) } }, modifier = Modifier .fillMaxWidth() .combinedClickable( onClick = { - if (enabled) { + if (available) { if (song.id == mediaMetadata?.id) { playerConnection.player.togglePlayPause() } else if (song.id.startsWith("LA")) { @@ -336,12 +334,14 @@ fun HistoryScreen( } }, onLongClick = { - menuState.show { - YouTubeSongMenu( - song = song, - navController = navController, - onDismiss = menuState::dismiss - ) + if (available) { + menuState.show { + YouTubeSongMenu( + song = song, + navController = navController, + onDismiss = menuState::dismiss + ) + } } } ) @@ -349,12 +349,15 @@ fun HistoryScreen( ) } - SwipeToQueueBox( - enabled = enabled, - item = song.toMediaItem(), - content = { content() }, - snackbarHostState = snackbarHostState - ) + if (available){ + SwipeToQueueBox( + item = song.toMediaItem(), + content = { content() }, + snackbarHostState = snackbarHostState + ) + } else { + content() + } } } } else { @@ -376,23 +379,23 @@ fun HistoryScreen( if (inSelectMode) { SelectHeader( - selectedItems = selection.mapNotNull { eventId -> - filteredEventIndex[eventId]?.song - }.map { it.toMediaMetadata() }, - totalItemCount = selection.size, + selectedItems = eventsMap.flatMap { + group -> group.value.filter{ it.song.song.id in selection } + }.map { it.song.toMediaMetadata() }, + totalItemCount = eventsMap.flatMap { group -> group.value.map { it.song }.getAvailableSongs(isNetworkConnected)}.size, onSelectAll = { selection.clear() selection.addAll(eventsMap.flatMap { group -> - group.value.map { it.event.id } + group.value.filter{ it.song.song.isAvailableOffline() || isNetworkConnected }.map { it.song.song.id } }) }, onDeselectAll = { selection.clear() }, menuState = menuState, onDismiss = onExitSelectionMode, onRemoveFromHistory = { - val sel = selection.mapNotNull { eventId -> - filteredEventIndex[eventId]?.event - } + val sel = eventsMap.flatMap { + group -> group.value.filter{ it.song.song.id in selection } + }.map { it.event } database.query { sel.forEach { delete(it) @@ -409,84 +412,22 @@ fun HistoryScreen( itemsIndexed( items = eventsGroup, ) { index, event -> - val onCheckedChange: (Boolean) -> Unit = { - if (it) { - selection.add(event.event.id) - } else { - selection.remove(event.event.id) - } - } - - val enabled = event.song.song.isAvailableOffline() || isNetworkConnected - - val content: @Composable () -> Unit = { - SongListItem( - song = event.song, - isActive = event.song.id == mediaMetadata?.id, - isPlaying = isPlaying, - showInLibraryIcon = true, - trailingContent = { - if (inSelectMode) { - Checkbox( - checked = event.event.id in selection, - onCheckedChange = onCheckedChange - ) - } else { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = event.song, - event = event.event, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - } - }, - modifier = Modifier - .fillMaxWidth() - .combinedClickable( - onClick = { - if (inSelectMode) { - onCheckedChange(event.event.id !in selection) - } else if (enabled) { - if (event.song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue( - ListQueue( - title = dateAgoToString(dateAgo), - items = eventsGroup.map { it.song.toMediaMetadata() }, - startIndex = index - ) - ) - } - } - }, - onLongClick = { - if (!inSelectMode) { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - inSelectMode = true - onCheckedChange(true) - } - } + SongListItem( + song = event.song, + onPlay = { + playerConnection.playQueue( + ListQueue( + title = dateAgoToString(dateAgo), + items = eventsGroup.map { it.song.toMediaMetadata() }, + startIndex = index ) - .animateItem() - ) - } - SwipeToQueueBox( - enabled = enabled, - item = event.song.toMediaItem(), - content = { content() }, - snackbarHostState = snackbarHostState + ) + }, + onSelectModeActivation = { inSelectMode = true }, + inSelectMode = inSelectMode, + selectionIds = selection, + navController = navController, + modifier = Modifier.fillMaxWidth().animateItem() ) } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt index bc76dab3a..732a2e7d6 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt @@ -60,7 +60,6 @@ import coil.compose.AsyncImage import com.dd3boh.outertune.LocalDatabase import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection -import com.dd3boh.outertune.LocalIsInternetConnected import com.dd3boh.outertune.R import com.dd3boh.outertune.constants.GridThumbnailHeight import com.dd3boh.outertune.constants.InnerTubeCookieKey @@ -123,7 +122,6 @@ fun HomeScreen( val menuState = LocalMenuState.current val database = LocalDatabase.current val playerConnection = LocalPlayerConnection.current ?: return - val isNetworkConnected = LocalIsInternetConnected.current val haptic = LocalHapticFeedback.current val isPlaying by playerConnection.isPlaying.collectAsState() @@ -474,33 +472,18 @@ fun HomeScreen( val song by database.song(originalSong.id).collectAsState(initial = originalSong) SongListItem( song = song!!, - showInLibraryIcon = true, - isActive = song!!.id == mediaMetadata?.id, - isPlaying = isPlaying, - modifier = Modifier - .width(horizontalLazyGridItemWidth) - .combinedClickable( - onClick = { - if (song!!.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue(YouTubeQueue.radio(song!!.toMediaMetadata())) - } - }, - onLongClick = { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - menuState.show { - SongMenu( - originalSong = song!!, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) + onPlay = { + playerConnection.playQueue(YouTubeQueue.radio(song!!.toMediaMetadata())) + }, + onSelectModeActivation = {}, + inSelectMode = false, + selectionIds = null, + navController = navController, + modifier = Modifier.width(horizontalLazyGridItemWidth) ) } } + forgottenFavorites?.takeIf { it.isNotEmpty() }?.let { forgottenFavorites -> NavigationTitle( title = stringResource(R.string.forgotten_favorites) @@ -523,32 +506,14 @@ fun HomeScreen( val song by database.song(originalSong.id).collectAsState(initial = originalSong) SongListItem( song = song!!, - showInLibraryIcon = true, - isActive = song!!.id == mediaMetadata?.id, - isPlaying = isPlaying, - modifier = Modifier - .width(horizontalLazyGridItemWidth) - .combinedClickable( - onClick = { - if (song!!.song.isAvailableOffline() || isNetworkConnected){ - if (song!!.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue(YouTubeQueue.radio(song!!.toMediaMetadata())) - } - } - }, - onLongClick = { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - menuState.show { - SongMenu( - originalSong = song!!, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) + onPlay = { + playerConnection.playQueue(YouTubeQueue.radio(song!!.toMediaMetadata())) + }, + onSelectModeActivation = {}, + inSelectMode = false, + selectionIds = null, + navController = navController, + modifier = Modifier.width(horizontalLazyGridItemWidth) ) } } @@ -674,30 +639,14 @@ fun HomeScreen( SongListItem( song = song!!, - showInLibraryIcon = true, - isActive = song!!.id == mediaMetadata?.id, - isPlaying = isPlaying, - modifier = Modifier - .width(horizontalLazyGridItemWidth) - .combinedClickable( - onClick = { - if (song!!.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue(YouTubeQueue.radio(song!!.toMediaMetadata())) - } - }, - onLongClick = { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - menuState.show { - SongMenu( - originalSong = song!!, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) + onPlay = { + playerConnection.playQueue(YouTubeQueue.radio(song!!.toMediaMetadata())) + }, + onSelectModeActivation = {}, + inSelectMode = false, + selectionIds = null, + navController = navController, + modifier = Modifier.width(horizontalLazyGridItemWidth) ) } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt index 9f43330c4..98a8e164f 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt @@ -2,11 +2,9 @@ package com.dd3boh.outertune.ui.screens import android.provider.Settings import androidx.activity.compose.BackHandler -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -72,9 +70,7 @@ import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource @@ -125,13 +121,11 @@ import com.zionhuang.innertube.utils.parseCookieString import kotlinx.coroutines.delay import java.time.LocalDateTime -@OptIn(ExperimentalFoundationApi::class) @Composable fun SetupWizard( navController: NavController, ) { val context = LocalContext.current - val haptic = LocalHapticFeedback.current val layoutDirection = LocalLayoutDirection.current val uriHandler = LocalUriHandler.current @@ -286,7 +280,11 @@ fun SetupWizard( colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.primary, BlendMode.SrcIn), modifier = Modifier .clip(CircleShape) - .background(MaterialTheme.colorScheme.surfaceColorAtElevation(NavigationBarDefaults.Elevation)) + .background( + MaterialTheme.colorScheme.surfaceColorAtElevation( + NavigationBarDefaults.Elevation + ) + ) .clickable { } ) Column(verticalArrangement = Arrangement.Center) { @@ -595,27 +593,13 @@ fun SetupWizard( dummySongs.forEach { song -> SongListItem( song = song, - isActive = false, - isPlaying = false, - trailingContent = { - IconButton( - onClick = {} - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - }, - modifier = Modifier - .fillMaxWidth() - .combinedClickable( - onClick = { - }, - onLongClick = { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - } - ) + onPlay = {}, + inSelectMode = false, + selectionIds = mutableListOf(), + onSelectModeActivation = {}, + navController = navController, + enableSwipeToQueue = false, + disableShowMenu = true ) } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/StatsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/StatsScreen.kt index 37f61436d..202848b6e 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/StatsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/StatsScreen.kt @@ -12,17 +12,13 @@ import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack -import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.res.pluralStringResource @@ -31,11 +27,8 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection -import com.dd3boh.outertune.LocalIsInternetConnected import com.dd3boh.outertune.R import com.dd3boh.outertune.constants.StatPeriod -import com.dd3boh.outertune.extensions.toMediaItem -import com.dd3boh.outertune.extensions.togglePlayPause import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.queues.YouTubeQueue import com.dd3boh.outertune.ui.component.AlbumGridItem @@ -45,13 +38,10 @@ import com.dd3boh.outertune.ui.component.IconButton import com.dd3boh.outertune.ui.component.LocalMenuState import com.dd3boh.outertune.ui.component.NavigationTitle import com.dd3boh.outertune.ui.component.SongListItem -import com.dd3boh.outertune.ui.component.SwipeToQueueBox import com.dd3boh.outertune.ui.menu.AlbumMenu import com.dd3boh.outertune.ui.menu.ArtistMenu -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.ui.utils.backToMain import com.dd3boh.outertune.viewmodels.StatsViewModel -import com.zionhuang.innertube.models.WatchEndpoint @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) @Composable @@ -60,13 +50,10 @@ fun StatsScreen( viewModel: StatsViewModel = hiltViewModel(), ) { val menuState = LocalMenuState.current - val isNetworkConnected = LocalIsInternetConnected.current val playerConnection = LocalPlayerConnection.current ?: return val isPlaying by playerConnection.isPlaying.collectAsState() val mediaMetadata by playerConnection.mediaMetadata.collectAsState() - val snackbarHostState = remember { SnackbarHostState() } - val statPeriod by viewModel.statPeriod.collectAsState() val mostPlayedSongs by viewModel.mostPlayedSongs.collectAsState() val mostPlayedArtists by viewModel.mostPlayedArtists.collectAsState() @@ -104,62 +91,18 @@ fun StatsScreen( items = mostPlayedSongs, key = { it.id } ) { song -> - val enabled = song.song.isAvailableOffline() || isNetworkConnected - val content: @Composable () -> Unit = { - SongListItem( - song = song, - isActive = song.id == mediaMetadata?.id, - isPlaying = isPlaying, - trailingContent = { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = song, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - }, - modifier = Modifier - .fillMaxWidth() - .combinedClickable( - onClick = { - if (enabled) { - if (song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue( - YouTubeQueue.radio(song.toMediaMetadata()) - ) - } - } - }, - onLongClick = { - menuState.show { - SongMenu( - originalSong = song, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) - .animateItem() - ) - } - SwipeToQueueBox( - enabled = enabled, - item = song.toMediaItem(), - content = { content() }, - snackbarHostState = snackbarHostState + SongListItem( + song = song, + onPlay = { + playerConnection.playQueue( + YouTubeQueue.radio(song.toMediaMetadata()) + ) + }, + onSelectModeActivation = {}, + inSelectMode = false, + selectionIds = null, + navController = navController, + modifier = Modifier.fillMaxWidth().animateItem() ) } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistItemsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistItemsScreen.kt index 3d9ec6330..a9f5612f8 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistItemsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistItemsScreen.kt @@ -50,6 +50,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection +import com.dd3boh.outertune.constants.CONTENT_TYPE_HEADER import com.dd3boh.outertune.constants.GridThumbnailHeight import com.dd3boh.outertune.extensions.toMediaItem import com.dd3boh.outertune.extensions.togglePlayPause @@ -149,27 +150,36 @@ fun ArtistItemsScreen( } if (itemsPage?.items?.firstOrNull() is SongItem) { - if (inSelectMode) { - Row { - SelectHeader( - selectedItems = selection.mapNotNull { songId -> - songIndex[songId] - }.map { it.toMediaMetadata() }, - totalItemCount = selection.size, - onSelectAll = { - selection.clear() - selection.addAll(itemsPage?.items?.map { it.id }.orEmpty()) - }, - onDeselectAll = { selection.clear() }, - menuState = menuState, - onDismiss = onExitSelectionMode - ) - } - } LazyColumn( state = lazyListState, contentPadding = LocalPlayerAwareWindowInsets.current.asPaddingValues() ) { + item( + key = "header", + contentType = CONTENT_TYPE_HEADER + ) { + if (inSelectMode) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 16.dp) + ) { + SelectHeader( + selectedItems = selection.mapNotNull { songId -> + songIndex[songId] + }.map { it.toMediaMetadata() }, + totalItemCount = selection.size, + onSelectAll = { + selection.clear() + selection.addAll(itemsPage?.items?.map { it.id }.orEmpty()) + }, + onDeselectAll = { selection.clear() }, + menuState = menuState, + onDismiss = onExitSelectionMode + ) + } + } + } + itemsIndexed( items = itemsPage?.items?.filterIsInstance().orEmpty(), key = { _, item -> item.hashCode() } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt index c5b44aa3a..0c718fbdd 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt @@ -97,7 +97,6 @@ import com.dd3boh.outertune.ui.component.YouTubeGridItem import com.dd3boh.outertune.ui.component.YouTubeListItem import com.dd3boh.outertune.ui.component.shimmer.ArtistPagePlaceholder import com.dd3boh.outertune.ui.menu.AlbumMenu -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.ui.menu.YouTubeAlbumMenu import com.dd3boh.outertune.ui.menu.YouTubeArtistMenu import com.dd3boh.outertune.ui.menu.YouTubePlaylistMenu @@ -296,55 +295,23 @@ fun ArtistScreen( items = librarySongs, key = { _, item -> item.hashCode() } ) { index, song -> - val enabled = song.song.isAvailableOffline() || isNetworkConnected - SwipeToQueueBox( - enabled = enabled, - item = song.toMediaItem(), - content = { - SongListItem( - song = song, - isActive = song.id == mediaMetadata?.id, - isPlaying = isPlaying, - trailingContent = { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = song, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) { - Icon( - imageVector = Icons.Rounded.MoreVert, - contentDescription = null - ) - } - }, - modifier = Modifier - .fillMaxWidth() - .combinedClickable { - if (enabled){ - if (song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue( - ListQueue( - title = "Library: ${libraryArtist?.artist?.name}", - items = librarySongs.filter { it.song.isLocal }.toList() - .shuffled().map { it.toMediaMetadata() }, - startIndex = index - ) - ) - } - } - } - .animateItemPlacement() + SongListItem( + song = song, + onPlay = { + playerConnection.playQueue( + ListQueue( + title = "Library: ${libraryArtist?.artist?.name}", + items = librarySongs.filter { it.song.isLocal }.toList() + .shuffled().map { it.toMediaMetadata() }, + startIndex = index + ) ) }, - snackbarHostState = snackbarHostState + onSelectModeActivation = { }, + inSelectMode = false, + selectionIds = null, + navController = navController, + modifier = Modifier.fillMaxWidth().animateItem() ) } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt index 91457a2d8..c98eb324e 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt @@ -1,8 +1,6 @@ package com.dd3boh.outertune.ui.screens.artist import androidx.activity.compose.BackHandler -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -16,12 +14,9 @@ import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack -import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.Shuffle -import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -40,8 +35,6 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.hapticfeedback.HapticFeedbackType -import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel @@ -55,8 +48,7 @@ import com.dd3boh.outertune.constants.ArtistSongSortDescendingKey import com.dd3boh.outertune.constants.ArtistSongSortType import com.dd3boh.outertune.constants.ArtistSongSortTypeKey import com.dd3boh.outertune.constants.CONTENT_TYPE_HEADER -import com.dd3boh.outertune.extensions.toMediaItem -import com.dd3boh.outertune.extensions.togglePlayPause +import com.dd3boh.outertune.extensions.getAvailableSongs import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.queues.ListQueue import com.dd3boh.outertune.ui.component.HideOnScrollFAB @@ -65,8 +57,6 @@ import com.dd3boh.outertune.ui.component.LocalMenuState import com.dd3boh.outertune.ui.component.SelectHeader import com.dd3boh.outertune.ui.component.SongListItem import com.dd3boh.outertune.ui.component.SortHeader -import com.dd3boh.outertune.ui.component.SwipeToQueueBox -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.ui.utils.backToMain import com.dd3boh.outertune.utils.rememberEnumPreference import com.dd3boh.outertune.utils.rememberPreference @@ -76,19 +66,16 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun ArtistSongsScreen( navController: NavController, scrollBehavior: TopAppBarScrollBehavior, viewModel: ArtistSongsViewModel = hiltViewModel(), ) { - val haptic = LocalHapticFeedback.current val menuState = LocalMenuState.current val playerConnection = LocalPlayerConnection.current ?: return val isNetworkConnected = LocalIsInternetConnected.current - val isPlaying by playerConnection.isPlaying.collectAsState() - val mediaMetadata by playerConnection.mediaMetadata.collectAsState() val (sortType, onSortTypeChange) = rememberEnumPreference(ArtistSongSortTypeKey, ArtistSongSortType.CREATE_DATE) val (sortDescending, onSortDescendingChange) = rememberPreference(ArtistSongSortDescendingKey, true) @@ -135,10 +122,10 @@ fun ArtistSongsScreen( selectedItems = selection.mapNotNull { songId -> songs.find { it.id == songId } }.map { it.toMediaMetadata()}, - totalItemCount = songs.size, + totalItemCount = songs.getAvailableSongs(isNetworkConnected).size, onSelectAll = { selection.clear() - selection.addAll(songs.map { it.id }) + selection.addAll(songs.getAvailableSongs(isNetworkConnected).map{ it.id }) }, onDeselectAll = { selection.clear() }, menuState = menuState, @@ -174,89 +161,31 @@ fun ArtistSongsScreen( items = songs, key = { _, item -> item.id } ) { index, song -> - val onCheckedChange: (Boolean) -> Unit = { - if (it) { - selection.add(song.id) - } else { - selection.remove(song.id) - } - } - - val enabled = song.song.isAvailableOffline() || isNetworkConnected - SwipeToQueueBox( - enabled = enabled, - item = song.toMediaItem(), - content = { - SongListItem( - song = song, - isActive = song.id == mediaMetadata?.id, - isPlaying = isPlaying, - trailingContent = { - if (inSelectMode) { - Checkbox( - checked = song.id in selection, - onCheckedChange = onCheckedChange + SongListItem( + song = song, + onPlay = { + viewModel.viewModelScope.launch(Dispatchers.IO) { + val playlistId = YouTube.artist(artist?.id!!).getOrNull() + ?.artist?.shuffleEndpoint?.playlistId + + // for some reason this get called on the wrong thread and crashes, use main + CoroutineScope(Dispatchers.Main).launch { + playerConnection.playQueue( + ListQueue( + title = artist?.artist?.name, + items = songs.map { it.toMediaMetadata() }, + startIndex = index, + playlistId = playlistId ) - } else { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = song, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - } - }, - modifier = Modifier - .fillMaxWidth() - .combinedClickable( - onClick = { - if (inSelectMode) { - onCheckedChange(song.id !in selection) - } else if (enabled) { - if (song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - viewModel.viewModelScope.launch(Dispatchers.IO) { - val playlistId = YouTube.artist(artist?.id!!).getOrNull() - ?.artist?.shuffleEndpoint?.playlistId - - // for some reason this get called on the wrong thread and crashes, use main - CoroutineScope(Dispatchers.Main).launch { - playerConnection.playQueue( - ListQueue( - title = artist?.artist?.name, - items = songs.map { it.toMediaMetadata() }, - startIndex = index, - playlistId = playlistId - ) - ) - } - } - } - } - }, - onLongClick = { - if (!inSelectMode) { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - inSelectMode = true - onCheckedChange(true) - } - } ) - .animateItem() - ) + } + } }, - snackbarHostState = snackbarHostState + onSelectModeActivation = { inSelectMode = true }, + inSelectMode = inSelectMode, + selectionIds = selection, + navController = navController, + modifier = Modifier.fillMaxWidth().animateItem() ) } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryFoldersScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryFoldersScreen.kt index 33db00816..d420c1f70 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryFoldersScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryFoldersScreen.kt @@ -19,13 +19,9 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.Shuffle -import androidx.compose.material3.Checkbox import androidx.compose.material3.DividerDefaults import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -43,8 +39,6 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.hapticfeedback.HapticFeedbackType -import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastSumBy @@ -64,8 +58,6 @@ import com.dd3boh.outertune.constants.SongSortDescendingKey import com.dd3boh.outertune.constants.SongSortType import com.dd3boh.outertune.constants.SongSortTypeKey import com.dd3boh.outertune.db.entities.Song -import com.dd3boh.outertune.extensions.toMediaItem -import com.dd3boh.outertune.extensions.togglePlayPause import com.dd3boh.outertune.models.DirectoryTree import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.queues.ListQueue @@ -93,12 +85,9 @@ fun LibraryFoldersScreen( viewModel: LibrarySongsViewModel = hiltViewModel(), filterContent: @Composable() (() -> Unit)? = null ) { - val haptic = LocalHapticFeedback.current val menuState = LocalMenuState.current val database = LocalDatabase.current val playerConnection = LocalPlayerConnection.current ?: return - val isPlaying by playerConnection.isPlaying.collectAsState() - val mediaMetadata by playerConnection.mediaMetadata.collectAsState() val snackbarHostState = remember { SnackbarHostState() } /** @@ -308,7 +297,7 @@ fun LibraryFoldersScreen( } // separator - if (currDir.subdirs.size > 0 && mutableSongs.size > 0) { + if (currDir.subdirs.isNotEmpty() && mutableSongs.isNotEmpty()) { item( key = "folder_songs_divider", ) { @@ -325,78 +314,22 @@ fun LibraryFoldersScreen( key = { _, item -> item.id }, contentType = { _, _ -> CONTENT_TYPE_SONG } ) { index, song -> - val onCheckedChange: (Boolean) -> Unit = { - if (it) { - selection.add(song.id) - } else { - selection.remove(song.id) - } - } - - SwipeToQueueBox( - item = song.toMediaItem(), - content = { - SongListItem( - song = song, - isActive = song.id == mediaMetadata?.id, - isPlaying = isPlaying, - trailingContent = { - if (inSelectMode) { - Checkbox( - checked = song.id in selection, - onCheckedChange = onCheckedChange - ) - } else { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = song, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - } - }, - isSelected = inSelectMode && song.id in selection, - modifier = Modifier - .fillMaxWidth() - .combinedClickable( - onClick = { - if (inSelectMode) { - onCheckedChange(song.id !in selection) - } else if (song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - println() - playerConnection.playQueue( - ListQueue( - title = currDir.currentDir, - items = mutableSongs.map { it.toMediaMetadata() }, - startIndex = mutableSongs.indexOf(song) - ) - ) - } - }, - onLongClick = { - if (!inSelectMode) { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - inSelectMode = true - onCheckedChange(true) - } - } - ) - .animateItem() + SongListItem( + song = song, + onPlay = { + playerConnection.playQueue( + ListQueue( + title = currDir.currentDir, + items = mutableSongs.map { it.toMediaMetadata() }, + startIndex = mutableSongs.indexOf(song) + ) ) }, - snackbarHostState = snackbarHostState + onSelectModeActivation = { inSelectMode = true }, + inSelectMode = inSelectMode, + selectionIds = selection, + navController = navController, + modifier = Modifier.fillMaxWidth().animateItem() ) } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt index f8775b4c1..dd4d388b4 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt @@ -1,9 +1,7 @@ package com.dd3boh.outertune.ui.screens.library import androidx.activity.compose.BackHandler -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -17,12 +15,8 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.MusicNote import androidx.compose.material.icons.rounded.Shuffle -import androidx.compose.material3.Checkbox -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -40,9 +34,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -50,7 +42,6 @@ import androidx.compose.ui.util.fastForEachReversed import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import androidx.navigation.compose.currentBackStackEntryAsState -import com.dd3boh.outertune.LocalIsInternetConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R @@ -63,8 +54,6 @@ import com.dd3boh.outertune.constants.SongSortType import com.dd3boh.outertune.constants.SongSortTypeKey import com.dd3boh.outertune.db.entities.Song import com.dd3boh.outertune.extensions.isSyncEnabled -import com.dd3boh.outertune.extensions.toMediaItem -import com.dd3boh.outertune.extensions.togglePlayPause import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.queues.ListQueue import com.dd3boh.outertune.ui.component.ChipsRow @@ -74,26 +63,19 @@ import com.dd3boh.outertune.ui.component.LocalMenuState import com.dd3boh.outertune.ui.component.SelectHeader import com.dd3boh.outertune.ui.component.SongListItem import com.dd3boh.outertune.ui.component.SortHeader -import com.dd3boh.outertune.ui.component.SwipeToQueueBox -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.utils.rememberEnumPreference import com.dd3boh.outertune.utils.rememberPreference import com.dd3boh.outertune.viewmodels.LibrarySongsViewModel -@OptIn(ExperimentalFoundationApi::class) @Composable fun LibrarySongsScreen( navController: NavController, viewModel: LibrarySongsViewModel = hiltViewModel(), - libraryFilterContent: @Composable() (() -> Unit)? = null + libraryFilterContent: @Composable (() -> Unit)? = null ) { val context = LocalContext.current - val haptic = LocalHapticFeedback.current val menuState = LocalMenuState.current val playerConnection = LocalPlayerConnection.current ?: return - val isNetworkConnected = LocalIsInternetConnected.current - val isPlaying by playerConnection.isPlaying.collectAsState() - val mediaMetadata by playerConnection.mediaMetadata.collectAsState() val snackbarHostState = remember { SnackbarHostState() } var filter by rememberEnumPreference(SongFilterKey, SongFilter.LIKED) @@ -262,83 +244,23 @@ fun LibrarySongsScreen( key = { _, item -> item.id }, contentType = { _, _ -> CONTENT_TYPE_SONG } ) { index, song -> - val onCheckedChange: (Boolean) -> Unit = { - if (it) { - selection.add(song.id) - } else { - selection.remove(song.id) - } - } - - val enabled = song.song.isAvailableOffline() || isNetworkConnected - SwipeToQueueBox( - enabled = enabled, - item = song.toMediaItem(), - content = { - SongListItem( - song = song, - isActive = song.id == mediaMetadata?.id, - isPlaying = isPlaying, - showLikedIcon = filter != SongFilter.LIKED, - showDownloadIcon = filter != SongFilter.DOWNLOADED, - trailingContent = { - if (inSelectMode) { - Checkbox( - checked = song.id in selection, - onCheckedChange = onCheckedChange - ) - } else { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = song, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - } - }, - isSelected = inSelectMode && song.id in selection, - modifier = Modifier - .fillMaxWidth() - .combinedClickable( - onClick = { - if (inSelectMode) { - onCheckedChange(song.id !in selection) - } else if (enabled){ - if (song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue( - ListQueue( - title = context.getString(R.string.queue_all_songs), - items = songs.map { it.toMediaMetadata() }, - startIndex = index - ) - ) - } - } - }, - onLongClick = { - if (!inSelectMode) { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - inSelectMode = true - onCheckedChange(true) - } - } - ) - .animateItem() + SongListItem( + song = song, + showInLibraryIcon = filter != SongFilter.LIBRARY, + onPlay = { + playerConnection.playQueue( + ListQueue( + title = context.getString(R.string.queue_all_songs), + items = songs.map { it.toMediaMetadata() }, + startIndex = index + ) ) }, - snackbarHostState = snackbarHostState + onSelectModeActivation = { inSelectMode = true }, + inSelectMode = inSelectMode, + selectionIds = selection, + navController = navController, + modifier = Modifier.fillMaxWidth().animateItem() ) } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt index d73941198..7a331dc7c 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt @@ -3,7 +3,6 @@ package com.dd3boh.outertune.ui.screens.playlist import androidx.activity.compose.BackHandler import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -24,7 +23,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.QueueMusic import androidx.compose.material.icons.rounded.Download -import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.MusicNote import androidx.compose.material.icons.rounded.OfflinePin import androidx.compose.material.icons.rounded.PlayArrow @@ -59,9 +57,7 @@ import androidx.compose.runtime.setValue import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -88,9 +84,9 @@ import com.dd3boh.outertune.constants.SongSortTypeKey import com.dd3boh.outertune.constants.ThumbnailCornerRadius import com.dd3boh.outertune.db.entities.PlaylistEntity import com.dd3boh.outertune.db.entities.Song +import com.dd3boh.outertune.extensions.getAvailableSongs import com.dd3boh.outertune.extensions.isSyncEnabled import com.dd3boh.outertune.extensions.toMediaItem -import com.dd3boh.outertune.extensions.togglePlayPause import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.ExoDownloadService import com.dd3boh.outertune.playback.queues.ListQueue @@ -102,8 +98,6 @@ import com.dd3boh.outertune.ui.component.LocalMenuState import com.dd3boh.outertune.ui.component.SelectHeader import com.dd3boh.outertune.ui.component.SongListItem import com.dd3boh.outertune.ui.component.SortHeader -import com.dd3boh.outertune.ui.component.SwipeToQueueBox -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.ui.utils.getNSongsString import com.dd3boh.outertune.utils.makeTimeString import com.dd3boh.outertune.utils.rememberEnumPreference @@ -124,21 +118,18 @@ fun AutoPlaylistScreen( viewModel: AutoPlaylistViewModel = hiltViewModel(), ) { val context = LocalContext.current - val haptic = LocalHapticFeedback.current val menuState = LocalMenuState.current val database = LocalDatabase.current val syncUtils = LocalSyncUtils.current val playerConnection = LocalPlayerConnection.current ?: return val isNetworkConnected = LocalIsInternetConnected.current - val isPlaying by playerConnection.isPlaying.collectAsState() - val mediaMetadata by playerConnection.mediaMetadata.collectAsState() val songs by viewModel.songs.collectAsState() // multiselect var inSelectMode by rememberSaveable { mutableStateOf(false) } val selection = rememberSaveable( - saver = listSaver, Int>( + saver = listSaver, String>( save = { it.toList() }, restore = { it.toMutableStateList() } ) @@ -383,8 +374,7 @@ fun AutoPlaylistScreen( else -> { IconButton( onClick = { - val _songs = songs.map{ it.toMediaMetadata() } - downloadUtil.download(_songs) + downloadUtil.download(songs.map{ it.toMediaMetadata() }) } ) { Icon( @@ -474,11 +464,13 @@ fun AutoPlaylistScreen( ) { if (inSelectMode) { SelectHeader( - selectedItems = selection.map { songs[it] }.map { it.toMediaMetadata() }, - totalItemCount = songs.size, + selectedItems = selection.mapNotNull { id -> + songs.find { it.song.id == id } + }.map { it.toMediaMetadata() }, + totalItemCount = songs.getAvailableSongs(isNetworkConnected).size, onSelectAll = { selection.clear() - selection.addAll(songs.indices) + selection.addAll(songs.getAvailableSongs(isNetworkConnected).map { it.song.id }) }, onDeselectAll = { selection.clear() }, menuState = menuState, @@ -528,78 +520,26 @@ fun AutoPlaylistScreen( items = songs, key = { _, song -> song.id } ) { index, song -> - val onCheckedChange: (Boolean) -> Unit = { - if (it) { - selection.add(index) - } else { - selection.remove(index) - } - } - val enabled = song.song.isAvailableOffline() || isNetworkConnected - SwipeToQueueBox( - enabled = enabled, - item = song.toMediaItem(), - content = { - SongListItem( - song = song, - isActive = song.song.id == mediaMetadata?.id, - isPlaying = isPlaying, - showInLibraryIcon = true, - showLikedIcon = false, - trailingContent = { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = song, - playlistBrowseId = playlist.browseId, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - }, - isSelected = inSelectMode && index in selection, - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .combinedClickable( - onClick = { - if (inSelectMode) { - onCheckedChange(index !in selection) - } else if (enabled) { - if (song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue( - ListQueue( - title = playlist.name, - items = songs.map { it.toMediaMetadata()}, - startIndex = index, - playlistId = playlist.browseId - ) - ) - } - } - }, - onLongClick = { - if (!inSelectMode) { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - inSelectMode = true - onCheckedChange(true) - } - } - ) + SongListItem( + song = song, + onPlay = { + playerConnection.playQueue( + ListQueue( + title = playlist.name, + items = songs.map { it.toMediaMetadata()}, + startIndex = index, + playlistId = playlist.browseId + ) ) }, - snackbarHostState = snackbarHostState + showLikedIcon = playlistType != PlaylistType.LIKE, + showDownloadIcon = playlistType != PlaylistType.DOWNLOAD, + onSelectModeActivation = { inSelectMode = true }, + inSelectMode = inSelectMode, + selectionIds = selection, + navController = navController, + modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.background), ) } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt index b5f1434c6..26335a11b 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt @@ -1,9 +1,7 @@ package com.dd3boh.outertune.ui.screens.playlist import androidx.activity.compose.BackHandler -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background -import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -26,11 +24,9 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.automirrored.rounded.QueueMusic import androidx.compose.material.icons.rounded.Download -import androidx.compose.material.icons.rounded.DragHandle import androidx.compose.material.icons.rounded.Edit import androidx.compose.material.icons.rounded.Lock import androidx.compose.material.icons.rounded.LockOpen -import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.MusicNote import androidx.compose.material.icons.rounded.OfflinePin import androidx.compose.material.icons.rounded.PlayArrow @@ -67,10 +63,8 @@ import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.font.FontWeight @@ -104,9 +98,9 @@ import com.dd3boh.outertune.constants.ThumbnailCornerRadius import com.dd3boh.outertune.db.entities.Playlist import com.dd3boh.outertune.db.entities.PlaylistSong import com.dd3boh.outertune.db.entities.PlaylistSongMap +import com.dd3boh.outertune.extensions.getAvailableSongs import com.dd3boh.outertune.extensions.move import com.dd3boh.outertune.extensions.toMediaItem -import com.dd3boh.outertune.extensions.togglePlayPause import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.ExoDownloadService import com.dd3boh.outertune.playback.queues.ListQueue @@ -120,9 +114,7 @@ import com.dd3boh.outertune.ui.component.LocalMenuState import com.dd3boh.outertune.ui.component.SelectHeader import com.dd3boh.outertune.ui.component.SongListItem import com.dd3boh.outertune.ui.component.SortHeader -import com.dd3boh.outertune.ui.component.SwipeToQueueBox import com.dd3boh.outertune.ui.component.TextFieldDialog -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.ui.utils.backToMain import com.dd3boh.outertune.ui.utils.getLocalThumbnail import com.dd3boh.outertune.ui.utils.getNSongsString @@ -139,7 +131,7 @@ import kotlinx.coroutines.launch import sh.calvin.reorderable.ReorderableItem import sh.calvin.reorderable.rememberReorderableLazyListState -@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun LocalPlaylistScreen( navController: NavController, @@ -147,13 +139,10 @@ fun LocalPlaylistScreen( viewModel: LocalPlaylistViewModel = hiltViewModel(), ) { val context = LocalContext.current - val haptic = LocalHapticFeedback.current val menuState = LocalMenuState.current val database = LocalDatabase.current val playerConnection = LocalPlayerConnection.current ?: return val isNetworkConnected = LocalIsInternetConnected.current - val isPlaying by playerConnection.isPlaying.collectAsState() - val mediaMetadata by playerConnection.mediaMetadata.collectAsState() val playlist by viewModel.playlist.collectAsState() @@ -168,7 +157,7 @@ fun LocalPlaylistScreen( var inSelectMode by rememberSaveable { mutableStateOf(false) } val selection = rememberSaveable( - saver = listSaver, Int>( + saver = listSaver, String>( save = { it.toList() }, restore = { it.toMutableStateList() } ) @@ -411,13 +400,13 @@ fun LocalPlaylistScreen( ) { if (inSelectMode) { SelectHeader( - selectedItems = selection.mapNotNull { mapId -> - songs.find { it.map.id == mapId }?.song + selectedItems = selection.mapNotNull { id -> + songs.find { it.song.id == id }?.song }.map { it.toMediaMetadata() }, - totalItemCount = songs.size, + totalItemCount = songs.map { it.song }.getAvailableSongs(isNetworkConnected).size, onSelectAll = { selection.clear() - selection.addAll(songs.indices) + selection.addAll(songs.map { it.song }.getAvailableSongs(isNetworkConnected).map { it.song.id }) }, onDeselectAll = { selection.clear() }, menuState = menuState, @@ -467,90 +456,27 @@ fun LocalPlaylistScreen( state = reorderableState, key = song.map.id ) { - val onCheckedChange: (Boolean) -> Unit = { - if (it) { - selection.add(song.map.id) - } else { - selection.remove(song.map.id) - } - } - - val enabled = song.song.song.isAvailableOffline() || isNetworkConnected - SwipeToQueueBox( - enabled = enabled, - item = song.song.toMediaItem(), - content = { - SongListItem( - song = song.song, - isActive = song.song.id == mediaMetadata?.id, - isPlaying = isPlaying, - showInLibraryIcon = true, - trailingContent = { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = song.song, - playlistSong = song, - playlistBrowseId = playlist?.playlist?.browseId, - navController = navController, - onDismiss = menuState::dismiss - ) - } - } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - - if (sortType == PlaylistSongSortType.CUSTOM && !locked && editable) { - IconButton( - onClick = { }, - modifier = Modifier.draggableHandle() - ) { - Icon( - Icons.Rounded.DragHandle, - contentDescription = null - ) - } - } - }, - isSelected = inSelectMode && song.map.id in selection, - modifier = Modifier - .fillMaxWidth() - .background(MaterialTheme.colorScheme.background) - .combinedClickable( - onClick = { - if (inSelectMode) { - onCheckedChange(song.map.id !in selection) - } else if (enabled) { - if (song.song.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - playerConnection.playQueue( - ListQueue( - title = playlist!!.playlist.name, - items = songs.map { it.song.toMediaMetadata() }, - startIndex = index, - playlistId = playlist?.playlist?.browseId - ) - ) - } - } - }, - onLongClick = { - if (!inSelectMode) { - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - inSelectMode = true - onCheckedChange(true) - } - } - ) + SongListItem( + song = song.song, + onPlay = { + playerConnection.playQueue( + ListQueue( + title = playlist!!.playlist.name, + items = songs.map { it.song.toMediaMetadata() }, + startIndex = index, + playlistId = playlist?.playlist?.browseId + ) ) }, - snackbarHostState = snackbarHostState + showDragHandle = sortType == PlaylistSongSortType.CUSTOM && !locked && editable, + dragHandleModifier = Modifier.draggableHandle(), + onSelectModeActivation = { inSelectMode = true }, + inSelectMode = inSelectMode, + selectionIds = selection, + navController = navController, + modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.background), + playlistSong = song, + playlistBrowseId = playlist?.id, ) } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/OnlinePlaylistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/OnlinePlaylistScreen.kt index f1eb0f78a..3de25cccb 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/OnlinePlaylistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/OnlinePlaylistScreen.kt @@ -496,7 +496,9 @@ fun OnlinePlaylistScreen( modifier = Modifier.padding(horizontal = 16.dp) ) { SelectHeader( - selectedItems = selection.map { songs[it] }.map { it.toMediaMetadata() }, + selectedItems = selection.map { + songs[it] + }.map { it.toMediaMetadata() }, totalItemCount = songs.size, onSelectAll = { selection.clear() diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/LocalSearchScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/LocalSearchScreen.kt index 652c5d4d4..f8d648cc5 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/LocalSearchScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/LocalSearchScreen.kt @@ -1,6 +1,5 @@ package com.dd3boh.outertune.ui.screens.search -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -16,10 +15,8 @@ import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.NavigateNext -import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.Search import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState @@ -38,7 +35,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController -import com.dd3boh.outertune.LocalIsInternetConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R @@ -48,24 +44,18 @@ import com.dd3boh.outertune.db.entities.Album import com.dd3boh.outertune.db.entities.Artist import com.dd3boh.outertune.db.entities.Playlist import com.dd3boh.outertune.db.entities.Song -import com.dd3boh.outertune.extensions.toMediaItem -import com.dd3boh.outertune.extensions.togglePlayPause import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.queues.ListQueue import com.dd3boh.outertune.ui.component.AlbumListItem import com.dd3boh.outertune.ui.component.ArtistListItem import com.dd3boh.outertune.ui.component.ChipsRow import com.dd3boh.outertune.ui.component.EmptyPlaceholder -import com.dd3boh.outertune.ui.component.LocalMenuState import com.dd3boh.outertune.ui.component.PlaylistListItem import com.dd3boh.outertune.ui.component.SongListItem -import com.dd3boh.outertune.ui.component.SwipeToQueueBox -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.viewmodels.LocalFilter import com.dd3boh.outertune.viewmodels.LocalSearchViewModel import kotlinx.coroutines.flow.drop -@OptIn(ExperimentalFoundationApi::class) @Composable fun LocalSearchScreen( query: String, @@ -75,9 +65,7 @@ fun LocalSearchScreen( ) { val context = LocalContext.current val keyboardController = LocalSoftwareKeyboardController.current - val menuState = LocalMenuState.current val playerConnection = LocalPlayerConnection.current ?: return - val isNetworkConnected = LocalIsInternetConnected.current val isPlaying by playerConnection.isPlaying.collectAsState() val mediaMetadata by playerConnection.mediaMetadata.collectAsState() @@ -159,57 +147,24 @@ fun LocalSearchScreen( ) { item -> when (item) { is Song -> { - val enabled = item.song.isAvailableOffline() || isNetworkConnected - SwipeToQueueBox( - enabled = enabled, - item = item.toMediaItem(), - content = { - SongListItem( - song = item, - isActive = item.id == mediaMetadata?.id, - isPlaying = isPlaying, - trailingContent = { - IconButton( - onClick = { - menuState.show { - SongMenu( - originalSong = item, - navController = navController - ) { - onDismiss() - menuState.dismiss() - } - } - } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) - } - }, - modifier = Modifier - .clickable { - if (enabled) { - if (item.id == mediaMetadata?.id) { - playerConnection.player.togglePlayPause() - } else { - val songs = result.map - .getOrDefault(LocalFilter.SONG, emptyList()) - .filterIsInstance() - .map { it.toMediaMetadata() } - playerConnection.playQueue(ListQueue( - title = "${context.getString(R.string.queue_searched_songs)} $query", - items = songs, - startIndex = songs.indexOfFirst { it.id == item.id } - )) - } - } - } - .animateItem() - ) + SongListItem( + song = item, + onPlay = { + val songs = result.map + .getOrDefault(LocalFilter.SONG, emptyList()) + .filterIsInstance() + .map { it.toMediaMetadata() } + playerConnection.playQueue(ListQueue( + title = "${context.getString(R.string.queue_searched_songs)} $query", + items = songs, + startIndex = songs.indexOfFirst { it.id == item.id } + )) }, - snackbarHostState = snackbarHostState + onSelectModeActivation = { }, + inSelectMode = false, + selectionIds = null, + navController = navController, + modifier = Modifier.animateItem() ) } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt index 1df5efbed..98730dc36 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt @@ -114,8 +114,8 @@ fun OnlineSearchResult( } val ytItemContent: @Composable LazyItemScope.(YTItem) -> Unit = { item: YTItem -> - var enabled = true - if (item is SongItem) { enabled = downloads[item.id]?.isAvailableOffline() ?: false || isNetworkConnected } + var available = true + if (item is SongItem) { available = downloads[item.id]?.isAvailableOffline() ?: false || isNetworkConnected } val content: @Composable () -> Unit = { YouTubeListItem( @@ -127,40 +127,42 @@ fun OnlineSearchResult( }, isPlaying = isPlaying, trailingContent = { - IconButton( - onClick = { - menuState.show { - when (item) { - is SongItem -> YouTubeSongMenu( - song = item, - navController = navController, - onDismiss = menuState::dismiss - ) + if (available) { + IconButton( + onClick = { + menuState.show { + when (item) { + is SongItem -> YouTubeSongMenu( + song = item, + navController = navController, + onDismiss = menuState::dismiss + ) - is AlbumItem -> YouTubeAlbumMenu( - albumItem = item, - navController = navController, - onDismiss = menuState::dismiss - ) + is AlbumItem -> YouTubeAlbumMenu( + albumItem = item, + navController = navController, + onDismiss = menuState::dismiss + ) - is ArtistItem -> YouTubeArtistMenu( - artist = item, - onDismiss = menuState::dismiss - ) + is ArtistItem -> YouTubeArtistMenu( + artist = item, + onDismiss = menuState::dismiss + ) - is PlaylistItem -> YouTubePlaylistMenu( - playlist = item, - coroutineScope = coroutineScope, - onDismiss = menuState::dismiss - ) + is PlaylistItem -> YouTubePlaylistMenu( + playlist = item, + coroutineScope = coroutineScope, + onDismiss = menuState::dismiss + ) + } } } + ) { + Icon( + Icons.Rounded.MoreVert, + contentDescription = null + ) } - ) { - Icon( - Icons.Rounded.MoreVert, - contentDescription = null - ) } }, modifier = Modifier @@ -168,7 +170,7 @@ fun OnlineSearchResult( onClick = { when (item) { is SongItem -> { - if (enabled) { + if (available) { if (item.id == mediaMetadata?.id) { playerConnection.player.togglePlayPause() } else { @@ -213,7 +215,7 @@ fun OnlineSearchResult( if (item !is SongItem) content() else SwipeToQueueBox( - enabled = enabled, + enabled = available, item = item.toMediaItem(), content = { content() }, snackbarHostState = snackbarHostState diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt index 3d45d8702..af1e0253f 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt @@ -1,6 +1,5 @@ package com.dd3boh.outertune.ui.screens.search -import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row @@ -70,7 +69,6 @@ import com.zionhuang.innertube.models.PlaylistItem import com.zionhuang.innertube.models.SongItem import kotlinx.coroutines.flow.drop -@OptIn(ExperimentalFoundationApi::class) @Composable fun OnlineSearchScreen( query: String, @@ -173,8 +171,8 @@ fun OnlineSearchScreen( items = viewState.items, key = { it.id } ) { item -> - var enabled = true - if (item is SongItem) { enabled = downloads[item.id]?.isAvailableOffline() ?: false || isNetworkConnected } + var available = true + if (item is SongItem) { available = downloads[item.id]?.isAvailableOffline() ?: false || isNetworkConnected } val content: @Composable () -> Unit = { YouTubeListItem( @@ -186,51 +184,53 @@ fun OnlineSearchScreen( }, isPlaying = isPlaying, trailingContent = { - IconButton( - onClick = { - menuState.show { - when (item) { - is SongItem -> - YouTubeSongMenu( - song = item, - navController = navController, - onDismiss = menuState::dismiss, - ) - - is AlbumItem -> - YouTubeAlbumMenu( - albumItem = item, - navController = navController, - onDismiss = menuState::dismiss, - ) - - is ArtistItem -> - YouTubeArtistMenu( - artist = item, - onDismiss = menuState::dismiss, - ) - - is PlaylistItem -> - YouTubePlaylistMenu( - playlist = item, - coroutineScope = scope, - onDismiss = menuState::dismiss, - ) + if (available) { + IconButton( + onClick = { + menuState.show { + when (item) { + is SongItem -> + YouTubeSongMenu( + song = item, + navController = navController, + onDismiss = menuState::dismiss, + ) + + is AlbumItem -> + YouTubeAlbumMenu( + albumItem = item, + navController = navController, + onDismiss = menuState::dismiss, + ) + + is ArtistItem -> + YouTubeArtistMenu( + artist = item, + onDismiss = menuState::dismiss, + ) + + is PlaylistItem -> + YouTubePlaylistMenu( + playlist = item, + coroutineScope = scope, + onDismiss = menuState::dismiss, + ) + } } } + ) { + Icon( + imageVector = Icons.Rounded.MoreVert, + contentDescription = null + ) } - ) { - Icon( - imageVector = Icons.Rounded.MoreVert, - contentDescription = null - ) } }, modifier = Modifier .clickable { when (item) { is SongItem -> { - if (enabled){ + if (available){ if (item.id == mediaMetadata?.id) { playerConnection.player.togglePlayPause() } else if (item.id.startsWith("LA")) { @@ -284,7 +284,7 @@ fun OnlineSearchScreen( if (item !is SongItem) content() else { SwipeToQueueBox( - enabled = enabled, + enabled = available, item = item.toMediaItem(), content = { content() }, snackbarHostState = snackbarHostState From 87a94d38a087a8c3789237b07b1b2b9bf42b7baf Mon Sep 17 00:00:00 2001 From: mattcarter11 <38189440+mattcarter11@users.noreply.github.com> Date: Tue, 17 Dec 2024 23:12:34 +0100 Subject: [PATCH 2/6] songs: add play when offline logic (only play offline items) --- .../java/com/dd3boh/outertune/MainActivity.kt | 6 +-- .../dd3boh/outertune/ui/component/Items.kt | 6 +-- .../com/dd3boh/outertune/ui/menu/AlbumMenu.kt | 42 ++++++++++++------- .../dd3boh/outertune/ui/menu/ArtistMenu.kt | 4 ++ .../dd3boh/outertune/ui/menu/PlaylistMenu.kt | 37 ++++++++++------ .../outertune/ui/screens/AlbumScreen.kt | 15 ++++--- .../outertune/ui/screens/HistoryScreen.kt | 8 ++-- .../ui/screens/artist/ArtistScreen.kt | 18 ++++---- .../ui/screens/artist/ArtistSongsScreen.kt | 12 ++++-- .../ui/screens/library/LibrarySongsScreen.kt | 11 ++++- .../ui/screens/playlist/AutoPlaylistScreen.kt | 4 +- .../screens/playlist/LocalPlaylistScreen.kt | 16 +++++-- .../ui/screens/search/OnlineSearchResult.kt | 4 +- .../ui/screens/search/OnlineSearchScreen.kt | 4 +- 14 files changed, 124 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/com/dd3boh/outertune/MainActivity.kt b/app/src/main/java/com/dd3boh/outertune/MainActivity.kt index 404725099..8325258ec 100644 --- a/app/src/main/java/com/dd3boh/outertune/MainActivity.kt +++ b/app/src/main/java/com/dd3boh/outertune/MainActivity.kt @@ -314,7 +314,7 @@ class MainActivity : ComponentActivity() { setContent { val connectivityObserver = NetworkConnectivityObserver(this) - val isInternetConnected by connectivityObserver.networkStatus.collectAsState(false) + val isNetworkConnected by connectivityObserver.networkStatus.collectAsState(false) val enableDynamicTheme by rememberPreference(DynamicThemeKey, defaultValue = true) val darkTheme by rememberEnumPreference(DarkModeKey, defaultValue = DarkMode.AUTO) @@ -692,7 +692,7 @@ class MainActivity : ComponentActivity() { LocalDownloadUtil provides downloadUtil, LocalShimmerTheme provides ShimmerTheme, LocalSyncUtils provides syncUtils, - LocalIsInternetConnected provides isInternetConnected + LocalIsNetworkConnected provides isNetworkConnected ) { Scaffold( topBar = { @@ -1269,4 +1269,4 @@ val LocalPlayerConnection = staticCompositionLocalOf { error( val LocalPlayerAwareWindowInsets = compositionLocalOf { error("No WindowInsets provided") } val LocalDownloadUtil = staticCompositionLocalOf { error("No DownloadUtil provided") } val LocalSyncUtils = staticCompositionLocalOf { error("No SyncUtils provided") } -val LocalIsInternetConnected = staticCompositionLocalOf { error("No Network Status provided") } +val LocalIsNetworkConnected = staticCompositionLocalOf { error("No Network Status provided") } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt b/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt index 4331be38e..3ed6b5fce 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt @@ -89,7 +89,7 @@ import androidx.navigation.NavController import coil.compose.AsyncImage import com.dd3boh.outertune.LocalDatabase import com.dd3boh.outertune.LocalDownloadUtil -import com.dd3boh.outertune.LocalIsInternetConnected +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R import com.dd3boh.outertune.constants.GridThumbnailHeight @@ -374,7 +374,7 @@ fun SongListItem( ) { val menuState = LocalMenuState.current val haptic = LocalHapticFeedback.current - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val available = song.song.isAvailableOffline() || isNetworkConnected val playerConnection = LocalPlayerConnection.current ?: return @@ -1170,7 +1170,7 @@ fun YouTubeListItem( isPlaying: Boolean = false, trailingContent: @Composable RowScope.() -> Unit = {}, ) { - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val downloads by LocalDownloadUtil.current.downloads.collectAsState() var available = true diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/AlbumMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/AlbumMenu.kt index dde38e368..3998d7bbf 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/AlbumMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/AlbumMenu.kt @@ -59,6 +59,7 @@ import androidx.navigation.NavController import coil.compose.AsyncImage import com.dd3boh.outertune.LocalDatabase import com.dd3boh.outertune.LocalDownloadUtil +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R import com.dd3boh.outertune.constants.ListItemHeight @@ -89,6 +90,7 @@ fun AlbumMenu( val database = LocalDatabase.current val downloadUtil = LocalDownloadUtil.current val playerConnection = LocalPlayerConnection.current ?: return + val isNetworkConnected = LocalIsNetworkConnected.current val scope = rememberCoroutineScope() val libraryAlbum by database.album(originalAlbum.id).collectAsState(initial = originalAlbum) val album = libraryAlbum ?: originalAlbum @@ -98,6 +100,13 @@ fun AlbumMenu( val allInLibrary = remember(songs) { songs.all { it.song.inLibrary != null } } + + val songsAvailable = { + songs.filter { it.song.isAvailableOffline() || isNetworkConnected } + .map { it.toMediaMetadata() } + .toList() + } + // for when local albums are a thing // val allLocal by remember(songs) { // if only local songs in this selection // mutableStateOf(songs.isNotEmpty() && songs.all { it.song.isLocal }) @@ -330,21 +339,24 @@ fun AlbumMenu( showSelectArtistDialog = true } } - GridMenuItem( - icon = { - Icon( - imageVector = Icons.Rounded.Sync, - contentDescription = null, - modifier = Modifier.graphicsLayer(rotationZ = rotationAnimation) - ) - }, - title = R.string.refetch - ) { - refetchIconDegree -= 360 - scope.launch(Dispatchers.IO) { - YouTube.album(album.id).onSuccess { - database.transaction { - update(album.album, it) + if (isNetworkConnected) + { + GridMenuItem( + icon = { + Icon( + imageVector = Icons.Rounded.Sync, + contentDescription = null, + modifier = Modifier.graphicsLayer(rotationZ = rotationAnimation) + ) + }, + title = R.string.refetch + ) { + refetchIconDegree -= 360 + scope.launch(Dispatchers.IO) { + YouTube.album(album.id).onSuccess { + database.transaction { + update(album.album, it) + } } } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/ArtistMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/ArtistMenu.kt index 81100f583..a211bc9e1 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/ArtistMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/ArtistMenu.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.dd3boh.outertune.LocalDatabase +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R import com.dd3boh.outertune.constants.ArtistSongSortType @@ -45,6 +46,7 @@ fun ArtistMenu( val context = LocalContext.current val database = LocalDatabase.current val playerConnection = LocalPlayerConnection.current ?: return + val isNetworkConnected = LocalIsNetworkConnected.current val artistState = database.artist(originalArtist.id).collectAsState(initial = originalArtist) val artist = artistState.value ?: originalArtist @@ -86,6 +88,7 @@ fun ArtistMenu( coroutineScope.launch { val songs = withContext(Dispatchers.IO) { database.artistSongs(artist.id, ArtistSongSortType.CREATE_DATE, true).first() + .filter { it.song.isAvailableOffline() || isNetworkConnected } .map { it.toMediaMetadata() } } @@ -110,6 +113,7 @@ fun ArtistMenu( coroutineScope.launch { val songs = withContext(Dispatchers.IO) { database.artistSongs(artist.id, ArtistSongSortType.CREATE_DATE, true).first() + .filter { it.song.isAvailableOffline() || isNetworkConnected } .map { it.toMediaMetadata() } .shuffled() } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt index f6d38375d..3977df7e1 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt @@ -43,6 +43,7 @@ import androidx.media3.exoplayer.offline.Download import androidx.media3.exoplayer.offline.DownloadService import com.dd3boh.outertune.LocalDatabase import com.dd3boh.outertune.LocalDownloadUtil +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R import com.dd3boh.outertune.db.entities.Playlist @@ -76,11 +77,18 @@ fun PlaylistMenu( val database = LocalDatabase.current val downloadUtil = LocalDownloadUtil.current val playerConnection = LocalPlayerConnection.current ?: return + val isNetworkConnected = LocalIsNetworkConnected.current val dbPlaylist by database.playlist(playlist.id).collectAsState(initial = playlist) var songs by remember { mutableStateOf(emptyList()) } + val songsAvailable = { + songs.filter { it.song.isAvailableOffline() || isNetworkConnected } + .map { it.toMediaMetadata() } + .toList() + } + LaunchedEffect(Unit) { database.playlistSongs(playlist.id).collect { songs = it.map(PlaylistSong::song) @@ -294,7 +302,7 @@ fun PlaylistMenu( onDismiss() playerConnection.playQueue(ListQueue( title = playlist.playlist.name, - items = songs.map { it.toMediaMetadata()}, + items = songsAvailable(), playlistId = playlist.playlist.browseId )) } @@ -306,22 +314,25 @@ fun PlaylistMenu( onDismiss() playerConnection.playQueue(ListQueue( title = playlist.playlist.name, - items = songs.shuffled().map { it.toMediaMetadata() }, + items = songsAvailable().shuffled(), playlistId = playlist.playlist.browseId )) } - playlist.playlist.browseId?.let { browseId -> - playlist.playlist.radioEndpointParams?.let { radioEndpointParams -> - GridMenuItem( - icon = Icons.Rounded.Radio, - title = R.string.start_radio - ) { - playerConnection.playQueue(YouTubeQueue(WatchEndpoint( - playlistId = "RDAMPL$browseId", - params = radioEndpointParams, - ))) - onDismiss() + if (isNetworkConnected) + { + playlist.playlist.browseId?.let { browseId -> + playlist.playlist.radioEndpointParams?.let { radioEndpointParams -> + GridMenuItem( + icon = Icons.Rounded.Radio, + title = R.string.start_radio + ) { + playerConnection.playQueue(YouTubeQueue(WatchEndpoint( + playlistId = "RDAMPL$browseId", + params = radioEndpointParams, + ))) + onDismiss() + } } } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt index 01d3f538a..b7cb4bf81 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt @@ -79,7 +79,7 @@ import androidx.navigation.NavController import coil.compose.AsyncImage import com.dd3boh.outertune.LocalDatabase import com.dd3boh.outertune.LocalDownloadUtil -import com.dd3boh.outertune.LocalIsInternetConnected +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R @@ -87,7 +87,6 @@ import com.dd3boh.outertune.constants.AlbumThumbnailSize import com.dd3boh.outertune.constants.CONTENT_TYPE_HEADER import com.dd3boh.outertune.constants.ThumbnailCornerRadius import com.dd3boh.outertune.db.entities.Album -import com.dd3boh.outertune.db.entities.Song import com.dd3boh.outertune.extensions.getAvailableSongs import com.dd3boh.outertune.models.toMediaMetadata import com.dd3boh.outertune.playback.ExoDownloadService @@ -123,7 +122,7 @@ fun AlbumScreen( val menuState = LocalMenuState.current val database = LocalDatabase.current val playerConnection = LocalPlayerConnection.current ?: return - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val scope = rememberCoroutineScope() @@ -134,6 +133,12 @@ fun AlbumScreen( val otherVersions by viewModel.otherVersions.collectAsState() val state = rememberLazyListState() + val songsAvailable = { + albumWithSongs?.songs?.filter { it.song.isAvailableOffline() || isNetworkConnected } + ?.map { it.toMediaMetadata() } + ?.toList() ?: emptyList() + } + // multiselect var inSelectMode by rememberSaveable { mutableStateOf(false) } val selection = rememberSaveable( @@ -339,7 +344,7 @@ fun AlbumScreen( playerConnection.playQueue( ListQueue( title = albumWithSongsLocal.album.title, - items = albumWithSongsLocal.songs.map(Song::toMediaMetadata), + items = songsAvailable(), playlistId = albumWithSongsLocal.album.playlistId ) ) @@ -363,7 +368,7 @@ fun AlbumScreen( playerConnection.playQueue( ListQueue( title = albumWithSongsLocal.album.title, - items = albumWithSongsLocal.songs.shuffled().map(Song::toMediaMetadata), + items = songsAvailable().shuffled(), playlistId = albumWithSongsLocal.album.playlistId ) ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt index 095d619b9..d5eb250e2 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt @@ -63,7 +63,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.dd3boh.outertune.LocalDatabase import com.dd3boh.outertune.LocalDownloadUtil -import com.dd3boh.outertune.LocalIsInternetConnected +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R @@ -104,7 +104,7 @@ fun HistoryScreen( val context = LocalContext.current val menuState = LocalMenuState.current val playerConnection = LocalPlayerConnection.current ?: return - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val downloads by LocalDownloadUtil.current.downloads.collectAsState() val isPlaying by playerConnection.isPlaying.collectAsState() val mediaMetadata by playerConnection.mediaMetadata.collectAsState() @@ -442,7 +442,9 @@ fun HistoryScreen( playerConnection.playQueue( ListQueue( title = context.getString(R.string.history), - items = filteredEventIndex.values.map { it.song.toMediaMetadata() }.shuffled(), + items = filteredEventIndex.values + .filter { it.song.song.isAvailableOffline() || isNetworkConnected } + .map { it.song.toMediaMetadata() }.shuffled(), ) ) } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt index 0c718fbdd..4a8db582a 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt @@ -72,13 +72,12 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import coil.compose.AsyncImage import com.dd3boh.outertune.LocalDatabase -import com.dd3boh.outertune.LocalIsInternetConnected +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R import com.dd3boh.outertune.constants.AppBarHeight import com.dd3boh.outertune.db.entities.ArtistEntity -import com.dd3boh.outertune.db.entities.Song import com.dd3boh.outertune.extensions.toMediaItem import com.dd3boh.outertune.extensions.togglePlayPause import com.dd3boh.outertune.models.toMediaMetadata @@ -120,7 +119,7 @@ fun ArtistScreen( val context = LocalContext.current val database = LocalDatabase.current val menuState = LocalMenuState.current - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val coroutineScope = rememberCoroutineScope() val playerConnection = LocalPlayerConnection.current ?: return val isPlaying by playerConnection.isPlaying.collectAsState() @@ -141,6 +140,12 @@ fun ArtistScreen( } } + val librarySongsAvailable = { + librarySongs.filter { it.song.isAvailableOffline() || isNetworkConnected } + .map { it.toMediaMetadata() } + .toList() + } + LaunchedEffect(isNetworkConnected, libraryArtist) { // always show local page for local artists. Show local page remote artist when offline showLocal = !isNetworkConnected || libraryArtist?.artist?.isLocalArtist == true @@ -210,7 +215,7 @@ fun ArtistScreen( if (!showLocal && watchEndpoint != null) YouTubeQueue(watchEndpoint) else ListQueue( title = artistName, - items = librarySongs.shuffled().map(Song::toMediaMetadata), + items = librarySongsAvailable().shuffled(), ), title = artistName ) @@ -301,8 +306,7 @@ fun ArtistScreen( playerConnection.playQueue( ListQueue( title = "Library: ${libraryArtist?.artist?.name}", - items = librarySongs.filter { it.song.isLocal }.toList() - .shuffled().map { it.toMediaMetadata() }, + items = librarySongsAvailable().shuffled(), startIndex = index ) ) @@ -610,4 +614,4 @@ fun ArtistScreen( ) } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt index c98eb324e..cfc6bad3c 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt @@ -40,7 +40,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.viewModelScope import androidx.navigation.NavController -import com.dd3boh.outertune.LocalIsInternetConnected +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R @@ -75,7 +75,7 @@ fun ArtistSongsScreen( ) { val menuState = LocalMenuState.current val playerConnection = LocalPlayerConnection.current ?: return - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val (sortType, onSortTypeChange) = rememberEnumPreference(ArtistSongSortTypeKey, ArtistSongSortType.CREATE_DATE) val (sortDescending, onSortDescendingChange) = rememberPreference(ArtistSongSortDescendingKey, true) @@ -83,6 +83,12 @@ fun ArtistSongsScreen( val artist by viewModel.artist.collectAsState() val songs by viewModel.songs.collectAsState() + val songsAvailable = { + songs.filter { it.song.isAvailableOffline() || isNetworkConnected } + .map { it.toMediaMetadata() } + .toList() + } + val lazyListState = rememberLazyListState() var inSelectMode by rememberSaveable { mutableStateOf(false) } @@ -213,7 +219,7 @@ fun ArtistSongsScreen( playerConnection.playQueue( ListQueue( title = artist?.artist?.name, - items = songs.shuffled().map { it.toMediaMetadata() }, + items = songsAvailable().shuffled(), playlistId = null, ) ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt index dd4d388b4..b06c7b15a 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt @@ -42,6 +42,7 @@ import androidx.compose.ui.util.fastForEachReversed import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import androidx.navigation.compose.currentBackStackEntryAsState +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R @@ -76,6 +77,8 @@ fun LibrarySongsScreen( val context = LocalContext.current val menuState = LocalMenuState.current val playerConnection = LocalPlayerConnection.current ?: return + val isNetworkConnected = LocalIsNetworkConnected.current + val snackbarHostState = remember { SnackbarHostState() } var filter by rememberEnumPreference(SongFilterKey, SongFilter.LIKED) @@ -99,6 +102,12 @@ fun LibrarySongsScreen( } } + val songsAvailable = { + songs?.filter { it.song.isAvailableOffline() || isNetworkConnected } + ?.map { it.toMediaMetadata() } + ?.toList() ?: emptyList() + } + // multiselect var inSelectMode by rememberSaveable { mutableStateOf(false) } val selection = rememberSaveable( @@ -274,7 +283,7 @@ fun LibrarySongsScreen( playerConnection.playQueue( ListQueue( title = context.getString(R.string.queue_all_songs), - items = songs!!.shuffled().map { it.toMediaMetadata()} + items = songsAvailable().shuffled() ) ) } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt index 7a331dc7c..c84785673 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt @@ -71,7 +71,7 @@ import androidx.media3.exoplayer.offline.DownloadService import androidx.navigation.NavController import com.dd3boh.outertune.LocalDatabase import com.dd3boh.outertune.LocalDownloadUtil -import com.dd3boh.outertune.LocalIsInternetConnected +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.LocalSyncUtils @@ -122,7 +122,7 @@ fun AutoPlaylistScreen( val database = LocalDatabase.current val syncUtils = LocalSyncUtils.current val playerConnection = LocalPlayerConnection.current ?: return - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val songs by viewModel.songs.collectAsState() diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt index 26335a11b..77e165dc6 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt @@ -84,7 +84,7 @@ import androidx.navigation.NavController import coil.compose.AsyncImage import com.dd3boh.outertune.LocalDatabase import com.dd3boh.outertune.LocalDownloadUtil -import com.dd3boh.outertune.LocalIsInternetConnected +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R @@ -142,7 +142,7 @@ fun LocalPlaylistScreen( val menuState = LocalMenuState.current val database = LocalDatabase.current val playerConnection = LocalPlayerConnection.current ?: return - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val playlist by viewModel.playlist.collectAsState() @@ -521,12 +521,19 @@ fun LocalPlaylistHeader( val playerConnection = LocalPlayerConnection.current ?: return val context = LocalContext.current val database = LocalDatabase.current + val isNetworkConnected = LocalIsNetworkConnected.current val scope = rememberCoroutineScope() val playlistLength = remember(songs) { songs.fastSumBy { it.song.song.duration } } + val songsAvailable = { + songs.filter { it.song.song.isAvailableOffline() || isNetworkConnected } + .map { it.song.toMediaMetadata() } + .toList() + } + val downloadUtil = LocalDownloadUtil.current var downloadState by remember { mutableIntStateOf(Download.STATE_STOPPED) @@ -659,6 +666,7 @@ fun LocalPlaylistHeader( if (playlist.playlist.browseId != null) { IconButton( + enabled = isNetworkConnected, onClick = { scope.launch(Dispatchers.IO) { val playlistPage = YouTube.playlist(playlist.playlist.browseId).completed().getOrNull() ?: return@launch @@ -766,7 +774,7 @@ fun LocalPlaylistHeader( playerConnection.playQueue( ListQueue( title = playlist.playlist.name, - items = songs.map { it.song.toMediaMetadata() } + items = songsAvailable() ) ) }, @@ -787,7 +795,7 @@ fun LocalPlaylistHeader( playerConnection.playQueue( ListQueue( title = playlist.playlist.name, - items = songs.shuffled().map { it.song.toMediaMetadata() } + items = songsAvailable().shuffled() ) ) }, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt index 98730dc36..3dd5d4e85 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt @@ -38,7 +38,7 @@ import androidx.compose.ui.res.stringResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.dd3boh.outertune.LocalDownloadUtil -import com.dd3boh.outertune.LocalIsInternetConnected +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R @@ -87,7 +87,7 @@ fun OnlineSearchResult( val playerConnection = LocalPlayerConnection.current ?: return val isPlaying by playerConnection.isPlaying.collectAsState() val mediaMetadata by playerConnection.mediaMetadata.collectAsState() - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val downloads by LocalDownloadUtil.current.downloads.collectAsState() val coroutineScope = rememberCoroutineScope() diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt index af1e0253f..1870816fb 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt @@ -43,7 +43,7 @@ import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.NavController import com.dd3boh.outertune.LocalDatabase import com.dd3boh.outertune.LocalDownloadUtil -import com.dd3boh.outertune.LocalIsInternetConnected +import com.dd3boh.outertune.LocalIsNetworkConnected import com.dd3boh.outertune.LocalPlayerAwareWindowInsets import com.dd3boh.outertune.LocalPlayerConnection import com.dd3boh.outertune.R @@ -83,7 +83,7 @@ fun OnlineSearchScreen( val context = LocalContext.current val keyboardController = LocalSoftwareKeyboardController.current val playerConnection = LocalPlayerConnection.current ?: return - val isNetworkConnected = LocalIsInternetConnected.current + val isNetworkConnected = LocalIsNetworkConnected.current val downloads by LocalDownloadUtil.current.downloads.collectAsState() val scope = rememberCoroutineScope() From d201b1245214c615d068193e9ba5ba8dd4ad7fd4 Mon Sep 17 00:00:00 2001 From: mattcarter11 <38189440+mattcarter11@users.noreply.github.com> Date: Wed, 18 Dec 2024 21:00:59 +0100 Subject: [PATCH 3/6] songs: fix song selection when offline --- .../dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt index b06c7b15a..20a3bf698 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt @@ -173,7 +173,9 @@ fun LibrarySongsScreen( modifier = Modifier.padding(horizontal = 16.dp) ) { if (inSelectMode && songs != null) { - val s: List = (songs as Iterable).toList() + val s: List = (songs as Iterable) + .filter { it.song.isAvailableOffline() || isNetworkConnected } + .toList() SelectHeader( selectedItems = selection.mapNotNull { songId -> s.find { it.id == songId } From 838a01a8d8928af12002e2d7b16d3fd91140a952 Mon Sep 17 00:00:00 2001 From: mikooomich Date: Wed, 1 Jan 2025 12:24:06 -0500 Subject: [PATCH 4/6] app: Fix codestyle --- .../com/dd3boh/outertune/db/daos/ArtistsDao.kt | 2 +- .../com/dd3boh/outertune/extensions/SongsExt.kt | 2 +- .../dd3boh/outertune/playback/DownloadUtil.kt | 16 ++++++++-------- .../com/dd3boh/outertune/ui/component/Items.kt | 11 +++++------ .../com/dd3boh/outertune/ui/menu/AlbumMenu.kt | 3 +-- .../com/dd3boh/outertune/ui/menu/PlaylistMenu.kt | 3 +-- .../outertune/ui/menu/SelectionSongsMenu.kt | 4 ++-- .../dd3boh/outertune/ui/screens/HistoryScreen.kt | 4 ++-- .../dd3boh/outertune/ui/screens/SetupWizzard.kt | 2 ++ .../outertune/ui/screens/artist/ArtistScreen.kt | 4 ++-- .../ui/screens/library/LibraryAlbumsScreen.kt | 2 +- .../ui/screens/library/LibraryArtistsScreen.kt | 2 +- .../ui/screens/library/LibraryPlaylistsScreen.kt | 2 +- .../ui/screens/library/LibrarySongsScreen.kt | 4 ++-- .../ui/screens/playlist/AutoPlaylistScreen.kt | 2 +- .../ui/screens/playlist/LocalPlaylistScreen.kt | 2 +- .../ui/screens/search/OnlineSearchResult.kt | 2 +- .../ui/screens/search/OnlineSearchScreen.kt | 4 ++-- .../ui/screens/settings/ContentSettings.kt | 12 +++++------- .../java/com/dd3boh/outertune/utils/SyncUtils.kt | 12 ++++++------ 20 files changed, 46 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/com/dd3boh/outertune/db/daos/ArtistsDao.kt b/app/src/main/java/com/dd3boh/outertune/db/daos/ArtistsDao.kt index 9e49a7ca3..03833cbfb 100644 --- a/app/src/main/java/com/dd3boh/outertune/db/daos/ArtistsDao.kt +++ b/app/src/main/java/com/dd3boh/outertune/db/daos/ArtistsDao.kt @@ -113,7 +113,7 @@ interface ArtistsDao { ArtistSortType.PLAY_TIME -> "SUM(totalPlayTime) ASC" } - val where = when (filter){ + val where = when (filter) { ArtistFilter.DOWNLOADED -> "song.dateDownload IS NOT NULL" ArtistFilter.LIBRARY -> "song.inLibrary IS NOT NULL" ArtistFilter.LIKED -> "artist.bookmarkedAt IS NOT NULL" diff --git a/app/src/main/java/com/dd3boh/outertune/extensions/SongsExt.kt b/app/src/main/java/com/dd3boh/outertune/extensions/SongsExt.kt index 71021ea0f..49a521939 100644 --- a/app/src/main/java/com/dd3boh/outertune/extensions/SongsExt.kt +++ b/app/src/main/java/com/dd3boh/outertune/extensions/SongsExt.kt @@ -3,7 +3,7 @@ package com.dd3boh.outertune.extensions import com.dd3boh.outertune.db.entities.Song fun List.getAvailableSongs(isInternetConnected: Boolean): List { - if (isInternetConnected){ + if (isInternetConnected) { return this } return filter { it.song.isAvailableOffline() } diff --git a/app/src/main/java/com/dd3boh/outertune/playback/DownloadUtil.kt b/app/src/main/java/com/dd3boh/outertune/playback/DownloadUtil.kt index 8b796f90c..be68040ec 100644 --- a/app/src/main/java/com/dd3boh/outertune/playback/DownloadUtil.kt +++ b/app/src/main/java/com/dd3boh/outertune/playback/DownloadUtil.kt @@ -134,15 +134,15 @@ class DownloadUtil @Inject constructor( songs.forEach { song -> downloadSong(song.id, song.title) } } - fun download(song: MediaMetadata){ + fun download(song: MediaMetadata) { downloadSong(song.id, song.title) } - fun download(song: SongEntity){ + fun download(song: SongEntity) { downloadSong(song.id, song.title) } - private fun downloadSong(id: String, title: String){ + private fun downloadSong(id: String, title: String) { val downloadRequest = DownloadRequest.Builder(id, id.toUri()) .setCustomCacheKey(id) .setData(title.toByteArray()) @@ -154,19 +154,19 @@ class DownloadUtil @Inject constructor( false) } - fun resumeDownloadsOnStart(){ + fun resumeDownloadsOnStart() { DownloadService.sendResumeDownloads( context, ExoDownloadService::class.java, false) } - fun autoDownloadIfLiked(songs: List){ + fun autoDownloadIfLiked(songs: List) { songs.forEach { song -> autoDownloadIfLiked(song) } } - fun autoDownloadIfLiked(song: SongEntity){ - if (!song.liked || song.dateDownload != null){ + fun autoDownloadIfLiked(song: SongEntity) { + if (!song.liked || song.dateDownload != null) { return } @@ -199,7 +199,7 @@ class DownloadUtil @Inject constructor( } CoroutineScope(Dispatchers.IO).launch { - if (download.state == Download.STATE_COMPLETED){ + if (download.state == Download.STATE_COMPLETED) { val updateTime = Instant.ofEpochMilli(download.updateTimeMs).atZone(ZoneOffset.UTC).toLocalDateTime() database.updateDownloadStatus(download.request.id, updateTime) } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt b/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt index 3ed6b5fce..ac497d270 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt @@ -428,7 +428,7 @@ fun SongListItem( ) }, trailingContent = { - if (available){ + if (available) { if (inSelectMode) { Checkbox( checked = isSelected, @@ -476,7 +476,7 @@ fun SongListItem( available = available, modifier = modifier.combinedClickable( onClick = { - if (available){ + if (available) { if (inSelectMode) { onCheckedChange(!isSelected) } else if (song.id == mediaMetadata?.id) { @@ -487,9 +487,8 @@ fun SongListItem( } }, onLongClick = { - if (available){ - if (selectionIds == null) - { + if (available) { + if (selectionIds == null) { menuState.show { SongMenu( originalSong = song, @@ -509,7 +508,7 @@ fun SongListItem( ) } - if (enableSwipeToQueue && available){ + if (enableSwipeToQueue && available) { SwipeToQueueBox( item = song.toMediaItem(), content = { listItem() }, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/AlbumMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/AlbumMenu.kt index 3998d7bbf..3d2775976 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/AlbumMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/AlbumMenu.kt @@ -339,8 +339,7 @@ fun AlbumMenu( showSelectArtistDialog = true } } - if (isNetworkConnected) - { + if (isNetworkConnected) { GridMenuItem( icon = { Icon( diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt index 3977df7e1..7303ea92e 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/PlaylistMenu.kt @@ -319,8 +319,7 @@ fun PlaylistMenu( )) } - if (isNetworkConnected) - { + if (isNetworkConnected) { playlist.playlist.browseId?.let { browseId -> playlist.playlist.radioEndpointParams?.let { radioEndpointParams -> GridMenuItem( diff --git a/app/src/main/java/com/dd3boh/outertune/ui/menu/SelectionSongsMenu.kt b/app/src/main/java/com/dd3boh/outertune/ui/menu/SelectionSongsMenu.kt index 0cc5209ac..7d0efde26 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/menu/SelectionSongsMenu.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/menu/SelectionSongsMenu.kt @@ -55,7 +55,7 @@ fun SelectionMediaMetadataMenu( onDismiss: () -> Unit, clearAction: () -> Unit, onRemoveFromHistory: (() -> Unit)? = null, -){ +) { val context = LocalContext.current val database = LocalDatabase.current val downloadUtil = LocalDownloadUtil.current @@ -173,7 +173,7 @@ fun SelectionMediaMetadataMenu( end = 8.dp, bottom = 8.dp + WindowInsets.systemBars.asPaddingValues().calculateBottomPadding() ) - ){ + ) { GridMenuItem( icon = R.drawable.play, title = R.string.play diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt index d5eb250e2..f6a78218d 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt @@ -286,7 +286,7 @@ fun HistoryScreen( isActive = song.id == mediaMetadata?.id, isPlaying = isPlaying, trailingContent = { - if (available){ + if (available) { IconButton( onClick = { menuState.show { @@ -349,7 +349,7 @@ fun HistoryScreen( ) } - if (available){ + if (available) { SwipeToQueueBox( item = song.toMediaItem(), content = { content() }, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt index 98a8e164f..676d9f27b 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt @@ -71,6 +71,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource @@ -126,6 +127,7 @@ fun SetupWizard( navController: NavController, ) { val context = LocalContext.current + val haptic = LocalHapticFeedback.current val layoutDirection = LocalLayoutDirection.current val uriHandler = LocalUriHandler.current diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt index 4a8db582a..4c39c2ad4 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt @@ -234,7 +234,7 @@ fun ArtistScreen( ) } - if (!showLocal){ + if (!showLocal) { artistPage?.artist?.radioEndpoint?.let { radioEndpoint -> OutlinedButton( onClick = { @@ -527,7 +527,7 @@ fun ArtistScreen( lazyListState = lazyListState, icon = if (showLocal) Icons.Rounded.LibraryMusic else Icons.Rounded.Language, onClick = { - if (isNetworkConnected){ + if (isNetworkConnected) { showLocal = showLocal.not() if (!showLocal && artistPage == null) viewModel.fetchArtistsFromYTM() } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryAlbumsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryAlbumsScreen.kt index 0b64e9163..a3143c96e 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryAlbumsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryAlbumsScreen.kt @@ -119,7 +119,7 @@ fun LibraryAlbumsScreen( currentValue = filter, onValueUpdate = { filter = it - if (context.isSyncEnabled()){ + if (context.isSyncEnabled()) { if (it == AlbumFilter.LIBRARY) viewModel.syncAlbums() } }, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryArtistsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryArtistsScreen.kt index 672a84dd6..ef182b9a5 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryArtistsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryArtistsScreen.kt @@ -113,7 +113,7 @@ fun LibraryArtistsScreen( currentValue = filter, onValueUpdate = { filter = it - if (context.isSyncEnabled()){ + if (context.isSyncEnabled()) { if (it == ArtistFilter.LIBRARY) viewModel.syncArtists() } }, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryPlaylistsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryPlaylistsScreen.kt index c569de28b..624b89b86 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryPlaylistsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryPlaylistsScreen.kt @@ -200,7 +200,7 @@ fun LibraryPlaylistsScreen( currentValue = filter, onValueUpdate = { filter = it - if (context.isSyncEnabled()){ + if (context.isSyncEnabled()) { if (it == PlaylistFilter.LIBRARY) viewModel.syncPlaylists() } }, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt index 20a3bf698..ddfba1e03 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt @@ -137,7 +137,7 @@ fun LibrarySongsScreen( } LaunchedEffect(Unit) { - if (context.isSyncEnabled()){ + if (context.isSyncEnabled()) { when (filter) { SongFilter.LIKED -> viewModel.syncLikedSongs() SongFilter.LIBRARY -> viewModel.syncLibrarySongs() @@ -156,7 +156,7 @@ fun LibrarySongsScreen( currentValue = filter, onValueUpdate = { filter = it - if (context.isSyncEnabled()){ + if (context.isSyncEnabled()) { if (it == SongFilter.LIKED) viewModel.syncLikedSongs() else if (it == SongFilter.LIBRARY) viewModel.syncLibrarySongs() } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt index c84785673..54ac3307b 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt @@ -309,7 +309,7 @@ fun AutoPlaylistScreen( Spacer(modifier = Modifier.width(8.dp)) } - if (playlistType == PlaylistType.LIKE && downloadCount > 0){ + if (playlistType == PlaylistType.LIKE && downloadCount > 0) { Icon( imageVector = Icons.Rounded.OfflinePin, contentDescription = null, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt index 77e165dc6..795fab53a 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt @@ -631,7 +631,7 @@ fun LocalPlaylistHeader( ) Row(verticalAlignment = Alignment.CenterVertically) { - if (playlist.downloadCount > 0){ + if (playlist.downloadCount > 0) { Icon( imageVector = Icons.Rounded.OfflinePin, contentDescription = null, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt index 3dd5d4e85..2653f8986 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchResult.kt @@ -175,7 +175,7 @@ fun OnlineSearchResult( playerConnection.player.togglePlayPause() } else { playerConnection.playQueue( - if (isNetworkConnected){ + if (isNetworkConnected) { YouTubeQueue.radio(item.toMediaMetadata()) } else { diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt index 1870816fb..717b2bfa8 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/OnlineSearchScreen.kt @@ -230,7 +230,7 @@ fun OnlineSearchScreen( .clickable { when (item) { is SongItem -> { - if (available){ + if (available) { if (item.id == mediaMetadata?.id) { playerConnection.player.togglePlayPause() } else if (item.id.startsWith("LA")) { @@ -244,7 +244,7 @@ fun OnlineSearchScreen( ) } else { playerConnection.playQueue( - if (isNetworkConnected){ + if (isNetworkConnected) { YouTubeQueue.radio(item.toMediaMetadata()) } else { diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/settings/ContentSettings.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/settings/ContentSettings.kt index 63abc38bc..3c039a5c1 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/settings/ContentSettings.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/settings/ContentSettings.kt @@ -193,13 +193,11 @@ fun ContentSettings( icon = { Icon(Icons.Rounded.Favorite, null) }, values = listOf(LikedAutodownloadMode.OFF, LikedAutodownloadMode.ON, LikedAutodownloadMode.WIFI_ONLY), selectedValue = likedAutoDownload, - valueText = { - when (it) { - LikedAutodownloadMode.OFF -> stringResource(androidx.compose.ui.R.string.state_off) - LikedAutodownloadMode.ON -> stringResource(androidx.compose.ui.R.string.state_on) - LikedAutodownloadMode.WIFI_ONLY -> stringResource(R.string.wifi_only) - } - }, + valueText = { when (it) { + LikedAutodownloadMode.OFF -> stringResource(androidx.compose.ui.R.string.state_off) + LikedAutodownloadMode.ON -> stringResource(androidx.compose.ui.R.string.state_on) + LikedAutodownloadMode.WIFI_ONLY -> stringResource(R.string.wifi_only) + } }, onValueSelected = onLikedAutoDownload ) diff --git a/app/src/main/java/com/dd3boh/outertune/utils/SyncUtils.kt b/app/src/main/java/com/dd3boh/outertune/utils/SyncUtils.kt index bd2177567..44955280c 100644 --- a/app/src/main/java/com/dd3boh/outertune/utils/SyncUtils.kt +++ b/app/src/main/java/com/dd3boh/outertune/utils/SyncUtils.kt @@ -80,7 +80,7 @@ class SyncUtils @Inject constructor( // Get remote and local liked songs YouTube.playlist("LM").completed().onSuccess{ page-> - if (!context.isInternetConnected()){ + if (!context.isInternetConnected()) { return } @@ -135,7 +135,7 @@ class SyncUtils @Inject constructor( // Get remote songs (from library and uploads) val remoteSongs = getRemoteData("FEmusic_liked_videos", "FEmusic_library_privately_owned_tracks") - if (!context.isInternetConnected()){ + if (!context.isInternetConnected()) { return } @@ -189,7 +189,7 @@ class SyncUtils @Inject constructor( // Get remote albums (from library and uploads) val remoteAlbums = getRemoteData("FEmusic_liked_albums", "FEmusic_library_privately_owned_releases") - if (!context.isInternetConnected()){ + if (!context.isInternetConnected()) { return } @@ -243,7 +243,7 @@ class SyncUtils @Inject constructor( // Get remote artists (from library and uploads) val remoteArtists = getRemoteData("FEmusic_library_corpus_track_artists", "FEmusic_library_privately_owned_artists") - if (!context.isInternetConnected()){ + if (!context.isInternetConnected()) { return } @@ -304,7 +304,7 @@ class SyncUtils @Inject constructor( // Get remote and local playlists YouTube.library("FEmusic_liked_playlists").completed().onSuccess { page -> - if (!context.isInternetConnected()){ + if (!context.isInternetConnected()) { return } @@ -375,7 +375,7 @@ class SyncUtils @Inject constructor( suspend fun syncPlaylist(browseId: String, playlistId: String) { YouTube.playlist(browseId).completed().onSuccess { playlistPage -> - if (!context.isInternetConnected()){ + if (!context.isInternetConnected()) { return } From 4a36270d8093e180c26478b4892e323753ad2785 Mon Sep 17 00:00:00 2001 From: mikooomich Date: Wed, 1 Jan 2025 12:48:19 -0500 Subject: [PATCH 5/6] WIP: Fix history screen multiselect --- .../outertune/ui/screens/HistoryScreen.kt | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt index f6a78218d..f041a343f 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt @@ -132,7 +132,7 @@ fun HistoryScreen( var inSelectMode by rememberSaveable { mutableStateOf(false) } val selection = rememberSaveable( - saver = listSaver, String>( + saver = listSaver, Long>( save = { it.toList() }, restore = { it.toMutableStateList() } ) @@ -175,15 +175,15 @@ fun HistoryScreen( } .filterValues { it.isNotEmpty() } } - val filteredEventIndex: Map by remember(filteredEventsMap) { + val filteredEventIndex: Map by remember(filteredEventsMap) { derivedStateOf { - filteredEventsMap.flatMap { it.value }.associateBy { it.song.song.id } + filteredEventsMap.flatMap { it.value }.associateBy { it.event.id } } } LaunchedEffect(filteredEventsMap) { - selection.fastForEachReversed { songId -> - if (filteredEventIndex[songId] == null) { - selection.remove(songId) + selection.fastForEachReversed { eventId -> + if (filteredEventIndex[eventId] == null) { + selection.remove(eventId) } } } @@ -380,22 +380,22 @@ fun HistoryScreen( if (inSelectMode) { SelectHeader( selectedItems = eventsMap.flatMap { - group -> group.value.filter{ it.song.song.id in selection } + group -> group.value.filter{ it.event.id in selection } }.map { it.song.toMediaMetadata() }, totalItemCount = eventsMap.flatMap { group -> group.value.map { it.song }.getAvailableSongs(isNetworkConnected)}.size, onSelectAll = { selection.clear() selection.addAll(eventsMap.flatMap { group -> - group.value.filter{ it.song.song.isAvailableOffline() || isNetworkConnected }.map { it.song.song.id } + group.value.filter{ it.song.song.isAvailableOffline() || isNetworkConnected }.map { it.event.id } }) }, onDeselectAll = { selection.clear() }, menuState = menuState, onDismiss = onExitSelectionMode, onRemoveFromHistory = { - val sel = eventsMap.flatMap { - group -> group.value.filter{ it.song.song.id in selection } - }.map { it.event } + val sel = selection.mapNotNull { eventId -> + filteredEventIndex[eventId]?.event + } database.query { sel.forEach { delete(it) From 5b3f17dee741afca243ff5e1a68bdb6d720e7327 Mon Sep 17 00:00:00 2001 From: mattcarter11 <38189440+mattcarter11@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:48:25 +0100 Subject: [PATCH 6/6] refactor: move select logic to parent --- .../dd3boh/outertune/ui/component/Items.kt | 36 +++++++------------ .../outertune/ui/screens/AlbumScreen.kt | 11 ++++-- .../outertune/ui/screens/HistoryScreen.kt | 11 ++++-- .../dd3boh/outertune/ui/screens/HomeScreen.kt | 18 +++++----- .../outertune/ui/screens/SetupWizzard.kt | 7 ++-- .../outertune/ui/screens/StatsScreen.kt | 4 +-- .../ui/screens/artist/ArtistItemsScreen.kt | 1 + .../ui/screens/artist/ArtistScreen.kt | 4 +-- .../ui/screens/artist/ArtistSongsScreen.kt | 11 ++++-- .../screens/library/LibraryFoldersScreen.kt | 15 +++++--- .../ui/screens/library/LibrarySongsScreen.kt | 11 ++++-- .../ui/screens/playlist/AutoPlaylistScreen.kt | 11 ++++-- .../screens/playlist/LocalPlaylistScreen.kt | 11 ++++-- .../ui/screens/search/LocalSearchScreen.kt | 4 +-- 14 files changed, 96 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt b/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt index ac497d270..e84e384c3 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/component/Items.kt @@ -355,9 +355,9 @@ fun GridItem( fun SongListItem( song: Song, onPlay: () -> Unit, - onSelectModeActivation : () -> Unit, - inSelectMode : Boolean, - selectionIds: MutableList?, + onSelectedChange: (Boolean) -> Unit, + inSelectMode: Boolean?, + isSelected: Boolean, navController: NavController, modifier: Modifier = Modifier, enableSwipeToQueue: Boolean = true, @@ -366,8 +366,8 @@ fun SongListItem( showInLibraryIcon: Boolean = true, showDownloadIcon: Boolean = true, showLocalIcon: Boolean = true, - playlistSong : PlaylistSong? = null, - playlistBrowseId : String? = null, + playlistSong: PlaylistSong? = null, + playlistBrowseId: String? = null, showDragHandle: Boolean = false, dragHandleModifier: Modifier? = null, disableShowMenu: Boolean = false, @@ -385,15 +385,6 @@ fun SongListItem( val snackbarHostState = remember { SnackbarHostState() } - val isSelected = selectionIds?.contains(song.id) == true - val onCheckedChange: (Boolean) -> Unit = { - if (it) { - selectionIds?.add(song.id) - } else { - selectionIds?.remove(song.id) - } - } - val listItem: @Composable () -> Unit = { ListItem( title = song.song.title, @@ -429,10 +420,10 @@ fun SongListItem( }, trailingContent = { if (available) { - if (inSelectMode) { + if (inSelectMode == true) { Checkbox( checked = isSelected, - onCheckedChange = onCheckedChange + onCheckedChange = onSelectedChange ) } else { IconButton( @@ -471,14 +462,14 @@ fun SongListItem( } } }, - isSelected = inSelectMode && isSelected, + isSelected = inSelectMode == true && isSelected, isActive = isActive, available = available, modifier = modifier.combinedClickable( onClick = { if (available) { - if (inSelectMode) { - onCheckedChange(!isSelected) + if (inSelectMode == true) { + onSelectedChange(!isSelected) } else if (song.id == mediaMetadata?.id) { playerConnection.player.togglePlayPause() } else { @@ -488,7 +479,7 @@ fun SongListItem( }, onLongClick = { if (available) { - if (selectionIds == null) { + if (inSelectMode == null){ menuState.show { SongMenu( originalSong = song, @@ -497,10 +488,9 @@ fun SongListItem( ) } } - if (!inSelectMode) { + else if (!inSelectMode) { haptic.performHapticFeedback(HapticFeedbackType.LongPress) - onSelectModeActivation() - onCheckedChange(true) + onSelectedChange(true) } } } diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt index b7cb4bf81..156f21f84 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/AlbumScreen.kt @@ -432,9 +432,16 @@ fun AlbumScreen( ) ) }, - onSelectModeActivation = { inSelectMode = true }, + onSelectedChange = { + inSelectMode = true + if (it) { + selection.add(song.id) + } else { + selection.remove(song.id) + } + }, inSelectMode = inSelectMode, - selectionIds = selection, + isSelected = selection.contains(song.id), navController = navController, modifier = Modifier.fillMaxWidth().animateItem() ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt index f041a343f..71f1e5577 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/HistoryScreen.kt @@ -423,9 +423,16 @@ fun HistoryScreen( ) ) }, - onSelectModeActivation = { inSelectMode = true }, + onSelectedChange = { + inSelectMode = true + if (it) { + selection.add(event.event.id) + } else { + selection.remove(event.event.id) + } + }, inSelectMode = inSelectMode, - selectionIds = selection, + isSelected = selection.contains(event.event.id), navController = navController, modifier = Modifier.fillMaxWidth().animateItem() ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt index 732a2e7d6..18cd43093 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/HomeScreen.kt @@ -475,9 +475,9 @@ fun HomeScreen( onPlay = { playerConnection.playQueue(YouTubeQueue.radio(song!!.toMediaMetadata())) }, - onSelectModeActivation = {}, - inSelectMode = false, - selectionIds = null, + onSelectedChange = {}, + inSelectMode = null, + isSelected = false, navController = navController, modifier = Modifier.width(horizontalLazyGridItemWidth) ) @@ -509,9 +509,9 @@ fun HomeScreen( onPlay = { playerConnection.playQueue(YouTubeQueue.radio(song!!.toMediaMetadata())) }, - onSelectModeActivation = {}, - inSelectMode = false, - selectionIds = null, + onSelectedChange = {}, + inSelectMode = null, + isSelected = false, navController = navController, modifier = Modifier.width(horizontalLazyGridItemWidth) ) @@ -642,9 +642,9 @@ fun HomeScreen( onPlay = { playerConnection.playQueue(YouTubeQueue.radio(song!!.toMediaMetadata())) }, - onSelectModeActivation = {}, - inSelectMode = false, - selectionIds = null, + onSelectedChange = {}, + inSelectMode = null, + isSelected = false, navController = navController, modifier = Modifier.width(horizontalLazyGridItemWidth) ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt index 676d9f27b..df49aec5a 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/SetupWizzard.kt @@ -37,7 +37,6 @@ import androidx.compose.material.icons.rounded.Backup import androidx.compose.material.icons.rounded.Contrast import androidx.compose.material.icons.rounded.DarkMode import androidx.compose.material.icons.rounded.Lyrics -import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.MusicVideo import androidx.compose.material.icons.rounded.NotInterested import androidx.compose.material.icons.rounded.Palette @@ -596,9 +595,9 @@ fun SetupWizard( SongListItem( song = song, onPlay = {}, - inSelectMode = false, - selectionIds = mutableListOf(), - onSelectModeActivation = {}, + onSelectedChange = {}, + inSelectMode = null, + isSelected = false, navController = navController, enableSwipeToQueue = false, disableShowMenu = true diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/StatsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/StatsScreen.kt index 202848b6e..348bd48c1 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/StatsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/StatsScreen.kt @@ -98,9 +98,9 @@ fun StatsScreen( YouTubeQueue.radio(song.toMediaMetadata()) ) }, - onSelectModeActivation = {}, + onSelectedChange = {}, inSelectMode = false, - selectionIds = null, + isSelected = false, navController = navController, modifier = Modifier.fillMaxWidth().animateItem() ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistItemsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistItemsScreen.kt index a9f5612f8..9755b0917 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistItemsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistItemsScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.grid.GridCells diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt index 4c39c2ad4..48a25f879 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistScreen.kt @@ -311,9 +311,9 @@ fun ArtistScreen( ) ) }, - onSelectModeActivation = { }, + onSelectedChange = { }, inSelectMode = false, - selectionIds = null, + isSelected = false, navController = navController, modifier = Modifier.fillMaxWidth().animateItem() ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt index cfc6bad3c..464d17231 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/artist/ArtistSongsScreen.kt @@ -187,9 +187,16 @@ fun ArtistSongsScreen( } } }, - onSelectModeActivation = { inSelectMode = true }, + onSelectedChange = { + inSelectMode = true + if (it) { + selection.add(song.id) + } else { + selection.remove(song.id) + } + }, inSelectMode = inSelectMode, - selectionIds = selection, + isSelected = selection.contains(song.id), navController = navController, modifier = Modifier.fillMaxWidth().animateItem() ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryFoldersScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryFoldersScreen.kt index d420c1f70..b95c1bf9b 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryFoldersScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibraryFoldersScreen.kt @@ -67,8 +67,6 @@ import com.dd3boh.outertune.ui.component.SelectHeader import com.dd3boh.outertune.ui.component.SongFolderItem import com.dd3boh.outertune.ui.component.SongListItem import com.dd3boh.outertune.ui.component.SortHeader -import com.dd3boh.outertune.ui.component.SwipeToQueueBox -import com.dd3boh.outertune.ui.menu.SongMenu import com.dd3boh.outertune.utils.numberToAlpha import com.dd3boh.outertune.utils.rememberEnumPreference import com.dd3boh.outertune.utils.rememberPreference @@ -83,7 +81,7 @@ import java.util.Stack fun LibraryFoldersScreen( navController: NavController, viewModel: LibrarySongsViewModel = hiltViewModel(), - filterContent: @Composable() (() -> Unit)? = null + filterContent: @Composable (() -> Unit)? = null ) { val menuState = LocalMenuState.current val database = LocalDatabase.current @@ -325,9 +323,16 @@ fun LibraryFoldersScreen( ) ) }, - onSelectModeActivation = { inSelectMode = true }, + onSelectedChange = { + inSelectMode = true + if (it) { + selection.add(song.id) + } else { + selection.remove(song.id) + } + }, inSelectMode = inSelectMode, - selectionIds = selection, + isSelected = selection.contains(song.id), navController = navController, modifier = Modifier.fillMaxWidth().animateItem() ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt index ddfba1e03..46fea936b 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/library/LibrarySongsScreen.kt @@ -267,9 +267,16 @@ fun LibrarySongsScreen( ) ) }, - onSelectModeActivation = { inSelectMode = true }, + onSelectedChange = { + inSelectMode = true + if (it) { + selection.add(song.id) + } else { + selection.remove(song.id) + } + }, inSelectMode = inSelectMode, - selectionIds = selection, + isSelected = selection.contains(song.id), navController = navController, modifier = Modifier.fillMaxWidth().animateItem() ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt index 54ac3307b..52228007e 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/AutoPlaylistScreen.kt @@ -535,9 +535,16 @@ fun AutoPlaylistScreen( }, showLikedIcon = playlistType != PlaylistType.LIKE, showDownloadIcon = playlistType != PlaylistType.DOWNLOAD, - onSelectModeActivation = { inSelectMode = true }, + onSelectedChange = { + inSelectMode = true + if (it) { + selection.add(song.id) + } else { + selection.remove(song.id) + } + }, inSelectMode = inSelectMode, - selectionIds = selection, + isSelected = selection.contains(song.id), navController = navController, modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.background), ) diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt index 795fab53a..5de513327 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/playlist/LocalPlaylistScreen.kt @@ -470,9 +470,16 @@ fun LocalPlaylistScreen( }, showDragHandle = sortType == PlaylistSongSortType.CUSTOM && !locked && editable, dragHandleModifier = Modifier.draggableHandle(), - onSelectModeActivation = { inSelectMode = true }, + onSelectedChange = { + inSelectMode = true + if (it) { + selection.add(song.song.id) + } else { + selection.remove(song.song.id) + } + }, inSelectMode = inSelectMode, - selectionIds = selection, + isSelected = selection.contains(song.song.id), navController = navController, modifier = Modifier.fillMaxWidth().background(MaterialTheme.colorScheme.background), playlistSong = song, diff --git a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/LocalSearchScreen.kt b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/LocalSearchScreen.kt index f8d648cc5..7e9fe1efb 100644 --- a/app/src/main/java/com/dd3boh/outertune/ui/screens/search/LocalSearchScreen.kt +++ b/app/src/main/java/com/dd3boh/outertune/ui/screens/search/LocalSearchScreen.kt @@ -160,9 +160,9 @@ fun LocalSearchScreen( startIndex = songs.indexOfFirst { it.id == item.id } )) }, - onSelectModeActivation = { }, + onSelectedChange = { }, inSelectMode = false, - selectionIds = null, + isSelected = false, navController = navController, modifier = Modifier.animateItem() )