diff --git a/app/src/main/java/org/schabi/newpipe/ui/DetectDragModifier.kt b/app/src/main/java/org/schabi/newpipe/ui/DetectDragModifier.kt
index 1a5c6e5dc..ca844d855 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/DetectDragModifier.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/DetectDragModifier.kt
@@ -1,37 +1,75 @@
package org.schabi.newpipe.ui
+import android.view.MotionEvent
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerEventTimeoutCancellationException
+import androidx.compose.ui.input.pointer.changedToUp
+import androidx.compose.ui.input.pointer.isOutOfBounds
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.input.pointer.positionChange
import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import androidx.compose.ui.util.fastAll
+import androidx.compose.ui.util.fastAny
/**
* Detects a drag gesture **without** trying to filter out any misclicks. This is useful in menus
* where items are dragged around, where the usual misclick guardrails would cause unexpected lags
- * or strange behaviors when dragging stuff around quickly. For other use cases, use
- * [androidx.compose.foundation.gestures.detectDragGestures] or
+ * or strange behaviors when dragging stuff around quickly. Also detects whether a drag gesture
+ * began with a long press or not, which can be useful to decide whether an item should be dragged
+ * around (in case of long-press) or the view should be scrolled (otherwise). For other use cases,
+ * use [androidx.compose.foundation.gestures.detectDragGestures] or
* [androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress].
*
* @param beginDragGesture called when the user first touches the screen (down event) with the
- * pointer position.
+ * pointer position and whether a long press was detected.
* @param handleDragGestureChange called with the current pointer position and the difference from
* the last position, every time the user moves the finger after [beginDragGesture] has been called.
* @param endDragGesture called when the drag gesture finishes, after [beginDragGesture] has been
* called.
*/
fun Modifier.detectDragGestures(
- beginDragGesture: (position: IntOffset) -> Unit,
+ beginDragGesture: (position: IntOffset, wasLongPressed: Boolean) -> Unit,
handleDragGestureChange: (position: IntOffset, positionChange: Offset) -> Unit,
endDragGesture: () -> Unit
): Modifier {
return this.pointerInput(Unit) {
awaitEachGesture {
val down = awaitFirstDown()
+ val wasLongPressed = try {
+ // code in this branch was taken from AwaitPointerEventScope.waitForLongPress(),
+ // which unfortunately is private
+ withTimeout(viewConfiguration.longPressTimeoutMillis) {
+ while (true) {
+ val event = awaitPointerEvent()
+ if (event.changes.fastAll { it.changedToUp() }) {
+ // All pointers are up
+ break
+ }
+
+ if (event.classification == MotionEvent.CLASSIFICATION_DEEP_PRESS) {
+ return@withTimeout true
+ }
+
+ if (
+ event.changes.fastAny {
+ it.isConsumed || it.isOutOfBounds(IntSize(0, 0), extendedTouchPadding)
+ }
+ ) {
+ break
+ }
+ }
+ return@withTimeout false
+ }
+ } catch (_: PointerEventTimeoutCancellationException) {
+ true
+ }
+
val pointerId = down.id
- beginDragGesture(down.position.toIntOffset())
+ beginDragGesture(down.position.toIntOffset(), wasLongPressed)
while (true) {
val change = awaitPointerEvent().changes.find { it.id == pointerId }
if (change == null || !change.pressed) {
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditor.kt b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditor.kt
index 9f60b356a..5ff418913 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditor.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/menu/LongPressMenuEditor.kt
@@ -94,7 +94,7 @@ import kotlin.math.floor
import kotlin.math.max
import kotlin.math.min
-const val TAG = "LongPressMenuEditor"
+internal const val TAG = "LongPressMenuEditor"
// TODO padding doesn't seem to work as expected when the list becomes scrollable?
@Composable
@@ -166,7 +166,12 @@ fun LongPressMenuEditor(modifier: Modifier = Modifier) {
}
// this beginDragGesture() overload is only called when moving the finger (not on DPAD's Enter)
- fun beginDragGesture(pos: IntOffset) {
+ fun beginDragGesture(pos: IntOffset, wasLongPressed: Boolean) {
+ if (!wasLongPressed) {
+ // items can be dragged around only if they are long-pressed;
+ // use the drag as scroll otherwise
+ return
+ }
val rawItem = findItemForOffsetOrClosestInRow(pos) ?: return
beginDragGesture(pos, rawItem)
autoScrollSpeed = 0f
@@ -229,7 +234,8 @@ fun LongPressMenuEditor(modifier: Modifier = Modifier) {
fun handleDragGestureChange(pos: IntOffset, posChangeForScrolling: Offset) {
val dragItem = activeDragItem
if (dragItem == null) {
- // when the user clicks outside of any draggable item, let the list be scrolled
+ // when the user clicks outside of any draggable item, or if the user did not long-press
+ // on an item to begin with, let the list be scrolled
gridState.dispatchRawDelta(-posChangeForScrolling.y)
return
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 5620f9382..8d0ba0799 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -904,7 +904,7 @@
Popup\nfrom here
Play\nfrom here
Enabled actions:
- Reorder the actions by dragging them around
+ Reorder the actions by long pressing and then dragging them around
Hidden actions:
Drag the header or the actions to this section to hide them
Header