Skip to content

Commit

Permalink
Fixes #7266 touch event disallow propagation.
Browse files Browse the repository at this point in the history
Directly calls `requestDisallowInterceptTouchEvent` on the view.
This allows the flags to be properly sent up the view tree.

PiperOrigin-RevId: 444509476
  • Loading branch information
wesalvaro authored and Copybara-Service committed Apr 26, 2022
1 parent c8aa22d commit ea3e5d1
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
Expand Up @@ -8,7 +8,9 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.robolectric.Shadows.shadowOf;
import static org.robolectric.shadows.ShadowLooper.idleMainLooper;

import android.app.Activity;
import android.app.Application;
import android.view.MotionEvent;
import android.view.View;
Expand All @@ -27,6 +29,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.R;
import org.robolectric.Robolectric;

@RunWith(AndroidJUnit4.class)
public class ShadowViewGroupTest {
Expand Down Expand Up @@ -384,6 +387,65 @@ public void removeAllViews_shouldCallOnChildViewRemovedWithEachChild() {
assertTrue(testListener.wasCalled());
}

@Test
public void requestDisallowInterceptTouchEvent_storedOnShadow() {
child3.requestDisallowInterceptTouchEvent(true);

assertTrue(shadowOf(child3).getDisallowInterceptTouchEvent());
}

@Test
public void requestDisallowInterceptTouchEvent_bubblesUp() {
child3.requestDisallowInterceptTouchEvent(true);

assertTrue(shadowOf(child3).getDisallowInterceptTouchEvent());
assertTrue(shadowOf(root).getDisallowInterceptTouchEvent());
}

@Test
public void requestDisallowInterceptTouchEvent_isReflected() {
// Set up an Activity to accurately dispatch touch events.
Activity activity = Robolectric.setupActivity(Activity.class);
activity.setContentView(root);
idleMainLooper();
// Set a no-op click listener so we collect all the touch events.
child3a.setOnClickListener(view -> {});
// Request our parent not intercept our touch events.
// This must be _during the initial down MotionEvent_ and not before.
// The down event will reset this state (and so we do not need to reset it).
// The value in getDisallowInterceptTouchEvent() is not in-sync with the flag and
// only records the last call to requestDisallowInterceptTouchEvent().
child3a.setOnTouchListener(
(view, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
view.getParent().requestDisallowInterceptTouchEvent(true);
}
return false;
});

MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);

root.dispatchTouchEvent(downEvent);
// Down event is _always_ intercepted by the root.
assertTrue(shadowOf(root).getDisallowInterceptTouchEvent());
assertSame(shadowOf(root).getInterceptedTouchEvent(), downEvent);
assertSame(shadowOf(child3a).getLastTouchEvent(), downEvent);

root.dispatchTouchEvent(moveEvent);
// Subsequent event types are _not_ intercepted:
assertTrue(shadowOf(root).getDisallowInterceptTouchEvent());
assertSame(shadowOf(root).getInterceptedTouchEvent(), downEvent);
assertSame(shadowOf(child3a).getLastTouchEvent(), moveEvent);

root.dispatchTouchEvent(upEvent);
// Subsequent event types are _not_ intercepted:
assertTrue(shadowOf(root).getDisallowInterceptTouchEvent());
assertSame(shadowOf(root).getInterceptedTouchEvent(), downEvent);
assertSame(shadowOf(child3a).getLastTouchEvent(), upEvent);
}

private void makeFocusable(View... views) {
for (View view : views) {
view.setFocusable(true);
Expand Down
Expand Up @@ -84,6 +84,8 @@ public void dump(PrintStream out, int indent) {

@Implementation
protected void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
reflector(ViewGroupReflector.class, realViewGroup)
.requestDisallowInterceptTouchEvent(disallowIntercept);
disallowInterceptTouchEvent = disallowIntercept;
}

Expand Down Expand Up @@ -113,5 +115,8 @@ interface ViewGroupReflector {

@Direct
void addView(View child, int index, ViewGroup.LayoutParams params);

@Direct
void requestDisallowInterceptTouchEvent(boolean disallowIntercept);
}
}

0 comments on commit ea3e5d1

Please sign in to comment.