Loading quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java +1 −1 Original line number Diff line number Diff line Loading @@ -89,7 +89,7 @@ public class AllAppsState extends LauncherState { } @Override public float getOverviewTranslationX(Launcher launcher) { public float getOverviewTranslationFactor(Launcher launcher) { return 0; } Loading quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +1 −1 Original line number Diff line number Diff line Loading @@ -58,7 +58,7 @@ public class OverviewState extends LauncherState { } @Override public float getOverviewTranslationX(Launcher launcher) { public float getOverviewTranslationFactor(Launcher launcher) { return 0; } Loading quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java +166 −3 Original line number Diff line number Diff line Loading @@ -18,16 +18,24 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.view.MotionEvent; import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.launcher3.touch.SwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.quickstep.TouchInteractionService; import com.android.quickstep.util.SysuiEventLogger; Loading @@ -36,6 +44,47 @@ import com.android.quickstep.util.SysuiEventLogger; */ public class PortraitStatesTouchController extends AbstractStateChangeTouchController { private static final float TOTAL_DISTANCE_MULTIPLIER = 2f; private static final float LINEAR_SCALE_LIMIT = 1 / TOTAL_DISTANCE_MULTIPLIER; // Much be greater than LINEAR_SCALE_LIMIT; private static final float MAXIMUM_DISTANCE_FACTOR = 0.9f; // Maximum amount to overshoot. private static final float MAX_OVERSHOOT = 0.3f; private static final double PI_BY_2 = Math.PI / 2; private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper(); // If > 0, the animation progress is clamped at that value as long as user is dragging. private float mClampProgressUpdate = -1; // If true, we will finish the current animation instantly on second touch. private boolean mFinishFastOnSecondTouch; private final Interpolator mAllAppsDampedInterpolator = new Interpolator() { private final double mAngleMultiplier = Math.PI / (2 * (MAXIMUM_DISTANCE_FACTOR - LINEAR_SCALE_LIMIT)); @Override public float getInterpolation(float v) { if (v <= LINEAR_SCALE_LIMIT) { return v * TOTAL_DISTANCE_MULTIPLIER; } float overshoot = (v - LINEAR_SCALE_LIMIT); return (float) (1 + MAX_OVERSHOOT * Math.sin(overshoot * mAngleMultiplier)); } }; private final Interpolator mOverviewBoundInterpolator = (v) -> { if (v >= MAXIMUM_DISTANCE_FACTOR) { return 1; } return FAST_OUT_SLOW_IN.getInterpolation(v / MAXIMUM_DISTANCE_FACTOR); }; public PortraitStatesTouchController(Launcher l) { super(l, SwipeDetector.VERTICAL); } Loading @@ -43,6 +92,11 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr @Override protected boolean canInterceptTouch(MotionEvent ev) { if (mCurrentAnimation != null) { if (mFinishFastOnSecondTouch) { // TODO: Animate to finish instead. mCurrentAnimation.getAnimationPlayer().end(); } // If we are already animating from a previous state, we can intercept. return true; } Loading Loading @@ -100,17 +154,49 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr } } private AnimatorSetBuilder getNormalToOverviewAnimation() { mAllAppsInterpolatorWrapper.baseInterpolator = mAllAppsDampedInterpolator; AnimatorSetBuilder builder = new AnimatorSetBuilder(); builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper); builder.setInterpolator(ANIM_OVERVIEW_TRANSLATION, mOverviewBoundInterpolator); return builder; } @Override protected void updateProgress(float fraction) { if (mClampProgressUpdate > 0) { mCurrentAnimation.setPlayFraction(Math.min(fraction, mClampProgressUpdate)); } else { super.updateProgress(fraction); } } @Override protected float initCurrentAnimation() { float range = getShiftRange(); long maxAccuracy = (long) (2 * range); mCurrentAnimation = mLauncher.getStateManager() .createAnimationToNewWorkspace(mToState, maxAccuracy); float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range; float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range; float totalShift = endVerticalShift - startVerticalShift; final AnimatorSetBuilder builder; if (mFromState == NORMAL && mToState == OVERVIEW && totalShift != 0) { builder = getNormalToOverviewAnimation(); totalShift = totalShift * TOTAL_DISTANCE_MULTIPLIER; mClampProgressUpdate = MAXIMUM_DISTANCE_FACTOR; } else { builder = new AnimatorSetBuilder(); mClampProgressUpdate = -1; } mCurrentAnimation = mLauncher.getStateManager() .createAnimationToNewWorkspace(mToState, builder, maxAccuracy); if (totalShift == 0) { totalShift = Math.signum(mFromState.ordinal - mToState.ordinal) * OverviewState.getDefaultSwipeHeight(mLauncher); Loading @@ -118,6 +204,73 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr return 1 / totalShift; } @Override protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration, LauncherState targetState, float velocity, boolean isFling) { if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) { mFinishFastOnSecondTouch = true; // Update all apps interpolator float currentFraction = mCurrentAnimation.getProgressFraction(); float absVelocity = Math.abs(velocity); float currentValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction); if (isFling && absVelocity > 1 && currentFraction < LINEAR_SCALE_LIMIT) { // TODO: Clean up these magic calculations // Linearly interpolate the max value based on the velocity. float maxValue = Math.max(absVelocity > 4 ? 1 + MAX_OVERSHOOT : 1 + (absVelocity - 1) * MAX_OVERSHOOT / 3, currentValue); double angleToPeak = PI_BY_2 - Math.asin(currentValue / maxValue); if (expectedDuration != 0 && angleToPeak != 0) { float distanceLeft = 1 - currentFraction; mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> { float scaledF = (f - currentFraction) / distanceLeft; if (scaledF < 0.5f) { double angle = PI_BY_2 - angleToPeak + scaledF * angleToPeak / 0.5f; return (float) (maxValue * Math.sin(angle)); } scaledF = ((scaledF - .5f) / .5f); double angle = PI_BY_2 + 3 * scaledF * PI_BY_2; float amplitude = (1 - scaledF) * (1 - scaledF) * (maxValue - 1); return 1 + (float) (amplitude * Math.sin(angle)); }; animator.setDuration(expectedDuration).setInterpolator(LINEAR); return; } } if (currentFraction < LINEAR_SCALE_LIMIT) { mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR; super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, velocity, isFling); return; } float extraValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction) - 1; float distanceLeft = 1 - currentFraction; animator.setFloatValues(currentFraction, 1); mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> { float scaledF = (f - currentFraction) / distanceLeft; double angle = scaledF * 1.5 * Math.PI; float amplitude = (1 - scaledF) * (1 - scaledF) * extraValue; return 1 + (float) (amplitude * Math.sin(angle)); }; animator.setDuration(200).setInterpolator(LINEAR); return; } mFinishFastOnSecondTouch = false; super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, velocity, isFling); } @Override protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { super.onSwipeInteractionCompleted(targetState, logAction); Loading @@ -125,4 +278,14 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr SysuiEventLogger.writeDummyRecentsTransition(0); } } private static class InterpolatorWrapper implements Interpolator { public TimeInterpolator baseInterpolator = LINEAR; @Override public float getInterpolation(float v) { return baseInterpolator.getInterpolation(v); } } } quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +24 −81 Original line number Diff line number Diff line Loading @@ -16,32 +16,30 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_FACTOR; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.view.View; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.os.Build; 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.PagedView; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.views.RecentsView; import com.android.launcher3.anim.PropertySetter; import com.android.quickstep.views.LauncherRecentsView; @TargetApi(Build.VERSION_CODES.O) public class RecentsViewStateController implements StateHandler { private final Launcher mLauncher; private final RecentsView mRecentsView; private final AnimatedFloat mTransitionProgress = new AnimatedFloat(this::onTransitionProgress); // The fraction representing the visibility of the RecentsView. This allows delaying the // overall transition while the RecentsView is being shown or hidden. private final AnimatedFloat mVisibilityMultiplier = new AnimatedFloat(this::onVisibilityProgress); private final LauncherRecentsView mRecentsView; public RecentsViewStateController(Launcher launcher) { mLauncher = launcher; Loading @@ -50,14 +48,12 @@ public class RecentsViewStateController implements StateHandler { @Override public void setState(LauncherState state) { setVisibility(state.overviewUi); setTransitionProgress(state.overviewUi ? 1 : 0); mRecentsView.setAlpha(state.overviewUi ? 1 : 0); updateVisibility(mRecentsView, isAccessibilityEnabled(mLauncher)); mRecentsView.setTranslationFactor(state.getOverviewTranslationFactor(mLauncher)); if (state.overviewUi) { mRecentsView.resetTaskVisuals(); } float overviewTranslationX = state.getOverviewTranslationX(mLauncher); int direction = mRecentsView.isRtl() ? -1 : 1; mRecentsView.setTranslationX(overviewTranslationX * direction); } @Override Loading @@ -76,73 +72,20 @@ public class RecentsViewStateController implements StateHandler { builder.setStartDelay(snapDuration / 4); } ObjectAnimator progressAnim = mTransitionProgress.animateToValue(toState.overviewUi ? 1 : 0); progressAnim.setDuration(config.duration); progressAnim.setInterpolator(Interpolators.LINEAR); builder.play(progressAnim); ObjectAnimator visibilityAnim = animateVisibility(toState.overviewUi); visibilityAnim.setDuration(config.duration); visibilityAnim.setInterpolator(Interpolators.LINEAR); builder.play(visibilityAnim); PropertySetter setter = config.getProperSetter(builder); setter.setFloat(mRecentsView, TRANSLATION_FACTOR, toState.getOverviewTranslationFactor(mLauncher), builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR)); setter.setViewAlpha(mRecentsView, toState.overviewUi ? 1 : 0, LINEAR); int direction = mRecentsView.isRtl() ? -1 : 1; float fromTranslationX = fromState.getOverviewTranslationX(mLauncher) * direction; float toTranslationX = toState.getOverviewTranslationX(mLauncher) * direction; ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(mRecentsView, View.TRANSLATION_X, fromTranslationX, toTranslationX); translationXAnim.setDuration(config.duration); translationXAnim.setInterpolator(Interpolators.ACCEL); if (toState.overviewUi) { translationXAnim.addUpdateListener(valueAnimator -> { ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1); updateAnim.addUpdateListener(valueAnimator -> { // While animating into recents, update the visible task data as needed mRecentsView.loadVisibleTaskData(); }); updateAnim.setDuration(config.duration); builder.play(updateAnim); } builder.play(translationXAnim); } public void setVisibility(boolean isVisible) { mVisibilityMultiplier.cancelAnimation(); mRecentsView.setVisibility(isVisible ? View.VISIBLE : View.GONE); mVisibilityMultiplier.updateValue(isVisible ? 1 : 0); } public ObjectAnimator animateVisibility(boolean isVisible) { ObjectAnimator anim = mVisibilityMultiplier.animateToValue(isVisible ? 1 : 0); if (isVisible) { anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { mRecentsView.setVisibility(View.VISIBLE); } }); } else { anim.addListener(new AnimationSuccessListener() { @Override public void onAnimationSuccess(Animator animator) { mRecentsView.setVisibility(View.GONE); } }); } return anim; } public void setTransitionProgress(float progress) { mTransitionProgress.cancelAnimation(); mTransitionProgress.updateValue(progress); } private void onTransitionProgress() { applyProgress(); } private void onVisibilityProgress() { applyProgress(); } private void applyProgress() { mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value); } } quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +37 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.quickstep.views; import static com.android.launcher3.LauncherState.NORMAL; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapShader; Loading @@ -30,7 +31,9 @@ import android.graphics.Shader; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; import android.util.FloatProperty; import android.widget.FrameLayout; import com.android.launcher3.DeviceProfile; Loading @@ -41,14 +44,31 @@ import com.android.launcher3.R; /** * {@link RecentsView} used in Launcher activity */ @TargetApi(Build.VERSION_CODES.O) public class LauncherRecentsView extends RecentsView<Launcher> implements Insettable { public static final FloatProperty<LauncherRecentsView> TRANSLATION_FACTOR = new FloatProperty<LauncherRecentsView>("translationFactor") { @Override public void setValue(LauncherRecentsView view, float v) { view.setTranslationFactor(v); } @Override public Float get(LauncherRecentsView view) { return view.mTranslationFactor; } }; private Bitmap mScrim; private Paint mFadePaint; private Shader mFadeShader; private Matrix mFadeMatrix; private boolean mScrimOnLeft; private float mTranslationFactor; public LauncherRecentsView(Context context) { this(context, null); } Loading Loading @@ -131,4 +151,21 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements Insett protected void onAllTasksRemoved() { mActivity.getStateManager().goToState(NORMAL); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); int width = right - left; setTranslationX(mTranslationFactor * (mIsRtl ? -width : width)); } public void setTranslationFactor(float translationFactor) { mTranslationFactor = translationFactor; setTranslationX(translationFactor * (mIsRtl ? -getWidth() : getWidth())); } public float getTranslationFactor() { return mTranslationFactor; } } Loading
quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java +1 −1 Original line number Diff line number Diff line Loading @@ -89,7 +89,7 @@ public class AllAppsState extends LauncherState { } @Override public float getOverviewTranslationX(Launcher launcher) { public float getOverviewTranslationFactor(Launcher launcher) { return 0; } Loading
quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +1 −1 Original line number Diff line number Diff line Loading @@ -58,7 +58,7 @@ public class OverviewState extends LauncherState { } @Override public float getOverviewTranslationX(Launcher launcher) { public float getOverviewTranslationFactor(Launcher launcher) { return 0; } Loading
quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java +166 −3 Original line number Diff line number Diff line Loading @@ -18,16 +18,24 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.view.MotionEvent; import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.launcher3.touch.SwipeDetector; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.launcher3.touch.AbstractStateChangeTouchController; import com.android.quickstep.TouchInteractionService; import com.android.quickstep.util.SysuiEventLogger; Loading @@ -36,6 +44,47 @@ import com.android.quickstep.util.SysuiEventLogger; */ public class PortraitStatesTouchController extends AbstractStateChangeTouchController { private static final float TOTAL_DISTANCE_MULTIPLIER = 2f; private static final float LINEAR_SCALE_LIMIT = 1 / TOTAL_DISTANCE_MULTIPLIER; // Much be greater than LINEAR_SCALE_LIMIT; private static final float MAXIMUM_DISTANCE_FACTOR = 0.9f; // Maximum amount to overshoot. private static final float MAX_OVERSHOOT = 0.3f; private static final double PI_BY_2 = Math.PI / 2; private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper(); // If > 0, the animation progress is clamped at that value as long as user is dragging. private float mClampProgressUpdate = -1; // If true, we will finish the current animation instantly on second touch. private boolean mFinishFastOnSecondTouch; private final Interpolator mAllAppsDampedInterpolator = new Interpolator() { private final double mAngleMultiplier = Math.PI / (2 * (MAXIMUM_DISTANCE_FACTOR - LINEAR_SCALE_LIMIT)); @Override public float getInterpolation(float v) { if (v <= LINEAR_SCALE_LIMIT) { return v * TOTAL_DISTANCE_MULTIPLIER; } float overshoot = (v - LINEAR_SCALE_LIMIT); return (float) (1 + MAX_OVERSHOOT * Math.sin(overshoot * mAngleMultiplier)); } }; private final Interpolator mOverviewBoundInterpolator = (v) -> { if (v >= MAXIMUM_DISTANCE_FACTOR) { return 1; } return FAST_OUT_SLOW_IN.getInterpolation(v / MAXIMUM_DISTANCE_FACTOR); }; public PortraitStatesTouchController(Launcher l) { super(l, SwipeDetector.VERTICAL); } Loading @@ -43,6 +92,11 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr @Override protected boolean canInterceptTouch(MotionEvent ev) { if (mCurrentAnimation != null) { if (mFinishFastOnSecondTouch) { // TODO: Animate to finish instead. mCurrentAnimation.getAnimationPlayer().end(); } // If we are already animating from a previous state, we can intercept. return true; } Loading Loading @@ -100,17 +154,49 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr } } private AnimatorSetBuilder getNormalToOverviewAnimation() { mAllAppsInterpolatorWrapper.baseInterpolator = mAllAppsDampedInterpolator; AnimatorSetBuilder builder = new AnimatorSetBuilder(); builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper); builder.setInterpolator(ANIM_OVERVIEW_TRANSLATION, mOverviewBoundInterpolator); return builder; } @Override protected void updateProgress(float fraction) { if (mClampProgressUpdate > 0) { mCurrentAnimation.setPlayFraction(Math.min(fraction, mClampProgressUpdate)); } else { super.updateProgress(fraction); } } @Override protected float initCurrentAnimation() { float range = getShiftRange(); long maxAccuracy = (long) (2 * range); mCurrentAnimation = mLauncher.getStateManager() .createAnimationToNewWorkspace(mToState, maxAccuracy); float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range; float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range; float totalShift = endVerticalShift - startVerticalShift; final AnimatorSetBuilder builder; if (mFromState == NORMAL && mToState == OVERVIEW && totalShift != 0) { builder = getNormalToOverviewAnimation(); totalShift = totalShift * TOTAL_DISTANCE_MULTIPLIER; mClampProgressUpdate = MAXIMUM_DISTANCE_FACTOR; } else { builder = new AnimatorSetBuilder(); mClampProgressUpdate = -1; } mCurrentAnimation = mLauncher.getStateManager() .createAnimationToNewWorkspace(mToState, builder, maxAccuracy); if (totalShift == 0) { totalShift = Math.signum(mFromState.ordinal - mToState.ordinal) * OverviewState.getDefaultSwipeHeight(mLauncher); Loading @@ -118,6 +204,73 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr return 1 / totalShift; } @Override protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration, LauncherState targetState, float velocity, boolean isFling) { if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) { mFinishFastOnSecondTouch = true; // Update all apps interpolator float currentFraction = mCurrentAnimation.getProgressFraction(); float absVelocity = Math.abs(velocity); float currentValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction); if (isFling && absVelocity > 1 && currentFraction < LINEAR_SCALE_LIMIT) { // TODO: Clean up these magic calculations // Linearly interpolate the max value based on the velocity. float maxValue = Math.max(absVelocity > 4 ? 1 + MAX_OVERSHOOT : 1 + (absVelocity - 1) * MAX_OVERSHOOT / 3, currentValue); double angleToPeak = PI_BY_2 - Math.asin(currentValue / maxValue); if (expectedDuration != 0 && angleToPeak != 0) { float distanceLeft = 1 - currentFraction; mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> { float scaledF = (f - currentFraction) / distanceLeft; if (scaledF < 0.5f) { double angle = PI_BY_2 - angleToPeak + scaledF * angleToPeak / 0.5f; return (float) (maxValue * Math.sin(angle)); } scaledF = ((scaledF - .5f) / .5f); double angle = PI_BY_2 + 3 * scaledF * PI_BY_2; float amplitude = (1 - scaledF) * (1 - scaledF) * (maxValue - 1); return 1 + (float) (amplitude * Math.sin(angle)); }; animator.setDuration(expectedDuration).setInterpolator(LINEAR); return; } } if (currentFraction < LINEAR_SCALE_LIMIT) { mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR; super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, velocity, isFling); return; } float extraValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction) - 1; float distanceLeft = 1 - currentFraction; animator.setFloatValues(currentFraction, 1); mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> { float scaledF = (f - currentFraction) / distanceLeft; double angle = scaledF * 1.5 * Math.PI; float amplitude = (1 - scaledF) * (1 - scaledF) * extraValue; return 1 + (float) (amplitude * Math.sin(angle)); }; animator.setDuration(200).setInterpolator(LINEAR); return; } mFinishFastOnSecondTouch = false; super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, velocity, isFling); } @Override protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { super.onSwipeInteractionCompleted(targetState, logAction); Loading @@ -125,4 +278,14 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr SysuiEventLogger.writeDummyRecentsTransition(0); } } private static class InterpolatorWrapper implements Interpolator { public TimeInterpolator baseInterpolator = LINEAR; @Override public float getInterpolation(float v) { return baseInterpolator.getInterpolation(v); } } }
quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +24 −81 Original line number Diff line number Diff line Loading @@ -16,32 +16,30 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_FACTOR; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.view.View; import android.animation.ValueAnimator; import android.annotation.TargetApi; import android.os.Build; 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.PagedView; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.Interpolators; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.views.RecentsView; import com.android.launcher3.anim.PropertySetter; import com.android.quickstep.views.LauncherRecentsView; @TargetApi(Build.VERSION_CODES.O) public class RecentsViewStateController implements StateHandler { private final Launcher mLauncher; private final RecentsView mRecentsView; private final AnimatedFloat mTransitionProgress = new AnimatedFloat(this::onTransitionProgress); // The fraction representing the visibility of the RecentsView. This allows delaying the // overall transition while the RecentsView is being shown or hidden. private final AnimatedFloat mVisibilityMultiplier = new AnimatedFloat(this::onVisibilityProgress); private final LauncherRecentsView mRecentsView; public RecentsViewStateController(Launcher launcher) { mLauncher = launcher; Loading @@ -50,14 +48,12 @@ public class RecentsViewStateController implements StateHandler { @Override public void setState(LauncherState state) { setVisibility(state.overviewUi); setTransitionProgress(state.overviewUi ? 1 : 0); mRecentsView.setAlpha(state.overviewUi ? 1 : 0); updateVisibility(mRecentsView, isAccessibilityEnabled(mLauncher)); mRecentsView.setTranslationFactor(state.getOverviewTranslationFactor(mLauncher)); if (state.overviewUi) { mRecentsView.resetTaskVisuals(); } float overviewTranslationX = state.getOverviewTranslationX(mLauncher); int direction = mRecentsView.isRtl() ? -1 : 1; mRecentsView.setTranslationX(overviewTranslationX * direction); } @Override Loading @@ -76,73 +72,20 @@ public class RecentsViewStateController implements StateHandler { builder.setStartDelay(snapDuration / 4); } ObjectAnimator progressAnim = mTransitionProgress.animateToValue(toState.overviewUi ? 1 : 0); progressAnim.setDuration(config.duration); progressAnim.setInterpolator(Interpolators.LINEAR); builder.play(progressAnim); ObjectAnimator visibilityAnim = animateVisibility(toState.overviewUi); visibilityAnim.setDuration(config.duration); visibilityAnim.setInterpolator(Interpolators.LINEAR); builder.play(visibilityAnim); PropertySetter setter = config.getProperSetter(builder); setter.setFloat(mRecentsView, TRANSLATION_FACTOR, toState.getOverviewTranslationFactor(mLauncher), builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR)); setter.setViewAlpha(mRecentsView, toState.overviewUi ? 1 : 0, LINEAR); int direction = mRecentsView.isRtl() ? -1 : 1; float fromTranslationX = fromState.getOverviewTranslationX(mLauncher) * direction; float toTranslationX = toState.getOverviewTranslationX(mLauncher) * direction; ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(mRecentsView, View.TRANSLATION_X, fromTranslationX, toTranslationX); translationXAnim.setDuration(config.duration); translationXAnim.setInterpolator(Interpolators.ACCEL); if (toState.overviewUi) { translationXAnim.addUpdateListener(valueAnimator -> { ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1); updateAnim.addUpdateListener(valueAnimator -> { // While animating into recents, update the visible task data as needed mRecentsView.loadVisibleTaskData(); }); updateAnim.setDuration(config.duration); builder.play(updateAnim); } builder.play(translationXAnim); } public void setVisibility(boolean isVisible) { mVisibilityMultiplier.cancelAnimation(); mRecentsView.setVisibility(isVisible ? View.VISIBLE : View.GONE); mVisibilityMultiplier.updateValue(isVisible ? 1 : 0); } public ObjectAnimator animateVisibility(boolean isVisible) { ObjectAnimator anim = mVisibilityMultiplier.animateToValue(isVisible ? 1 : 0); if (isVisible) { anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { mRecentsView.setVisibility(View.VISIBLE); } }); } else { anim.addListener(new AnimationSuccessListener() { @Override public void onAnimationSuccess(Animator animator) { mRecentsView.setVisibility(View.GONE); } }); } return anim; } public void setTransitionProgress(float progress) { mTransitionProgress.cancelAnimation(); mTransitionProgress.updateValue(progress); } private void onTransitionProgress() { applyProgress(); } private void onVisibilityProgress() { applyProgress(); } private void applyProgress() { mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value); } }
quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +37 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.quickstep.views; import static com.android.launcher3.LauncherState.NORMAL; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapShader; Loading @@ -30,7 +31,9 @@ import android.graphics.Shader; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; import android.util.FloatProperty; import android.widget.FrameLayout; import com.android.launcher3.DeviceProfile; Loading @@ -41,14 +44,31 @@ import com.android.launcher3.R; /** * {@link RecentsView} used in Launcher activity */ @TargetApi(Build.VERSION_CODES.O) public class LauncherRecentsView extends RecentsView<Launcher> implements Insettable { public static final FloatProperty<LauncherRecentsView> TRANSLATION_FACTOR = new FloatProperty<LauncherRecentsView>("translationFactor") { @Override public void setValue(LauncherRecentsView view, float v) { view.setTranslationFactor(v); } @Override public Float get(LauncherRecentsView view) { return view.mTranslationFactor; } }; private Bitmap mScrim; private Paint mFadePaint; private Shader mFadeShader; private Matrix mFadeMatrix; private boolean mScrimOnLeft; private float mTranslationFactor; public LauncherRecentsView(Context context) { this(context, null); } Loading Loading @@ -131,4 +151,21 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements Insett protected void onAllTasksRemoved() { mActivity.getStateManager().goToState(NORMAL); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); int width = right - left; setTranslationX(mTranslationFactor * (mIsRtl ? -width : width)); } public void setTranslationFactor(float translationFactor) { mTranslationFactor = translationFactor; setTranslationX(translationFactor * (mIsRtl ? -getWidth() : getWidth())); } public float getTranslationFactor() { return mTranslationFactor; } }