Animate AbstractSlideInView translation shift with PendingAnimation.
The PendingAnimation gives us more flexibility than the AnimatorSet.
Test: Manual
Bug: 289290185
Flag: No
Change-Id: I3156a659098c44ce9e6b20e5e79e88742add629a
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 5691ecf..db225be 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -158,11 +158,11 @@
}
private void animateOpen() {
- if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+ if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
return;
}
mIsOpen = true;
- setUpDefaultOpenAnimator().start();
+ setUpDefaultOpenAnimation().start();
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
index 3fe7359..9ee7b0e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsSlideInView.java
@@ -17,6 +17,7 @@
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import android.animation.Animator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
@@ -58,7 +59,7 @@
/** Opens the all apps view. */
void show(boolean animate) {
- if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+ if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
return;
}
mIsOpen = true;
@@ -66,10 +67,12 @@
mAllAppsCallbacks.onAllAppsTransitionStart(true);
if (animate) {
- setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, EMPHASIZED);
- mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(
+ setUpOpenAnimation(mAllAppsCallbacks.getOpenDuration());
+ Animator animator = mOpenCloseAnimation.getAnimationPlayer();
+ animator.setInterpolator(EMPHASIZED);
+ animator.addListener(AnimatorListeners.forEndCallback(
() -> mAllAppsCallbacks.onAllAppsTransitionEnd(true)));
- mOpenCloseAnimator.setDuration(mAllAppsCallbacks.getOpenDuration()).start();
+ animator.start();
} else {
mTranslationShift = TRANSLATION_SHIFT_OPENED;
mAllAppsCallbacks.onAllAppsTransitionEnd(true);
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index f69d299..30e0971 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -17,6 +17,7 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static com.android.app.animation.Interpolators.LINEAR;
import static com.android.app.animation.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
@@ -24,16 +25,14 @@
import static com.android.launcher3.allapps.AllAppsTransitionController.REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS;
import static com.android.launcher3.util.ScrollableLayoutManager.PREDICTIVE_BACK_MIN_SCALE;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
-import android.util.Property;
+import android.util.FloatProperty;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -51,6 +50,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
@@ -66,8 +67,8 @@
public abstract class AbstractSlideInView<T extends Context & ActivityContext>
extends AbstractFloatingView implements SingleAxisSwipeDetector.Listener {
- protected static final Property<AbstractSlideInView, Float> TRANSLATION_SHIFT =
- new Property<AbstractSlideInView, Float>(Float.class, "translationShift") {
+ protected static final FloatProperty<AbstractSlideInView<?>> TRANSLATION_SHIFT =
+ new FloatProperty<>("translationShift") {
@Override
public Float get(AbstractSlideInView view) {
@@ -75,31 +76,54 @@
}
@Override
- public void set(AbstractSlideInView view, Float value) {
+ public void setValue(AbstractSlideInView view, float value) {
view.setTranslationShift(value);
}
};
protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
protected static final float TRANSLATION_SHIFT_OPENED = 0f;
private static final float VIEW_NO_SCALE = 1f;
- private static final int NO_DURATION = -1;
+ private static final int DEFAULT_DURATION = 300;
protected final T mActivityContext;
protected final SingleAxisSwipeDetector mSwipeDetector;
- protected @NonNull AnimatorSet mOpenCloseAnimator;
- private final ObjectAnimator mTranslationShiftAnimator;
+ protected @NonNull AnimatorPlaybackController mOpenCloseAnimation;
protected ViewGroup mContent;
protected final View mColorScrim;
+ /**
+ * Interpolator for {@link #mOpenCloseAnimation} when we are closing due to dragging downwards.
+ */
private Interpolator mScrollInterpolator;
private long mScrollDuration;
+ /**
+ * End progress for {@link #mOpenCloseAnimation} when we are closing due to dragging downloads.
+ * <p>
+ * There are two cases that determine this value:
+ * <ol>
+ * <li>
+ * If the drag interrupts the opening transition (i.e. {@link #mToTranslationShift}
+ * is {@link #TRANSLATION_SHIFT_OPENED}), we need to animate back to {@code 0} to
+ * reverse the animation that was paused at {@link #onDragStart(boolean, float)}.
+ * </li>
+ * <li>
+ * If the drag started after the view is fully opened (i.e.
+ * {@link #mToTranslationShift} is {@link #TRANSLATION_SHIFT_CLOSED}), the animation
+ * that was set up at {@link #onDragStart(boolean, float)} for closing the view
+ * should go forward to {@code 1}.
+ * </li>
+ * </ol>
+ */
+ private float mScrollEndProgress;
// range [0, 1], 0=> completely open, 1=> completely closed
protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
- /** {@link #mTranslationShift} at the invocation of {@link #onDragStart(boolean, float)}. */
- protected float mDragStartTranslationShift;
+ protected float mFromTranslationShift;
+ protected float mToTranslationShift;
+ /** {@link #mOpenCloseAnimation} progress at {@link #onDragStart(boolean, float)}. */
+ private float mDragStartProgress;
protected boolean mNoIntercept;
protected @Nullable OnCloseListener mOnCloseBeginListener;
@@ -128,52 +152,78 @@
mActivityContext = ActivityContext.lookupContext(context);
mScrollInterpolator = Interpolators.SCROLL_CUBIC;
- mScrollDuration = NO_DURATION;
+ mScrollDuration = DEFAULT_DURATION;
mSwipeDetector = new SingleAxisSwipeDetector(context, this,
SingleAxisSwipeDetector.VERTICAL);
- mOpenCloseAnimator = new AnimatorSet();
- mTranslationShiftAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
+ mOpenCloseAnimation = new PendingAnimation(0).createPlaybackController();
int scrimColor = getScrimColor(context);
mColorScrim = scrimColor != -1 ? createColorScrim(context, scrimColor) : null;
}
/**
- * Sets up a {@link #mOpenCloseAnimator} for opening with default parameters.
+ * Sets up a {@link #mOpenCloseAnimation} for opening with default parameters.
*
- * @see #setUpOpenCloseAnimator(float, Interpolator)
+ * @see #setUpOpenCloseAnimation(float, float, long)
*/
- protected final AnimatorSet setUpDefaultOpenAnimator() {
- return setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.FAST_OUT_SLOW_IN);
+ protected final AnimatorPlaybackController setUpDefaultOpenAnimation() {
+ AnimatorPlaybackController animation = setUpOpenCloseAnimation(
+ TRANSLATION_SHIFT_CLOSED, TRANSLATION_SHIFT_OPENED, DEFAULT_DURATION);
+ animation.getAnimationPlayer().setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ return animation;
}
/**
- * Initializes a new {@link #mOpenCloseAnimator}.
- * <p>
- * Subclasses should override this method if they want to add more {@code Animator} instances
- * to the set.
+ * Sets up a {@link #mOpenCloseAnimation} for opening with a given duration.
*
- * @param translationShift translation shift to animate to.
- * @param translationShiftInterpolator interpolator for {@link #mTranslationShiftAnimator}.
- * @return {@link #mOpenCloseAnimator}
+ * @see #setUpOpenCloseAnimation(float, float, long)
*/
- protected AnimatorSet setUpOpenCloseAnimator(
- float translationShift, Interpolator translationShiftInterpolator) {
- mOpenCloseAnimator = new AnimatorSet();
- mOpenCloseAnimator.addListener(AnimatorListeners.forEndCallback(() -> {
+ protected final AnimatorPlaybackController setUpOpenAnimation(long duration) {
+ return setUpOpenCloseAnimation(
+ TRANSLATION_SHIFT_CLOSED, TRANSLATION_SHIFT_OPENED, duration);
+ }
+
+ private AnimatorPlaybackController setUpCloseAnimation(long duration) {
+ return setUpOpenCloseAnimation(
+ TRANSLATION_SHIFT_OPENED, TRANSLATION_SHIFT_CLOSED, duration);
+ }
+
+ /**
+ * Initializes a new {@link #mOpenCloseAnimation}.
+ *
+ * @param fromTranslationShift translation shift to animate from.
+ * @param toTranslationShift translation shift to animate to.
+ * @param duration animation duration.
+ * @return {@link #mOpenCloseAnimation}
+ */
+ private AnimatorPlaybackController setUpOpenCloseAnimation(
+ float fromTranslationShift, float toTranslationShift, long duration) {
+ mFromTranslationShift = fromTranslationShift;
+ mToTranslationShift = toTranslationShift;
+
+ PendingAnimation animation = new PendingAnimation(duration);
+ animation.addEndListener(b -> {
mSwipeDetector.finishedScrolling();
announceAccessibilityChanges();
- }));
+ });
- mTranslationShiftAnimator.setValues(PropertyValuesHolder.ofFloat(
- TRANSLATION_SHIFT, translationShift));
- mTranslationShiftAnimator.setInterpolator(translationShiftInterpolator);
- mOpenCloseAnimator.play(mTranslationShiftAnimator);
+ animation.addFloat(
+ this, TRANSLATION_SHIFT, fromTranslationShift, toTranslationShift, LINEAR);
+ onOpenCloseAnimationPending(animation);
- return mOpenCloseAnimator;
+ mOpenCloseAnimation = animation.createPlaybackController();
+ return mOpenCloseAnimation;
}
+ /**
+ * Invoked when a {@link #mOpenCloseAnimation} is being set up.
+ * <p>
+ * Subclasses can override this method to modify the animation before it's used to create a
+ * {@link AnimatorPlaybackController}.
+ */
+ protected void onOpenCloseAnimationPending(PendingAnimation animation) {}
+
protected void attachToContainer() {
if (mColorScrim != null) {
getPopupContainer().addView(mColorScrim);
@@ -316,29 +366,33 @@
}
private boolean isOpeningAnimationRunning() {
- return mIsOpen && mOpenCloseAnimator.isRunning();
+ return mIsOpen && mOpenCloseAnimation.getAnimationPlayer().isRunning();
}
/* SingleAxisSwipeDetector.Listener */
@Override
public void onDragStart(boolean start, float startDisplacement) {
- mOpenCloseAnimator.cancel();
- mDragStartTranslationShift = mTranslationShift;
+ if (mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
+ mOpenCloseAnimation.pause();
+ mDragStartProgress = mOpenCloseAnimation.getProgressFraction();
+ } else {
+ setUpCloseAnimation(DEFAULT_DURATION);
+ mDragStartProgress = 0;
+ }
}
@Override
public boolean onDrag(float displacement) {
- setTranslationShift(Utilities.boundToRange(
- mDragStartTranslationShift + displacement / getShiftRange(),
- TRANSLATION_SHIFT_OPENED,
- TRANSLATION_SHIFT_CLOSED));
+ float progress = mDragStartProgress
+ + Math.signum(mToTranslationShift - mFromTranslationShift)
+ * (displacement / getShiftRange());
+ mOpenCloseAnimation.setPlayFraction(Utilities.boundToRange(progress, 0, 1));
return true;
}
@Override
public void onDragEnd(float velocity) {
- mDragStartTranslationShift = 0;
float successfulShiftThreshold = mActivityContext.getDeviceProfile().isTablet
? TABLET_BOTTOM_SHEET_SUCCESS_TRANSITION_PROGRESS : SUCCESS_TRANSITION_PROGRESS;
if ((mSwipeDetector.isFling(velocity) && velocity > 0)
@@ -346,10 +400,15 @@
mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
mScrollDuration = BaseSwipeDetector.calculateDuration(
velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift);
+ mScrollEndProgress = mToTranslationShift == TRANSLATION_SHIFT_OPENED ? 0 : 1;
close(true);
} else {
- setUpOpenCloseAnimator(TRANSLATION_SHIFT_OPENED, Interpolators.DECELERATE)
- .setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
+ ValueAnimator animator = mOpenCloseAnimation.getAnimationPlayer();
+ animator.setInterpolator(Interpolators.DECELERATE);
+ animator.setFloatValues(
+ mOpenCloseAnimation.getProgressFraction(),
+ mToTranslationShift == TRANSLATION_SHIFT_OPENED ? 1 : 0);
+ animator.setDuration(BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
.start();
}
}
@@ -371,24 +430,27 @@
Optional.ofNullable(mOnCloseBeginListener).ifPresent(OnCloseListener::onSlideInViewClosed);
if (!animate) {
- mOpenCloseAnimator.cancel();
+ mOpenCloseAnimation.pause();
setTranslationShift(TRANSLATION_SHIFT_CLOSED);
onCloseComplete();
return;
}
- final Interpolator interpolator;
- final long duration;
+ final ValueAnimator animator;
if (mSwipeDetector.isIdleState()) {
- interpolator = getIdleInterpolator();
- duration = defaultDuration;
+ setUpCloseAnimation(defaultDuration);
+ animator = mOpenCloseAnimation.getAnimationPlayer();
+ animator.setInterpolator(getIdleInterpolator());
} else {
- interpolator = mScrollInterpolator;
- duration = mScrollDuration > NO_DURATION ? mScrollDuration : defaultDuration;
+ animator = mOpenCloseAnimation.getAnimationPlayer();
+ animator.setInterpolator(mScrollInterpolator);
+ animator.setDuration(mScrollDuration);
+ mOpenCloseAnimation.getAnimationPlayer().setFloatValues(
+ mOpenCloseAnimation.getProgressFraction(), mScrollEndProgress);
}
- setUpOpenCloseAnimator(TRANSLATION_SHIFT_CLOSED, interpolator)
- .addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
- mOpenCloseAnimator.setDuration(duration).start();
+
+ animator.addListener(AnimatorListeners.forEndCallback(this::onCloseComplete));
+ animator.start();
}
protected Interpolator getIdleInterpolator() {
diff --git a/src/com/android/launcher3/views/WidgetsEduView.java b/src/com/android/launcher3/views/WidgetsEduView.java
index 92e048b..e70b1cb 100644
--- a/src/com/android/launcher3/views/WidgetsEduView.java
+++ b/src/com/android/launcher3/views/WidgetsEduView.java
@@ -116,11 +116,11 @@
}
private void animateOpen() {
- if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+ if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
return;
}
mIsOpen = true;
- setUpDefaultOpenAnimator().start();
+ setUpDefaultOpenAnimation().start();
}
/** Shows widget education dialog. */
diff --git a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
index c6fc5fe..80b1cdd 100644
--- a/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/AddItemWidgetsBottomSheet.java
@@ -128,11 +128,11 @@
}
private void animateOpen() {
- if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+ if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
return;
}
mIsOpen = true;
- setUpDefaultOpenAnimator().start();
+ setUpDefaultOpenAnimation().start();
}
@Override
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 82394f1..c347939 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -224,12 +224,12 @@
}
private void animateOpen() {
- if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+ if (mIsOpen || mOpenCloseAnimation.getAnimationPlayer().isRunning()) {
return;
}
mIsOpen = true;
setupNavBarColor();
- setUpDefaultOpenAnimator().start();
+ setUpDefaultOpenAnimation().start();
}
@Override
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 43f1846..abca1f8 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
+import android.animation.Animator;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.res.Configuration;
@@ -624,13 +625,12 @@
mContent.setAlpha(0);
setTranslationShift(VERTICAL_START_POSITION);
}
- setUpOpenCloseAnimator(
- TRANSLATION_SHIFT_OPENED,
- AnimationUtils.loadInterpolator(
- getContext(), android.R.interpolator.linear_out_slow_in));
+ setUpOpenAnimation(mActivityContext.getDeviceProfile().bottomSheetOpenDuration);
+ Animator animator = mOpenCloseAnimation.getAnimationPlayer();
+ animator.setInterpolator(AnimationUtils.loadInterpolator(
+ getContext(), android.R.interpolator.linear_out_slow_in));
post(() -> {
- mOpenCloseAnimator
- .setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
+ animator.setDuration(mActivityContext.getDeviceProfile().bottomSheetOpenDuration)
.start();
mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
});