Tune quick scrub

- Change durations/interpolators when starting from home vs an app
- Increase dim animation duration after quick scrub from an app
  - Also fixed contention between setting dim alpha for the animation
    and setting dim alpha for scrolling
Change-Id: I2c9ea52d4eafaad8954f3ff9d3c0c459a07953bc
This commit is contained in:
Tony Wickham 2018-05-04 12:10:55 -07:00
parent 99dcc9fd83
commit d58c2d5a48
9 changed files with 95 additions and 39 deletions

View File

@ -28,7 +28,7 @@ public class FastOverviewState extends OverviewState {
| FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON | FLAG_DISABLE_ACCESSIBILITY;
public FastOverviewState(int id) {
super(id, QuickScrubController.QUICK_SCRUB_START_DURATION, STATE_FLAGS);
super(id, QuickScrubController.QUICK_SCRUB_FROM_HOME_START_DURATION, STATE_FLAGS);
}
@Override

View File

@ -17,6 +17,8 @@ package com.android.launcher3.uioverrides;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
import static com.android.quickstep.views.RecentsView.ADJACENT_SCALE;
import static com.android.quickstep.views.RecentsViewContainer.CONTENT_ALPHA;
@ -24,12 +26,14 @@ import static com.android.quickstep.views.RecentsViewContainer.CONTENT_ALPHA;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.os.Build;
import android.view.animation.Interpolator;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PropertySetter;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsViewContainer;
@ -69,7 +73,13 @@ public class RecentsViewStateController implements StateHandler {
PropertySetter setter = config.getPropertySetter(builder);
float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0], LINEAR);
setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1], LINEAR);
Interpolator transYInterpolator = LINEAR;
if (toState == LauncherState.FAST_OVERVIEW) {
transYInterpolator = Interpolators.clampToProgress(QUICK_SCRUB_START_INTERPOLATOR, 0,
QUICK_SCRUB_TRANSLATION_Y_FACTOR);
}
setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
transYInterpolator);
setter.setFloat(mRecentsViewContainer, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
AGGRESSIVE_EASE_IN_OUT);

View File

@ -16,8 +16,11 @@
package com.android.quickstep;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.animation.Interpolator;
import com.android.launcher3.Alarm;
import com.android.launcher3.BaseActivity;
@ -37,7 +40,11 @@ import com.android.quickstep.views.TaskView;
*/
public class QuickScrubController implements OnAlarmListener {
public static final int QUICK_SCRUB_START_DURATION = 210;
public static final int QUICK_SCRUB_FROM_APP_START_DURATION = 240;
public static final int QUICK_SCRUB_FROM_HOME_START_DURATION = 150;
// We want the translation y to finish faster than the rest of the animation.
public static final float QUICK_SCRUB_TRANSLATION_Y_FACTOR = 5f / 6;
public static final Interpolator QUICK_SCRUB_START_INTERPOLATOR = FAST_OUT_SLOW_IN;
/**
* Snap to a new page when crossing these thresholds. The first and last auto-advance.
@ -168,23 +175,27 @@ public class QuickScrubController implements OnAlarmListener {
public void snapToNextTaskIfAvailable() {
if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
int duration = mStartedFromHome ? QUICK_SCRUB_FROM_HOME_START_DURATION
: QUICK_SCRUB_FROM_APP_START_DURATION;
int pageToGoTo = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1;
goToPageWithHaptic(pageToGoTo, QUICK_SCRUB_START_DURATION, true /* forceHaptic */);
goToPageWithHaptic(pageToGoTo, duration, true /* forceHaptic */,
QUICK_SCRUB_START_INTERPOLATOR);
}
}
private void goToPageWithHaptic(int pageToGoTo) {
goToPageWithHaptic(pageToGoTo, -1 /* overrideDuration */, false /* forceHaptic */);
goToPageWithHaptic(pageToGoTo, -1 /* overrideDuration */, false /* forceHaptic */, null);
}
private void goToPageWithHaptic(int pageToGoTo, int overrideDuration, boolean forceHaptic) {
private void goToPageWithHaptic(int pageToGoTo, int overrideDuration, boolean forceHaptic,
Interpolator interpolator) {
pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getPageCount() - 1);
boolean snappingToPage = pageToGoTo != mRecentsView.getNextPage();
if (snappingToPage) {
int duration = overrideDuration > -1 ? overrideDuration
: Math.abs(pageToGoTo - mRecentsView.getNextPage())
* QUICKSCRUB_SNAP_DURATION_PER_PAGE;
mRecentsView.snapToPage(pageToGoTo, duration);
mRecentsView.snapToPage(pageToGoTo, duration, interpolator);
}
if (snappingToPage || forceHaptic) {
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,

View File

@ -17,11 +17,9 @@ package com.android.quickstep;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_DURATION;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
@ -455,7 +453,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
// Start the window animation without waiting for launcher.
animateToProgress(1f, QUICK_SCRUB_START_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
animateToProgress(1f, QUICK_SCRUB_FROM_APP_START_DURATION, LINEAR);
}
@WorkerThread
@ -502,10 +500,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
if (controller != null) {
Interpolator interpolator = mInteractionType == INTERACTION_QUICK_SCRUB
? ACCEL_2 : LINEAR;
float interpolated = interpolator.getInterpolation(shift);
mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, interpolated);
mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, shift);
// TODO: This logic is spartanic!
boolean passedThreshold = shift > 0.12f;
@ -793,7 +788,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
}
mClipAnimationHelper.offsetTarget(
firstTask.getCurveScaleForInterpolation(interpolation), offsetFromFirstTask,
mActivityControlHelper.getTranslationYForQuickScrub(mActivity));
mActivityControlHelper.getTranslationYForQuickScrub(mActivity),
QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
}
}

