@@ -149,6 +149,12 @@ public void afterTextChanged(Editable s) {}
149149 if ("button" .equals (role )) {
150150 view .setClickable (true );
151151 view .setOnClickListener (v -> sendToUnity (id , "click" , "" ));
152+ } else if ("main_scroll_view" .equals (id )) {
153+ // Claim ACTION_DOWN on the scrollable's empty regions so the parent
154+ // E2EOverlay sees subsequent ACTION_MOVE events and can intercept
155+ // vertical drags. Without this, swipes on bare space fall straight
156+ // through the overlay and Appium scrollIntoView never advances.
157+ view .setClickable (true );
152158 }
153159 }
154160
@@ -211,44 +217,69 @@ private static final class Entry {
211217 }
212218
213219 private static final class E2EOverlay extends FrameLayout {
214- private final int gutterWidth ;
215220 private final int touchSlop ;
216- private boolean trackingGutterDrag ;
221+ // Once a swipe is detected we hijack the gesture: the originally-targeted
222+ // child (often a button TextView) receives ACTION_CANCEL and every later
223+ // ACTION_MOVE/UP routes to this overlay's onTouchEvent.
224+ private boolean interceptingScroll ;
225+ private float startX ;
217226 private float startY ;
218227
219228 E2EOverlay (Activity activity ) {
220229 super (activity );
221- // The test swipe lane is x=10. Keep this narrow so dialog checkboxes
222- // near the left edge still receive normal clicks.
223- gutterWidth = 24 ;
224230 touchSlop = ViewConfiguration .get (activity ).getScaledTouchSlop ();
225231 }
226232
227233 @ Override
228- public boolean dispatchTouchEvent (MotionEvent event ) {
229- if (event .getActionMasked () == MotionEvent .ACTION_DOWN ) {
230- trackingGutterDrag = event .getX () <= gutterWidth ;
231- startY = event .getY ();
232- if (trackingGutterDrag ) return true ;
233- }
234-
235- if (!trackingGutterDrag ) return super .dispatchTouchEvent (event );
236-
237- if (event .getActionMasked () == MotionEvent .ACTION_UP ) {
238- float deltaY = event .getY () - startY ;
239- if (Math .abs (deltaY ) > touchSlop ) {
240- sendToUnity ("main_scroll_view" , "scroll" , deltaY < 0 ? "down" : "up" );
241- }
242- trackingGutterDrag = false ;
243- return true ;
234+ public boolean onInterceptTouchEvent (MotionEvent ev ) {
235+ // Unity renders into a SurfaceView, so UI Toolkit's ScrollView never
236+ // sees touches that an overlay accessibility child consumed first.
237+ // Appium's `scrollIntoView` swipes through the horizontal centre of
238+ // `main_scroll_view`, often starting on top of a clickable overlay
239+ // TextView (button) — that child eats ACTION_DOWN and the rest of the
240+ // gesture, so the ScrollView never scrolls. Catch any vertical drag
241+ // here and convert it to a real ScrollView offset change. Plain taps
242+ // stay under touchSlop and pass through unaffected.
243+ switch (ev .getActionMasked ()) {
244+ case MotionEvent .ACTION_DOWN :
245+ startX = ev .getX ();
246+ startY = ev .getY ();
247+ interceptingScroll = false ;
248+ return false ;
249+ case MotionEvent .ACTION_MOVE :
250+ if (interceptingScroll ) return true ;
251+ float dx = ev .getX () - startX ;
252+ float dy = ev .getY () - startY ;
253+ if (Math .abs (dy ) > touchSlop && Math .abs (dy ) > Math .abs (dx )) {
254+ interceptingScroll = true ;
255+ return true ;
256+ }
257+ return false ;
258+ default :
259+ return false ;
244260 }
261+ }
245262
246- if (event .getActionMasked () == MotionEvent .ACTION_CANCEL ) {
247- trackingGutterDrag = false ;
248- return true ;
263+ @ Override
264+ public boolean onTouchEvent (MotionEvent ev ) {
265+ if (!interceptingScroll ) return false ;
266+ switch (ev .getActionMasked ()) {
267+ case MotionEvent .ACTION_UP :
268+ case MotionEvent .ACTION_CANCEL :
269+ float deltaY = ev .getY () - startY ;
270+ interceptingScroll = false ;
271+ if (Math .abs (deltaY ) > touchSlop ) {
272+ // Positive value = scroll forward (reveal content below),
273+ // matching a finger that travels upward on screen. Unity converts
274+ // screen pixels to UI Toolkit panel units before mutating
275+ // scrollOffset.
276+ sendToUnity (
277+ "main_scroll_view" , "scrollDelta" , String .valueOf (Math .round (-deltaY )));
278+ }
279+ return true ;
280+ default :
281+ return true ;
249282 }
250-
251- return true ;
252283 }
253284 }
254285
0 commit comments