Merge "Add taskbar stashing feedforward, i.e. hint at upcoming stash/unstash" into sc-v2-dev

This commit is contained in:
Tony Wickham
2021-07-20 02:44:28 +00:00
committed by Android (Google) Code Review
12 changed files with 184 additions and 32 deletions
@@ -233,11 +233,6 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
}
}
@Override
public boolean onLongPressToUnstashTaskbar() {
return mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
}
/**
* @param ev MotionEvent in screen coordinates.
* @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
@@ -40,6 +40,8 @@ public class StashedHandleViewController {
private final int mStashedHandleHeight;
private final AnimatedFloat mTaskbarStashedHandleAlpha = new AnimatedFloat(
this::updateStashedHandleAlpha);
private final AnimatedFloat mTaskbarStashedHandleHintScale = new AnimatedFloat(
this::updateStashedHandleHintScale);
// Initialized in init.
private TaskbarControllers mControllers;
@@ -64,6 +66,7 @@ public class StashedHandleViewController {
mStashedHandleView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
updateStashedHandleAlpha();
mTaskbarStashedHandleHintScale.updateValue(1f);
final int stashedTaskbarHeight = mControllers.taskbarStashController.getStashedHeight();
mStashedHandleView.setOutlineProvider(new ViewOutlineProvider() {
@@ -80,12 +83,24 @@ public class StashedHandleViewController {
outline.setRoundRect(mStashedHandleBounds, mStashedHandleRadius);
}
});
mStashedHandleView.addOnLayoutChangeListener((view, i, i1, i2, i3, i4, i5, i6, i7) -> {
final int stashedCenterX = view.getWidth() / 2;
final int stashedCenterY = view.getHeight() - stashedTaskbarHeight / 2;
view.setPivotX(stashedCenterX);
view.setPivotY(stashedCenterY);
});
}
public AnimatedFloat getStashedHandleAlpha() {
return mTaskbarStashedHandleAlpha;
}
public AnimatedFloat getStashedHandleHintScale() {
return mTaskbarStashedHandleHintScale;
}
/**
* Creates and returns a {@link RevealOutlineAnimation} Animator that updates the stashed handle
* shape and size. When stashed, the shape is a thin rounded pill. When unstashed, the shape
@@ -105,4 +120,9 @@ public class StashedHandleViewController {
protected void updateStashedHandleAlpha() {
mStashedHandleView.setAlpha(mTaskbarStashedHandleAlpha.value);
}
protected void updateStashedHandleHintScale() {
mStashedHandleView.setScaleX(mTaskbarStashedHandleHintScale.value);
mStashedHandleView.setScaleY(mTaskbarStashedHandleHintScale.value);
}
}
@@ -332,4 +332,20 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
AbstractFloatingView.closeAllOpenViews(this);
}
/**
* Called when we detect a long press in the nav region before passing the gesture slop.
* @return Whether taskbar handled the long press, and thus should cancel the gesture.
*/
public boolean onLongPressToUnstashTaskbar() {
return mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
}
/**
* Called when we detect a motion down or up/cancel in the nav region while stashed.
* @param animateForward Whether to animate towards the unstashed hint state or back to stashed.
*/
public void startTaskbarUnstashHint(boolean animateForward) {
mControllers.taskbarStashController.startUnstashHint(animateForward);
}
}
@@ -27,6 +27,7 @@ import android.hardware.display.DisplayManager;
import android.view.Display;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
@@ -188,4 +189,8 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen
mDisplayController.removeChangeListener(this);
mSysUINavigationMode.removeModeChangeListener(this);
}
public @Nullable TaskbarActivityContext getCurrentActivityContext() {
return mTaskbarActivityContext;
}
}
@@ -23,6 +23,7 @@ import android.animation.AnimatorSet;
import android.annotation.Nullable;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.view.ViewConfiguration;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -46,6 +47,22 @@ public class TaskbarStashController {
*/
private static final float STASHED_TASKBAR_SCALE = 0.5f;
/**
* How long the hint animation plays, starting on motion down.
*/
private static final long TASKBAR_HINT_STASH_DURATION =
ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
/**
* The scale that TaskbarView animates to when hinting towards the stashed state.
*/
private static final float STASHED_TASKBAR_HINT_SCALE = 0.9f;
/**
* The scale that the stashed handle animates to when hinting towards the unstashed state.
*/
private static final float UNSTASHED_TASKBAR_HANDLE_HINT_SCALE = 1.1f;
/**
* The SharedPreferences key for whether user has manually stashed the taskbar.
*/
@@ -71,6 +88,7 @@ public class TaskbarStashController {
private AnimatedFloat mIconTranslationYForStash;
// Stashed handle properties.
private AnimatedFloat mTaskbarStashedHandleAlpha;
private AnimatedFloat mTaskbarStashedHandleHintScale;
/** Whether the user has manually invoked taskbar stashing, which we persist. */
private boolean mIsStashedInApp;
@@ -102,6 +120,7 @@ public class TaskbarStashController {
StashedHandleViewController stashedHandleController =
controllers.stashedHandleViewController;
mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha();
mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
mIsStashedInApp = supportsStashing()
&& mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
@@ -246,6 +265,9 @@ public class TaskbarStashController {
if (stashedHandleRevealAnim != null) {
fullLengthAnimatorSet.play(stashedHandleRevealAnim);
}
// Return the stashed handle to its default scale in case it was changed as part of the
// feedforward hint. Note that the reveal animation above also visually scales it.
fullLengthAnimatorSet.play(mTaskbarStashedHandleHintScale.animateToValue(1f));
fullLengthAnimatorSet.setDuration(duration);
firstHalfAnimatorSet.setDuration((long) (duration * firstHalfDurationScale));
@@ -271,4 +293,36 @@ public class TaskbarStashController {
});
return mAnimator;
}
/**
* Creates and starts a partial stash animation, hinting at the new state that will trigger when
* long press is detected.
* @param animateForward Whether we are going towards the new stashed state or returning to the
* unstashed state.
*/
public void startStashHint(boolean animateForward) {
if (isStashed() || !supportsStashing()) {
// Already stashed, no need to hint in that direction.
return;
}
mIconScaleForStash.animateToValue(
animateForward ? STASHED_TASKBAR_HINT_SCALE : 1)
.setDuration(TASKBAR_HINT_STASH_DURATION).start();
}
/**
* Creates and starts a partial unstash animation, hinting at the new state that will trigger
* when long press is detected.
* @param animateForward Whether we are going towards the new unstashed state or returning to
* the stashed state.
*/
public void startUnstashHint(boolean animateForward) {
if (!isStashed()) {
// Already unstashed, no need to hint in that direction.
return;
}
mTaskbarStashedHandleHintScale.animateToValue(
animateForward ? UNSTASHED_TASKBAR_HANDLE_HINT_SCALE : 1)
.setDuration(TASKBAR_HINT_STASH_DURATION).start();
}
}
@@ -33,8 +33,4 @@ public class TaskbarUIController {
}
protected void updateContentInsets(Rect outContentInsets) { }
protected boolean onLongPressToUnstashTaskbar() {
return false;
}
}
@@ -222,6 +222,15 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mTouchEnabled) {
return true;
}
mControllerCallbacks.onTouchEvent(event);
return super.onTouchEvent(event);
}
public void setTouchesEnabled(boolean touchEnabled) {
this.mTouchEnabled = touchEnabled;
}
@@ -17,14 +17,17 @@ package com.android.launcher3.taskbar;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.AnimatedFloat.VALUE;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.data.ItemInfo;
@@ -188,6 +191,11 @@ public class TaskbarViewController {
* Callbacks for {@link TaskbarView} to interact with its controller.
*/
public class TaskbarViewCallbacks {
private final float mSquaredTouchSlop = Utilities.squaredTouchSlop(mActivity);
private float mDownX, mDownY;
private boolean mCanceledStashHint;
public View.OnClickListener getIconOnClickListener() {
return mActivity::onTaskbarIconClicked;
}
@@ -199,5 +207,33 @@ public class TaskbarViewController {
public View.OnLongClickListener getBackgroundOnLongClickListener() {
return view -> mControllers.taskbarStashController.updateAndAnimateIsStashedInApp(true);
}
public void onTouchEvent(MotionEvent motionEvent) {
final float x = motionEvent.getRawX();
final float y = motionEvent.getRawY();
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = x;
mDownY = y;
mControllers.taskbarStashController.startStashHint(/* animateForward = */ true);
mCanceledStashHint = false;
break;
case MotionEvent.ACTION_MOVE:
if (!mCanceledStashHint
&& squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
mCanceledStashHint = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (!mCanceledStashHint) {
mControllers.taskbarStashController.startStashHint(
/* animateForward= */ false);
}
break;
}
}
}
}
@@ -363,14 +363,6 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
return null;
}
/**
* Called when we detect a long press in the nav region before passing the gesture slop.
* @return Whether taskbar handled the long press, and thus should cancel the gesture.
*/
public boolean onLongPressToUnstashTaskbar() {
return false;
}
/**
* Returns the color of the scrim behind overview when at rest in this state.
* Return {@link Color#TRANSPARENT} for no scrim.
@@ -317,15 +317,6 @@ public final class LauncherActivityInterface extends
}
}
@Override
public boolean onLongPressToUnstashTaskbar() {
LauncherTaskbarUIController taskbarController = getTaskbarController();
if (taskbarController == null) {
return super.onLongPressToUnstashTaskbar();
}
return taskbarController.onLongPressToUnstashTaskbar();
}
@Override
protected int getOverviewScrimColorForState(BaseQuickstepLauncher launcher,
LauncherState state) {
@@ -680,7 +680,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
StatefulActivity activity = activityInterface.getCreatedActivity();
if (activity != null && activity.getDeviceProfile().isTaskbarPresent) {
base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat,
activityInterface);
mTaskbarManager.getCurrentActivityContext());
}
if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
@@ -15,12 +15,15 @@
*/
package com.android.quickstep.inputconsumers;
import static com.android.launcher3.Utilities.squaredHypot;
import android.content.Context;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import com.android.quickstep.BaseActivityInterface;
import com.android.launcher3.Utilities;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -29,13 +32,18 @@ import com.android.systemui.shared.system.InputMonitorCompat;
*/
public class TaskbarStashInputConsumer extends DelegateInputConsumer {
private final BaseActivityInterface mActivityInterface;
private final TaskbarActivityContext mTaskbarActivityContext;
private final GestureDetector mLongPressDetector;
private final float mSquaredTouchSlop;
private float mDownX, mDownY;
private boolean mCanceledUnstashHint;
public TaskbarStashInputConsumer(Context context, InputConsumer delegate,
InputMonitorCompat inputMonitor, BaseActivityInterface activityInterface) {
InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) {
super(delegate, inputMonitor);
mActivityInterface = activityInterface;
mTaskbarActivityContext = taskbarActivityContext;
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
mLongPressDetector = new GestureDetector(context, new SimpleOnGestureListener() {
@Override
@@ -55,11 +63,41 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer {
mLongPressDetector.onTouchEvent(ev);
if (mState != STATE_ACTIVE) {
mDelegate.onMotionEvent(ev);
if (mTaskbarActivityContext != null) {
final float x = ev.getRawX();
final float y = ev.getRawY();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownX = x;
mDownY = y;
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ true);
mCanceledUnstashHint = false;
break;
case MotionEvent.ACTION_MOVE:
if (!mCanceledUnstashHint
&& squaredHypot(mDownX - x, mDownY - y) > mSquaredTouchSlop) {
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ false);
mCanceledUnstashHint = true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (!mCanceledUnstashHint) {
mTaskbarActivityContext.startTaskbarUnstashHint(
/* animateForward = */ false);
}
break;
}
}
}
}
private void onLongPressDetected(MotionEvent motionEvent) {
if (mActivityInterface.onLongPressToUnstashTaskbar()) {
if (mTaskbarActivityContext != null
&& mTaskbarActivityContext.onLongPressToUnstashTaskbar()) {
setActive(motionEvent);
}
}