View File

@ -16,7 +16,7 @@
package com.android.quickstep.util;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.SCROLL;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@ -30,11 +30,13 @@ import android.graphics.RectF;
import android.os.Build;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.view.animation.Interpolator;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.views.RecentsView;
@ -78,6 +80,9 @@ public class ClipAnimationHelper {
private final RectF mTmpRectF = new RectF();
private float mTargetScale = 1f;
private Interpolator mInterpolator = LINEAR;
// We translate y slightly faster than the rest of the animation for quick scrub.
private Interpolator mOffsetYInterpolator = LINEAR;
// Whether to boost the opening animation target layers, or the closing
private int mBoostModeTargetLayers = -1;
@ -134,12 +139,13 @@ public class ClipAnimationHelper {
RectF currentRect;
mTmpRectF.set(mTargetRect);
Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
float offsetYProgress = mOffsetYInterpolator.getInterpolation(progress);
progress = mInterpolator.getInterpolation(progress);
currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
synchronized (mTargetOffset) {
// Stay lined up with the center of the target, since it moves for quick scrub.
currentRect.offset(mTargetOffset.x * SCROLL.getInterpolation(progress),
mTargetOffset.y * LINEAR.getInterpolation(progress));
currentRect.offset(mTargetOffset.x * progress, mTargetOffset.y * offsetYProgress);
}
mClipRect.left = (int) (mSourceWindowClipInsets.left * progress);
@ -180,11 +186,14 @@ public class ClipAnimationHelper {
mTaskTransformCallback = callback;
}
public void offsetTarget(float scale, float offsetX, float offsetY) {
public void offsetTarget(float scale, float offsetX, float offsetY, Interpolator interpolator) {
synchronized (mTargetOffset) {
mTargetScale = scale;
mTargetOffset.set(offsetX, offsetY);
}
mTargetScale = scale;
mInterpolator = interpolator;
mOffsetYInterpolator = Interpolators.clampToProgress(mInterpolator, 0,
QUICK_SCRUB_TRANSLATION_Y_FACTOR);
}
public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {

View File

@ -16,7 +16,6 @@
package com.android.quickstep.views;
import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
import android.content.Context;
@ -52,16 +51,16 @@ public class TaskThumbnailView extends View {
private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
new FloatProperty<TaskThumbnailView>("dimAlpha") {
public static final Property<TaskThumbnailView, Float> DIM_ALPHA_MULTIPLIER =
new FloatProperty<TaskThumbnailView>("dimAlphaMultiplier") {
@Override
public void setValue(TaskThumbnailView thumbnail, float dimAlpha) {
thumbnail.setDimAlpha(dimAlpha);
public void setValue(TaskThumbnailView thumbnail, float dimAlphaMultiplier) {
thumbnail.setDimAlphaMultipler(dimAlphaMultiplier);
}
@Override
public Float get(TaskThumbnailView thumbnailView) {
return thumbnailView.mDimAlpha;
return thumbnailView.mDimAlphaMultiplier;
}
};
@ -81,6 +80,7 @@ public class TaskThumbnailView extends View {
protected BitmapShader mBitmapShader;
private float mDimAlpha = 1f;
private float mDimAlphaMultiplier = 1f;
public TaskThumbnailView(Context context) {
this(context, null);
@ -128,6 +128,11 @@ public class TaskThumbnailView extends View {
updateThumbnailPaintFilter();
}
public void setDimAlphaMultipler(float dimAlphaMultipler) {
mDimAlphaMultiplier = dimAlphaMultipler;
setDimAlpha(mDimAlpha);
}
/**
* Sets the alpha of the dim layer on top of this view.
*
@ -191,7 +196,7 @@ public class TaskThumbnailView extends View {
}
private void updateThumbnailPaintFilter() {
int mul = (int) ((1 - mDimAlpha) * 255);
int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
if (mBitmapShader != null) {
LightingColorFilter filter = getLightingColorFilter(mul);
mPaint.setColorFilter(filter);

View File

@ -17,7 +17,7 @@
package com.android.quickstep.views;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA_MULTIPLIER;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@ -78,6 +78,7 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback
private static final float EDGE_SCALE_DOWN_FACTOR = 0.03f;
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
public static final Property<TaskView, Float> ZOOM_SCALE =
new FloatProperty<TaskView>("zoomScale") {
@ -97,7 +98,6 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback
private IconView mIconView;
private float mCurveScale;
private float mZoomScale;
private float mCurveDimAlpha;
private Animator mDimAlphaAnim;
public TaskView(Context context) {
@ -200,8 +200,9 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback
public void animateIconToScaleAndDim(float scale) {
mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start();
mDimAlphaAnim = ObjectAnimator.ofFloat(mSnapshotView, DIM_ALPHA, scale * mCurveDimAlpha);
mDimAlphaAnim.setDuration(SCALE_ICON_DURATION);
mDimAlphaAnim = ObjectAnimator.ofFloat(mSnapshotView, DIM_ALPHA_MULTIPLIER, 1 - scale,
scale);
mDimAlphaAnim.setDuration(DIM_ANIM_DURATION);
mDimAlphaAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@ -218,7 +219,7 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback
if (mDimAlphaAnim != null) {
mDimAlphaAnim.cancel();
}
mSnapshotView.setDimAlpha(iconScale * mCurveDimAlpha);
mSnapshotView.setDimAlphaMultipler(iconScale);
}
public void resetVisualProperties() {
@ -235,11 +236,7 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback
float curveInterpolation =
CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
mCurveDimAlpha = curveInterpolation * MAX_PAGE_SCRIM_ALPHA;
if (mDimAlphaAnim == null && mIconView.getScaleX() > 0) {
mSnapshotView.setDimAlpha(mCurveDimAlpha);
}
mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
setCurveScale(getCurveScaleForCurveInterpolation(curveInterpolation));
}

View File

@ -27,6 +27,7 @@ import android.content.res.TypedArray;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InputDevice;
@ -44,6 +45,7 @@ import android.view.animation.Interpolator;
import android.widget.ScrollView;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.util.Thunk;
@ -1422,7 +1424,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
return snapToPage(whichPage, duration, false, null);
}
protected boolean snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
public boolean snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
return snapToPage(whichPage, duration, false, interpolator);
}
@ -1441,6 +1443,12 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
TimeInterpolator interpolator) {
if (FeatureFlags.IS_DOGFOOD_BUILD) {
duration *= Settings.System.getFloat(getContext().getContentResolver(),
Settings.System.WINDOW_ANIMATION_SCALE, 1);
}
whichPage = validateNewPage(whichPage);
mNextPage = whichPage;

View File

@ -115,4 +115,24 @@ public class Interpolators {
public static Interpolator scrollInterpolatorForVelocity(float velocity) {
return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC;
}
/**
* Runs the given interpolator such that the entire progress is set between the given bounds.
* That is, we set the interpolation to 0 until lowerBound and reach 1 by upperBound.
*/
public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound,
float upperBound) {
if (upperBound <= lowerBound) {
throw new IllegalArgumentException("lowerBound must be less than upperBound");
}
return t -> {
if (t < lowerBound) {
return 0;
}
if (t > upperBound) {
return 1;
}
return interpolator.getInterpolation((t - lowerBound) / (upperBound - lowerBound));
};
}
}