mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
library: drop the download progress indicator
This commit is contained in:
@@ -78,13 +78,6 @@ data class ListDownloadStatus(
|
||||
val complete: Boolean get() = total > 0 && downloaded == total
|
||||
}
|
||||
|
||||
data class DownloadProgress(
|
||||
val bytes: Long,
|
||||
val total: Long,
|
||||
) {
|
||||
val fraction: Float? get() = fractionOf(bytes, total)
|
||||
}
|
||||
|
||||
data class EntityDownloadStatus(
|
||||
val id: String,
|
||||
val total: Long = 0,
|
||||
|
||||
@@ -396,7 +396,6 @@ private fun MainNavigation() {
|
||||
navigateDetail(Routes.PLAYLIST_DETAIL, "playlistId", playlist.id, Routes.playlist(playlist.id))
|
||||
},
|
||||
onOpenSettings = { navController.navigate(Routes.SETTINGS) },
|
||||
onOpenDownloads = { navController.navigate(Routes.DOWNLOADS) },
|
||||
onOpenSearch = { navController.navigate(Routes.SEARCH) },
|
||||
onViewAlbum = { albumId -> navigateDetail(Routes.ALBUM_DETAIL, "albumId", albumId, Routes.album(albumId)) },
|
||||
onViewArtist = { artistId ->
|
||||
|
||||
@@ -34,7 +34,6 @@ import androidx.compose.material.icons.rounded.Album
|
||||
import androidx.compose.material.icons.rounded.ArrowDownward
|
||||
import androidx.compose.material.icons.rounded.ArrowUpward
|
||||
import androidx.compose.material.icons.rounded.CloudOff
|
||||
import androidx.compose.material.icons.rounded.Download
|
||||
import androidx.compose.material.icons.rounded.DownloadDone
|
||||
import androidx.compose.material.icons.rounded.FilterAltOff
|
||||
import androidx.compose.material.icons.rounded.Home
|
||||
@@ -111,7 +110,6 @@ import com.subtracks.data.model.Artist
|
||||
import com.subtracks.data.model.BulkDownloadAction
|
||||
import com.subtracks.data.model.CoverArtRef
|
||||
import com.subtracks.data.model.DownloadList
|
||||
import com.subtracks.data.model.DownloadProgress
|
||||
import com.subtracks.data.model.ListDownloadStatus
|
||||
import com.subtracks.data.model.Playlist
|
||||
import com.subtracks.data.prefs.AlbumSort
|
||||
@@ -230,7 +228,6 @@ fun LibraryRoute(
|
||||
onArtistClick: (Artist) -> Unit,
|
||||
onPlaylistClick: (Playlist) -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
onOpenDownloads: () -> Unit = {},
|
||||
onOpenSearch: () -> Unit = {},
|
||||
onViewAlbum: (String) -> Unit,
|
||||
onViewArtist: (String) -> Unit,
|
||||
@@ -261,7 +258,6 @@ fun LibraryRoute(
|
||||
val listQueryState = listTab?.let { viewModel.listQuery(it).collectAsStateWithLifecycle() }
|
||||
val listQuery = listQueryState?.value ?: ListQuery("")
|
||||
val offline by viewModel.offline.collectAsStateWithLifecycle()
|
||||
val downloadProgress by viewModel.downloadProgress.collectAsStateWithLifecycle()
|
||||
val playSort by viewModel.playSortAvailability.collectAsStateWithLifecycle()
|
||||
val displayQuery = if (offline) listQuery.copy(downloaded = true) else listQuery
|
||||
val searchState = listTab?.let { viewModel.search(it).collectAsStateWithLifecycle() }
|
||||
@@ -319,7 +315,6 @@ fun LibraryRoute(
|
||||
onPlaylistClick = onPlaylistClick,
|
||||
onSync = viewModel::sync,
|
||||
onOpenSettings = onOpenSettings,
|
||||
onOpenDownloads = onOpenDownloads,
|
||||
onOpenSearch = onOpenSearch,
|
||||
onItemLongClick = {
|
||||
focusManager.clearFocus()
|
||||
@@ -329,7 +324,6 @@ fun LibraryRoute(
|
||||
resetKeys = resetKeys,
|
||||
offline = offline,
|
||||
onExitOffline = { viewModel.setOffline(false) },
|
||||
downloadProgress = downloadProgress,
|
||||
sortOptions = sortOptionsFor(selectedTab, playSort),
|
||||
starredSupported = listTab?.supportsStarred == true,
|
||||
onSortChange = { sort -> listTab?.let { viewModel.setListQuery(it, listQuery.copy(sort = sort)) } },
|
||||
@@ -404,12 +398,10 @@ fun LibraryScreen(
|
||||
onSync: () -> Unit,
|
||||
onOpenSettings: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onOpenDownloads: () -> Unit = {},
|
||||
artistArt: (Artist) -> ArtistArt = { ArtistArt() },
|
||||
onOpenSearch: () -> Unit = {},
|
||||
offline: Boolean = false,
|
||||
onExitOffline: () -> Unit = {},
|
||||
downloadProgress: DownloadProgress? = null,
|
||||
onItemLongClick: (MenuTarget) -> Unit = {},
|
||||
syncing: Boolean = false,
|
||||
refreshing: Boolean = false,
|
||||
@@ -602,8 +594,6 @@ fun LibraryScreen(
|
||||
onOpenSettings = onOpenSettings,
|
||||
offline = offline,
|
||||
onExitOffline = onExitOffline,
|
||||
downloadProgress = downloadProgress,
|
||||
onOpenDownloads = onOpenDownloads,
|
||||
artwork = artwork,
|
||||
syncing = syncing,
|
||||
)
|
||||
@@ -868,8 +858,6 @@ internal fun LibraryTabs(
|
||||
onOpenSettings: () -> Unit,
|
||||
offline: Boolean = false,
|
||||
onExitOffline: () -> Unit = {},
|
||||
downloadProgress: DownloadProgress? = null,
|
||||
onOpenDownloads: () -> Unit = {},
|
||||
artwork: ArtworkColors?,
|
||||
syncing: Boolean = false,
|
||||
) {
|
||||
@@ -953,14 +941,7 @@ internal fun LibraryTabs(
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
if (downloadProgress != null) {
|
||||
IconButton(onClick = onOpenDownloads) {
|
||||
DownloadProgressIcon(
|
||||
progress = downloadProgress,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
} else if (offline) {
|
||||
if (offline) {
|
||||
IconButton(onClick = onExitOffline) {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.CloudOff,
|
||||
@@ -991,41 +972,6 @@ internal fun LibraryTabs(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DownloadProgressIcon(
|
||||
progress: DownloadProgress,
|
||||
color: Color,
|
||||
) {
|
||||
val description = stringResource(R.string.downloads_in_progress)
|
||||
Box(
|
||||
modifier = Modifier.size(32.dp).semantics { contentDescription = description },
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
val fraction = progress.fraction
|
||||
if (fraction != null) {
|
||||
CircularProgressIndicator(
|
||||
progress = { fraction },
|
||||
color = color,
|
||||
trackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
strokeWidth = 2.dp,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
} else {
|
||||
CircularProgressIndicator(
|
||||
color = color,
|
||||
strokeWidth = 2.dp,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Download,
|
||||
contentDescription = null,
|
||||
tint = color,
|
||||
modifier = Modifier.size(16.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TabButton(
|
||||
|
||||
@@ -9,11 +9,8 @@ import com.subtracks.data.model.Artist
|
||||
import com.subtracks.data.model.BulkDownloadAction
|
||||
import com.subtracks.data.model.CoverArtRef
|
||||
import com.subtracks.data.model.DownloadList
|
||||
import com.subtracks.data.model.DownloadProgress
|
||||
import com.subtracks.data.model.DownloadStatus
|
||||
import com.subtracks.data.model.ListDownloadStatus
|
||||
import com.subtracks.data.model.Playlist
|
||||
import com.subtracks.data.model.SongDownload
|
||||
import com.subtracks.data.prefs.AlbumSort
|
||||
import com.subtracks.data.prefs.ArtistSort
|
||||
import com.subtracks.data.prefs.LibraryListTab
|
||||
@@ -118,13 +115,6 @@ class LibraryViewModel(
|
||||
|
||||
val offline: StateFlow<Boolean> = sourceRepository.offline
|
||||
|
||||
val downloadProgress: StateFlow<DownloadProgress?> =
|
||||
combine(
|
||||
downloadRepository.activeDownloads(),
|
||||
sourceRepository.activeSourceId(),
|
||||
) { rows, sourceId -> downloadProgress(rows, sourceId) }
|
||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null)
|
||||
|
||||
fun setOffline(enabled: Boolean) = sourceRepository.setOfflineMode(enabled)
|
||||
|
||||
val syncing: StateFlow<Boolean> =
|
||||
@@ -233,21 +223,6 @@ class LibraryViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
internal fun downloadProgress(
|
||||
rows: List<SongDownload>,
|
||||
sourceId: Long?,
|
||||
): DownloadProgress? {
|
||||
if (sourceId == null) return null
|
||||
val active = rows.filter { it.sourceId == sourceId }
|
||||
if (active.isEmpty()) return null
|
||||
val running = active.filter { it.status == DownloadStatus.Running }
|
||||
if (running.isEmpty() || running.any { it.total <= 0 }) return DownloadProgress(bytes = 0, total = 0)
|
||||
return DownloadProgress(
|
||||
bytes = running.sumOf { it.bytes },
|
||||
total = running.sumOf { it.total },
|
||||
)
|
||||
}
|
||||
|
||||
internal fun ListQuery.albumSort(): AlbumSort = AlbumSort.entries.firstOrNull { it.name == sort } ?: AlbumSort.Name
|
||||
|
||||
internal fun ListQuery.artistSort(): ArtistSort = ArtistSort.entries.firstOrNull { it.name == sort } ?: ArtistSort.Name
|
||||
|
||||
@@ -176,7 +176,6 @@
|
||||
<string name="filter_downloaded">Downloaded</string>
|
||||
<string name="close_search">Close search</string>
|
||||
<string name="offline_mode_status">Offline mode; tap to go online</string>
|
||||
<string name="downloads_in_progress">Downloads in progress; tap to open the download manager</string>
|
||||
<string name="shuffle_artist">Shuffle artist</string>
|
||||
|
||||
<string name="status_playing">Playing</string>
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package com.subtracks.ui.components
|
||||
|
||||
import com.subtracks.data.model.DownloadProgress
|
||||
import com.subtracks.data.model.ListDownloadStatus
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
@@ -38,14 +35,4 @@ class DownloadIndicatorTest {
|
||||
fun aFullyDownloadedListIsNotDownloading() {
|
||||
assertFalse(ListDownloadStatus(total = 4, downloaded = 4).isDownloading())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun totalDownloadProgressCombinesEveryInFlightTransfer() {
|
||||
assertEquals(0.4f, DownloadProgress(bytes = 40, total = 100).fraction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun queuedDownloadsWithoutASizeShowAnIndeterminateRing() {
|
||||
assertNull(DownloadProgress(bytes = 0, total = 0).fraction)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package com.subtracks.ui.library
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.subtracks.data.model.DownloadProgress
|
||||
import com.subtracks.data.model.DownloadStatus
|
||||
import com.subtracks.data.model.SongDownload
|
||||
import com.subtracks.data.prefs.AlbumSort
|
||||
import com.subtracks.data.prefs.ArtistSort
|
||||
import com.subtracks.data.prefs.ListQuery
|
||||
import com.subtracks.data.prefs.PlaylistSort
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@@ -28,39 +24,4 @@ class LibraryViewModelTest {
|
||||
assertEquals(ArtistSort.Name, ListQuery("LegacyArtistOrder").artistSort())
|
||||
assertEquals(PlaylistSort.Name, ListQuery("LegacyPlaylistOrder").playlistSort())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun downloadProgressOnlyCountsTheActiveSource() {
|
||||
val rows = listOf(download(1, bytes = 40, total = 100), download(2, bytes = 10, total = 20))
|
||||
|
||||
assertEquals(DownloadProgress(bytes = 40, total = 100), downloadProgress(rows, sourceId = 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun anotherSourcesDownloadsDoNotShowAProgressRing() {
|
||||
assertNull(downloadProgress(listOf(download(2, bytes = 10, total = 20)), sourceId = 1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aTransferWithoutAKnownSizeIsIndeterminate() {
|
||||
val rows =
|
||||
listOf(
|
||||
download(1, bytes = 100, total = 100),
|
||||
download(1, bytes = 20, total = 0).copy(songId = "s2"),
|
||||
)
|
||||
|
||||
assertEquals(DownloadProgress(bytes = 0, total = 0), downloadProgress(rows, sourceId = 1))
|
||||
}
|
||||
|
||||
private fun download(
|
||||
sourceId: Long,
|
||||
bytes: Long,
|
||||
total: Long,
|
||||
) = SongDownload(
|
||||
sourceId = sourceId,
|
||||
songId = "s1",
|
||||
status = DownloadStatus.Running,
|
||||
bytes = bytes,
|
||||
total = total,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user