Merge "Enabling springs for start dismiss animation" into ub-launcher3-master
This commit is contained in:
committed by
Android (Google) Code Review
commit
a9bcd82554
+2
-1
@@ -171,7 +171,8 @@ public class NavBarToHomeTouchController implements TouchController,
|
||||
}
|
||||
}
|
||||
anim.setDuration(accuracy);
|
||||
mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy, this::clearState);
|
||||
mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy)
|
||||
.setOnCancelRunnable(this::clearState);
|
||||
}
|
||||
|
||||
private void clearState() {
|
||||
|
||||
+3
-4
@@ -393,7 +393,7 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
|
||||
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 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
|
||||
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 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
|
||||
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));
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import android.view.MotionEvent;
|
||||
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;
|
||||
|
||||
|
||||
+6
-20
@@ -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.LauncherAnimUtils;
|
||||
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 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
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 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
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) {
|
||||
|
||||
@@ -1000,7 +1000,7 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
|
||||
mLauncherTransitionController.getAnimationPlayer().setDuration(Math.max(0, duration));
|
||||
|
||||
if (UNSTABLE_SPRINGS.get()) {
|
||||
mLauncherTransitionController.dispatchOnStartWithVelocity(end, velocityPxPerMs.y);
|
||||
mLauncherTransitionController.dispatchOnStart();
|
||||
}
|
||||
mLauncherTransitionController.getAnimationPlayer().start();
|
||||
mHasLauncherTransitionControllerStarted = true;
|
||||
|
||||
@@ -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.FAST_OUT_SLOW_IN;
|
||||
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.AnimatorSet;
|
||||
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.R;
|
||||
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.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
|
||||
protected void applyLoadPlan(ArrayList<Task> tasks) {
|
||||
if (mPendingAnimation != null) {
|
||||
mPendingAnimation.addEndListener((onEndListener) -> applyLoadPlan(tasks));
|
||||
mPendingAnimation.addEndListener((endState) -> applyLoadPlan(tasks));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1210,37 +1206,29 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
Property translationProperty = mOrientationHandler.getPrimaryViewTranslate();
|
||||
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
}
|
||||
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
|
||||
int count = getTaskViewCount();
|
||||
if (count == 0) {
|
||||
return new PendingAnimation(new AnimatorSet());
|
||||
return new PendingAnimation();
|
||||
}
|
||||
|
||||
int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
|
||||
@@ -1780,13 +1755,12 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
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 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
||||
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;
|
||||
});
|
||||
|
||||
@@ -58,6 +58,7 @@ import com.android.launcher3.R;
|
||||
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.touch.PagedOrientationHandler;
|
||||
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 class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||
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);
|
||||
|
||||
+3
-4
@@ -87,8 +87,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
|
||||
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 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
|
||||
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());
|
||||
|
||||
@@ -376,8 +376,8 @@ public class LauncherStateManager {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRE
|
||||
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.R;
|
||||
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 class AllAppsTransitionController implements StateHandler, OnDeviceProfil
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.AnimatorListenerAdapter;
|
||||
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 @@ import java.util.Set;
|
||||
* 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 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
* 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 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
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 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
}
|
||||
});
|
||||
|
||||
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() {
|
||||
@@ -159,10 +161,69 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
mAnimationPlayer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
/**
|
||||
* 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 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
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 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -42,11 +42,11 @@ import com.android.launcher3.Utilities;
|
||||
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 @@ public abstract class AbstractStateChangeTouchController
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user