|
1 | 1 | package org.schabi.newpipe.ui |
2 | 2 |
|
| 3 | +import android.view.MotionEvent |
3 | 4 | import androidx.compose.foundation.gestures.awaitEachGesture |
4 | 5 | import androidx.compose.foundation.gestures.awaitFirstDown |
5 | 6 | import androidx.compose.ui.Modifier |
6 | 7 | import androidx.compose.ui.geometry.Offset |
| 8 | +import androidx.compose.ui.input.pointer.PointerEventTimeoutCancellationException |
| 9 | +import androidx.compose.ui.input.pointer.changedToUp |
| 10 | +import androidx.compose.ui.input.pointer.isOutOfBounds |
7 | 11 | import androidx.compose.ui.input.pointer.pointerInput |
8 | 12 | import androidx.compose.ui.input.pointer.positionChange |
9 | 13 | import androidx.compose.ui.unit.IntOffset |
| 14 | +import androidx.compose.ui.unit.IntSize |
| 15 | +import androidx.compose.ui.util.fastAll |
| 16 | +import androidx.compose.ui.util.fastAny |
10 | 17 |
|
11 | 18 | /** |
12 | 19 | * Detects a drag gesture **without** trying to filter out any misclicks. This is useful in menus |
13 | 20 | * where items are dragged around, where the usual misclick guardrails would cause unexpected lags |
14 | | - * or strange behaviors when dragging stuff around quickly. For other use cases, use |
15 | | - * [androidx.compose.foundation.gestures.detectDragGestures] or |
| 21 | + * or strange behaviors when dragging stuff around quickly. Also detects whether a drag gesture |
| 22 | + * began with a long press or not, which can be useful to decide whether an item should be dragged |
| 23 | + * around (in case of long-press) or the view should be scrolled (otherwise). For other use cases, |
| 24 | + * use [androidx.compose.foundation.gestures.detectDragGestures] or |
16 | 25 | * [androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress]. |
17 | 26 | * |
18 | 27 | * @param beginDragGesture called when the user first touches the screen (down event) with the |
19 | | - * pointer position. |
| 28 | + * pointer position and whether a long press was detected. |
20 | 29 | * @param handleDragGestureChange called with the current pointer position and the difference from |
21 | 30 | * the last position, every time the user moves the finger after [beginDragGesture] has been called. |
22 | 31 | * @param endDragGesture called when the drag gesture finishes, after [beginDragGesture] has been |
23 | 32 | * called. |
24 | 33 | */ |
25 | 34 | fun Modifier.detectDragGestures( |
26 | | - beginDragGesture: (position: IntOffset) -> Unit, |
| 35 | + beginDragGesture: (position: IntOffset, wasLongPressed: Boolean) -> Unit, |
27 | 36 | handleDragGestureChange: (position: IntOffset, positionChange: Offset) -> Unit, |
28 | 37 | endDragGesture: () -> Unit |
29 | 38 | ): Modifier { |
30 | 39 | return this.pointerInput(Unit) { |
31 | 40 | awaitEachGesture { |
32 | 41 | val down = awaitFirstDown() |
| 42 | + val wasLongPressed = try { |
| 43 | + // code in this branch was taken from AwaitPointerEventScope.waitForLongPress(), |
| 44 | + // which unfortunately is private |
| 45 | + withTimeout(viewConfiguration.longPressTimeoutMillis) { |
| 46 | + while (true) { |
| 47 | + val event = awaitPointerEvent() |
| 48 | + if (event.changes.fastAll { it.changedToUp() }) { |
| 49 | + // All pointers are up |
| 50 | + break |
| 51 | + } |
| 52 | + |
| 53 | + if (event.classification == MotionEvent.CLASSIFICATION_DEEP_PRESS) { |
| 54 | + return@withTimeout true |
| 55 | + } |
| 56 | + |
| 57 | + if ( |
| 58 | + event.changes.fastAny { |
| 59 | + it.isConsumed || it.isOutOfBounds(IntSize(0, 0), extendedTouchPadding) |
| 60 | + } |
| 61 | + ) { |
| 62 | + break |
| 63 | + } |
| 64 | + } |
| 65 | + return@withTimeout false |
| 66 | + } |
| 67 | + } catch (_: PointerEventTimeoutCancellationException) { |
| 68 | + true |
| 69 | + } |
| 70 | + |
33 | 71 | val pointerId = down.id |
34 | | - beginDragGesture(down.position.toIntOffset()) |
| 72 | + beginDragGesture(down.position.toIntOffset(), wasLongPressed) |
35 | 73 | while (true) { |
36 | 74 | val change = awaitPointerEvent().changes.find { it.id == pointerId } |
37 | 75 | if (change == null || !change.pressed) { |
|
0 commit comments