From 49f941b8b9bc6e26c97a31a8bec39200f09f7b2c Mon Sep 17 00:00:00 2001 From: christopher Date: Thu, 17 Sep 2026 19:23:03 -0400 Subject: [PATCH] Move Downloads out of Settings into a dynamic Library collection Downloads is no longer a Settings sub-screen - it's now a pinned entry at the top of Library > Playlists (Settings keeps only the Wi-Fi-only and default-quality preference toggles, which govern how downloads behave rather than being downloaded content itself). The Downloads screen it opens is no longer a flat track list. It now splits into two sections, both fully reactive to what's actually on disk: - Playlists: any playlist with at least one downloaded track. Tapping one opens the real PlaylistDetailScreen, not a special-cased view - this is a live filter over existing playlists, not a copy of their data. - Tracks: downloaded tracks that aren't a member of any playlist. A track belonging to a playlist is never listed twice - that playlist already qualifies for the Playlists section by virtue of this same track being downloaded, so once it's shown there this list skips it. New PlaylistDao.observeAllMemberships() (and PlaylistRepository.observeTrackIdsByPlaylist()) exposes the full playlist-to-track-id membership map in one reactive query, so working out which playlists have a downloaded track doesn't require loading every playlist's full track list. DownloadsViewModel combines that with the existing downloaded-tracks and playlists flows to derive both sections - no new database tables, this is a computed view over the same Room state Phase 5 and Phase 6 already maintain. Verified on-device: Settings no longer shows a Downloads row; the Library tab shows the new pinned "Downloads" entry; added a downloaded track to a test playlist and confirmed it moved from the flat Tracks list into a Playlists entry with no duplication; confirmed tapping that entry opens the actual playlist detail screen; deleted the playlist and confirmed the track reactively reverted to the flat Tracks list with no manual refresh. Co-Authored-By: Claude Sonnet 5 --- .../data/local/playlist/PlaylistDao.kt | 7 ++ .../data/playlist/PlaylistRepository.kt | 6 ++ .../deepwave/ui/downloads/DownloadsScreen.kt | 77 +++++++++++++------ .../ui/downloads/DownloadsViewModel.kt | 37 +++++++-- .../deepwave/ui/library/LibraryScreen.kt | 9 ++- .../deepwave/ui/navigation/DeepwaveNavHost.kt | 7 +- .../deepwave/ui/navigation/MainScreen.kt | 9 ++- .../deepwave/ui/playlist/PlaylistsScreen.kt | 24 +++++- .../deepwave/ui/settings/SettingsScreen.kt | 10 --- app/src/main/res/values/strings.xml | 4 +- 10 files changed, 141 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/data/local/playlist/PlaylistDao.kt b/app/src/main/java/com/InfernalAquatics/deepwave/data/local/playlist/PlaylistDao.kt index 342f989..5604d01 100644 --- a/app/src/main/java/com/InfernalAquatics/deepwave/data/local/playlist/PlaylistDao.kt +++ b/app/src/main/java/com/InfernalAquatics/deepwave/data/local/playlist/PlaylistDao.kt @@ -13,6 +13,10 @@ data class PlaylistWithCount( val trackCount: Int, ) +/** One (playlist, track) membership row - the raw shape the Downloads collection needs to work + * out which playlists contain a downloaded track, without loading every playlist's full track list. */ +data class PlaylistTrackMembership(val playlistLocalId: String, val trackId: String) + @Dao interface PlaylistDao { @Query( @@ -51,6 +55,9 @@ interface PlaylistDao { @Query("SELECT * FROM playlist_tracks WHERE playlistLocalId = :localId ORDER BY position ASC") fun observeTracks(localId: String): Flow> + @Query("SELECT playlistLocalId, trackId FROM playlist_tracks") + fun observeAllMemberships(): Flow> + @Query("SELECT * FROM playlist_tracks WHERE playlistLocalId = :localId ORDER BY position ASC") suspend fun getTracks(localId: String): List diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/data/playlist/PlaylistRepository.kt b/app/src/main/java/com/InfernalAquatics/deepwave/data/playlist/PlaylistRepository.kt index 983d3c4..ca70099 100644 --- a/app/src/main/java/com/InfernalAquatics/deepwave/data/playlist/PlaylistRepository.kt +++ b/app/src/main/java/com/InfernalAquatics/deepwave/data/playlist/PlaylistRepository.kt @@ -62,6 +62,12 @@ class PlaylistRepository @Inject constructor( fun observeTracks(localId: String): Flow> = playlistDao.observeTracks(localId).map { list -> list.map { it.toDomain() } } + /** playlistId -> the set of track ids it contains, for the Downloads collection to work out + * which playlists have a downloaded track without loading every playlist's full track list. */ + fun observeTrackIdsByPlaylist(): Flow>> = + playlistDao.observeAllMemberships() + .map { rows -> rows.groupBy({ it.playlistLocalId }, { it.trackId }).mapValues { it.value.toSet() } } + suspend fun createPlaylist(name: String, initialTracks: List = emptyList()): String { val localId = UUID.randomUUID().toString() val now = System.currentTimeMillis() diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/downloads/DownloadsScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/downloads/DownloadsScreen.kt index c65c28e..fb7e88d 100644 --- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/downloads/DownloadsScreen.kt +++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/downloads/DownloadsScreen.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack @@ -12,6 +13,7 @@ import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -21,6 +23,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -28,6 +31,7 @@ import androidx.media3.common.util.UnstableApi import com.InfernalAquatics.deepwave.R import com.InfernalAquatics.deepwave.data.local.download.DownloadStatus import com.InfernalAquatics.deepwave.data.local.download.DownloadedTrackEntity +import com.InfernalAquatics.deepwave.data.model.Playlist import com.InfernalAquatics.deepwave.data.network.coverArtUrl import com.InfernalAquatics.deepwave.ui.components.TrackRow import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme @@ -36,12 +40,14 @@ import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme @Composable fun DownloadsScreen( onBack: () -> Unit, + onPlaylistClick: (String) -> Unit, viewModel: DownloadsViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsState() DownloadsContent( uiState = uiState, onBack = onBack, + onPlaylistClick = onPlaylistClick, onTrackClick = viewModel::playTrack, onRemove = viewModel::remove, ) @@ -52,6 +58,7 @@ fun DownloadsScreen( private fun DownloadsContent( uiState: DownloadsUiState, onBack: () -> Unit, + onPlaylistClick: (String) -> Unit, onTrackClick: (Int) -> Unit, onRemove: (String) -> Unit, ) { @@ -67,40 +74,56 @@ private fun DownloadsContent( ) }, ) { innerPadding -> - if (uiState.tracks.isEmpty()) { + if (uiState.downloadedPlaylists.isEmpty() && uiState.looseTracks.isEmpty()) { Box( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), + modifier = Modifier.fillMaxSize().padding(innerPadding), contentAlignment = Alignment.Center, ) { Text(text = stringResource(R.string.downloads_empty), style = MaterialTheme.typography.bodyLarge) } } else { - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), - ) { - itemsIndexed(uiState.tracks, key = { _, track -> track.trackId }) { index, track -> - TrackRow( - title = track.title, - subtitle = track.artistName.orEmpty(), - coverArtUrl = coverArtUrl(track.coverArtId), - onClick = { onTrackClick(index) }, - trailingContent = { - IconButton(onClick = { onRemove(track.trackId) }) { - Icon(Icons.Filled.Delete, contentDescription = stringResource(R.string.download_remove)) - } - }, - ) + LazyColumn(modifier = Modifier.fillMaxSize().padding(innerPadding)) { + if (uiState.downloadedPlaylists.isNotEmpty()) { + item { SectionHeader(stringResource(R.string.downloads_section_playlists)) } + items(uiState.downloadedPlaylists, key = { "playlist:${it.id}" }) { playlist -> + TrackRow( + title = playlist.name, + subtitle = pluralStringResource(R.plurals.playlist_track_count, playlist.trackCount, playlist.trackCount), + coverArtUrl = coverArtUrl(playlist.coverArtId), + onClick = { onPlaylistClick(playlist.id) }, + ) + } + } + if (uiState.looseTracks.isNotEmpty()) { + item { SectionHeader(stringResource(R.string.downloads_section_tracks)) } + itemsIndexed(uiState.looseTracks, key = { _, track -> "track:${track.trackId}" }) { index, track -> + TrackRow( + title = track.title, + subtitle = track.artistName.orEmpty(), + coverArtUrl = coverArtUrl(track.coverArtId), + onClick = { onTrackClick(index) }, + trailingContent = { + IconButton(onClick = { onRemove(track.trackId) }) { + Icon(Icons.Filled.Delete, contentDescription = stringResource(R.string.download_remove)) + } + }, + ) + } } } } } } -private val sampleTracks = List(3) { index -> +@Composable +private fun SectionHeader(title: String) { + ListItem(headlineContent = { Text(title, style = MaterialTheme.typography.titleMedium) }) +} + +private val samplePlaylists = listOf( + Playlist(id = "1", name = "Riddim Favs", coverArtId = null, trackCount = 12, isSynced = true), +) +private val sampleTracks = List(2) { index -> DownloadedTrackEntity( trackId = "sample-$index", title = "Sample Track ${index + 1}", @@ -120,7 +143,13 @@ private val sampleTracks = List(3) { index -> @Composable private fun DownloadsContentPreview() { DeepwaveTheme { - DownloadsContent(uiState = DownloadsUiState(tracks = sampleTracks), onBack = {}, onTrackClick = {}, onRemove = {}) + DownloadsContent( + uiState = DownloadsUiState(downloadedPlaylists = samplePlaylists, looseTracks = sampleTracks), + onBack = {}, + onPlaylistClick = {}, + onTrackClick = {}, + onRemove = {}, + ) } } @@ -128,6 +157,6 @@ private fun DownloadsContentPreview() { @Composable private fun DownloadsContentEmptyPreview() { DeepwaveTheme { - DownloadsContent(uiState = DownloadsUiState(), onBack = {}, onTrackClick = {}, onRemove = {}) + DownloadsContent(uiState = DownloadsUiState(), onBack = {}, onPlaylistClick = {}, onTrackClick = {}, onRemove = {}) } } diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/downloads/DownloadsViewModel.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/downloads/DownloadsViewModel.kt index cd003dc..c758da9 100644 --- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/downloads/DownloadsViewModel.kt +++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/downloads/DownloadsViewModel.kt @@ -6,31 +6,56 @@ import androidx.media3.common.util.UnstableApi import com.InfernalAquatics.deepwave.data.download.DownloadRepository import com.InfernalAquatics.deepwave.data.local.download.DownloadStatus import com.InfernalAquatics.deepwave.data.local.download.DownloadedTrackEntity +import com.InfernalAquatics.deepwave.data.model.Playlist +import com.InfernalAquatics.deepwave.data.playlist.PlaylistRepository import com.InfernalAquatics.deepwave.media.PlaybackController import com.InfernalAquatics.deepwave.media.toMediaItem import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject -data class DownloadsUiState(val tracks: List = emptyList()) +/** + * [downloadedPlaylists]: any playlist with at least one downloaded track. [looseTracks]: + * downloaded tracks that aren't a member of any playlist - a track belonging to a playlist is + * never listed twice, since that playlist already qualifies for [downloadedPlaylists] by virtue + * of this same track being downloaded. + */ +data class DownloadsUiState( + val downloadedPlaylists: List = emptyList(), + val looseTracks: List = emptyList(), +) @UnstableApi @HiltViewModel class DownloadsViewModel @Inject constructor( private val downloadRepository: DownloadRepository, + private val playlistRepository: PlaylistRepository, private val playbackController: PlaybackController, ) : ViewModel() { - val uiState: StateFlow = downloadRepository.downloadedTracks() - .map { tracks -> DownloadsUiState(tracks.filter { it.status == DownloadStatus.COMPLETE }) } - .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), DownloadsUiState()) + val uiState: StateFlow = combine( + downloadRepository.downloadedTracks(), + playlistRepository.observePlaylists(), + playlistRepository.observeTrackIdsByPlaylist(), + ) { tracks, playlists, trackIdsByPlaylist -> + val downloaded = tracks.filter { it.status == DownloadStatus.COMPLETE } + val downloadedIds = downloaded.map { it.trackId }.toSet() + + val downloadedPlaylists = playlists.filter { playlist -> + trackIdsByPlaylist[playlist.id].orEmpty().any { it in downloadedIds } + } + val trackIdsInAnyPlaylist = trackIdsByPlaylist.values.flatten().toSet() + val looseTracks = downloaded.filter { it.trackId !in trackIdsInAnyPlaylist } + + DownloadsUiState(downloadedPlaylists, looseTracks) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), DownloadsUiState()) fun playTrack(index: Int) { - val tracks = uiState.value.tracks + val tracks = uiState.value.looseTracks if (index !in tracks.indices) return viewModelScope.launch { playbackController.ensureConnected() diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/library/LibraryScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/library/LibraryScreen.kt index 6733e65..1202484 100644 --- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/library/LibraryScreen.kt +++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/library/LibraryScreen.kt @@ -31,6 +31,7 @@ private enum class LibrarySection(val labelRes: Int) { fun LibraryScreen( onArtistClick: (String) -> Unit, onPlaylistClick: (String) -> Unit, + onOpenDownloads: () -> Unit, modifier: Modifier = Modifier, ) { var section by remember { mutableStateOf(LibrarySection.Playlists) } @@ -47,7 +48,11 @@ fun LibraryScreen( } } when (section) { - LibrarySection.Playlists -> PlaylistsScreen(onPlaylistClick = onPlaylistClick, modifier = Modifier.weight(1f)) + LibrarySection.Playlists -> PlaylistsScreen( + onPlaylistClick = onPlaylistClick, + onOpenDownloads = onOpenDownloads, + modifier = Modifier.weight(1f), + ) LibrarySection.Artists -> ArtistsScreen(onArtistClick = onArtistClick, modifier = Modifier.weight(1f)) } } @@ -57,6 +62,6 @@ fun LibraryScreen( @Composable private fun LibraryScreenPreview() { DeepwaveTheme { - LibraryScreen(onArtistClick = {}, onPlaylistClick = {}) + LibraryScreen(onArtistClick = {}, onPlaylistClick = {}, onOpenDownloads = {}) } } diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/DeepwaveNavHost.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/DeepwaveNavHost.kt index b5a7bb8..deb699d 100644 --- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/DeepwaveNavHost.kt +++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/DeepwaveNavHost.kt @@ -95,6 +95,7 @@ fun DeepwaveNavHost( onOpenArtist = { artistId -> navController.navigate(Route.ArtistDetail(artistId)) }, onOpenAlbum = { albumId -> navController.navigate(Route.AlbumDetail(albumId)) }, onOpenPlaylist = { playlistId -> navController.navigate(Route.PlaylistDetail(playlistId)) }, + onOpenDownloads = { navController.navigate(Route.Downloads) }, ) } composable { @@ -105,11 +106,13 @@ fun DeepwaveNavHost( popUpTo { inclusive = true } } }, - onOpenDownloads = { navController.navigate(Route.Downloads) }, ) } composable { - DownloadsScreen(onBack = { navController.popBackStack() }) + DownloadsScreen( + onBack = { navController.popBackStack() }, + onPlaylistClick = { playlistId -> navController.navigate(Route.PlaylistDetail(playlistId)) }, + ) } composable { ArtistDetailScreen( diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/MainScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/MainScreen.kt index 105cd0d..37861c5 100644 --- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/MainScreen.kt +++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/navigation/MainScreen.kt @@ -41,6 +41,7 @@ fun MainScreen( onOpenArtist: (String) -> Unit, onOpenAlbum: (String) -> Unit, onOpenPlaylist: (String) -> Unit, + onOpenDownloads: () -> Unit, ) { var selectedTab by remember { mutableStateOf(BottomTab.Home) } @@ -67,7 +68,11 @@ fun MainScreen( when (selectedTab) { BottomTab.Home -> HomeScreen(onAlbumClick = onOpenAlbum) BottomTab.Search -> SearchScreen(onArtistClick = onOpenArtist, onAlbumClick = onOpenAlbum) - BottomTab.Library -> LibraryScreen(onArtistClick = onOpenArtist, onPlaylistClick = onOpenPlaylist) + BottomTab.Library -> LibraryScreen( + onArtistClick = onOpenArtist, + onPlaylistClick = onOpenPlaylist, + onOpenDownloads = onOpenDownloads, + ) } } } @@ -78,6 +83,6 @@ fun MainScreen( @Composable private fun MainScreenPreview() { DeepwaveTheme { - MainScreen(onOpenSettings = {}, onOpenArtist = {}, onOpenAlbum = {}, onOpenPlaylist = {}) + MainScreen(onOpenSettings = {}, onOpenArtist = {}, onOpenAlbum = {}, onOpenPlaylist = {}, onOpenDownloads = {}) } } diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/playlist/PlaylistsScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/playlist/PlaylistsScreen.kt index deefab4..4f9b0bc 100644 --- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/playlist/PlaylistsScreen.kt +++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/playlist/PlaylistsScreen.kt @@ -7,6 +7,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Download import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.Text @@ -27,6 +28,7 @@ import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme @Composable fun PlaylistsScreen( onPlaylistClick: (String) -> Unit, + onOpenDownloads: () -> Unit, modifier: Modifier = Modifier, viewModel: PlaylistsViewModel = hiltViewModel(), ) { @@ -34,6 +36,7 @@ fun PlaylistsScreen( PlaylistsContent( uiState = uiState, onPlaylistClick = onPlaylistClick, + onOpenDownloads = onOpenDownloads, onNewPlaylist = viewModel::openCreate, modifier = modifier, ) @@ -47,6 +50,7 @@ fun PlaylistsScreen( private fun PlaylistsContent( uiState: PlaylistsUiState, onPlaylistClick: (String) -> Unit, + onOpenDownloads: () -> Unit, onNewPlaylist: () -> Unit, modifier: Modifier = Modifier, ) { @@ -58,6 +62,17 @@ private fun PlaylistsContent( modifier = Modifier.clickable(onClick = onNewPlaylist), ) } + item { + // Pinned, not a real Room playlist: its membership is computed live from what's + // actually on disk (see DownloadsViewModel), so it never drifts from reality and + // never syncs to the server - downloads are inherently per-device. + ListItem( + headlineContent = { Text(stringResource(R.string.downloads_title)) }, + supportingContent = { Text(stringResource(R.string.downloads_subtitle)) }, + leadingContent = { Icon(Icons.Filled.Download, contentDescription = null) }, + modifier = Modifier.clickable(onClick = onOpenDownloads), + ) + } if (uiState.playlists.isEmpty()) { item { ListItem(headlineContent = { Text(stringResource(R.string.playlist_empty)) }) @@ -84,7 +99,12 @@ private val samplePlaylists = listOf( @Composable private fun PlaylistsContentPreview() { DeepwaveTheme { - PlaylistsContent(uiState = PlaylistsUiState(playlists = samplePlaylists), onPlaylistClick = {}, onNewPlaylist = {}) + PlaylistsContent( + uiState = PlaylistsUiState(playlists = samplePlaylists), + onPlaylistClick = {}, + onOpenDownloads = {}, + onNewPlaylist = {}, + ) } } @@ -92,6 +112,6 @@ private fun PlaylistsContentPreview() { @Composable private fun PlaylistsContentEmptyPreview() { DeepwaveTheme { - PlaylistsContent(uiState = PlaylistsUiState(), onPlaylistClick = {}, onNewPlaylist = {}) + PlaylistsContent(uiState = PlaylistsUiState(), onPlaylistClick = {}, onOpenDownloads = {}, onNewPlaylist = {}) } } diff --git a/app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsScreen.kt b/app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsScreen.kt index f67b1e3..ef8511a 100644 --- a/app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/com/InfernalAquatics/deepwave/ui/settings/SettingsScreen.kt @@ -34,7 +34,6 @@ import com.InfernalAquatics.deepwave.ui.theme.DeepwaveTheme fun SettingsScreen( onBack: () -> Unit, onLoggedOut: () -> Unit, - onOpenDownloads: () -> Unit, viewModel: SettingsViewModel = hiltViewModel(), ) { val uiState by viewModel.uiState.collectAsState() @@ -46,7 +45,6 @@ fun SettingsScreen( defaultBitrate = defaultBitrate, onBack = onBack, onLogOutClick = { viewModel.logout(onLoggedOut) }, - onOpenDownloads = onOpenDownloads, onWifiOnlyChange = viewModel::setWifiOnly, onOpenDefaultQualityPicker = viewModel::openDefaultQualityPicker, onDismissDefaultQualityPicker = viewModel::dismissDefaultQualityPicker, @@ -62,7 +60,6 @@ private fun SettingsContent( defaultBitrate: BitrateOption, onBack: () -> Unit, onLogOutClick: () -> Unit, - onOpenDownloads: () -> Unit, onWifiOnlyChange: (Boolean) -> Unit, onOpenDefaultQualityPicker: () -> Unit, onDismissDefaultQualityPicker: () -> Unit, @@ -81,12 +78,6 @@ private fun SettingsContent( }, ) { innerPadding -> Column(modifier = Modifier.fillMaxSize().padding(innerPadding)) { - ListItem( - headlineContent = { Text(stringResource(R.string.settings_downloads)) }, - modifier = Modifier - .fillMaxWidth() - .clickable(onClick = onOpenDownloads), - ) ListItem( headlineContent = { Text(stringResource(R.string.settings_wifi_only_downloads)) }, trailingContent = { Switch(checked = wifiOnly, onCheckedChange = onWifiOnlyChange) }, @@ -124,7 +115,6 @@ private fun SettingsContentPreview() { defaultBitrate = BitrateOption.Normal, onBack = {}, onLogOutClick = {}, - onOpenDownloads = {}, onWifiOnlyChange = {}, onOpenDefaultQualityPicker = {}, onDismissDefaultQualityPicker = {}, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c65ae5a..c0bef8a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -17,7 +17,6 @@ Settings Log out - Downloads Download over Wi-Fi only Default download quality @@ -33,6 +32,9 @@ Downloads No downloads yet + Available offline + Playlists + Tracks Nothing playing Play