summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java60
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java68
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java78
5 files changed, 184 insertions, 76 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index d16c8c8c59d6..0509c66f2cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -21,6 +21,9 @@ import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_
import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION;
import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.InputEvent;
@@ -75,6 +78,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
private final FlingAnimationUtils mFlingAnimationUtils;
private final FlingAnimationUtils mFlingAnimationUtilsClosing;
+ private final DisplayMetrics mDisplayMetrics;
+
private Boolean mCapture;
private TouchSession mTouchSession;
@@ -85,40 +90,9 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
private final GestureDetector.OnGestureListener mOnGestureListener =
new GestureDetector.SimpleOnGestureListener() {
- boolean mTrack;
- boolean mBouncerPresent;
-
- @Override
- public boolean onDown(MotionEvent e) {
- // We only consider gestures that originate from the lower portion of the
- // screen.
- final float displayHeight = mStatusBar.getDisplayHeight();
-
- mBouncerPresent = mStatusBar.isBouncerShowing();
-
- // The target zone is either at the top or bottom of the screen, dependent on
- // whether the bouncer is present.
- final float zonePercentage =
- Math.abs(e.getY() - (mBouncerPresent ? 0 : displayHeight))
- / displayHeight;
-
- mTrack = zonePercentage < mBouncerZoneScreenPercentage;
-
- // Never capture onDown. While this might lead to some false positive touches
- // being sent to other windows/layers, this is necessary to make sure the
- // proper touch event sequence is received by others in the event we do not
- // consume the sequence here.
- return false;
- }
-
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
- // Do not handle scroll gestures if not tracking touch events.
- if (!mTrack) {
- return false;
- }
-
if (mCapture == null) {
// If the user scrolling favors a vertical direction, begin capturing
// scrolls.
@@ -141,8 +115,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
// (0).
final float screenTravelPercentage =
Math.abs((e1.getY() - e2.getY()) / mStatusBar.getDisplayHeight());
- setPanelExpansion(
- mBouncerPresent ? screenTravelPercentage : 1 - screenTravelPercentage);
+ setPanelExpansion(mStatusBar.isBouncerShowing()
+ ? screenTravelPercentage : 1 - screenTravelPercentage);
return true;
}
@@ -155,6 +129,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
@Inject
public BouncerSwipeTouchHandler(
+ DisplayMetrics displayMetrics,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
StatusBar statusBar,
NotificationShadeWindowController notificationShadeWindowController,
@@ -165,6 +140,7 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
@Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
FlingAnimationUtils flingAnimationUtilsClosing,
@Named(SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage) {
+ mDisplayMetrics = displayMetrics;
mStatusBar = statusBar;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationShadeWindowController = notificationShadeWindowController;
@@ -176,6 +152,21 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
}
@Override
+ public void getTouchInitiationRegion(Region region) {
+ if (mStatusBar.isBouncerShowing()) {
+ region.op(new Rect(0, 0, mDisplayMetrics.widthPixels,
+ Math.round(mDisplayMetrics.heightPixels * mBouncerZoneScreenPercentage)),
+ Region.Op.UNION);
+ } else {
+ region.op(new Rect(0,
+ Math.round(mDisplayMetrics.heightPixels * (1 - mBouncerZoneScreenPercentage)),
+ mDisplayMetrics.widthPixels,
+ mDisplayMetrics.heightPixels),
+ Region.Op.UNION);
+ }
+ }
+
+ @Override
public void onSessionStart(TouchSession session) {
mVelocityTracker = mVelocityTrackerFactory.obtain();
mTouchSession = session;
@@ -202,7 +193,9 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
final MotionEvent motionEvent = (MotionEvent) event;
switch(motionEvent.getAction()) {
+ case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
+ mTouchSession.pop();
// If we are not capturing any input, there is no need to consider animating to
// finish transition.
if (mCapture == null || !mCapture) {
@@ -226,7 +219,6 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler {
if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
mStatusBarKeyguardViewManager.reset(false);
}
- mTouchSession.pop();
break;
default:
mVelocityTracker.addMovement(motionEvent);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
index 3e5efb2115a0..695b59ac45ad 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.touch;
+import android.graphics.Region;
import android.view.GestureDetector;
import android.view.InputEvent;
import android.view.MotionEvent;
@@ -34,6 +35,7 @@ import com.android.systemui.shared.system.InputChannelCompat;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -100,6 +102,10 @@ public class DreamOverlayTouchMonitor {
});
}
+ private int getSessionCount() {
+ return mActiveTouchSessions.size();
+ }
+
/**
* {@link TouchSessionImpl} implements {@link DreamTouchHandler.TouchSession} for
* {@link DreamOverlayTouchMonitor}. It enables the monitor to access the associated listeners
@@ -146,6 +152,11 @@ public class DreamOverlayTouchMonitor {
return mTouchMonitor.pop(this);
}
+ @Override
+ public int getActiveSessionCount() {
+ return mTouchMonitor.getSessionCount();
+ }
+
/**
* Returns the active listeners to receive touch events.
*/
@@ -229,12 +240,39 @@ public class DreamOverlayTouchMonitor {
public void onInputEvent(InputEvent ev) {
// No Active sessions are receiving touches. Create sessions for each listener
if (mActiveTouchSessions.isEmpty()) {
+ final HashMap<DreamTouchHandler, DreamTouchHandler.TouchSession> sessionMap =
+ new HashMap<>();
+
for (DreamTouchHandler handler : mHandlers) {
+ final Region initiationRegion = Region.obtain();
+ handler.getTouchInitiationRegion(initiationRegion);
+
+ if (!initiationRegion.isEmpty()) {
+ // Initiation regions require a motion event to determine pointer location
+ // within the region.
+ if (!(ev instanceof MotionEvent)) {
+ continue;
+ }
+
+ final MotionEvent motionEvent = (MotionEvent) ev;
+
+ // If the touch event is outside the region, then ignore.
+ if (!initiationRegion.contains(Math.round(motionEvent.getX()),
+ Math.round(motionEvent.getY()))) {
+ continue;
+ }
+ }
+
final TouchSessionImpl sessionStack =
new TouchSessionImpl(DreamOverlayTouchMonitor.this, null);
mActiveTouchSessions.add(sessionStack);
- handler.onSessionStart(sessionStack);
+ sessionMap.put(handler, sessionStack);
}
+
+ // Informing handlers of new sessions is delayed until we have all created so the
+ // final session is correct.
+ sessionMap.forEach((dreamTouchHandler, touchSession)
+ -> dreamTouchHandler.onSessionStart(touchSession));
}
// Find active sessions and invoke on InputEvent.
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
index c73ff733854d..20008d5b02c8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.touch;
+import android.graphics.Region;
import android.view.GestureDetector;
import com.android.systemui.shared.system.InputChannelCompat;
@@ -71,6 +72,19 @@ public interface DreamTouchHandler {
* if the popped {@link TouchSession} was the initial session or has already been popped.
*/
ListenableFuture<TouchSession> pop();
+
+ /**
+ * Returns the number of currently active sessions.
+ */
+ int getActiveSessionCount();
+ }
+
+ /**
+ * Returns the region the touch handler is interested in. By default, no region is specified,
+ * indicating the entire screen should be considered.
+ * @param region A {@link Region} that is passed in to the target entry touch region.
+ */
+ default void getTouchInitiationRegion(Region region) {
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index cad98f4dc713..d51151627be1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -18,7 +18,6 @@ package com.android.systemui.dreams.touch;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -27,12 +26,14 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.testing.AndroidTestingRunner;
+import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -51,8 +52,6 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Random;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@@ -89,13 +88,20 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@Mock
VelocityTracker mVelocityTracker;
+ final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+
private static final float TOUCH_REGION = .3f;
- private static final float SCREEN_HEIGHT_PX = 100;
+ private static final int SCREEN_WIDTH_PX = 1024;
+ private static final int SCREEN_HEIGHT_PX = 100;
@Before
public void setup() {
+ mDisplayMetrics.widthPixels = SCREEN_WIDTH_PX;
+ mDisplayMetrics.heightPixels = SCREEN_HEIGHT_PX;
+
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
+ mDisplayMetrics,
mStatusBarKeyguardViewManager,
mStatusBar,
mNotificationShadeWindowController,
@@ -104,23 +110,29 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
mFlingAnimationUtils,
mFlingAnimationUtilsClosing,
TOUCH_REGION);
- when(mStatusBar.getDisplayHeight()).thenReturn(SCREEN_HEIGHT_PX);
+
+ when(mStatusBar.getDisplayHeight()).thenReturn((float) SCREEN_HEIGHT_PX);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
}
- private static void beginValidSwipe(GestureDetector.OnGestureListener listener) {
- listener.onDown(MotionEvent.obtain(0, 0,
- MotionEvent.ACTION_DOWN, 0,
- SCREEN_HEIGHT_PX - (.5f * TOUCH_REGION * SCREEN_HEIGHT_PX), 0));
- }
-
/**
* Ensures expansion only happens when touch down happens in valid part of the screen.
*/
- @FlakyTest
@Test
public void testSessionStart() {
+ final Region region = Region.obtain();
+ mTouchHandler.getTouchInitiationRegion(region);
+
+ final Rect bounds = region.getBounds();
+
+ final Rect expected = new Rect();
+
+ expected.set(0, Math.round(SCREEN_HEIGHT_PX * (1 - TOUCH_REGION)), SCREEN_WIDTH_PX,
+ SCREEN_HEIGHT_PX);
+
+ assertThat(bounds).isEqualTo(expected);
+
mTouchHandler.onSessionStart(mTouchSession);
verify(mNotificationShadeWindowController).setForcePluginOpen(eq(true), any());
ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor =
@@ -130,34 +142,12 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
verify(mTouchSession).registerInputListener(eventListenerCaptor.capture());
- final Random random = new Random(System.currentTimeMillis());
-
- // If an initial touch down meeting criteria has been met, scroll behavior should be
- // ignored.
- assertThat(gestureListenerCaptor.getValue()
- .onScroll(Mockito.mock(MotionEvent.class),
- Mockito.mock(MotionEvent.class),
- random.nextFloat(),
- random.nextFloat())).isFalse();
-
- // A touch at the top of the screen should also not trigger listening.
- final MotionEvent touchDownEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN,
- 0, 0, 0);
-
- gestureListenerCaptor.getValue().onDown(touchDownEvent);
- assertThat(gestureListenerCaptor.getValue()
- .onScroll(Mockito.mock(MotionEvent.class),
- Mockito.mock(MotionEvent.class),
- random.nextFloat(),
- random.nextFloat())).isFalse();
-
// A touch within range at the bottom of the screen should trigger listening
- beginValidSwipe(gestureListenerCaptor.getValue());
assertThat(gestureListenerCaptor.getValue()
.onScroll(Mockito.mock(MotionEvent.class),
Mockito.mock(MotionEvent.class),
- random.nextFloat(),
- random.nextFloat())).isTrue();
+ 1,
+ 2)).isTrue();
}
/**
@@ -170,8 +160,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
- beginValidSwipe(gestureListenerCaptor.getValue());
-
final float scrollAmount = .3f;
final float distanceY = SCREEN_HEIGHT_PX * scrollAmount;
@@ -203,8 +191,6 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
when(mVelocityTracker.getYVelocity()).thenReturn(velocityY);
- beginValidSwipe(gestureListenerCaptor.getValue());
-
final float distanceY = SCREEN_HEIGHT_PX * position;
final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index 74b217b2fdbf..29f56e0d8bf0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -21,10 +21,13 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.GestureDetector;
@@ -137,6 +140,81 @@ public class DreamOverlayTouchMonitorTest extends SysuiTestCase {
}
@Test
+ public void testEntryTouchZone() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final Rect touchArea = new Rect(4, 4, 8 , 8);
+
+ doAnswer(invocation -> {
+ final Region region = (Region) invocation.getArguments()[0];
+ region.set(touchArea);
+ return null;
+ }).when(touchHandler).getTouchInitiationRegion(any());
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ // Ensure touch outside specified region is not delivered.
+ final MotionEvent initialEvent = Mockito.mock(MotionEvent.class);
+ when(initialEvent.getX()).thenReturn(0.0f);
+ when(initialEvent.getY()).thenReturn(1.0f);
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler, never()).onSessionStart(any());
+
+ // Make sure touch inside region causes session start.
+ when(initialEvent.getX()).thenReturn(5.0f);
+ when(initialEvent.getY()).thenReturn(5.0f);
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler).onSessionStart(any());
+ }
+
+ @Test
+ public void testSessionCount() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final Rect touchArea = new Rect(4, 4, 8 , 8);
+
+ final DreamTouchHandler unzonedTouchHandler = Mockito.mock(DreamTouchHandler.class);
+ doAnswer(invocation -> {
+ final Region region = (Region) invocation.getArguments()[0];
+ region.set(touchArea);
+ return null;
+ }).when(touchHandler).getTouchInitiationRegion(any());
+
+ final Environment environment = new Environment(Stream.of(touchHandler, unzonedTouchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ // Ensure touch outside specified region is delivered to unzoned touch handler.
+ final MotionEvent initialEvent = Mockito.mock(MotionEvent.class);
+ when(initialEvent.getX()).thenReturn(0.0f);
+ when(initialEvent.getY()).thenReturn(1.0f);
+ environment.publishInputEvent(initialEvent);
+
+ ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionCaptor = ArgumentCaptor.forClass(
+ DreamTouchHandler.TouchSession.class);
+
+ // Make sure only one active session.
+ {
+ verify(unzonedTouchHandler).onSessionStart(touchSessionCaptor.capture());
+ final DreamTouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
+ assertThat(touchSession.getActiveSessionCount()).isEqualTo(1);
+ touchSession.pop();
+ environment.executeAll();
+ }
+
+ // Make sure touch inside the touch region.
+ when(initialEvent.getX()).thenReturn(5.0f);
+ when(initialEvent.getY()).thenReturn(5.0f);
+ environment.publishInputEvent(initialEvent);
+
+ // Make sure there are two active sessions.
+ {
+ verify(touchHandler).onSessionStart(touchSessionCaptor.capture());
+ final DreamTouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
+ assertThat(touchSession.getActiveSessionCount()).isEqualTo(2);
+ touchSession.pop();
+ }
+ }
+
+ @Test
public void testInputEventPropagation() {
final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);