From bfaa9760dd06d13618a17b3ca362db0639fc8008 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 25 Apr 2019 18:04:41 -0700 Subject: [PATCH] Updating the touch proxy logic: In draglayer, we always dispatch touch events to child views. If the touch originated from gesture area, when we dont route it through touch controllers. The proxy events are only send to touch controller. If any controller consumes the event, then we cancel the view touch (pilferPointers) This allows the controllers to work outside the dragView area, and prevents normal view interaction when there is a window on top (like keyboard) while keeping our activity focused Bug: 131088901 Bug: 130618737 Change-Id: If033dde3a0f9cb6a6e449c9586c1fa050af5bdcb --- .../quickstep/OverviewInputConsumer.java | 121 +++++--------- .../quickstep/TouchInteractionService.java | 4 +- .../WindowTransformSwipeHandler.java | 2 +- .../quickstep/fallback/RecentsRootView.java | 29 +--- .../android/launcher3/LauncherRootView.java | 27 +--- .../popup/PopupContainerWithArrow.java | 4 +- .../launcher3/views/BaseDragLayer.java | 150 +++++++++++++----- 7 files changed, 153 insertions(+), 184 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java index b803071cfc..32e0e48a4d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java @@ -15,24 +15,20 @@ */ package com.android.quickstep; -import static android.view.MotionEvent.ACTION_CANCEL; -import static android.view.MotionEvent.ACTION_DOWN; -import static android.view.MotionEvent.ACTION_MOVE; -import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE; import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; -import android.graphics.PointF; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.ViewConfiguration; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.Utilities; import com.android.launcher3.views.BaseDragLayer; -import com.android.quickstep.util.CachedEventDispatcher; import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.InputMonitorCompat; + +import androidx.annotation.Nullable; /** * Input consumer for handling touch on the recents/Launcher activity. @@ -40,24 +36,27 @@ import com.android.systemui.shared.system.ActivityManagerWrapper; public class OverviewInputConsumer implements InputConsumer { - private final CachedEventDispatcher mCachedEventDispatcher = new CachedEventDispatcher(); private final T mActivity; private final BaseDragLayer mTarget; + private final InputMonitorCompat mInputMonitor; + private final int[] mLocationOnScreen = new int[2]; - private final PointF mDownPos = new PointF(); - private final int mTouchSlopSquared; + private final boolean mProxyTouch; private final boolean mStartingInActivityBounds; + private boolean mTargetHandledTouch; - private boolean mTrackingStarted = false; - private boolean mInvalidated = false; - - OverviewInputConsumer(T activity, boolean startingInActivityBounds) { + OverviewInputConsumer(T activity, @Nullable InputMonitorCompat inputMonitor, + boolean startingInActivityBounds) { mActivity = activity; - mTarget = activity.getDragLayer(); - int touchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop(); - mTouchSlopSquared = touchSlop * touchSlop; + mInputMonitor = inputMonitor; mStartingInActivityBounds = startingInActivityBounds; + + mTarget = activity.getDragLayer(); + if (!startingInActivityBounds) { + mTarget.getLocationOnScreen(mLocationOnScreen); + } + mProxyTouch = mTarget.prepareProxyEventStarting(); } @Override @@ -67,45 +66,29 @@ public class OverviewInputConsumer @Override public void onMotionEvent(MotionEvent ev) { - if (mInvalidated) { + if (!mProxyTouch) { return; } - mCachedEventDispatcher.dispatchEvent(ev); - int action = ev.getActionMasked(); - if (action == ACTION_DOWN) { - if (mStartingInActivityBounds) { - startTouchTracking(ev, false /* updateLocationOffset */, - false /* closeActiveWindows */); - return; - } - mTrackingStarted = false; - mDownPos.set(ev.getX(), ev.getY()); - } else if (!mTrackingStarted) { - switch (action) { - case ACTION_CANCEL: - case ACTION_UP: - startTouchTracking(ev, true /* updateLocationOffset */, - false /* closeActiveWindows */); - break; - case ACTION_MOVE: { - float x = ev.getX() - mDownPos.x; - float y = ev.getY() - mDownPos.y; - double hypotSquared = x * x + y * y; - if (hypotSquared >= mTouchSlopSquared) { - // Start tracking only when touch slop is crossed. - startTouchTracking(ev, true /* updateLocationOffset */, - true /* closeActiveWindows */); - } - } - } + + int flags = ev.getEdgeFlags(); + if (!mStartingInActivityBounds) { + ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR); } + ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]); + boolean handled = mTarget.proxyTouchEvent(ev); + ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]); + ev.setEdgeFlags(flags); - if (action == ACTION_UP || action == ACTION_CANCEL) { - mInvalidated = true; - - // Set an empty consumer to that all the cached events are cleared - if (!mCachedEventDispatcher.hasConsumer()) { - mCachedEventDispatcher.setConsumer(motionEvent -> { }); + if (!mTargetHandledTouch && handled) { + mTargetHandledTouch = true; + if (!mStartingInActivityBounds) { + OverviewCallbacks.get(mActivity).closeAllWindows(); + ActivityManagerWrapper.getInstance() + .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); + TOUCH_INTERACTION_LOG.addLog("startQuickstep"); + } + if (mInputMonitor != null) { + mInputMonitor.pilferPointers(); } } } @@ -117,42 +100,12 @@ public class OverviewInputConsumer } } - private void startTouchTracking(MotionEvent ev, boolean updateLocationOffset, - boolean closeActiveWindows) { - if (updateLocationOffset) { - mTarget.getLocationOnScreen(mLocationOnScreen); - } - - if (closeActiveWindows) { - OverviewCallbacks.get(mActivity).closeAllWindows(); - ActivityManagerWrapper.getInstance() - .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); - TOUCH_INTERACTION_LOG.addLog("startQuickstep"); - } - - mTrackingStarted = true; - mCachedEventDispatcher.setConsumer(this::sendEvent); - - } - - private void sendEvent(MotionEvent ev) { - if (mInvalidated) { - return; - } - int flags = ev.getEdgeFlags(); - ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR); - ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]); - mInvalidated = !mTarget.dispatchTouchEvent(this, ev); - ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]); - ev.setEdgeFlags(flags); - } - public static InputConsumer newInstance(ActivityControlHelper activityHelper, - boolean startingInActivityBounds) { + @Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) { BaseDraggingActivity activity = activityHelper.getCreatedActivity(); if (activity == null) { return InputConsumer.NO_OP; } - return new OverviewInputConsumer(activity, startingInActivityBounds); + return new OverviewInputConsumer(activity, inputMonitor, startingInActivityBounds); } } \ No newline at end of file diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index fc3f332d5d..b62bac66dd 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -468,10 +468,10 @@ public class TouchInteractionService extends Service implements mInputMonitorCompat, activityControl); } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) { - return OverviewInputConsumer.newInstance(activityControl, false); + return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false); } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) { - return OverviewInputConsumer.newInstance(activityControl, false); + return OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false); } else { return createOtherActivityInputConsumer(event, runningTaskInfo); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index 7ffd8d7354..fd63ddce5a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -738,7 +738,7 @@ public class WindowTransformSwipeHandler setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha); } - return OverviewInputConsumer.newInstance(mActivityControlHelper, true); + return OverviewInputConsumer.newInstance(mActivityControlHelper, null, true); } @UiThread diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java index 777e59252f..09d323ee69 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java @@ -17,18 +17,13 @@ package com.android.quickstep.fallback; import android.annotation.TargetApi; import android.content.Context; -import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.RectF; import android.util.AttributeSet; -import android.view.MotionEvent; -import android.view.ViewDebug; import android.view.WindowInsets; import com.android.launcher3.BaseActivity; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.util.Themes; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.BaseDragLayer; @@ -39,9 +34,6 @@ public class RecentsRootView extends BaseDragLayer { private static final int MIN_SIZE = 10; private final RecentsActivity mActivity; - @ViewDebug.ExportedProperty(category = "launcher") - private final RectF mTouchExcludeRegion = new RectF(); - private final Point mLastKnownSize = new Point(MIN_SIZE, MIN_SIZE); public RecentsRootView(Context context, AttributeSet attrs) { @@ -100,26 +92,7 @@ public class RecentsRootView extends BaseDragLayer { @Override public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { - if (Utilities.ATLEAST_Q) { - Insets gestureInsets = insets.getMandatorySystemGestureInsets(); - mTouchExcludeRegion.set(gestureInsets.left, gestureInsets.top, - gestureInsets.right, gestureInsets.bottom); - } + updateTouchExcludeRegion(insets); return super.dispatchApplyWindowInsets(insets); } - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - float x = ev.getX(); - float y = ev.getY(); - if (y < mTouchExcludeRegion.top - || x < mTouchExcludeRegion.left - || x > (getWidth() - mTouchExcludeRegion.right) - || y > (getHeight() - mTouchExcludeRegion.bottom)) { - return false; - } - } - return super.dispatchTouchEvent(ev); - } } \ No newline at end of file diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java index e6c2d0c9c5..90e673b3e9 100644 --- a/src/com/android/launcher3/LauncherRootView.java +++ b/src/com/android/launcher3/LauncherRootView.java @@ -8,13 +8,10 @@ import android.app.ActivityManager; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Insets; import android.graphics.Paint; import android.graphics.Rect; -import android.graphics.RectF; import android.os.Build; import android.util.AttributeSet; -import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; import android.view.WindowInsets; @@ -31,9 +28,6 @@ public class LauncherRootView extends InsettableFrameLayout { @ViewDebug.ExportedProperty(category = "launcher") private final Rect mConsumedInsets = new Rect(); - @ViewDebug.ExportedProperty(category = "launcher") - private final RectF mTouchExcludeRegion = new RectF(); - @ViewDebug.ExportedProperty(category = "launcher") private static final List SYSTEM_GESTURE_EXCLUSION_RECT = Collections.singletonList(new Rect()); @@ -164,29 +158,10 @@ public class LauncherRootView extends InsettableFrameLayout { @Override public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { - if (Utilities.ATLEAST_Q) { - Insets gestureInsets = insets.getMandatorySystemGestureInsets(); - mTouchExcludeRegion.set(gestureInsets.left, gestureInsets.top, - gestureInsets.right, gestureInsets.bottom); - } + mLauncher.getDragLayer().updateTouchExcludeRegion(insets); return super.dispatchApplyWindowInsets(insets); } - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - float x = ev.getX(); - float y = ev.getY(); - if (y < mTouchExcludeRegion.top - || x < mTouchExcludeRegion.left - || x > (getWidth() - mTouchExcludeRegion.right) - || y > (getHeight() - mTouchExcludeRegion.bottom)) { - return false; - } - } - return super.dispatchTouchEvent(ev); - } - @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java index 593dbd46c8..c7d93fec9c 100644 --- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java +++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java @@ -22,7 +22,6 @@ import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFI import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType; import static com.android.launcher3.userevent.nano.LauncherLogProto.Target; -import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import android.animation.AnimatorSet; import android.animation.LayoutTransition; @@ -173,8 +172,7 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource, public boolean onControllerInterceptTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { BaseDragLayer dl = getPopupContainer(); - final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0; - if (!cameFromNavBar && !dl.isEventOverView(this, ev)) { + if (!dl.isEventOverView(this, ev)) { mLauncher.getUserEventDispatcher().logActionTapOutside( LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS)); close(true); diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index 66cd536a54..8a15220360 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -22,13 +22,19 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; +import android.annotation.TargetApi; import android.content.Context; +import android.graphics.Insets; import android.graphics.Rect; +import android.graphics.RectF; +import android.os.Build; import android.util.AttributeSet; import android.util.Property; import android.view.MotionEvent; import android.view.View; +import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; @@ -74,18 +80,32 @@ public abstract class BaseDragLayer } }; + // Touch is being dispatched through the normal view dispatch system + private static final int TOUCH_DISPATCHING_VIEW = 1 << 0; + // Touch is being dispatched through the normal view dispatch system, and started at the + // system gesture region + private static final int TOUCH_DISPATCHING_GESTURE = 1 << 1; + // Touch is being dispatched through a proxy from InputMonitor + private static final int TOUCH_DISPATCHING_PROXY = 1 << 2; + protected final int[] mTmpXY = new int[2]; protected final Rect mHitRect = new Rect(); + @ViewDebug.ExportedProperty(category = "launcher") + private final RectF mSystemGestureRegion = new RectF(); + private int mTouchDispatchState = 0; + protected final T mActivity; private final MultiValueAlpha mMultiValueAlpha; + // All the touch controllers for the view protected TouchController[] mControllers; + // Touch controller which is currently active for the normal view dispatch protected TouchController mActiveController; - private TouchCompleteListener mTouchCompleteListener; + // Touch controller which is being used for the proxy events + protected TouchController mProxyTouchController; - // Object controlling the current touch interaction - private Object mCurrentTouchOwner; + private TouchCompleteListener mTouchCompleteListener; public BaseDragLayer(Context context, AttributeSet attrs, int alphaChannelCount) { super(context, attrs); @@ -113,30 +133,36 @@ public abstract class BaseDragLayer return findActiveController(ev); } + private TouchController findControllerToHandleTouch(MotionEvent ev) { + AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); + if (topView != null && topView.onControllerInterceptTouchEvent(ev)) { + return topView; + } + + for (TouchController controller : mControllers) { + if (controller.onControllerInterceptTouchEvent(ev)) { + return controller; + } + } + return null; + } + protected boolean findActiveController(MotionEvent ev) { if (com.android.launcher3.TestProtocol.sDebugTracing) { android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG, "mActiveController = null"); } mActiveController = null; + if ((mTouchDispatchState & (TOUCH_DISPATCHING_GESTURE | TOUCH_DISPATCHING_PROXY)) == 0) { + // Only look for controllers if we are not dispatching from gesture area and proxy is + // not active + mActiveController = findControllerToHandleTouch(ev); - AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); - if (topView != null && topView.onControllerInterceptTouchEvent(ev)) { - if (com.android.launcher3.TestProtocol.sDebugTracing) { - android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG, - "setting controller1: " + topView.getClass().getSimpleName()); - } - mActiveController = topView; - return true; - } - - for (TouchController controller : mControllers) { - if (controller.onControllerInterceptTouchEvent(ev)) { + if (mActiveController != null) { if (com.android.launcher3.TestProtocol.sDebugTracing) { android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG, - "setting controller1: " + controller.getClass().getSimpleName()); + "setting controller1: " + mActiveController.getClass().getSimpleName()); } - mActiveController = controller; return true; } } @@ -223,39 +249,74 @@ public abstract class BaseDragLayer @Override public boolean dispatchTouchEvent(MotionEvent ev) { - return dispatchTouchEvent(this, ev); - } + switch (ev.getAction()) { + case ACTION_DOWN: { + float x = ev.getX(); + float y = ev.getY(); + mTouchDispatchState |= TOUCH_DISPATCHING_VIEW; - public boolean dispatchTouchEvent(Object caller, MotionEvent ev) { - return verifyTouchDispatch(caller, ev) && super.dispatchTouchEvent(ev); + if ((y < mSystemGestureRegion.top + || x < mSystemGestureRegion.left + || x > (getWidth() - mSystemGestureRegion.right) + || y > (getHeight() - mSystemGestureRegion.bottom))) { + mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE; + } else { + mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE; + } + break; + } + case ACTION_CANCEL: + case ACTION_UP: + mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE; + mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW; + break; + } + super.dispatchTouchEvent(ev); + + // We want to get all events so that mTouchDispatchSource is maintained properly + return true; } /** - * Returns true if the {@param caller} is allowed to dispatch {@param ev} on this view, - * false otherwise. + * Called before we are about to receive proxy events. + * + * @return false if we can't handle proxy at this time */ - private boolean verifyTouchDispatch(Object caller, MotionEvent ev) { - int action = ev.getAction(); - if (action == ACTION_DOWN) { - if (mCurrentTouchOwner != null) { - // Another touch in progress. - ev.setAction(ACTION_CANCEL); - super.dispatchTouchEvent(ev); - ev.setAction(action); - } - mCurrentTouchOwner = caller; - return true; - } - if (mCurrentTouchOwner != caller) { - // Someone else is controlling the touch + public boolean prepareProxyEventStarting() { + mProxyTouchController = null; + if ((mTouchDispatchState & TOUCH_DISPATCHING_VIEW) != 0 && mActiveController != null) { + // We are already dispatching using view system and have an active controller, we can't + // handle another controller. + + // This flag was already cleared in proxy ACTION_UP or ACTION_CANCEL. Added here just + // to be safe + mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY; return false; } - if (action == ACTION_UP || action == ACTION_CANCEL) { - mCurrentTouchOwner = null; - } + + mTouchDispatchState |= TOUCH_DISPATCHING_PROXY; return true; } + /** + * Proxies the touch events to the gesture handlers + */ + public boolean proxyTouchEvent(MotionEvent ev) { + boolean handled; + if (mProxyTouchController != null) { + handled = mProxyTouchController.onControllerTouchEvent(ev); + } else { + mProxyTouchController = findControllerToHandleTouch(ev); + handled = mProxyTouchController != null; + } + int action = ev.getAction(); + if (action == ACTION_UP || action == ACTION_CANCEL) { + mProxyTouchController = null; + mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY; + } + return handled; + } + /** * Determine the rect of the descendant in this DragLayer's coordinates * @@ -423,4 +484,13 @@ public abstract class BaseDragLayer } } } + + @TargetApi(Build.VERSION_CODES.Q) + public void updateTouchExcludeRegion(WindowInsets insets) { + if (Utilities.ATLEAST_Q) { + Insets gestureInsets = insets.getMandatorySystemGestureInsets(); + mSystemGestureRegion.set(gestureInsets.left, gestureInsets.top, + gestureInsets.right, gestureInsets.bottom); + } + } }