Merge "Adding spring animation when swiping up to home in 3P Launcher" into ub-launcher3-qt-r1-dev

This commit is contained in:
TreeHugger Robot
2019-07-17 21:41:18 +00:00
committed by Android (Google) Code Review
6 changed files with 198 additions and 128 deletions
@@ -19,12 +19,15 @@ import static android.os.VibrationEffect.EFFECT_CLICK;
import static android.os.VibrationEffect.createPredefined;
import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
import android.animation.Animator;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
@@ -32,6 +35,7 @@ import android.content.Intent;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -48,12 +52,19 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.inputconsumers.InputConsumer;
import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
import com.android.quickstep.views.RecentsView;
@@ -369,9 +380,117 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
return TaskView.getCurveScaleForInterpolation(interpolation);
}
/**
* Creates an animation that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
* @param homeAnimationFactory The home animation factory.
*/
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
mTransformParams.setProgress(startProgress), false /* launcherOnTop */));
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
final View floatingView = homeAnimationFactory.getFloatingView();
final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext.getResources());
if (isFloatingIconView) {
FloatingIconView fiv = (FloatingIconView) floatingView;
anim.addAnimatorListener(fiv);
fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
}
AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
// rounding at the end of the animation.
float startRadius = mClipAnimationHelper.getCurrentCornerRadius();
float endRadius = startRect.width() / 6f;
// We want the window alpha to be 0 once this threshold is met, so that the
// FolderIconView can be seen morphing into the icon shape.
final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
@Override
public void onUpdate(RectF currentRect, float progress) {
homeAnim.setPlayFraction(progress);
float alphaProgress = ACCEL_1_5.getInterpolation(progress);
float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0,
windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1);
mTransformParams.setProgress(progress)
.setCurrentRectAndTargetAlpha(currentRect, windowAlpha);
if (isFloatingIconView) {
mTransformParams.setCornerRadius(endRadius * progress + startRadius
* (1f - progress));
}
mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
false /* launcherOnTop */);
if (isFloatingIconView) {
((FloatingIconView) floatingView).update(currentRect, 1f, progress,
windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false);
}
}
@Override
public void onCancel() {
if (isFloatingIconView) {
((FloatingIconView) floatingView).fastFinish();
}
}
});
anim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
homeAnim.dispatchOnStart();
}
@Override
public void onAnimationSuccess(Animator animator) {
homeAnim.getAnimationPlayer().end();
}
});
return anim;
}
public interface Factory {
BaseSwipeUpHandler newHandler(RunningTaskInfo runningTask,
long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask);
}
protected interface RunningWindowAnim {
void end();
void cancel();
static RunningWindowAnim wrap(Animator animator) {
return new RunningWindowAnim() {
@Override
public void end() {
animator.end();
}
@Override
public void cancel() {
animator.cancel();
}
};
}
static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
return new RunningWindowAnim() {
@Override
public void end() {
rectFSpringAnim.end();
}
@Override
public void cancel() {
rectFSpringAnim.cancel();
}
};
}
}
}
@@ -151,16 +151,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
@NonNull
@Override
public RectF getWindowTargetRect() {
final int halfIconSize = dp.iconSizePx / 2;
final float targetCenterX = dp.availableWidthPx / 2f;
final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
if (canUseWorkspaceView) {
return iconLocation;
} else {
// Fallback to animate to center of screen.
return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
targetCenterX + halfIconSize, targetCenterY + halfIconSize);
return HomeAnimationFactory.getDefaultWindowTargetRect(dp);
}
}
@@ -18,7 +18,6 @@ package com.android.quickstep;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
@@ -27,7 +26,6 @@ import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
@@ -52,7 +50,6 @@ import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewTreeObserver.OnDrawListener;
@@ -68,13 +65,11 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.RaceConditionTracker;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
@@ -83,7 +78,6 @@ import com.android.quickstep.inputconsumers.InputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ClipAnimationHelper.TargetAlphaProvider;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.views.LiveTileOverlay;
import com.android.quickstep.views.RecentsView;
@@ -968,67 +962,15 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
* @param homeAnimationFactory The home animation factory.
*/
private RectFSpringAnim createWindowAnimationToHome(float startProgress,
@Override
protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
HomeAnimationFactory homeAnimationFactory) {
final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
final RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
mTransformParams.setProgress(startProgress), false /* launcherOnTop */));
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
final View floatingView = homeAnimationFactory.getFloatingView();
final boolean isFloatingIconView = floatingView instanceof FloatingIconView;
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mActivity.getResources());
if (isFloatingIconView) {
FloatingIconView fiv = (FloatingIconView) floatingView;
anim.addAnimatorListener(fiv);
fiv.setOnTargetChangeListener(anim::onTargetPositionChanged);
}
AnimatorPlaybackController homeAnim = homeAnimationFactory.createActivityAnimationToHome();
// End on a "round-enough" radius so that the shape reveal doesn't have to do too much
// rounding at the end of the animation.
float startRadius = mClipAnimationHelper.getCurrentCornerRadius();
float endRadius = startRect.width() / 6f;
// We want the window alpha to be 0 once this threshold is met, so that the
// FolderIconView can be seen morphing into the icon shape.
final float windowAlphaThreshold = isFloatingIconView ? 1f - SHAPE_PROGRESS_DURATION : 1f;
anim.addOnUpdateListener(new RectFSpringAnim.OnUpdateListener() {
@Override
public void onUpdate(RectF currentRect, float progress) {
homeAnim.setPlayFraction(progress);
float alphaProgress = ACCEL_1_5.getInterpolation(progress);
float windowAlpha = Utilities.boundToRange(Utilities.mapToRange(alphaProgress, 0,
windowAlphaThreshold, 1.5f, 0f, Interpolators.LINEAR), 0, 1);
mTransformParams.setProgress(progress)
.setCurrentRectAndTargetAlpha(currentRect, windowAlpha);
if (isFloatingIconView) {
mTransformParams.setCornerRadius(endRadius * progress + startRadius
* (1f - progress));
}
mClipAnimationHelper.applyTransform(targetSet, mTransformParams,
false /* launcherOnTop */);
if (isFloatingIconView) {
((FloatingIconView) floatingView).update(currentRect, 1f, progress,
windowAlphaThreshold, mClipAnimationHelper.getCurrentCornerRadius(), false);
}
updateSysUiFlags(Math.max(progress, mCurrentShift.value));
}
@Override
public void onCancel() {
if (isFloatingIconView) {
((FloatingIconView) floatingView).fastFinish();
}
}
});
RectFSpringAnim anim =
super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
anim.addOnUpdateListener((r, p) -> updateSysUiFlags(Math.max(p, mCurrentShift.value)));
anim.addAnimatorListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
homeAnim.dispatchOnStart();
if (mActivity != null) {
mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
}
@@ -1036,7 +978,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
@Override
public void onAnimationSuccess(Animator animator) {
homeAnim.getAnimationPlayer().end();
if (mRecentsView != null) {
mRecentsView.post(mRecentsView::resetTaskVisuals);
}
@@ -1270,38 +1211,4 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
return app.isNotInRecents
|| app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
}
private interface RunningWindowAnim {
void end();
void cancel();
static RunningWindowAnim wrap(Animator animator) {
return new RunningWindowAnim() {
@Override
public void end() {
animator.end();
}
@Override
public void cancel() {
animator.cancel();
}
};
}
static RunningWindowAnim wrap(RectFSpringAnim rectFSpringAnim) {
return new RunningWindowAnim() {
@Override
public void end() {
rectFSpringAnim.end();
}
@Override
public void cancel() {
rectFSpringAnim.cancel();
}
};
}
}
}
@@ -18,11 +18,12 @@ package com.android.quickstep.inputconsumers;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.HOME;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer.GestureEndTarget.RECENTS;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -31,10 +32,13 @@ import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Bundle;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseSwipeUpHandler;
import com.android.quickstep.MultiStateCallback;
@@ -44,6 +48,7 @@ import com.android.quickstep.RecentsModel;
import com.android.quickstep.SwipeSharedState;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.ObjectWrapper;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.SwipeAnimationTargetSet;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -102,10 +107,10 @@ public class FallbackNoButtonInputConsumer extends
private final boolean mRunningOverHome;
private final boolean mSwipeUpOverHome;
private final RunningTaskInfo mRunningTaskInfo;
private Animator mFinishAnimation;
private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
private RunningWindowAnim mFinishAnimation;
public FallbackNoButtonInputConsumer(Context context,
OverviewComponentObserver overviewComponentObserver,
@@ -226,6 +231,8 @@ public class FallbackNoButtonInputConsumer extends
@Override
public void updateFinalShift() {
mTransformParams.setProgress(mCurrentShift.value);
mRecentsAnimationWrapper.setWindowThresholdCrossed(!mInQuickSwitchMode
&& (mCurrentShift.value > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD));
if (mRecentsAnimationWrapper.targetSet != null) {
applyTransformUnchecked();
}
@@ -240,6 +247,7 @@ public class FallbackNoButtonInputConsumer extends
@Override
public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
mEndVelocityPxPerMs.set(0, velocity.y / 1000);
if (mInQuickSwitchMode) {
// For now set it to non-null, it will be reset before starting the animation
mEndTarget = LAST_TASK;
@@ -288,12 +296,14 @@ public class FallbackNoButtonInputConsumer extends
}
}
private void onHandlerInvalidated() {
mActivityInitListener.unregister();
if (mGestureEndCallback != null) {
mGestureEndCallback.run();
}
if (mFinishAnimation != null) {
mFinishAnimation.end();
}
}
private void onHandlerInvalidatedWithRecents() {
@@ -364,31 +374,39 @@ public class FallbackNoButtonInputConsumer extends
}
float endProgress = mEndTarget.mEndProgress;
long duration = (long) (mEndTarget.mDurationMultiplier *
Math.abs(endProgress - mCurrentShift.value));
if (mRecentsView != null) {
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
if (mCurrentShift.value != endProgress || mInQuickSwitchMode) {
AnimatorSet anim = new AnimatorSet();
anim.play(mLauncherAlpha.animateToValue(
mLauncherAlpha.value, mEndTarget.mLauncherAlpha));
anim.play(mCurrentShift.animateToValue(mCurrentShift.value, endProgress));
long duration = (long) (mEndTarget.mDurationMultiplier *
Math.abs(endProgress - mCurrentShift.value));
if (mRecentsView != null) {
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
anim.setDuration(duration);
anim.addListener(new AnimationSuccessListener() {
AnimationSuccessListener endListener = new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
finishAnimationTargetSetAnimationComplete();
mFinishAnimation = null;
}
});
anim.start();
mFinishAnimation = anim;
};
if (mEndTarget == HOME && !mRunningOverHome) {
RectFSpringAnim anim = createWindowAnimationToHome(mCurrentShift.value, duration);
anim.addAnimatorListener(endListener);
anim.start(mEndVelocityPxPerMs);
mFinishAnimation = RunningWindowAnim.wrap(anim);
} else {
AnimatorSet anim = new AnimatorSet();
anim.play(mLauncherAlpha.animateToValue(
mLauncherAlpha.value, mEndTarget.mLauncherAlpha));
anim.play(mCurrentShift.animateToValue(mCurrentShift.value, endProgress));
anim.setDuration(duration);
anim.addListener(endListener);
anim.start();
mFinishAnimation = RunningWindowAnim.wrap(anim);
}
} else {
finishAnimationTargetSetAnimationComplete();
}
@@ -412,4 +430,26 @@ public class FallbackNoButtonInputConsumer extends
mRecentsAnimationWrapper.setController(null);
setStateOnUiThread(STATE_HANDLER_INVALIDATED);
}
/**
* Creates an animation that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
*/
private RectFSpringAnim createWindowAnimationToHome(float startProgress, long duration) {
HomeAnimationFactory factory = new HomeAnimationFactory() {
@Override
public RectF getWindowTargetRect() {
return HomeAnimationFactory.getDefaultWindowTargetRect(mDp);
}
@Override
public AnimatorPlaybackController createActivityAnimationToHome() {
AnimatorSet anim = new AnimatorSet();
anim.play(mLauncherAlpha.animateToValue(mLauncherAlpha.value, 1));
anim.setDuration(duration);
return AnimatorPlaybackController.wrap(anim, duration);
}
};
return createWindowAnimationToHome(startProgress, factory);
}
}
@@ -237,6 +237,6 @@ public class RectFSpringAnim {
public interface OnUpdateListener {
void onUpdate(RectF currentRect, float progress);
void onCancel();
default void onCancel() { }
}
}
@@ -152,5 +152,15 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> {
default void playAtomicAnimation(float velocity) {
// No-op
}
static RectF getDefaultWindowTargetRect(DeviceProfile dp) {
final int halfIconSize = dp.iconSizePx / 2;
final float targetCenterX = dp.availableWidthPx / 2f;
final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
// Fallback to animate to center of screen.
return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
targetCenterX + halfIconSize, targetCenterY + halfIconSize);
}
}
}