diff --git a/quickstep/res/values-sw720dp/dimens.xml b/quickstep/res/values-sw720dp/dimens.xml index f093185d14..0832a91981 100644 --- a/quickstep/res/values-sw720dp/dimens.xml +++ b/quickstep/res/values-sw720dp/dimens.xml @@ -43,7 +43,7 @@ 52dp - 30dp + 30dp 100dp 180dp 300dp diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 225bdcc1db..3f4f527b55 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -303,10 +303,12 @@ 16dp - 40dp + 40dp 88dp 156dp 264dp + + 24dp 24dp diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java index 6db58397c3..5eec6a47ae 100644 --- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java +++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleView.java @@ -69,6 +69,10 @@ public class StashedHandleView extends View { */ public void updateSampledRegion(Rect stashedHandleBounds) { getLocationOnScreen(mTmpArr); + // Translations are temporary due to animations, remove them for the purpose of determining + // the final region we want sampled. + mTmpArr[0] -= Math.round(getTranslationX()); + mTmpArr[1] -= Math.round(getTranslationY()); mSampledRegion.set(stashedHandleBounds); mSampledRegion.offset(mTmpArr[0], mTmpArr[1]); } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java index 58d6244a5f..6cc6a846df 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java @@ -116,22 +116,6 @@ public class TaskbarDragLayer extends BaseDragLayer { return true; } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - if (mControllerCallbacks != null) { - mControllerCallbacks.tryStashBasedOnMotionEvent(ev); - } - return super.onInterceptTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (mControllerCallbacks != null) { - mControllerCallbacks.tryStashBasedOnMotionEvent(ev); - } - return super.onTouchEvent(ev); - } - @Override public void onViewRemoved(View child) { super.onViewRemoved(child); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java index 7c3d14d28b..56be48e141 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java @@ -18,15 +18,12 @@ package com.android.launcher3.taskbar; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; -import android.view.MotionEvent; import android.view.ViewTreeObserver; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.anim.AnimatedFloat; -import com.android.launcher3.testing.shared.ResourceUtils; import com.android.launcher3.util.DimensionUtils; -import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.TouchController; import java.io.PrintWriter; @@ -40,7 +37,6 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa private final TaskbarActivityContext mActivity; private final TaskbarDragLayer mTaskbarDragLayer; private final int mFolderMargin; - private float mGestureHeightYThreshold; // Alpha properties for taskbar background. private final AnimatedFloat mBgTaskbar = new AnimatedFloat(this::updateBackgroundAlpha); @@ -59,6 +55,7 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa // Initialized in init. private TaskbarControllers mControllers; + private TaskbarStashViaTouchController mTaskbarStashViaTouchController; private AnimatedFloat mOnBackgroundNavButtonColorIntensity; private float mLastSetBackgroundAlpha; @@ -69,11 +66,11 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa mTaskbarDragLayer = taskbarDragLayer; final Resources resources = mTaskbarDragLayer.getResources(); mFolderMargin = resources.getDimensionPixelSize(R.dimen.taskbar_folder_margin); - updateGestureHeight(); } public void init(TaskbarControllers controllers) { mControllers = controllers; + mTaskbarStashViaTouchController = new TaskbarStashViaTouchController(mControllers); mTaskbarDragLayer.init(new TaskbarDragLayerCallbacks()); mOnBackgroundNavButtonColorIntensity = mControllers.navbarButtonsViewController @@ -130,17 +127,11 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa return mBgOffset; } - private void updateGestureHeight() { - int gestureHeight = ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, - mActivity.getResources()); - mGestureHeightYThreshold = mActivity.getDeviceProfile().heightPx - gestureHeight; - } - /** * Make updates when configuration changes. */ public void onConfigurationChanged() { - updateGestureHeight(); + mTaskbarStashViaTouchController.updateGestureHeight(); } private void updateBackgroundAlpha() { @@ -206,8 +197,6 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa */ public class TaskbarDragLayerCallbacks { - private final int[] mTempOutLocation = new int[2]; - /** * Called to update the touchable insets. * @see ViewTreeObserver.InternalInsetsInfo#setTouchableInsets(int) @@ -216,37 +205,6 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa mControllers.taskbarInsetsController.updateInsetsTouchability(insetsInfo); } - /** - * Listens to TaskbarDragLayer touch events and responds accordingly. - */ - public void tryStashBasedOnMotionEvent(MotionEvent ev) { - if (!DisplayController.isTransientTaskbar(mActivity)) { - return; - } - if (mControllers.taskbarStashController.isStashed()) { - return; - } - - boolean stashTaskbar = false; - - MotionEvent screenCoordinates = MotionEvent.obtain(ev); - if (ev.getAction() == MotionEvent.ACTION_OUTSIDE) { - stashTaskbar = true; - } else if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mTaskbarDragLayer.getLocationOnScreen(mTempOutLocation); - screenCoordinates.offsetLocation(mTempOutLocation[0], mTempOutLocation[1]); - - if (!mControllers.taskbarViewController.isEventOverAnyItem(screenCoordinates) - && screenCoordinates.getY() < mGestureHeightYThreshold) { - stashTaskbar = true; - } - } - - if (stashTaskbar) { - mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); - } - } - /** * Called when a child is removed from TaskbarDragLayer. */ @@ -276,9 +234,12 @@ public class TaskbarDragLayerController implements TaskbarControllers.LoggableTa * Returns touch controllers. */ public TouchController[] getTouchControllers() { - return new TouchController[]{mActivity.getDragController(), + return new TouchController[] { + mActivity.getDragController(), mControllers.taskbarForceVisibleImmersiveController, - mControllers.navbarButtonsViewController.getTouchController()}; + mControllers.navbarButtonsViewController.getTouchController(), + mTaskbarStashViaTouchController, + }; } } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt new file mode 100644 index 0000000000..0e5fa380e7 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.launcher3.taskbar + +import android.view.MotionEvent +import com.android.launcher3.R +import com.android.launcher3.Utilities +import com.android.launcher3.anim.Interpolators.LINEAR +import com.android.launcher3.testing.shared.ResourceUtils +import com.android.launcher3.touch.SingleAxisSwipeDetector +import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE +import com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL +import com.android.launcher3.util.DisplayController +import com.android.launcher3.util.TouchController +import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer + +/** + * A helper [TouchController] for [TaskbarDragLayerController], specifically to handle touch events + * to stash Transient Taskbar. There are two cases to handle: + * - A touch outside of Transient Taskbar bounds will immediately stash on [MotionEvent.ACTION_DOWN] + * or [MotionEvent.ACTION_OUTSIDE]. + * - Touches inside Transient Taskbar bounds will stash if it is detected as a swipe down gesture. + * + * Note: touches to *unstash* Taskbar are handled by [TaskbarStashInputConsumer]. + */ +class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : TouchController { + + private val activity: TaskbarActivityContext = controllers.taskbarActivityContext + private val enabled = DisplayController.isTransientTaskbar(activity) + private val swipeDownDetector: SingleAxisSwipeDetector + private val translationCallback = controllers.taskbarTranslationController.transitionCallback + /** Interpolator to apply resistance as user swipes down to the bottom of the screen. */ + private val displacementInterpolator = LINEAR + /** How far we can translate the TaskbarView before it's offscreen. */ + private val maxVisualDisplacement = + activity.resources.getDimensionPixelSize(R.dimen.transient_taskbar_margin).toFloat() + /** How far the swipe could go, if user swiped from the very top of TaskbarView. */ + private val maxTouchDisplacement = maxVisualDisplacement + activity.deviceProfile.taskbarSize + private val touchDisplacementToStash = + activity.resources.getDimensionPixelSize(R.dimen.taskbar_to_nav_threshold).toFloat() + + /** The height of the system gesture region, so we don't stash when touching down there. */ + private var gestureHeightYThreshold = 0f + + init { + updateGestureHeight() + swipeDownDetector = SingleAxisSwipeDetector(activity, createSwipeListener(), VERTICAL) + swipeDownDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false) + } + + fun updateGestureHeight() { + if (!enabled) return + + val gestureHeight: Int = + ResourceUtils.getNavbarSize( + ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, + activity.resources + ) + gestureHeightYThreshold = (activity.deviceProfile.heightPx - gestureHeight).toFloat() + } + + private fun createSwipeListener() = + object : SingleAxisSwipeDetector.Listener { + private var lastDisplacement = 0f + + override fun onDragStart(start: Boolean, startDisplacement: Float) {} + + override fun onDrag(displacement: Float): Boolean { + lastDisplacement = displacement + if (displacement < 0) return false + // Apply resistance so that the visual displacement doesn't go beyond the screen. + translationCallback.onActionMove( + Utilities.mapToRange( + displacement, + 0f, + maxTouchDisplacement, + 0f, + maxVisualDisplacement, + displacementInterpolator + ) + ) + return false + } + + override fun onDragEnd(velocity: Float) { + val isFlingDown = swipeDownDetector.isFling(velocity) && velocity > 0 + val isSignificantDistance = lastDisplacement > touchDisplacementToStash + if (isFlingDown || isSignificantDistance) { + // Successfully triggered stash. + controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true) + } + translationCallback.onActionEnd() + swipeDownDetector.finishedScrolling() + } + } + + override fun onControllerInterceptTouchEvent(ev: MotionEvent): Boolean { + if (!enabled || controllers.taskbarStashController.isStashed) { + return false + } + + val screenCoordinatesEv = MotionEvent.obtain(ev) + screenCoordinatesEv.setLocation(ev.rawX, ev.rawY) + if (ev.action == MotionEvent.ACTION_OUTSIDE) { + controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true) + } else if (controllers.taskbarViewController.isEventOverAnyItem(screenCoordinatesEv)) { + swipeDownDetector.onTouchEvent(ev) + if (swipeDownDetector.isDraggingState) { + return true + } + } else if (ev.action == MotionEvent.ACTION_DOWN) { + if (screenCoordinatesEv.y < gestureHeightYThreshold) { + controllers.taskbarStashController.updateAndAnimateTransientTaskbar(true) + } + } + return false + } + + override fun onControllerTouchEvent(ev: MotionEvent) = swipeDownDetector.onTouchEvent(ev) +} diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index 0116e1648b..695c3e74e9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -419,20 +419,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar } } - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - mControllerCallbacks.onInterceptTouchEvent(ev); - return super.onInterceptTouchEvent(ev); - } - @Override public boolean onTouchEvent(MotionEvent event) { - if (mIconLayoutBounds.left <= event.getX() - && event.getX() <= mIconLayoutBounds.right - && !DisplayController.isTransientTaskbar(mActivityContext)) { - // Don't allow long pressing between icons, or above/below them - // unless its transient taskbar. - mControllerCallbacks.clearTouchInProgress(); + if (mIconLayoutBounds.left <= event.getX() && event.getX() <= mIconLayoutBounds.right) { + // Don't allow long pressing between icons, or above/below them. return true; } if (mControllerCallbacks.onTouchEvent(event)) { diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 4d92a9e36f..1560791e4f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -26,8 +26,6 @@ import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP; import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode; -import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE; -import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE; import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM; import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_REVEAL_ANIM; @@ -61,7 +59,6 @@ import com.android.launcher3.anim.RoundedRectRevealOutlineProvider; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.ThemedIconDrawable; import com.android.launcher3.model.data.ItemInfo; -import com.android.launcher3.touch.SingleAxisSwipeDetector; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.util.LauncherBindableItemsContainer; @@ -115,9 +112,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar private final TaskbarModelCallbacks mModelCallbacks; - // Captures swipe down action to close transient Taskbar. - protected @Nullable SingleAxisSwipeDetector mSwipeDownDetector; - // Initialized in init. private TaskbarControllers mControllers; @@ -154,31 +148,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar colorHSL[2] = TASKBAR_DARK_THEME_ICONS_BACKGROUND_LUMINANCE; mTaskbarThemedIconsBackgroundColor = ColorUtils.HSLToColor(colorHSL); } - - if (DisplayController.isTransientTaskbar(mActivity)) { - mSwipeDownDetector = new SingleAxisSwipeDetector(activity, - new SingleAxisSwipeDetector.Listener() { - private float mLastDisplacement; - - @Override - public boolean onDrag(float displacement) { - mLastDisplacement = displacement; - return false; - } - - @Override - public void onDragEnd(float velocity) { - if (mLastDisplacement > 0) { - mControllers.taskbarStashController - .updateAndAnimateTransientTaskbar(true); - } - } - - @Override - public void onDragStart(boolean start, float startDisplacement) {} - }, VERTICAL); - mSwipeDownDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false); - } mIsRtl = Utilities.isRtl(mTaskbarView.getResources()); } @@ -649,8 +618,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar private float mDownX, mDownY; private boolean mCanceledStashHint; - private boolean mTouchInProgress; - public View.OnClickListener getIconOnClickListener() { return mActivity.getItemOnClickListener(); } @@ -671,76 +638,40 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar .updateAndAnimateIsManuallyStashedInApp(true); } - /** - * Simply listens to all intercept touch events passed to TaskbarView. - */ - public void onInterceptTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mTouchInProgress = true; - } - - if (mTouchInProgress && mSwipeDownDetector != null) { - mSwipeDownDetector.onTouchEvent(ev); - } - - if (ev.getAction() == MotionEvent.ACTION_UP - || ev.getAction() == MotionEvent.ACTION_CANCEL) { - clearTouchInProgress(); - } - } - /** * Get the first chance to handle TaskbarView#onTouchEvent, and return whether we want to * consume the touch so TaskbarView treats it as an ACTION_CANCEL. + * TODO(b/270395798): We can remove this entirely once we remove the Transient Taskbar flag. */ public boolean onTouchEvent(MotionEvent motionEvent) { - boolean shouldConsumeTouch = false; - boolean clearTouchInProgress = false; - final float x = motionEvent.getRawX(); final float y = motionEvent.getRawY(); switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: - mTouchInProgress = true; mDownX = x; mDownY = y; mControllers.taskbarStashController.startStashHint(/* animateForward = */ true); mCanceledStashHint = false; break; case MotionEvent.ACTION_MOVE: - if (mTouchInProgress - && !mCanceledStashHint + if (!mCanceledStashHint && squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) { mControllers.taskbarStashController.startStashHint( /* animateForward= */ false); mCanceledStashHint = true; - shouldConsumeTouch = true; + return true; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (mTouchInProgress && !mCanceledStashHint) { + if (!mCanceledStashHint) { mControllers.taskbarStashController.startStashHint( /* animateForward= */ false); } - clearTouchInProgress = true; break; } - if (mTouchInProgress && mSwipeDownDetector != null) { - mSwipeDownDetector.onTouchEvent(motionEvent); - } - if (clearTouchInProgress) { - clearTouchInProgress(); - } - return shouldConsumeTouch; - } - - /** - * Ensures that we do not pass any more touch events to the SwipeDetector. - */ - public void clearTouchInProgress() { - mTouchInProgress = false; + return false; } /** diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java index 1ddb855ef2..87559fb7f3 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java @@ -74,7 +74,7 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { Resources res = context.getResources(); mUnstashArea = res.getDimensionPixelSize(R.dimen.taskbar_unstash_input_area); - mTaskbarNavThreshold = res.getDimensionPixelSize(R.dimen.taskbar_nav_threshold); + mTaskbarNavThreshold = res.getDimensionPixelSize(R.dimen.taskbar_from_nav_threshold); mTaskbarNavThresholdY = taskbarActivityContext.getDeviceProfile().heightPx - mTaskbarNavThreshold; mIsTaskbarAllAppsOpen = diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index 8ff68880a6..823003ce06 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -18,6 +18,7 @@ package com.android.launcher3.views; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_OUTSIDE; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs; @@ -260,7 +261,10 @@ public abstract class BaseDragLayer mTouchCompleteListener = null; } - if (mActiveController != null) { + if (mActiveController != null && ev.getAction() != ACTION_OUTSIDE) { + // For some reason, once we intercept touches and have an mActiveController, we won't + // get onInterceptTouchEvent() for ACTION_OUTSIDE. Thus, we must recalculate a new + // TouchController (if any) to handle the ACTION_OUTSIDE here in onTouchEvent() as well. return mActiveController.onControllerTouchEvent(ev); } else { // In case no child view handled the touch event, we may not get onIntercept anymore