Merge "Handle swipe down on taskbar to stash" into tm-qpr-dev

This commit is contained in:
Tony Wickham
2023-03-23 18:54:09 +00:00
committed by Android (Google) Code Review
10 changed files with 162 additions and 153 deletions

View File

@@ -43,7 +43,7 @@
<dimen name="transient_taskbar_icon_size">52dp</dimen>
<!-- Taskbar swipe up thresholds -->
<dimen name="taskbar_nav_threshold">30dp</dimen>
<dimen name="taskbar_from_nav_threshold">30dp</dimen>
<dimen name="taskbar_app_window_threshold">100dp</dimen>
<dimen name="taskbar_home_overview_threshold">180dp</dimen>
<dimen name="taskbar_catch_up_threshold">300dp</dimen>

View File

@@ -305,10 +305,12 @@
<!-- An additional touch slop to prevent x-axis movement during the swipe up to show taskbar -->
<dimen name="transient_taskbar_clamped_offset_bound">16dp</dimen>
<!-- Taskbar swipe up thresholds -->
<dimen name="taskbar_nav_threshold">40dp</dimen>
<dimen name="taskbar_from_nav_threshold">40dp</dimen>
<dimen name="taskbar_app_window_threshold">88dp</dimen>
<dimen name="taskbar_home_overview_threshold">156dp</dimen>
<dimen name="taskbar_catch_up_threshold">264dp</dimen>
<!-- Taskbar swipe down threshold -->
<dimen name="taskbar_to_nav_threshold">24dp</dimen>
<!-- Taskbar 3 button spacing -->
<dimen name="taskbar_button_space_inbetween">24dp</dimen>

View File

@@ -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]);
}

View File

@@ -116,22 +116,6 @@ public class TaskbarDragLayer extends BaseDragLayer<TaskbarActivityContext> {
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);

View File

@@ -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() {
@@ -213,8 +204,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)
@@ -223,37 +212,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.
*/
@@ -283,9 +241,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,
};
}
}
}

View File

@@ -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)
}

View File

@@ -424,20 +424,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)) {

View File

@@ -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;
@@ -116,9 +113,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;
@@ -155,31 +149,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());
}
@@ -659,8 +628,6 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
private float mDownX, mDownY;
private boolean mCanceledStashHint;
private boolean mTouchInProgress;
public View.OnClickListener getIconOnClickListener() {
return mActivity.getItemOnClickListener();
}
@@ -681,76 +648,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;
}
/**

View File

@@ -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 =

View File

@@ -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<T extends Context & ActivityContext>
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