diff options
Diffstat (limited to 'quickstep/src/com/android/quickstep/inputconsumers')
9 files changed, 288 insertions, 127 deletions
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java index 63771f0d56..5557639b1c 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java @@ -48,6 +48,14 @@ public abstract class DelegateInputConsumer implements InputConsumer { */ protected abstract String getDelegatorName(); + @Override + public <T extends InputConsumer> T getInputConsumerOfClass(Class<T> c) { + if (getClass().equals(c)) { + return c.cast(this); + } + return mDelegate.getInputConsumerOfClass(c); + } + protected void setActive(MotionEvent ev) { ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString(getDelegatorName()) .append(" became active")); diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java index 2a355848b5..f2643641e0 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java @@ -68,6 +68,7 @@ import java.util.HashMap; */ public class DeviceLockedInputConsumer implements InputConsumer, RecentsAnimationCallbacks.RecentsAnimationListener, BuilderProxy { + private final String TAG = "DeviceLockedInputConsumer"; private static final String[] STATE_NAMES = DEBUG_STATES ? new String[2] : null; private static int getFlagForIndex(int index, String name) { @@ -207,7 +208,12 @@ public class DeviceLockedInputConsumer implements InputConsumer, animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - if (ENABLE_SHELL_TRANSITIONS) { + if (dismissTask) { + // Just start the home intent so the user is prompted to unlock the device. + // This will come back and cancel the interaction. + startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null, TAG); + mHomeLaunched = true; + } else if (ENABLE_SHELL_TRANSITIONS) { if (mTaskAnimationManager.getCurrentCallbacks() != null) { if (mRecentsAnimationController != null) { finishRecentsAnimationForShell(dismissTask); @@ -217,11 +223,6 @@ public class DeviceLockedInputConsumer implements InputConsumer, mDismissTask = dismissTask; } } - } else if (dismissTask) { - // For now, just start the home intent so user is prompted to - // unlock the device. - startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null); - mHomeLaunched = true; } mStateCallback.setState(STATE_HANDLER_INVALIDATED); } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java index 7a2b3430ee..4d47f07b04 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressHandler.java @@ -46,4 +46,15 @@ public class NavHandleLongPressHandler implements ResourceBasedOverride { public @Nullable Runnable getLongPressRunnable() { return null; } + + /** + * Called when nav handle gesture starts. + */ + public void onTouchStarted() {} + + /** + * Called when nav handle gesture is finished by the user lifting their finger or the system + * cancelling the touch for some other reason. + */ + public void onTouchFinished(String reason) {} } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java index f824210bd8..0a558e2a98 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/NavHandleLongPressInputConsumer.java @@ -15,16 +15,23 @@ */ package com.android.quickstep.inputconsumers; +import static com.android.launcher3.LauncherPrefs.LONG_PRESS_NAV_HANDLE_TIMEOUT_MS; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_DEEP_PRESS_NAVBAR; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_LONG_PRESS_NAVBAR; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.content.Context; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; +import android.view.ViewConfiguration; +import com.android.launcher3.LauncherPrefs; import com.android.launcher3.R; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.util.DisplayController; import com.android.quickstep.InputConsumer; +import com.android.quickstep.RecentsAnimationDeviceState; +import com.android.quickstep.TopTaskTracker; import com.android.systemui.shared.system.InputMonitorCompat; /** @@ -32,33 +39,36 @@ import com.android.systemui.shared.system.InputMonitorCompat; */ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer { - private final GestureDetector mLongPressDetector; private final NavHandleLongPressHandler mNavHandleLongPressHandler; private final float mNavHandleWidth; private final float mScreenWidth; + private final Runnable mTriggerLongPress = this::triggerLongPress; + private final float mTouchSlopSquared; + private final int mLongPressTimeout; + private final boolean mDeepPressEnabled; + private final StatsLogManager mStatsLogManager; + private final TopTaskTracker mTopTaskTracker; + + private MotionEvent mCurrentDownEvent; + private boolean mDeepPressLogged; // Whether deep press has been logged for the current touch. + public NavHandleLongPressInputConsumer(Context context, InputConsumer delegate, - InputMonitorCompat inputMonitor) { + InputMonitorCompat inputMonitor, RecentsAnimationDeviceState deviceState) { super(delegate, inputMonitor); mNavHandleWidth = context.getResources().getDimensionPixelSize( R.dimen.navigation_home_handle_width); mScreenWidth = DisplayController.INSTANCE.get(context).getInfo().currentSize.x; - + mDeepPressEnabled = FeatureFlags.ENABLE_LPNH_DEEP_PRESS.get(); + if (FeatureFlags.CUSTOM_LPNH_THRESHOLDS.get()) { + mLongPressTimeout = LauncherPrefs.get(context).get(LONG_PRESS_NAV_HANDLE_TIMEOUT_MS); + } else { + mLongPressTimeout = ViewConfiguration.getLongPressTimeout(); + } + mTouchSlopSquared = deviceState.getSquaredTouchSlop(); mNavHandleLongPressHandler = NavHandleLongPressHandler.newInstance(context); - - mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() { - @Override - public void onLongPress(MotionEvent motionEvent) { - if (isInArea(motionEvent.getRawX())) { - Runnable longPressRunnable = mNavHandleLongPressHandler.getLongPressRunnable(); - if (longPressRunnable != null) { - setActive(motionEvent); - - MAIN_EXECUTOR.getHandler().postDelayed(longPressRunnable, 50); - } - } - } - }); + mStatsLogManager = StatsLogManager.newInstance(context); + mTopTaskTracker = TopTaskTracker.INSTANCE.get(context); } @Override @@ -68,13 +78,93 @@ public class NavHandleLongPressInputConsumer extends DelegateInputConsumer { @Override public void onMotionEvent(MotionEvent ev) { - mLongPressDetector.onTouchEvent(ev); + if (mDelegate.allowInterceptByParent()) { + handleMotionEvent(ev); + } else if (MAIN_EXECUTOR.getHandler().hasCallbacks(mTriggerLongPress)) { + cancelLongPress("intercept disallowed by child input consumer"); + } + if (mState != STATE_ACTIVE) { mDelegate.onMotionEvent(ev); } } - protected boolean isInArea(float x) { + private void handleMotionEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN -> { + if (mCurrentDownEvent != null) { + mCurrentDownEvent.recycle(); + } + mCurrentDownEvent = MotionEvent.obtain(ev); + mDeepPressLogged = false; + if (isInNavBarHorizontalArea(ev.getRawX())) { + mNavHandleLongPressHandler.onTouchStarted(); + MAIN_EXECUTOR.getHandler().postDelayed(mTriggerLongPress, + mLongPressTimeout); + } + } + case MotionEvent.ACTION_MOVE -> { + if (!MAIN_EXECUTOR.getHandler().hasCallbacks(mTriggerLongPress)) { + break; + } + + float touchSlopSquared = mTouchSlopSquared; + float dx = ev.getX() - mCurrentDownEvent.getX(); + float dy = ev.getY() - mCurrentDownEvent.getY(); + double distanceSquared = (dx * dx) + (dy * dy); + if (distanceSquared > touchSlopSquared) { + cancelLongPress("touch slop passed"); + } + } + case MotionEvent.ACTION_UP -> cancelLongPress("touch action up"); + case MotionEvent.ACTION_CANCEL -> cancelLongPress("touch action cancel"); + } + + // If the gesture is deep press then trigger long press asap + if (MAIN_EXECUTOR.getHandler().hasCallbacks(mTriggerLongPress) + && ev.getClassification() == MotionEvent.CLASSIFICATION_DEEP_PRESS + && !mDeepPressLogged) { + // Log deep press even if feature is disabled. + String runningPackage = mTopTaskTracker.getCachedTopTask( + /* filterOnlyVisibleRecents */ true).getPackageName(); + mStatsLogManager.logger().withPackageName(runningPackage) + .log(LAUNCHER_DEEP_PRESS_NAVBAR); + mDeepPressLogged = true; + + // But only trigger if the feature is enabled. + if (mDeepPressEnabled) { + MAIN_EXECUTOR.getHandler().removeCallbacks(mTriggerLongPress); + MAIN_EXECUTOR.getHandler().post(mTriggerLongPress); + } + } + } + + private void triggerLongPress() { + String runningPackage = mTopTaskTracker.getCachedTopTask( + /* filterOnlyVisibleRecents */ true).getPackageName(); + mStatsLogManager.logger().withPackageName(runningPackage).log(LAUNCHER_LONG_PRESS_NAVBAR); + + Runnable longPressRunnable = mNavHandleLongPressHandler.getLongPressRunnable(); + if (longPressRunnable == null) { + return; + } + + OtherActivityInputConsumer oaic = getInputConsumerOfClass(OtherActivityInputConsumer.class); + if (oaic != null && oaic.hasStartedTouchTracking()) { + oaic.setForceFinishRecentsTransitionCallback(longPressRunnable); + setActive(mCurrentDownEvent); + } else { + setActive(mCurrentDownEvent); + MAIN_EXECUTOR.post(longPressRunnable); + } + } + + private void cancelLongPress(String reason) { + MAIN_EXECUTOR.getHandler().removeCallbacks(mTriggerLongPress); + mNavHandleLongPressHandler.onTouchFinished(reason); + } + + private boolean isInNavBarHorizontalArea(float x) { float areaFromMiddle = mNavHandleWidth / 2.0f; float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x); diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 7e6116720b..eedd20485b 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -40,7 +40,6 @@ import android.os.Build; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; -import android.view.ViewConfiguration; import androidx.annotation.UiThread; @@ -125,6 +124,9 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar. private float mStartDisplacement; + // The callback called upon finishing the recents transition if it was force-canceled + private Runnable mForceFinishRecentsTransitionCallback; + public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState, TaskAnimationManager taskAnimationManager, GestureState gestureState, boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback, @@ -151,7 +153,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning(); mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget; - mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop(); + mTouchSlop = mDeviceState.getTouchSlop(); mSquaredTouchSlop = mDeviceState.getSquaredTouchSlop(); mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture; @@ -411,6 +413,14 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } /** + * Returns whether this input consumer has started touch tracking (if touch tracking is not + * deferred). + */ + public boolean hasStartedTouchTracking() { + return mInteractionHandler != null; + } + + /** * Called when the gesture has ended. Does not correlate to the completion of the interaction as * the animation can still be running. */ @@ -444,7 +454,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC // animateToProgress so we have to manually finish here. In the case of // ACTION_CANCEL, someone else may be doing something so finish synchronously. mTaskAnimationManager.finishRunningRecentsAnimation(false /* toHome */, - isCanceled /* forceFinish */); + isCanceled /* forceFinish */, mForceFinishRecentsTransitionCallback); } else { // The animation hasn't started yet, so insert a replacement handler into the // callbacks which immediately finishes the animation after it starts. @@ -513,6 +523,14 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } /** + * Sets a callback to be called when the recents transition is force-canceled by another input + * consumer being made active. + */ + public void setForceFinishRecentsTransitionCallback(Runnable r) { + mForceFinishRecentsTransitionCallback = r; + } + + /** * A listener which just finishes the animation immediately after starting. Replaces * AbsSwipeUpHandler if the gesture itself finishes before the animation even starts. */ diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java index 338864264a..7d3a860b23 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java @@ -21,7 +21,9 @@ import android.media.AudioManager; import android.media.session.MediaSessionManager; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.View; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.Utilities; @@ -52,6 +54,7 @@ public class OverviewInputConsumer<S extends BaseState<S>, T extends StatefulAct private final boolean mStartingInActivityBounds; private boolean mTargetHandledTouch; private boolean mHasSetTouchModeForFirstDPadEvent; + private boolean mIsWaitingForAttachToWindow; public OverviewInputConsumer(GestureState gestureState, T activity, @Nullable InputMonitorCompat inputMonitor, boolean startingInActivityBounds) { @@ -118,21 +121,47 @@ public class OverviewInputConsumer<S extends BaseState<S>, T extends StatefulAct break; case KeyEvent.KEYCODE_DPAD_LEFT: case KeyEvent.KEYCODE_DPAD_RIGHT: - if (!mHasSetTouchModeForFirstDPadEvent) { - // When Overview is launched via meta+tab or swipe up from an app, the touch - // mode somehow is not changed to false by the Android framework. The subsequent - // key events (e.g. DPAD_LEFT, DPAD_RIGHT) can only be dispatched to focused - // views, while focus can only be requested in - // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To - // note, here we launch overview with live tile. - mHasSetTouchModeForFirstDPadEvent = true; - mActivity.getRootView().getViewRootImpl().touchModeChanged(false); + if (mHasSetTouchModeForFirstDPadEvent) { + break; } + View viewRoot = mActivity.getRootView(); + if (viewRoot.isAttachedToWindow()) { + setTouchModeChanged(viewRoot); + break; + } + if (mIsWaitingForAttachToWindow) { + break; + } + mIsWaitingForAttachToWindow = true; + viewRoot.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View view) { + view.removeOnAttachStateChangeListener(this); + mIsWaitingForAttachToWindow = false; + setTouchModeChanged(viewRoot); + } + + @Override + public void onViewDetachedFromWindow(View view) { + // Do nothing + } + }); break; default: break; } mActivity.dispatchKeyEvent(ev); } + + private void setTouchModeChanged(@NonNull View viewRoot) { + // When Overview is launched via meta+tab or swipe up from an app, the touch + // mode somehow is not changed to false by the Android framework. The + // subsequent key events (e.g. DPAD_LEFT, DPAD_RIGHT) can only be dispatched + // to focused views, while focus can only be requested in + // {@link View#requestFocusNoSearch(int, Rect)} when touch mode is false. To + // note, here we launch overview with live tile. + mHasSetTouchModeForFirstDPadEvent = true; + viewRoot.getViewRootImpl().touchModeChanged(false); + } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java index b70fe8e03e..41730bbc40 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java @@ -37,6 +37,7 @@ import com.android.systemui.shared.system.InputMonitorCompat; public class OverviewWithoutFocusInputConsumer implements InputConsumer, TriggerSwipeUpTouchTracker.OnSwipeUpListener { + private static final String TAG = "OverviewWithoutFocusInputConsumer"; private final Context mContext; private final InputMonitorCompat mInputMonitor; @@ -77,7 +78,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer, @Override public void onSwipeUp(boolean wasFling, PointF finalVelocity) { - startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null); + startHomeIntentSafely(mContext, mGestureState.getHomeIntent(), null, TAG); BaseActivity activity = BaseDraggingActivity.fromContext(mContext); int state = (mGestureState != null && mGestureState.getEndTarget() != null) ? mGestureState.getEndTarget().containerType diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java index c9c64b669c..6dcb7bceff 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java @@ -53,6 +53,7 @@ import java.util.HashMap; public class ProgressDelegateInputConsumer implements InputConsumer, RecentsAnimationCallbacks.RecentsAnimationListener, SingleAxisSwipeDetector.Listener { + private static final String TAG = "ProgressDelegateInputConsumer"; private static final float SWIPE_DISTANCE_THRESHOLD = 0.2f; @@ -165,7 +166,7 @@ public class ProgressDelegateInputConsumer implements InputConsumer, mRecentsAnimationController.finishController(endToRecents /* toRecents */, null /* callback */, false /* sendUserLeaveHint */); } else if (endToRecents) { - startHomeIntentSafely(mContext, null); + startHomeIntentSafely(mContext, null, TAG); } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java index 28ac65b721..cd180ba82d 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java @@ -15,27 +15,27 @@ */ package com.android.quickstep.inputconsumers; +import static android.view.MotionEvent.ACTION_CANCEL; +import static android.view.MotionEvent.ACTION_MOVE; +import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.INVALID_POINTER_ID; +import static com.android.launcher3.Flags.enableCursorHoverStates; import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; -import static com.android.launcher3.Utilities.squaredHypot; -import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES; import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING; import android.content.Context; import android.content.res.Resources; import android.graphics.PointF; import android.graphics.Rect; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; import android.view.InputDevice; import android.view.MotionEvent; +import android.view.ViewConfiguration; import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; -import com.android.launcher3.Utilities; import com.android.launcher3.taskbar.TaskbarActivityContext; import com.android.launcher3.taskbar.TaskbarThresholdUtils; import com.android.launcher3.taskbar.TaskbarTranslationController.TransitionCallback; @@ -47,27 +47,21 @@ import com.android.quickstep.OverviewCommandHelper; import com.android.systemui.shared.system.InputMonitorCompat; /** - * Listens for touch and hover events to unstash the Taskbar. - * - * <p>Cancels the current gesture if the long press causes the Taskbar to be unstashed. + * Listens for touch (swipe) and hover events to unstash the Taskbar. */ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { private final TaskbarActivityContext mTaskbarActivityContext; private final OverviewCommandHelper mOverviewCommandHelper; - private final GestureDetector mLongPressDetector; - private final float mSquaredTouchSlop; - - private float mLongPressDownX, mLongPressDownY; - private boolean mCanceledUnstashHint; private final float mUnstashArea; - private final float mScreenWidth; - private final int mTaskbarNavThreshold; private final int mTaskbarNavThresholdY; private final boolean mIsTaskbarAllAppsOpen; private boolean mHasPassedTaskbarNavThreshold; private boolean mIsInBubbleBarArea; + private boolean mIsVerticalGestureOverBubbleBar; + private boolean mIsPassedBubbleBarSlop; + private final int mTouchSlop; private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); @@ -89,9 +83,7 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { super(delegate, inputMonitor); mTaskbarActivityContext = taskbarActivityContext; mOverviewCommandHelper = overviewCommandHelper; - // TODO(b/270395798): remove this when cleaning up old Persistent Taskbar code. - mSquaredTouchSlop = Utilities.squaredTouchSlop(context); - mScreenWidth = taskbarActivityContext.getDeviceProfile().widthPx; + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); Resources res = context.getResources(); mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area); @@ -99,18 +91,10 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { taskbarActivityContext.getDeviceProfile()); mTaskbarNavThresholdY = taskbarActivityContext.getDeviceProfile().heightPx - mTaskbarNavThreshold; - mIsTaskbarAllAppsOpen = - mTaskbarActivityContext != null && mTaskbarActivityContext.isTaskbarAllAppsOpen(); + mIsTaskbarAllAppsOpen = mTaskbarActivityContext.isTaskbarAllAppsOpen(); mIsTransientTaskbar = DisplayController.isTransientTaskbar(context); - mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() { - @Override - public void onLongPress(MotionEvent motionEvent) { - onLongPressDetected(motionEvent); - } - }); - mBottomScreenEdge = res.getDimensionPixelSize( R.dimen.taskbar_stashed_screen_edge_hover_deadzone_height); mStashedTaskbarBottomEdge = @@ -128,16 +112,11 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { @Override public void onMotionEvent(MotionEvent ev) { - mLongPressDetector.onTouchEvent(ev); if (mState != STATE_ACTIVE) { boolean isStashedTaskbarHovered = isMouseEvent(ev) && isStashedTaskbarHovered((int) ev.getX(), (int) ev.getY()); - if (!isStashedTaskbarHovered) { - mDelegate.onMotionEvent(ev); - } - // Only show the transient task bar if the touch events are on the screen. - if (mTaskbarActivityContext != null && !isTrackpadMotionEvent(ev)) { + if (!isTrackpadMotionEvent(ev)) { final float x = ev.getRawX(); final float y = ev.getRawY(); switch (ev.getAction()) { @@ -149,15 +128,6 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { mHasPassedTaskbarNavThreshold = false; mTaskbarActivityContext.setAutohideSuspendFlag( FLAG_AUTOHIDE_SUSPEND_TOUCHING, true); - if (isInTaskbarArea(x)) { - if (!mIsTransientTaskbar) { - mLongPressDownX = x; - mLongPressDownY = y; - mTaskbarActivityContext.startTaskbarUnstashHint( - /* animateForward = */ true); - mCanceledUnstashHint = false; - } - } if (mTransitionCallback != null && !mIsTaskbarAllAppsOpen) { mTransitionCallback.onActionDown(); } @@ -178,29 +148,34 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { } break; case MotionEvent.ACTION_MOVE: - if (!mIsTransientTaskbar - && !mCanceledUnstashHint - && squaredHypot(mLongPressDownX - x, mLongPressDownY - y) - > mSquaredTouchSlop) { - mTaskbarActivityContext.startTaskbarUnstashHint( - /* animateForward = */ false); - mCanceledUnstashHint = true; - } - int pointerIndex = ev.findPointerIndex(mActivePointerId); if (pointerIndex == INVALID_POINTER_ID) { break; } mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + float dX = mLastPos.x - mDownPos.x; + float dY = mLastPos.y - mDownPos.y; + + if (!mIsPassedBubbleBarSlop && mIsInBubbleBarArea) { + boolean passedSlop = + Math.abs(dY) > 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 +192,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,34 +201,71 @@ 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 boolean isInTaskbarArea(float x) { - float areaFromMiddle = mUnstashArea / 2.0f; - float distFromMiddle = Math.abs(mScreenWidth / 2.0f - x); - return distFromMiddle < areaFromMiddle; + private void cleanupAfterMotionEvent() { + mTaskbarActivityContext.setAutohideSuspendFlag( + FLAG_AUTOHIDE_SUSPEND_TOUCHING, false); + if (mTransitionCallback != null) { + mTransitionCallback.onActionEnd(); + } + mHasPassedTaskbarNavThreshold = false; + mIsInBubbleBarArea = false; + mIsVerticalGestureOverBubbleBar = false; + mIsPassedBubbleBarSlop = false; } 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) { - if (mTaskbarActivityContext != null - && isInTaskbarArea(motionEvent.getRawX()) - && !mIsTransientTaskbar) { - boolean taskBarPressed = mTaskbarActivityContext.onLongPressToUnstashTaskbar(); - if (taskBarPressed) { - setActive(motionEvent); - } - } } /** @@ -276,7 +278,7 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { */ @Override public void onHoverEvent(MotionEvent ev) { - if (!ENABLE_CURSOR_HOVER_STATES.get() || mTaskbarActivityContext == null + if (!enableCursorHoverStates() || mTaskbarActivityContext == null || !mTaskbarActivityContext.isTaskbarStashed()) { return; } @@ -324,14 +326,14 @@ public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { } private void startStashedTaskbarHover(boolean isHovered) { - mTaskbarActivityContext.startTaskbarUnstashHint(isHovered, /* forceUnstash = */ true); + mTaskbarActivityContext.startTaskbarUnstashHint(isHovered); mIsStashedTaskbarHovered = isHovered; } private boolean isStashedTaskbarHovered(int x, int y) { if (!mTaskbarActivityContext.isTaskbarStashed() || mTaskbarActivityContext.isTaskbarAllAppsOpen() - || !ENABLE_CURSOR_HOVER_STATES.get()) { + || !enableCursorHoverStates()) { return false; } DeviceProfile dp = mTaskbarActivityContext.getDeviceProfile(); |