From d7f1942d4a9f219cb5d5ea69f6ee4d1c95e1e3bb Mon Sep 17 00:00:00 2001 From: Liran Binyamin Date: Mon, 7 Aug 2023 17:34:09 -0400 Subject: [PATCH] Let the bubble bar handle gestures over the bubble bar. This change introduces a motion event flag to signal that the gesture is being originated over the bubble bar (similar to the nav bar gesture flag). Downstream consumers use this flag to ignore the event in order to allow the bubble bar to handle it. The black screen in b/293348900 seemed to be related to the recents shell transition closing the app that was in the foreground before swiping on the bubble bar handle. Not sure why it ended up with a black screen, but this change avoids starting the recents transition entirely. Demo: http://recall/-/bJtug1HhvXkkeA4MQvIaiP/BSxLROoflr1i2yy0v6Y4G Fixes: 294421126 Bug: 293348900 Test: Manual: - Add bubble to the bubble bar - Swipe up over the bubble bar - Observe that the bubble bar expands - Launch application - Swipe up on the bubble bar handle - Observe that the bubble bar expands Change-Id: Iea15829de97edb6bfbfb7007388865230faaf736 --- .../taskbar/TaskbarUIController.java | 10 +- .../bubbles/BubbleStashController.java | 6 + .../BubbleStashedHandleViewController.java | 19 +++ .../quickstep/BaseActivityInterface.java | 5 +- .../TaskbarUnstashInputConsumer.java | 113 ++++++++++++++---- 5 files changed, 127 insertions(+), 26 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 139e8f0c98..445b312424 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -155,7 +155,7 @@ public class TaskbarUIController { mControllers.taskbarActivityContext.startTranslationSpring(); } - /* + /** * @param ev MotionEvent in screen coordinates. * @return Whether any Taskbar item could handle the given MotionEvent if given the chance. */ @@ -164,6 +164,14 @@ public class TaskbarUIController { || mControllers.navbarButtonsViewController.isEventOverAnyItem(ev); } + /** Checks if the given {@link MotionEvent} is over the bubble bar stash handle. */ + public boolean isEventOverBubbleBarStashHandle(MotionEvent ev) { + return mControllers.bubbleControllers.map( + bubbleControllers -> + bubbleControllers.bubbleStashController.isEventOverStashHandle(ev)) + .orElse(false); + } + /** * Returns true if icons should be aligned to hotseat in the current transition. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java index a5ea5a9955..09021edb91 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashController.java @@ -22,6 +22,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.annotation.Nullable; import android.view.InsetsController; +import android.view.MotionEvent; import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.taskbar.StashedHandleViewController; @@ -350,4 +351,9 @@ public class BubbleStashController { return mBubblesShowingOnHome ? getBubbleBarTranslationYForHotseat() : getBubbleBarTranslationYForTaskbar(); } + + /** Checks whether the motion event is over the stash handle. */ + public boolean isEventOverStashHandle(MotionEvent ev) { + return mHandleViewController.isEventOverHandle(ev); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java index fbab59531c..c998d9704e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java @@ -24,6 +24,7 @@ import android.animation.ValueAnimator; import android.content.res.Resources; import android.graphics.Outline; import android.graphics.Rect; +import android.view.MotionEvent; import android.view.View; import android.view.ViewOutlineProvider; @@ -268,4 +269,22 @@ public class BubbleStashedHandleViewController { }); return revealAnim; } + + /** Checks that the stash handle is visible and that the motion event is within bounds. */ + public boolean isEventOverHandle(MotionEvent ev) { + if (mStashedHandleView.getVisibility() != VISIBLE) { + return false; + } + + // the bounds of the handle only include the visible part, so we check that the Y coordinate + // is anywhere within the stashed taskbar height. + int top = mActivity.getDeviceProfile().heightPx - mStashedTaskbarHeight; + + return (int) ev.getRawY() >= top && containsX((int) ev.getRawX()); + } + + /** Checks if the given x coordinate is within the stashed handle bounds. */ + public boolean containsX(int x) { + return x >= mStashedHandleBounds.left && x <= mStashedHandleBounds.right; + } } diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 3f8813990f..8925bd61e2 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -191,8 +191,11 @@ public abstract class BaseActivityInterface mTouchSlop || Math.abs(dX) > mTouchSlop; + if (passedSlop) { + mIsPassedBubbleBarSlop = true; + mIsVerticalGestureOverBubbleBar = Math.abs(dY) > Math.abs(dX); + if (mIsVerticalGestureOverBubbleBar) { + setActive(ev); + } + } + } + if (mIsTransientTaskbar) { - float dY = mLastPos.y - mDownPos.y; boolean passedTaskbarNavThreshold = dY < 0 && Math.abs(dY) >= mTaskbarNavThreshold; if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) { mHasPassedTaskbarNavThreshold = true; - if (mIsInBubbleBarArea) { + if (mIsInBubbleBarArea && mIsVerticalGestureOverBubbleBar) { mTaskbarActivityContext.onSwipeToOpenBubblebar(); } else { mTaskbarActivityContext.onSwipeToUnstashTaskbar(); @@ -217,17 +234,7 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (!mIsTransientTaskbar && !mCanceledUnstashHint) { - mTaskbarActivityContext.startTaskbarUnstashHint( - /* animateForward = */ false); - } - mTaskbarActivityContext.setAutohideSuspendFlag( - FLAG_AUTOHIDE_SUSPEND_TOUCHING, false); - if (mTransitionCallback != null) { - mTransitionCallback.onActionEnd(); - } - mHasPassedTaskbarNavThreshold = false; - mIsInBubbleBarArea = false; + cleanupAfterMotionEvent(); break; case MotionEvent.ACTION_BUTTON_RELEASE: if (isStashedTaskbarHovered) { @@ -236,9 +243,61 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { break; } } + boolean isMovingInBubbleBarArea = mIsInBubbleBarArea && ev.getAction() == ACTION_MOVE; + if (!isStashedTaskbarHovered) { + // if we're moving in the bubble bar area but we haven't passed the slop yet, don't + // propagate to the delegate, until we can determine the direction of the gesture. + if (!isMovingInBubbleBarArea || mIsPassedBubbleBarSlop) { + mDelegate.onMotionEvent(ev); + } + } + } else if (mIsVerticalGestureOverBubbleBar) { + // if we get here then this gesture is a vertical swipe over the bubble bar. + // we're also active and there's no need to delegate any additional motion events. the + // rest of the gesture will be handled here. + switch (ev.getAction()) { + case ACTION_MOVE: + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == INVALID_POINTER_ID) { + break; + } + mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + + float dY = mLastPos.y - mDownPos.y; + + // bubble bar swipe gesture uses the same threshold as the taskbar. + boolean passedTaskbarNavThreshold = dY < 0 + && Math.abs(dY) >= mTaskbarNavThreshold; + + if (!mHasPassedTaskbarNavThreshold && passedTaskbarNavThreshold) { + mHasPassedTaskbarNavThreshold = true; + mTaskbarActivityContext.onSwipeToOpenBubblebar(); + } + break; + case ACTION_UP: + case ACTION_CANCEL: + cleanupAfterMotionEvent(); + break; + } } } + private void cleanupAfterMotionEvent() { + if (!mIsTransientTaskbar && !mCanceledUnstashHint) { + mTaskbarActivityContext.startTaskbarUnstashHint( + /* animateForward = */ false); + } + mTaskbarActivityContext.setAutohideSuspendFlag( + FLAG_AUTOHIDE_SUSPEND_TOUCHING, false); + if (mTransitionCallback != null) { + mTransitionCallback.onActionEnd(); + } + mHasPassedTaskbarNavThreshold = false; + mIsInBubbleBarArea = false; + mIsVerticalGestureOverBubbleBar = false; + mIsPassedBubbleBarSlop = false; + } + private boolean isInTaskbarArea(float x) { float areaFromMiddle = mUnstashArea / 2.0f; float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x); @@ -246,13 +305,19 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { } private boolean isInBubbleBarArea(float x) { - if (mTaskbarActivityContext != null && mIsTransientTaskbar) { - BubbleControllers controllers = mTaskbarActivityContext.getBubbleControllers(); - if (controllers == null) return false; + if (mTaskbarActivityContext == null || !mIsTransientTaskbar) { + return false; + } + BubbleControllers controllers = mTaskbarActivityContext.getBubbleControllers(); + if (controllers == null) { + return false; + } + if (controllers.bubbleStashController.isStashed()) { + return controllers.bubbleStashedHandleViewController.containsX((int) x); + } else { Rect bubbleBarBounds = controllers.bubbleBarViewController.getBubbleBarBounds(); return x >= bubbleBarBounds.left && x <= bubbleBarBounds.right; } - return false; } private void onLongPressDetected(MotionEvent motionEvent) {