Merge "Enabling springs for start dismiss animation" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 19a2bae..e500f0f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -171,7 +171,8 @@
}
}
anim.setDuration(accuracy);
- mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy, this::clearState);
+ mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy)
+ .setOnCancelRunnable(this::clearState);
}
private void clearState() {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 715529e..52625dc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -393,7 +393,7 @@
xOverviewAnim.setFloatValues(startXProgress, endXProgress);
xOverviewAnim.setDuration(xDuration)
.setInterpolator(scrollInterpolatorForVelocity(velocity.x));
- mXOverviewAnim.dispatchOnStartWithVelocity(endXProgress, velocity.x);
+ mXOverviewAnim.dispatchOnStart();
boolean flingUpToNormal = verticalFling && velocity.y < 0 && targetState == NORMAL;
@@ -414,7 +414,7 @@
ValueAnimator yOverviewAnim = mYOverviewAnim.getAnimationPlayer();
yOverviewAnim.setFloatValues(startYProgress, endYProgress);
yOverviewAnim.setDuration(yDuration);
- mYOverviewAnim.dispatchOnStartWithVelocity(endYProgress, velocity.y);
+ mYOverviewAnim.dispatchOnStart();
ValueAnimator nonOverviewAnim = mNonOverviewAnim.getAnimationPlayer();
if (flingUpToNormal && !mIsHomeScreenVisible) {
@@ -436,8 +436,7 @@
float startProgress = mNonOverviewAnim.getProgressFraction();
float endProgress = canceled ? 0 : 1;
nonOverviewAnim.setFloatValues(startProgress, endProgress);
- mNonOverviewAnim.dispatchOnStartWithVelocity(endProgress,
- horizontalFling ? velocity.x : velocity.y);
+ mNonOverviewAnim.dispatchOnStart();
}
nonOverviewAnim.setDuration(Math.max(xDuration, yDuration));
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
index 1f5228a..845699a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -22,7 +22,7 @@
import android.view.animation.Interpolator;
import com.android.launcher3.Launcher;
-import com.android.launcher3.util.PendingAnimation;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index e0532ac..cc58fcf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -16,17 +16,13 @@
package com.android.launcher3.uioverrides.touchcontrollers;
import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.view.MotionEvent;
import com.android.launcher3.AbstractFloatingView;
@@ -35,12 +31,12 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.FlingBlockCheck;
-import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.SysUINavigationMode;
@@ -218,8 +214,8 @@
if (mCurrentAnimation != null) {
mCurrentAnimation.setOnCancelRunnable(null);
}
- mCurrentAnimation = AnimatorPlaybackController.wrap(
- mPendingAnimation.anim, maxDuration, this::clearState);
+ mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation, maxDuration)
+ .setOnCancelRunnable(this::clearState);
onUserControlledAnimationCreated(mCurrentAnimation);
mCurrentAnimation.getTarget().addListener(this);
mCurrentAnimation.dispatchOnStart();
@@ -288,26 +284,16 @@
animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
}
- float nextFrameProgress = Utilities.boundToRange(progress
- + velocity * getSingleFrameMs(mActivity) / Math.abs(mEndDisplacement), 0f, 1f);
-
mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
-
- ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
- anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
- anim.setDuration(animationDuration);
- anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- anim.addUpdateListener(valueAnimator -> {
+ mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> {
if (mRecentsView.getCurrentPage() != 0 || mCurrentAnimationIsGoingUp) {
mRecentsView.redrawLiveTile(true);
}
});
}
- if (UNSTABLE_SPRINGS.get()) {
- mCurrentAnimation.dispatchOnStartWithVelocity(goingToEnd ? 1f : 0f, velocity);
- }
- anim.start();
+ mCurrentAnimation.startWithVelocity(mActivity, goingToEnd,
+ velocity, mEndDisplacement, animationDuration);
}
private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 4b6241c..3328abc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -1000,7 +1000,7 @@
mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
if (UNSTABLE_SPRINGS.get()) {
- mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
+ mLauncherTransitionController.dispatchOnStart();
}
mLauncherTransitionController.getAnimationPlayer().start();
mHasLauncherTransitionControllerStarted = true;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 62d6604..7eb3274 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -16,12 +16,9 @@
package com.android.quickstep.views;
-import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS;
-
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
@@ -32,7 +29,6 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.uioverrides.BackgroundBlurController.BACKGROUND_BLUR;
import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
@@ -46,7 +42,6 @@
import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
@@ -96,8 +91,10 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.anim.PendingAnimation.EndState;
import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
@@ -111,7 +108,6 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.OverScroller;
-import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.RecentsAnimationController;
@@ -623,7 +619,7 @@
protected void applyLoadPlan(ArrayList<Task> tasks) {
if (mPendingAnimation != null) {
- mPendingAnimation.addEndListener((onEndListener) -> applyLoadPlan(tasks));
+ mPendingAnimation.addEndListener((endState) -> applyLoadPlan(tasks));
return;
}
@@ -1210,37 +1206,29 @@
}
}
- private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) {
- addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
+ private void addDismissedTaskAnimations(View taskView, long duration, PendingAnimation anim) {
+ anim.add(ObjectAnimator.ofFloat(taskView, ALPHA, 0).setDuration(duration), ACCEL_2);
FloatProperty<View> secondaryViewTranslate =
mOrientationHandler.getSecondaryViewTranslate();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
int verticalFactor = mOrientationHandler.getTaskDismissDirectionFactor();
- if (UNSTABLE_SPRINGS.get() && taskView instanceof TaskView) {
- ResourceProvider rp = DynamicResource.provider(mActivity);
- float dampingRatio = rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio);
- float stiffness = rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness);
- addAnim(new SpringObjectAnimator<>(taskView, secondaryViewTranslate,
- MIN_VISIBLE_CHANGE_PIXELS, dampingRatio,
- stiffness, 0, verticalFactor * secondaryTaskDimension),
- duration, LINEAR, anim);
- } else {
- addAnim(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
- verticalFactor * secondaryTaskDimension), duration, LINEAR, anim);
- }
+ ResourceProvider rp = DynamicResource.provider(mActivity);
+ SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START)
+ .setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio))
+ .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness));
+
+ anim.add(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
+ verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp);
}
- private void removeTask(Task task, int index, PendingAnimation.OnEndListener onEndListener,
- boolean shouldLog) {
+ private void removeTask(Task task, int index, EndState endState) {
if (task != null) {
ActivityManagerWrapper.getInstance().removeTask(task.key.id);
- if (shouldLog) {
- ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
- mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
- onEndListener.logAction, Direction.UP, index, componentKey);
- mActivity.getStatsLogManager().logTaskDismiss(this, componentKey);
- }
+ ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
+ mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
+ endState.logAction, Direction.UP, index, componentKey);
+ mActivity.getStatsLogManager().logTaskDismiss(this, componentKey);
}
}
@@ -1249,12 +1237,11 @@
if (mPendingAnimation != null) {
mPendingAnimation.finish(false, Touch.SWIPE);
}
- AnimatorSet anim = new AnimatorSet();
- PendingAnimation pendingAnimation = new PendingAnimation(anim);
+ PendingAnimation anim = new PendingAnimation();
int count = getPageCount();
if (count == 0) {
- return pendingAnimation;
+ return anim;
}
int[] oldScroll = new int[count];
@@ -1273,7 +1260,7 @@
View child = getChildAt(i);
if (child == taskView) {
if (animateTaskView) {
- addDismissedTaskAnimations(taskView, anim, duration);
+ addDismissedTaskAnimations(taskView, duration, anim);
}
} else {
// If we just take newScroll - oldScroll, everything to the right of dragged task
@@ -1297,20 +1284,15 @@
}
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
- if (UNSTABLE_SPRINGS.get() && child instanceof TaskView) {
- ResourceProvider rp = DynamicResource.provider(mActivity);
- float damping = rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio);
- float stiffness = rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness);
+ Property translationProperty = mOrientationHandler.getPrimaryViewTranslate();
- addAnim(new SpringObjectAnimator<>(child, VIEW_TRANSLATE_X,
- MIN_VISIBLE_CHANGE_PIXELS, damping,
- stiffness, 0, scrollDiff), duration, ACCEL, anim);
- } else {
- Property translationProperty = mOrientationHandler.getPrimaryViewTranslate();
- addAnim(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff),
- duration, ACCEL, anim);
- }
-
+ ResourceProvider rp = DynamicResource.provider(mActivity);
+ SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END)
+ .setDampingRatio(
+ rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio))
+ .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness));
+ anim.add(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff)
+ .setDuration(duration), ACCEL, sp);
needsCurveUpdates = true;
}
}
@@ -1319,7 +1301,7 @@
if (needsCurveUpdates) {
ValueAnimator va = ValueAnimator.ofFloat(0, 1);
va.addUpdateListener((a) -> updateCurveProperties());
- anim.play(va);
+ anim.add(va);
}
// Add a tiny bit of translation Z, so that it draws on top of other views
@@ -1327,22 +1309,22 @@
taskView.setTranslationZ(0.1f);
}
- mPendingAnimation = pendingAnimation;
- mPendingAnimation.addEndListener(new Consumer<PendingAnimation.OnEndListener>() {
+ mPendingAnimation = anim;
+ mPendingAnimation.addEndListener(new Consumer<EndState>() {
@Override
- public void accept(PendingAnimation.OnEndListener onEndListener) {
+ public void accept(EndState endState) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
- taskView.isRunningTask() && onEndListener.isSuccess) {
- finishRecentsAnimation(true /* toHome */, () -> onEnd(onEndListener));
+ taskView.isRunningTask() && endState.isSuccess) {
+ finishRecentsAnimation(true /* toHome */, () -> onEnd(endState));
} else {
- onEnd(onEndListener);
+ onEnd(endState);
}
}
- private void onEnd(PendingAnimation.OnEndListener onEndListener) {
- if (onEndListener.isSuccess) {
+ private void onEnd(EndState endState) {
+ if (endState.isSuccess) {
if (shouldRemoveTask) {
- removeTask(taskView.getTask(), draggedIndex, onEndListener, true);
+ removeTask(taskView.getTask(), draggedIndex, endState);
}
int pageToSnapTo = mCurrentPage;
@@ -1364,24 +1346,23 @@
mPendingAnimation = null;
}
});
- return pendingAnimation;
+ return anim;
}
public PendingAnimation createAllTasksDismissAnimation(long duration) {
if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
throw new IllegalStateException("Another pending animation is still running");
}
- AnimatorSet anim = new AnimatorSet();
- PendingAnimation pendingAnimation = new PendingAnimation(anim);
+ PendingAnimation anim = new PendingAnimation();
int count = getTaskViewCount();
for (int i = 0; i < count; i++) {
- addDismissedTaskAnimations(getTaskViewAt(i), anim, duration);
+ addDismissedTaskAnimations(getTaskViewAt(i), duration, anim);
}
- mPendingAnimation = pendingAnimation;
- mPendingAnimation.addEndListener((onEndListener) -> {
- if (onEndListener.isSuccess) {
+ mPendingAnimation = anim;
+ mPendingAnimation.addEndListener((endState) -> {
+ if (endState.isSuccess) {
// Remove all the task views now
ActivityManagerWrapper.getInstance().removeAllRecentTasks();
removeTasksViewsAndClearAllButton();
@@ -1389,13 +1370,7 @@
}
mPendingAnimation = null;
});
- return pendingAnimation;
- }
-
- private static void addAnim(Animator anim, long duration,
- TimeInterpolator interpolator, AnimatorSet set) {
- anim.setDuration(duration).setInterpolator(interpolator);
- set.play(anim);
+ return anim;
}
private boolean snapToPageRelative(int pageCount, int delta, boolean cycle) {
@@ -1412,8 +1387,8 @@
}
private void runDismissAnimation(PendingAnimation pendingAnim) {
- AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(
- pendingAnim.anim, DISMISS_TASK_DURATION);
+ AnimatorPlaybackController controller =
+ AnimatorPlaybackController.wrap(pendingAnim, DISMISS_TASK_DURATION);
controller.dispatchOnStart();
controller.setEndAction(() -> pendingAnim.finish(true, Touch.SWIPE));
controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
@@ -1738,7 +1713,7 @@
int count = getTaskViewCount();
if (count == 0) {
- return new PendingAnimation(new AnimatorSet());
+ return new PendingAnimation();
}
int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
@@ -1780,13 +1755,12 @@
anim.setDuration(duration)
.setInterpolator(interpolator);
- Consumer<Boolean> onTaskLaunchFinish = this::onTaskLaunched;
-
- mPendingAnimation = new PendingAnimation(anim);
- mPendingAnimation.addEndListener((onEndListener) -> {
- if (onEndListener.isSuccess) {
+ mPendingAnimation = new PendingAnimation();
+ mPendingAnimation.add(anim);
+ mPendingAnimation.addEndListener((endState) -> {
+ if (endState.isSuccess) {
Consumer<Boolean> onLaunchResult = (result) -> {
- onTaskLaunchFinish.accept(result);
+ onTaskLaunched(result);
if (!result) {
tv.notifyTaskLaunchFailed(TAG);
}
@@ -1795,11 +1769,11 @@
Task task = tv.getTask();
if (task != null) {
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
- onEndListener.logAction, Direction.DOWN, indexOfChild(tv),
+ endState.logAction, Direction.DOWN, indexOfChild(tv),
TaskUtils.getLaunchComponentKeyForTask(task.key));
}
} else {
- onTaskLaunchFinish.accept(false);
+ onTaskLaunched(false);
}
mPendingAnimation = null;
});
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index e09e01f..abc037e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -58,6 +58,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.states.RotationHelper;
@@ -67,7 +68,6 @@
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
@@ -278,8 +278,8 @@
public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
final PendingAnimation pendingAnimation = getRecentsView().createTaskLaunchAnimation(
this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
- AnimatorPlaybackController currentAnimation = AnimatorPlaybackController.wrap(
- pendingAnimation.anim, RECENTS_LAUNCH_DURATION);
+ AnimatorPlaybackController currentAnimation =
+ AnimatorPlaybackController.wrap(pendingAnimation, RECENTS_LAUNCH_DURATION);
currentAnimation.setEndAction(() -> {
pendingAnimation.finish(true, Touch.SWIPE);
launchTask(false);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index fe830d2..95e38e3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -87,8 +87,7 @@
protected boolean canInterceptTouch(MotionEvent ev) {
if (mCurrentAnimation != null) {
if (mFinishFastOnSecondTouch) {
- // TODO: Animate to finish instead.
- mCurrentAnimation.skipToEnd();
+ mCurrentAnimation.getAnimationPlayer().end();
}
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
@@ -233,8 +232,8 @@
cancelPendingAnim();
clearState();
};
- mCurrentAnimation = AnimatorPlaybackController.wrap(
- mPendingAnimation.anim, maxAccuracy, onCancelRunnable);
+ mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation, maxAccuracy)
+ .setOnCancelRunnable(onCancelRunnable);
mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
mLauncher.getDeviceProfile());
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 9f25729..a8f2025 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -376,8 +376,8 @@
mConfig.animComponents = animComponents;
mConfig.duration = duration;
mConfig.playbackController = AnimatorPlaybackController.wrap(
- createAnimationToNewWorkspaceInternal(state, builder, null), duration,
- onCancelRunnable);
+ createAnimationToNewWorkspaceInternal(state, builder, null), duration)
+ .setOnCancelRunnable(onCancelRunnable);
return mConfig.playbackController;
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 2179162..32c65a3 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -12,7 +12,6 @@
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
@@ -31,11 +30,8 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.anim.SpringObjectAnimator;
-import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
-import com.android.systemui.plugins.ResourceProvider;
/**
* Handles AllApps view transition.
@@ -185,14 +181,6 @@
}
public Animator createSpringAnimation(float... progressValues) {
- if (UNSTABLE_SPRINGS.get()) {
- ResourceProvider rp = DynamicResource.provider(mLauncher);
- float damping = rp.getFloat(R.dimen.all_apps_spring_damping_ratio);
- float stiffness = rp.getFloat(R.dimen.all_apps_spring_stiffness);
-
- return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange,
- damping, stiffness, progressValues);
- }
return ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, progressValues);
}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 1c277ab..1fc21fd 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -16,7 +16,9 @@
package com.android.launcher3.anim;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
@@ -24,17 +26,18 @@
import android.animation.AnimatorSet;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.util.Log;
+import android.content.Context;
+import android.util.FloatProperty;
import androidx.annotation.Nullable;
-import androidx.dynamicanimation.animation.DynamicAnimation;
-import androidx.dynamicanimation.animation.SpringAnimation;
+
+import com.android.launcher3.Utilities;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
* Helper class to control the playback of an {@link AnimatorSet}, with custom interpolators
@@ -43,14 +46,7 @@
* Note: The implementation does not support start delays on child animations or
* sequential playbacks.
*/
-public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
-
- private static final String TAG = "AnimatorPlaybackCtrler";
- private static boolean DEBUG = false;
-
- public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
- return wrap(anim, duration, null);
- }
+public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
/**
* Creates an animation controller for the provided animation.
@@ -58,20 +54,41 @@
* needs to be larger than the total number of pixels so that we don't have jittering due
* to float (animation-fraction * total duration) to int conversion.
*/
- public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration,
- Runnable onCancelRunnable) {
-
+ public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
/**
* TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed.
*/
- return new AnimatorPlaybackControllerVL(anim, duration, onCancelRunnable);
+ ArrayList<Holder> childAnims = new ArrayList<>();
+ addAnimationHoldersRecur(anim, SpringProperty.DEFAULT, childAnims);
+
+ return new AnimatorPlaybackController(anim, duration, childAnims);
}
+ public static AnimatorPlaybackController wrap(PendingAnimation anim, long duration) {
+ /**
+ * TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed.
+ */
+ return new AnimatorPlaybackController(anim.anim, duration, anim.animHolders);
+ }
+
+ private static final FloatProperty<ValueAnimator> CURRENT_PLAY_TIME =
+ new FloatProperty<ValueAnimator>("current-play-time") {
+ @Override
+ public void setValue(ValueAnimator animator, float v) {
+ animator.setCurrentPlayTime((long) v);
+ }
+
+ @Override
+ public Float get(ValueAnimator animator) {
+ return (float) animator.getCurrentPlayTime();
+ }
+ };
+
private final ValueAnimator mAnimationPlayer;
private final long mDuration;
- protected final AnimatorSet mAnim;
- private Set<SpringAnimation> mSprings;
+ private final AnimatorSet mAnim;
+ private final Holder[] mChildAnimations;
protected float mCurrentFraction;
private Runnable mEndAction;
@@ -79,22 +96,14 @@
protected boolean mTargetCancelled = false;
protected Runnable mOnCancelRunnable;
- private OnAnimationEndDispatcher mEndListener;
- private DynamicAnimation.OnAnimationEndListener mSpringEndListener;
- // We need this variable to ensure the end listener is called immediately, otherwise we run into
- // issues where the callback interferes with the states of the swipe detector.
- private boolean mSkipToEnd = false;
-
- protected AnimatorPlaybackController(AnimatorSet anim, long duration,
- Runnable onCancelRunnable) {
+ private AnimatorPlaybackController(
+ AnimatorSet anim, long duration, ArrayList<Holder> childAnims) {
mAnim = anim;
mDuration = duration;
- mOnCancelRunnable = onCancelRunnable;
mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
mAnimationPlayer.setInterpolator(LINEAR);
- mEndListener = new OnAnimationEndDispatcher();
- mAnimationPlayer.addListener(mEndListener);
+ mAnimationPlayer.addListener(new OnAnimationEndDispatcher());
mAnimationPlayer.addUpdateListener(this);
mAnim.addListener(new AnimatorListenerAdapter() {
@@ -119,14 +128,7 @@
}
});
- mSprings = new HashSet<>();
- mSpringEndListener = (animation, canceled, value, velocity1) -> {
- if (canceled) {
- mEndListener.onAnimationCancel(mAnimationPlayer);
- } else {
- mEndListener.onAnimationEnd(mAnimationPlayer);
- }
- };
+ mChildAnimations = childAnims.toArray(new Holder[childAnims.size()]);
}
public AnimatorSet getTarget() {
@@ -160,9 +162,68 @@
}
/**
+ * Starts playing the animation with the provided velocity optionally playing any
+ * physics based animations
+ */
+ public void startWithVelocity(Context context, boolean goingToEnd,
+ float velocity, float scale, long animationDuration) {
+ float scaleInverse = 1 / Math.abs(scale);
+ float scaledVelocity = velocity * scaleInverse;
+
+ float nextFrameProgress = Utilities.boundToRange(getProgressFraction()
+ + scaledVelocity * getSingleFrameMs(context), 0f, 1f);
+
+ // Update setters for spring
+ int springFlag = goingToEnd
+ ? SpringProperty.FLAG_CAN_SPRING_ON_END
+ : SpringProperty.FLAG_CAN_SPRING_ON_START;
+
+ long springDuration = animationDuration;
+ for (Holder h : mChildAnimations) {
+ if ((h.springProperty.flags & springFlag) != 0) {
+ SpringAnimationBuilder s = new SpringAnimationBuilder(h.anim, CURRENT_PLAY_TIME)
+ .setStartValue(clampDuration(mCurrentFraction))
+ .setEndValue(goingToEnd ? h.anim.getDuration() : 0)
+ .setStartVelocity(scaledVelocity * h.anim.getDuration())
+ .setMinimumVisibleChange(scaleInverse)
+ .setDampingRatio(h.springProperty.mDampingRatio)
+ .setStiffness(h.springProperty.mStiffness);
+
+ long expectedDurationL = s.build(context).getDuration();
+ springDuration = Math.max(expectedDurationL, springDuration);
+
+ float expectedDuration = expectedDurationL;
+ h.setter = (a, l) ->
+ s.setValue(a, mAnimationPlayer.getCurrentPlayTime() / expectedDuration);
+ h.anim.setInterpolator(LINEAR);
+ }
+ }
+
+ mAnimationPlayer.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
+
+ if (springDuration <= animationDuration) {
+ mAnimationPlayer.setDuration(animationDuration);
+ mAnimationPlayer.setInterpolator(scrollInterpolatorForVelocity(velocity));
+ } else {
+ // Since spring requires more time to run, we let the other animations play with
+ // current time and interpolation and by clamping the duration.
+ mAnimationPlayer.setDuration(springDuration);
+
+ float cutOff = animationDuration / (float) springDuration;
+ mAnimationPlayer.setInterpolator(
+ clampToProgress(scrollInterpolatorForVelocity(velocity), 0, cutOff));
+ }
+ mAnimationPlayer.start();
+ }
+
+ /**
* Pauses the currently playing animation.
*/
public void pause() {
+ // Reset property setters
+ for (Holder h : mChildAnimations) {
+ h.reset();
+ }
mAnimationPlayer.cancel();
}
@@ -176,7 +237,18 @@
/**
* Sets the current animation position and updates all the child animators accordingly.
*/
- public abstract void setPlayFraction(float fraction);
+ public void setPlayFraction(float fraction) {
+ mCurrentFraction = fraction;
+ // Let the animator report the progress but don't apply the progress to child
+ // animations if it has been cancelled.
+ if (mTargetCancelled) {
+ return;
+ }
+ long playPos = clampDuration(fraction);
+ for (Holder holder : mChildAnimations) {
+ holder.setter.set(holder.anim, playPos);
+ }
+ }
public float getProgressFraction() {
return mCurrentFraction;
@@ -208,49 +280,6 @@
}
}
- /**
- * Starts playback and sets the spring.
- */
- public void dispatchOnStartWithVelocity(float end, float velocity) {
- if (!QUICKSTEP_SPRINGS.get()) {
- dispatchOnStart();
- return;
- }
-
- if (DEBUG) Log.d(TAG, "dispatchOnStartWithVelocity#end=" + end + ", velocity=" + velocity);
-
- for (Animator a : mAnim.getChildAnimations()) {
- if (a instanceof SpringObjectAnimator) {
- if (DEBUG) Log.d(TAG, "Found springAnimator=" + a);
- SpringObjectAnimator springAnimator = (SpringObjectAnimator) a;
- mSprings.add(springAnimator.getSpring());
- springAnimator.startSpring(end, velocity, mSpringEndListener);
- }
- }
-
- dispatchOnStart();
- }
-
- public void dispatchOnStart() {
- dispatchOnStartRecursively(mAnim);
- }
-
- private void dispatchOnStartRecursively(Animator animator) {
- List<AnimatorListener> listeners = animator instanceof SpringObjectAnimator
- ? nonNullList(((SpringObjectAnimator) animator).getObjectAnimatorListeners())
- : nonNullList(animator.getListeners());
-
- for (AnimatorListener l : listeners) {
- l.onAnimationStart(animator);
- }
-
- if (animator instanceof AnimatorSet) {
- for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
- dispatchOnStartRecursively(anim);
- }
- }
- }
-
/** @see #dispatchOnCancelWithoutCancelRunnable(Runnable) */
public void dispatchOnCancelWithoutCancelRunnable() {
dispatchOnCancelWithoutCancelRunnable(null);
@@ -272,115 +301,47 @@
setOnCancelRunnable(onCancel);
}
- public void dispatchOnCancel() {
- dispatchOnCancelRecursively(mAnim);
+
+ public AnimatorPlaybackController setOnCancelRunnable(Runnable runnable) {
+ mOnCancelRunnable = runnable;
+ return this;
}
- private void dispatchOnCancelRecursively(Animator animator) {
- for (AnimatorListener l : nonNullList(animator.getListeners())) {
- l.onAnimationCancel(animator);
- }
+ public void dispatchOnStart() {
+ callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationStart);
+ }
- if (animator instanceof AnimatorSet) {
- for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
- dispatchOnCancelRecursively(anim);
- }
- }
+ public void dispatchOnCancel() {
+ callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationCancel);
}
public void dispatchSetInterpolator(TimeInterpolator interpolator) {
- dispatchSetInterpolatorRecursively(mAnim, interpolator);
+ callAnimatorCommandRecursively(mAnim, a -> a.setInterpolator(interpolator));
}
- private void dispatchSetInterpolatorRecursively(Animator anim, TimeInterpolator interpolator) {
- anim.setInterpolator(interpolator);
+ private static void callListenerCommandRecursively(
+ Animator anim, BiConsumer<AnimatorListener, Animator> command) {
+ callAnimatorCommandRecursively(anim, a-> {
+ for (AnimatorListener l : nonNullList(a.getListeners())) {
+ command.accept(l, a);
+ }
+ });
+ }
+
+ private static void callAnimatorCommandRecursively(Animator anim, Consumer<Animator> command) {
+ command.accept(anim);
if (anim instanceof AnimatorSet) {
for (Animator child : nonNullList(((AnimatorSet) anim).getChildAnimations())) {
- dispatchSetInterpolatorRecursively(child, interpolator);
+ callAnimatorCommandRecursively(child, command);
}
}
}
- public void setOnCancelRunnable(Runnable runnable) {
- mOnCancelRunnable = runnable;
- }
-
- public void skipToEnd() {
- mSkipToEnd = true;
- for (SpringAnimation spring : mSprings) {
- if (spring.canSkipToEnd()) {
- spring.skipToEnd();
- }
- }
- mAnimationPlayer.end();
- mSkipToEnd = false;
- }
-
- public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController {
-
- private final ValueAnimator[] mChildAnimations;
-
- private AnimatorPlaybackControllerVL(AnimatorSet anim, long duration,
- Runnable onCancelRunnable) {
- super(anim, duration, onCancelRunnable);
-
- // Build animation list
- ArrayList<ValueAnimator> childAnims = new ArrayList<>();
- getAnimationsRecur(mAnim, childAnims);
- mChildAnimations = childAnims.toArray(new ValueAnimator[childAnims.size()]);
- }
-
- private void getAnimationsRecur(AnimatorSet anim, ArrayList<ValueAnimator> out) {
- long forceDuration = anim.getDuration();
- TimeInterpolator forceInterpolator = anim.getInterpolator();
- for (Animator child : anim.getChildAnimations()) {
- if (forceDuration > 0) {
- child.setDuration(forceDuration);
- }
- if (forceInterpolator != null) {
- child.setInterpolator(forceInterpolator);
- }
- if (child instanceof ValueAnimator) {
- out.add((ValueAnimator) child);
- } else if (child instanceof AnimatorSet) {
- getAnimationsRecur((AnimatorSet) child, out);
- } else {
- throw new RuntimeException("Unknown animation type " + child);
- }
- }
- }
-
- @Override
- public void setPlayFraction(float fraction) {
- mCurrentFraction = fraction;
- // Let the animator report the progress but don't apply the progress to child
- // animations if it has been cancelled.
- if (mTargetCancelled) {
- return;
- }
- long playPos = clampDuration(fraction);
- for (ValueAnimator anim : mChildAnimations) {
- anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration()));
- }
- }
- }
-
- private boolean isAnySpringRunning() {
- for (SpringAnimation spring : mSprings) {
- if (spring.isRunning()) {
- return true;
- }
- }
- return false;
- }
-
/**
* Only dispatches the on end actions once the animator and all springs have completed running.
*/
private class OnAnimationEndDispatcher extends AnimationSuccessListener {
- boolean mAnimatorDone = false;
- boolean mSpringsDone = false;
boolean mDispatched = false;
@Override
@@ -391,39 +352,76 @@
@Override
public void onAnimationSuccess(Animator animator) {
- if (mSprings.isEmpty()) {
- mSpringsDone = mAnimatorDone = true;
- }
- if (isAnySpringRunning()) {
- mAnimatorDone = true;
- } else {
- mSpringsDone = true;
- }
-
// We wait for the spring (if any) to finish running before completing the end callback.
- if (!mDispatched && (mSkipToEnd || (mAnimatorDone && mSpringsDone))) {
- dispatchOnEndRecursively(mAnim);
+ if (!mDispatched) {
+ callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationEnd);
if (mEndAction != null) {
mEndAction.run();
}
mDispatched = true;
}
}
-
- private void dispatchOnEndRecursively(Animator animator) {
- for (AnimatorListener l : nonNullList(animator.getListeners())) {
- l.onAnimationEnd(animator);
- }
-
- if (animator instanceof AnimatorSet) {
- for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
- dispatchOnEndRecursively(anim);
- }
- }
- }
}
private static <T> List<T> nonNullList(ArrayList<T> list) {
return list == null ? Collections.emptyList() : list;
}
+
+ /**
+ * Interface for setting position of value animator
+ */
+ private interface PositionSetter {
+
+ PositionSetter DEFAULT = (anim, playPos) ->
+ anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration()));
+
+ void set(ValueAnimator anim, long position);
+ }
+
+ /**
+ * Holder class for various child animations
+ */
+ static class Holder {
+
+ public final ValueAnimator anim;
+
+ public final SpringProperty springProperty;
+
+ public final TimeInterpolator interpolator;
+
+ public PositionSetter setter;
+
+ Holder(Animator anim, SpringProperty springProperty) {
+ this.anim = (ValueAnimator) anim;
+ this.springProperty = springProperty;
+ this.interpolator = this.anim.getInterpolator();
+ this.setter = PositionSetter.DEFAULT;
+ }
+
+ public void reset() {
+ anim.setInterpolator(interpolator);
+ setter = PositionSetter.DEFAULT;
+ }
+ }
+
+ static void addAnimationHoldersRecur(
+ Animator anim, SpringProperty springProperty, ArrayList<Holder> out) {
+ long forceDuration = anim.getDuration();
+ TimeInterpolator forceInterpolator = anim.getInterpolator();
+ if (anim instanceof ValueAnimator) {
+ out.add(new Holder(anim, springProperty));
+ } else if (anim instanceof AnimatorSet) {
+ for (Animator child : ((AnimatorSet) anim).getChildAnimations()) {
+ if (forceDuration > 0) {
+ child.setDuration(forceDuration);
+ }
+ if (forceInterpolator != null) {
+ child.setInterpolator(forceInterpolator);
+ }
+ addAnimationHoldersRecur(child, springProperty, out);
+ }
+ } else {
+ throw new RuntimeException("Unknown animation type " + anim);
+ }
+ }
}
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
new file mode 100644
index 0000000..562d160
--- /dev/null
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 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.anim;
+
+import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.os.Build;
+
+import com.android.launcher3.anim.AnimatorPlaybackController.Holder;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Utility class to keep track of a running animation.
+ *
+ * This class allows attaching end callbacks to an animation is intended to be used with
+ * {@link com.android.launcher3.anim.AnimatorPlaybackController}, since in that case
+ * AnimationListeners are not properly dispatched.
+ *
+ * TODO: Find a better name
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class PendingAnimation {
+
+ private final ArrayList<Consumer<EndState>> mEndListeners = new ArrayList<>();
+
+ /** package private **/
+ final AnimatorSet anim = new AnimatorSet();
+ final ArrayList<Holder> animHolders = new ArrayList<>();
+
+ /**
+ * Utility method to sent an interpolator on an animation and add it to the list
+ */
+ public void add(Animator anim, TimeInterpolator interpolator) {
+ add(anim, interpolator, SpringProperty.DEFAULT);
+ }
+
+ public void add(Animator anim, TimeInterpolator interpolator, SpringProperty springProperty) {
+ anim.setInterpolator(interpolator);
+ add(anim, springProperty);
+ }
+
+ public void add(Animator anim) {
+ add(anim, SpringProperty.DEFAULT);
+ }
+
+ public void add(Animator a, SpringProperty springProperty) {
+ anim.play(a);
+ addAnimationHoldersRecur(a, springProperty, animHolders);
+ }
+
+ public void finish(boolean isSuccess, int logAction) {
+ for (Consumer<EndState> listeners : mEndListeners) {
+ listeners.accept(new EndState(isSuccess, logAction));
+ }
+ mEndListeners.clear();
+ }
+
+ public void addEndListener(Consumer<EndState> listener) {
+ mEndListeners.add(listener);
+ }
+
+ public static class EndState {
+ public boolean isSuccess;
+ public int logAction;
+
+ public EndState(boolean isSuccess, int logAction) {
+ this.isSuccess = isSuccess;
+ this.logAction = logAction;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
deleted file mode 100644
index 27b9c18..0000000
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2019 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.anim;
-
-import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
-
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.FloatProperty;
-import android.util.Log;
-
-import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener;
-import androidx.dynamicanimation.animation.SpringAnimation;
-import androidx.dynamicanimation.animation.SpringForce;
-
-import java.util.ArrayList;
-
-
-/**
- * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or
- * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet.
- */
-public class SpringObjectAnimator<T> extends ValueAnimator {
-
- private static final String TAG = "SpringObjectAnimator";
- private static boolean DEBUG = false;
-
- private ObjectAnimator mObjectAnimator;
- private float[] mValues;
-
- private SpringAnimation mSpring;
- private SpringProperty<T> mProperty;
-
- private ArrayList<AnimatorListener> mListeners;
- private boolean mSpringEnded = true;
- private boolean mAnimatorEnded = true;
- private boolean mEnded = true;
-
- public SpringObjectAnimator(T object, FloatProperty<T> property, float minimumVisibleChange,
- float damping, float stiffness, float... values) {
- mSpring = new SpringAnimation(object, createFloatPropertyCompat(property));
- mSpring.setMinimumVisibleChange(minimumVisibleChange);
- mSpring.setSpring(new SpringForce(0)
- .setDampingRatio(damping)
- .setStiffness(stiffness));
- mSpring.setStartVelocity(0.01f);
- mProperty = new SpringProperty<>(property, mSpring);
- mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values);
- mValues = values;
- mListeners = new ArrayList<>();
- setFloatValues(values);
-
- // We use this listener and track mListeners so that we can sync the animator and spring
- // listeners.
- mObjectAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mAnimatorEnded = false;
- mEnded = false;
- for (AnimatorListener l : mListeners) {
- l.onAnimationStart(animation);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimatorEnded = true;
- tryEnding();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- for (AnimatorListener l : mListeners) {
- l.onAnimationCancel(animation);
- }
- mSpring.cancel();
- }
- });
-
- mSpring.addUpdateListener((animation, value, velocity) -> {
- mSpringEnded = false;
- mEnded = false;
- });
- mSpring.addEndListener((animation, canceled, value, velocity) -> {
- mSpringEnded = true;
- tryEnding();
- });
- }
-
- private void tryEnding() {
- if (DEBUG) {
- Log.d(TAG, "tryEnding#mAnimatorEnded=" + mAnimatorEnded + ", mSpringEnded="
- + mSpringEnded + ", mEnded=" + mEnded);
- }
-
- // If springs are disabled, ignore value of mSpringEnded
- if (mAnimatorEnded && (mSpringEnded || !QUICKSTEP_SPRINGS.get()) && !mEnded) {
- for (AnimatorListener l : mListeners) {
- l.onAnimationEnd(this);
- }
- mEnded = true;
- }
- }
-
- public SpringAnimation getSpring() {
- return mSpring;
- }
-
- /**
- * Initializes and sets up the spring to take over controlling the object.
- */
- public void startSpring(float end, float velocity, OnAnimationEndListener endListener) {
- // Cancel the spring so we can set new start velocity and final position. We need to remove
- // the listener since the spring is not actually ending.
- mSpring.removeEndListener(endListener);
- mSpring.cancel();
- mSpring.addEndListener(endListener);
-
- mProperty.switchToSpring();
-
- float startValue = end == 0 ? mValues[1] : mValues[0];
- float endValue = end == 0 ? mValues[0] : mValues[1];
-
- // Ensures that the velocity matches the direction of the values.
- velocity = Math.signum(endValue - startValue) * Math.abs(velocity);
- mSpring.setStartVelocity(velocity);
-
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- mSpring.animateToFinalPosition(endValue);
- }, getStartDelay());
- }
-
- @Override
- public void addListener(AnimatorListener listener) {
- mListeners.add(listener);
- }
-
- public ArrayList<AnimatorListener> getObjectAnimatorListeners() {
- return mObjectAnimator.getListeners();
- }
-
- @Override
- public ArrayList<AnimatorListener> getListeners() {
- return mListeners;
- }
-
- @Override
- public void removeAllListeners() {
- mListeners.clear();
- }
-
- @Override
- public void removeListener(AnimatorListener listener) {
- mListeners.remove(listener);
- }
-
- @Override
- public void addPauseListener(AnimatorPauseListener listener) {
- mObjectAnimator.addPauseListener(listener);
- }
-
- @Override
- public void cancel() {
- mObjectAnimator.cancel();
- mSpring.cancel();
- }
-
- @Override
- public void end() {
- mObjectAnimator.end();
- }
-
- @Override
- public long getDuration() {
- return mObjectAnimator.getDuration();
- }
-
- @Override
- public TimeInterpolator getInterpolator() {
- return mObjectAnimator.getInterpolator();
- }
-
- @Override
- public long getStartDelay() {
- return mObjectAnimator.getStartDelay();
- }
-
- @Override
- public long getTotalDuration() {
- return mObjectAnimator.getTotalDuration();
- }
-
- @Override
- public boolean isPaused() {
- return mObjectAnimator.isPaused();
- }
-
- @Override
- public boolean isRunning() {
- return mObjectAnimator.isRunning();
- }
-
- @Override
- public boolean isStarted() {
- return mObjectAnimator.isStarted();
- }
-
- @Override
- public void pause() {
- mObjectAnimator.pause();
- }
-
- @Override
- public void removePauseListener(AnimatorPauseListener listener) {
- mObjectAnimator.removePauseListener(listener);
- }
-
- @Override
- public void resume() {
- mObjectAnimator.resume();
- }
-
- @Override
- public ValueAnimator setDuration(long duration) {
- return mObjectAnimator.setDuration(duration);
- }
-
- @Override
- public void setInterpolator(TimeInterpolator value) {
- mObjectAnimator.setInterpolator(value);
- }
-
- @Override
- public void setStartDelay(long startDelay) {
- mObjectAnimator.setStartDelay(startDelay);
- }
-
- @Override
- public void setTarget(Object target) {
- mObjectAnimator.setTarget(target);
- }
-
- @Override
- public void start() {
- mObjectAnimator.start();
- }
-
- @Override
- public void setCurrentFraction(float fraction) {
- mObjectAnimator.setCurrentFraction(fraction);
- }
-
- @Override
- public void setCurrentPlayTime(long playTime) {
- mObjectAnimator.setCurrentPlayTime(playTime);
- }
-
- public static class SpringProperty<T> extends FloatProperty<T> {
-
- boolean useSpring = false;
- final FloatProperty<T> mProperty;
- final SpringAnimation mSpring;
-
- public SpringProperty(FloatProperty<T> property, SpringAnimation spring) {
- super(property.getName());
- mProperty = property;
- mSpring = spring;
- }
-
- public void switchToSpring() {
- useSpring = true;
- }
-
- @Override
- public Float get(T object) {
- return mProperty.get(object);
- }
-
- @Override
- public void setValue(T object, float progress) {
- if (useSpring) {
- mSpring.animateToFinalPosition(progress);
- } else {
- mProperty.setValue(object, progress);
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/anim/SpringProperty.java b/src/com/android/launcher3/anim/SpringProperty.java
new file mode 100644
index 0000000..caedd6c
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringProperty.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 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.anim;
+
+import androidx.dynamicanimation.animation.SpringForce;
+
+/**
+ * Utility class to store configurations for spring animation
+ */
+public class SpringProperty {
+
+ public static final SpringProperty DEFAULT = new SpringProperty();
+
+ // Play spring when the animation is going towards the end
+ public static final int FLAG_CAN_SPRING_ON_END = 1 << 0;
+ // Play spring when animation is going towards the start (in reverse direction)
+ public static final int FLAG_CAN_SPRING_ON_START = 1 << 1;
+
+ public final int flags;
+
+ float mDampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+ float mStiffness = SpringForce.STIFFNESS_MEDIUM;
+
+ public SpringProperty() {
+ this(0);
+ }
+
+ public SpringProperty(int flags) {
+ this.flags = flags;
+ }
+
+ public SpringProperty setDampingRatio(float dampingRatio) {
+ mDampingRatio = dampingRatio;
+ return this;
+ }
+
+ public SpringProperty setStiffness(float stiffness) {
+ mStiffness = stiffness;
+ return this;
+ }
+}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 34d69e9..4e98781 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -42,11 +42,11 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.FlingBlockCheck;
-import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.TouchController;
/**
@@ -434,7 +434,7 @@
maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
updateSwipeCompleteAnimation(anim, Math.max(duration, getRemainingAtomicDuration()),
targetState, velocity, fling);
- mCurrentAnimation.dispatchOnStartWithVelocity(endProgress, progressVelocity);
+ mCurrentAnimation.dispatchOnStart();
if (fling && targetState == LauncherState.ALL_APPS && !UNSTABLE_SPRINGS.get()) {
mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
}
diff --git a/src/com/android/launcher3/util/PendingAnimation.java b/src/com/android/launcher3/util/PendingAnimation.java
deleted file mode 100644
index 617a38b..0000000
--- a/src/com/android/launcher3/util/PendingAnimation.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2018 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.util;
-
-import android.animation.AnimatorSet;
-import android.annotation.TargetApi;
-import android.os.Build;
-
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Utility class to keep track of a running animation.
- *
- * This class allows attaching end callbacks to an animation is intended to be used with
- * {@link com.android.launcher3.anim.AnimatorPlaybackController}, since in that case
- * AnimationListeners are not properly dispatched.
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class PendingAnimation {
-
- private final ArrayList<Consumer<OnEndListener>> mEndListeners = new ArrayList<>();
-
- public final AnimatorSet anim;
-
- public PendingAnimation(AnimatorSet anim) {
- this.anim = anim;
- }
-
- public void finish(boolean isSuccess, int logAction) {
- for (Consumer<OnEndListener> listeners : mEndListeners) {
- listeners.accept(new OnEndListener(isSuccess, logAction));
- }
- mEndListeners.clear();
- }
-
- public void addEndListener(Consumer<OnEndListener> listener) {
- mEndListeners.add(listener);
- }
-
- public static class OnEndListener {
- public boolean isSuccess;
- public int logAction;
-
- public OnEndListener(boolean isSuccess, int logAction) {
- this.isSuccess = isSuccess;
- this.logAction = logAction;
- }
- }
-}