summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Joshua Trask <joshtrask@google.com> 2024-05-09 21:55:22 +0000
committer Joshua Trask <joshtrask@google.com> 2024-05-13 19:57:17 +0000
commit734129960386e37d634f8ad411a367038565bed9 (patch)
tree217e119b889917a6ba975207a5898620d12cd34a
parent55e53393ec8f3143014d756f174ac669cfe21d45 (diff)
Fix ScreenshotShelfView gesture handling
We didn't previously have logic to decide whether to scroll "within" the actions container vs. swiping away the screenshots UI. This CL adapts the legacy solution of using an internal GestureDetector to classify gestures as eligible for interception only if we're unable to interpret them as scrolls of the action container. Bug: 339105691, 332406583 Test: manual interactions w/ extra instrumentation Flag: ACONFIG com.android.systemui.screenshot_shelf_ui_2 DEVELOPMENT Change-Id: I25103b5791f30d9e07dfc27b5d9fd5fcf9c81014
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt57
1 files changed, 52 insertions, 5 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
index bd932260848b..969cf482be90 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ui/ScreenshotShelfView.kt
@@ -22,6 +22,8 @@ import android.graphics.Insets
import android.graphics.Rect
import android.graphics.Region
import android.util.AttributeSet
+import android.view.GestureDetector
+import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -43,13 +45,43 @@ class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
private val displayMetrics = context.resources.displayMetrics
private val tmpRect = Rect()
private lateinit var actionsContainerBackground: View
+ private lateinit var actionsContainer: View
private lateinit var dismissButton: View
+ // Prepare an internal `GestureDetector` to determine when we can initiate a touch-interception
+ // session (with the client's provided `onTouchInterceptListener`). We delegate out to their
+ // listener only for gestures that can't be handled by scrolling our `actionsContainer`.
+ private val gestureDetector =
+ GestureDetector(
+ context,
+ object : SimpleOnGestureListener() {
+ override fun onScroll(
+ ev1: MotionEvent?,
+ ev2: MotionEvent,
+ distanceX: Float,
+ distanceY: Float
+ ): Boolean {
+ actionsContainer.getBoundsOnScreen(tmpRect)
+ val touchedInActionsContainer =
+ tmpRect.contains(ev2.rawX.toInt(), ev2.rawY.toInt())
+ val canHandleInternallyByScrolling =
+ touchedInActionsContainer
+ && actionsContainer.canScrollHorizontally(distanceX.toInt())
+ return !canHandleInternallyByScrolling
+ }
+ }
+ )
+
init {
- setOnTouchListener({ _: View, _: MotionEvent ->
+
+ // Delegate to the client-provided `onTouchInterceptListener` if we've already initiated
+ // touch-interception.
+ setOnTouchListener({ _: View, ev: MotionEvent ->
userInteractionCallback?.invoke()
- true
+ onTouchInterceptListener?.invoke(ev) ?: false
})
+
+ gestureDetector.setIsLongpressEnabled(false)
}
override fun onFinishInflate() {
@@ -60,7 +92,15 @@ class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
blurredScreenshotPreview = requireViewById(R.id.screenshot_preview_blur)
screenshotStatic = requireViewById(R.id.screenshot_static)
actionsContainerBackground = requireViewById(R.id.actions_container_background)
+ actionsContainer = requireViewById(R.id.actions_container)
dismissButton = requireViewById(R.id.screenshot_dismiss_button)
+
+ // Configure to extend the timeout during ongoing gestures (i.e. scrolls) that are already
+ // being handled by our child views.
+ actionsContainer.setOnTouchListener({ _: View, ev: MotionEvent ->
+ userInteractionCallback?.invoke()
+ false
+ })
}
fun getTouchRegion(gestureInsets: Insets): Region {
@@ -171,9 +211,16 @@ class ScreenshotShelfView(context: Context, attrs: AttributeSet? = null) :
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
userInteractionCallback?.invoke()
- if (onTouchInterceptListener?.invoke(ev) == true) {
- return true
+ // Let the client-provided listener see all `DOWN` events so that they'll be able to
+ // interpret the remainder of the gesture, even if interception starts partway-through.
+ // TODO: is this really necessary? And if we don't go on to start interception, should we
+ // follow up with `ACTION_CANCEL`?
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ onTouchInterceptListener?.invoke(ev)
}
- return super.onInterceptTouchEvent(ev)
+
+ // Only allow the client-provided touch interceptor to take over the gesture if our
+ // top-level `GestureDetector` decides not to scroll the action container.
+ return gestureDetector.onTouchEvent(ev)
}
}