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); + } + } }