diff --git a/app/src/main/kotlin/com/subtracks/ui/components/HeroDetailScaffold.kt b/app/src/main/kotlin/com/subtracks/ui/components/HeroDetailScaffold.kt index d0100768..626a1015 100644 --- a/app/src/main/kotlin/com/subtracks/ui/components/HeroDetailScaffold.kt +++ b/app/src/main/kotlin/com/subtracks/ui/components/HeroDetailScaffold.kt @@ -2,6 +2,7 @@ package com.subtracks.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -209,11 +210,21 @@ private fun HeroSubtitle( ) } -fun ListDownloadStatus.action(): BulkDownloadAction = +fun ListDownloadStatus.actions(): List = when { - complete -> BulkDownloadAction.Delete - downloading > 0 -> BulkDownloadAction.Cancel - else -> BulkDownloadAction.Download + complete -> listOf(BulkDownloadAction.Delete) + downloading > 0 -> listOf(BulkDownloadAction.Cancel) + downloaded > 0 -> listOf(BulkDownloadAction.Download, BulkDownloadAction.Delete) + else -> listOf(BulkDownloadAction.Download) + } + +fun ListDownloadStatus.action(): BulkDownloadAction = actions().first() + +private fun downloadActionLabel(action: BulkDownloadAction): String = + when (action) { + BulkDownloadAction.Download -> "Download" + BulkDownloadAction.Cancel -> "Cancel download" + BulkDownloadAction.Delete -> "Delete download" } @Composable @@ -284,17 +295,29 @@ fun HeroHeader( modifier = controlsModifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, ) { - IconButton( - onClick = { onDownloadAction(downloadStatus.action()) }, - enabled = hasSongs, - modifier = Modifier.padding(horizontal = 8.dp), + Box( + modifier = + Modifier + .size(48.dp) + .combinedClickable( + enabled = hasSongs, + onClick = { onDownloadAction(downloadStatus.action()) }, + onClickLabel = downloadActionLabel(downloadStatus.action()), + onLongClick = { + if (BulkDownloadAction.Delete in downloadStatus.actions()) { + onDownloadAction(BulkDownloadAction.Delete) + } + }, + onLongClickLabel = "Delete download", + ), + contentAlignment = Alignment.Center, ) { when (downloadStatus.action()) { BulkDownloadAction.Delete -> { Icon( imageVector = Icons.Rounded.DownloadDone, contentDescription = "Delete download", - modifier = Modifier.size(28.dp).offset(y = 1.5.dp), + modifier = Modifier.size(28.dp), ) } @@ -310,7 +333,7 @@ fun HeroHeader( } BulkDownloadAction.Download -> { - Icon(Icons.Rounded.Download, contentDescription = "Download", modifier = Modifier.offset(y = 1.5.dp)) + Icon(Icons.Rounded.Download, contentDescription = "Download", modifier = Modifier.size(24.dp)) } } } diff --git a/app/src/test/kotlin/com/subtracks/ui/components/BulkDownloadActionTest.kt b/app/src/test/kotlin/com/subtracks/ui/components/BulkDownloadActionTest.kt index dd74bfb6..2e7915c9 100644 --- a/app/src/test/kotlin/com/subtracks/ui/components/BulkDownloadActionTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/components/BulkDownloadActionTest.kt @@ -26,4 +26,26 @@ class BulkDownloadActionTest { fun aFullyDownloadedListOffersDelete() { assertEquals(BulkDownloadAction.Delete, ListDownloadStatus(total = 3, downloaded = 3).action()) } + + @Test + fun aPartiallyDownloadedListOffersBoth() { + assertEquals( + listOf(BulkDownloadAction.Download, BulkDownloadAction.Delete), + ListDownloadStatus(total = 3, downloaded = 1).actions(), + ) + } + + @Test + fun anInFlightListOffersOnlyCancel() { + assertEquals( + listOf(BulkDownloadAction.Cancel), + ListDownloadStatus(total = 3, downloaded = 1, downloading = 1).actions(), + ) + } + + @Test + fun anUndownloadedListOffersOnlyDownload() { + assertEquals(listOf(BulkDownloadAction.Download), ListDownloadStatus(total = 3).actions()) + assertEquals(listOf(BulkDownloadAction.Download), ListDownloadStatus().actions()) + } } diff --git a/app/src/test/kotlin/com/subtracks/ui/components/HeroDownloadTest.kt b/app/src/test/kotlin/com/subtracks/ui/components/HeroDownloadTest.kt index 99685676..d46242ea 100644 --- a/app/src/test/kotlin/com/subtracks/ui/components/HeroDownloadTest.kt +++ b/app/src/test/kotlin/com/subtracks/ui/components/HeroDownloadTest.kt @@ -3,14 +3,17 @@ package com.subtracks.ui.components import androidx.activity.ComponentActivity import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule +import androidx.compose.ui.test.longClick import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.unit.dp import androidx.test.ext.junit.runners.AndroidJUnit4 import com.subtracks.data.model.BulkDownloadAction import com.subtracks.data.model.ListDownloadStatus import com.subtracks.ui.theme.SubtracksTheme import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -42,6 +45,26 @@ class HeroDownloadTest { assertEquals(listOf(BulkDownloadAction.Delete), actions) } + @Test + fun longPressingAPartiallyDownloadedListAsksToDelete() { + val actions = mutableListOf() + render(ListDownloadStatus(total = 3, downloaded = 1)) { actions += it } + + composeRule.onNodeWithContentDescription("Download").performTouchInput { longClick() } + + assertEquals(listOf(BulkDownloadAction.Delete), actions) + } + + @Test + fun longPressingAnUndownloadedListDoesNothing() { + val actions = mutableListOf() + render(ListDownloadStatus(total = 3)) { actions += it } + + composeRule.onNodeWithContentDescription("Download").performTouchInput { longClick() } + + assertTrue(actions.isEmpty()) + } + @Test fun theButtonIsDisabledWithoutSongs() { render(ListDownloadStatus(), hasSongs = false) {}