From bbdff4b09317ee4c01570f21030702c3d75f530c Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Tue, 9 Jul 2024 05:34:28 +0530 Subject: [PATCH] Show dropdown menu on long click, make some adjustments --- .../newpipe/compose/stream/StreamCardItem.kt | 74 ++++++++++++------- .../newpipe/compose/stream/StreamGridItem.kt | 61 ++++++++++----- .../newpipe/compose/stream/StreamList.kt | 30 +++++++- .../newpipe/compose/stream/StreamListItem.kt | 69 +++++++++++------ .../newpipe/compose/stream/StreamMenu.kt | 46 ++++++++++++ .../newpipe/compose/stream/StreamThumbnail.kt | 4 +- 6 files changed, 206 insertions(+), 78 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/compose/stream/StreamMenu.kt diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamCardItem.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamCardItem.kt index 0a69330ac..b558e099d 100644 --- a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamCardItem.kt +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamCardItem.kt @@ -1,8 +1,10 @@ package org.schabi.newpipe.compose.stream import android.content.res.Configuration -import androidx.compose.foundation.clickable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -20,39 +22,55 @@ import androidx.compose.ui.unit.dp import org.schabi.newpipe.compose.theme.AppTheme import org.schabi.newpipe.extractor.stream.StreamInfoItem +@OptIn(ExperimentalFoundationApi::class) @Composable -fun StreamCardItem(stream: StreamInfoItem, onClick: (StreamInfoItem) -> Unit) { - Column( - modifier = Modifier - .clickable(onClick = { onClick(stream) }) - .padding(top = 12.dp, start = 2.dp, end = 2.dp) - ) { - StreamThumbnail( - stream = stream, - modifier = Modifier.fillMaxWidth(), - contentScale = ContentScale.FillWidth - ) - - Column(modifier = Modifier.padding(10.dp)) { - Text( - text = stream.name, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.titleSmall, - maxLines = 2 +fun StreamCardItem( + stream: StreamInfoItem, + isSelected: Boolean = false, + onClick: (StreamInfoItem) -> Unit = {}, + onLongClick: (StreamInfoItem) -> Unit = {}, + onDismissPopup: () -> Unit = {} +) { + Box { + Column( + modifier = Modifier + .combinedClickable( + onLongClick = { onLongClick(stream) }, + onClick = { onClick(stream) } + ) + .padding(top = 12.dp, start = 2.dp, end = 2.dp) + ) { + StreamThumbnail( + modifier = Modifier.fillMaxWidth(), + stream = stream, + contentScale = ContentScale.FillWidth ) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) - + Column(modifier = Modifier.padding(10.dp)) { Text( - text = getStreamInfoDetail(stream), - style = MaterialTheme.typography.bodySmall + text = stream.name, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleSmall, + maxLines = 2 ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) + + Text( + text = getStreamInfoDetail(stream), + style = MaterialTheme.typography.bodySmall + ) + } } } + + if (isSelected) { + StreamMenu(onDismissPopup) + } } } @@ -64,7 +82,7 @@ private fun StreamCardItemPreview( ) { AppTheme { Surface(color = MaterialTheme.colorScheme.background) { - StreamCardItem(stream) {} + StreamCardItem(stream) } } } diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamGridItem.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamGridItem.kt index 432cc1594..eea1a0d41 100644 --- a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamGridItem.kt +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamGridItem.kt @@ -1,7 +1,9 @@ package org.schabi.newpipe.compose.stream import android.content.res.Configuration -import androidx.compose.foundation.clickable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -17,28 +19,47 @@ import androidx.compose.ui.unit.dp import org.schabi.newpipe.compose.theme.AppTheme import org.schabi.newpipe.extractor.stream.StreamInfoItem +@OptIn(ExperimentalFoundationApi::class) @Composable -fun StreamGridItem(stream: StreamInfoItem, onClick: (StreamInfoItem) -> Unit) { - Column( - modifier = Modifier - .clickable(onClick = { onClick(stream) }) - .padding(12.dp) - ) { - StreamThumbnail(stream = stream, modifier = Modifier.size(width = 246.dp, height = 138.dp)) +fun StreamGridItem( + stream: StreamInfoItem, + isSelected: Boolean = false, + onClick: (StreamInfoItem) -> Unit = {}, + onLongClick: (StreamInfoItem) -> Unit = {}, + onDismissPopup: () -> Unit = {} +) { + Box { + Column( + modifier = Modifier + .combinedClickable( + onLongClick = { onLongClick(stream) }, + onClick = { onClick(stream) } + ) + .padding(12.dp) + ) { + StreamThumbnail( + modifier = Modifier.size(width = 246.dp, height = 138.dp), + stream = stream + ) - Text( - text = stream.name, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.titleSmall, - maxLines = 2 - ) + Text( + text = stream.name, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleSmall, + maxLines = 2 + ) - Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) + Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) - Text( - text = getStreamInfoDetail(stream), - style = MaterialTheme.typography.bodySmall - ) + Text( + text = getStreamInfoDetail(stream), + style = MaterialTheme.typography.bodySmall + ) + } + + if (isSelected) { + StreamMenu(onDismissPopup) + } } } @@ -50,7 +71,7 @@ private fun StreamGridItemPreview( ) { AppTheme { Surface(color = MaterialTheme.colorScheme.background) { - StreamGridItem(stream, onClick = {}) + StreamGridItem(stream) } } } diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamList.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamList.kt index 732fdb460..e9c161d81 100644 --- a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamList.kt +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamList.kt @@ -8,7 +8,10 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.fragment.app.FragmentActivity @@ -36,7 +39,20 @@ fun StreamList( ) } } - // TODO: Handle long-click by showing a dropdown menu instead of a dialog. + + // Handle long clicks + // TODO: Adjust the menu display depending on where it was triggered + var selectedStream by remember { mutableStateOf(null) } + val onLongClick = remember { + { stream: StreamInfoItem -> + selectedStream = stream + } + } + val onDismissPopup = remember { + { + selectedStream = null + } + } if (mode == ItemViewMode.GRID) { val gridState = rememberLazyGridState() @@ -46,7 +62,11 @@ fun StreamList( gridHeader() items(streams.itemCount) { - StreamGridItem(streams[it]!!, onClick) + val stream = streams[it]!! + StreamGridItem( + stream, selectedStream == stream, onClick, onLongClick, + onDismissPopup + ) } } } @@ -60,10 +80,12 @@ fun StreamList( items(streams.itemCount) { val stream = streams[it]!! + val isSelected = selectedStream == stream + if (mode == ItemViewMode.CARD) { - StreamCardItem(stream, onClick) + StreamCardItem(stream, isSelected, onClick, onLongClick, onDismissPopup) } else { - StreamListItem(stream, onClick) + StreamListItem(stream, isSelected, onClick, onLongClick, onDismissPopup) } } } diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamListItem.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamListItem.kt index fa85c025e..7bcf9c797 100644 --- a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamListItem.kt +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamListItem.kt @@ -1,8 +1,10 @@ package org.schabi.newpipe.compose.stream import android.content.res.Configuration -import androidx.compose.foundation.clickable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -21,32 +23,51 @@ import androidx.compose.ui.unit.dp import org.schabi.newpipe.compose.theme.AppTheme import org.schabi.newpipe.extractor.stream.StreamInfoItem +@OptIn(ExperimentalFoundationApi::class) @Composable -fun StreamListItem(stream: StreamInfoItem, onClick: (StreamInfoItem) -> Unit) { - Row( - modifier = Modifier - .clickable(onClick = { onClick(stream) }) - .fillMaxWidth() - .padding(12.dp), - horizontalArrangement = Arrangement.spacedBy(4.dp), - verticalAlignment = Alignment.CenterVertically - ) { - StreamThumbnail(stream = stream, modifier = Modifier.size(width = 98.dp, height = 55.dp)) - - Column { - Text( - text = stream.name, - overflow = TextOverflow.Ellipsis, - style = MaterialTheme.typography.titleSmall, - maxLines = 1 +fun StreamListItem( + stream: StreamInfoItem, + isSelected: Boolean = false, + onClick: (StreamInfoItem) -> Unit = {}, + onLongClick: (StreamInfoItem) -> Unit = {}, + onDismissPopup: () -> Unit = {} +) { + Box { + Row( + modifier = Modifier + .combinedClickable( + onLongClick = { onLongClick(stream) }, + onClick = { onClick(stream) } + ) + .fillMaxWidth() + .padding(12.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + StreamThumbnail( + modifier = Modifier.size(width = 98.dp, height = 55.dp), + stream = stream ) - Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) + Column { + Text( + text = stream.name, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleSmall, + maxLines = 1 + ) - Text( - text = getStreamInfoDetail(stream), - style = MaterialTheme.typography.bodySmall - ) + Text(text = stream.uploaderName.orEmpty(), style = MaterialTheme.typography.bodySmall) + + Text( + text = getStreamInfoDetail(stream), + style = MaterialTheme.typography.bodySmall + ) + } + } + + if (isSelected) { + StreamMenu(onDismissPopup) } } } @@ -59,7 +80,7 @@ private fun StreamListItemPreview( ) { AppTheme { Surface(color = MaterialTheme.colorScheme.background) { - StreamListItem(stream, onClick = {}) + StreamListItem(stream) } } } diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamMenu.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamMenu.kt new file mode 100644 index 000000000..af7f2e63c --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamMenu.kt @@ -0,0 +1,46 @@ +package org.schabi.newpipe.compose.stream + +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.schabi.newpipe.R + +@Composable +fun StreamMenu(onDismissRequest: () -> Unit) { + DropdownMenu(expanded = true, onDismissRequest = onDismissRequest) { + DropdownMenuItem( + text = { Text(text = stringResource(R.string.start_here_on_background)) }, + onClick = onDismissRequest + ) + DropdownMenuItem( + text = { Text(text = stringResource(R.string.start_here_on_popup)) }, + onClick = onDismissRequest + ) + DropdownMenuItem( + text = { Text(text = stringResource(R.string.download)) }, + onClick = onDismissRequest + ) + DropdownMenuItem( + text = { Text(text = stringResource(R.string.add_to_playlist)) }, + onClick = onDismissRequest + ) + DropdownMenuItem( + text = { Text(text = stringResource(R.string.share)) }, + onClick = onDismissRequest + ) + DropdownMenuItem( + text = { Text(text = stringResource(R.string.open_in_browser)) }, + onClick = onDismissRequest + ) + DropdownMenuItem( + text = { Text(text = stringResource(R.string.mark_as_watched)) }, + onClick = onDismissRequest + ) + DropdownMenuItem( + text = { Text(text = stringResource(R.string.show_channel_details)) }, + onClick = onDismissRequest + ) + } +} diff --git a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamThumbnail.kt b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamThumbnail.kt index f9228da3c..494d6650a 100644 --- a/app/src/main/java/org/schabi/newpipe/compose/stream/StreamThumbnail.kt +++ b/app/src/main/java/org/schabi/newpipe/compose/stream/StreamThumbnail.kt @@ -20,9 +20,9 @@ import org.schabi.newpipe.util.image.ImageStrategy @Composable fun StreamThumbnail( - stream: StreamInfoItem, modifier: Modifier = Modifier, - contentScale: ContentScale = ContentScale.Fit, + stream: StreamInfoItem, + contentScale: ContentScale = ContentScale.Fit ) { Box(modifier = modifier, contentAlignment = Alignment.BottomEnd) { AsyncImage(