From 5765d42ada91f94b65a44ae982a2a3e714e65427 Mon Sep 17 00:00:00 2001 From: Nicolo' Mazzucato Date: Mon, 21 Feb 2022 18:11:23 +0100 Subject: [PATCH] Fix jump in animation for hotseat while re-arranging icons While re-arranging icons the hotseat remains in scale 1.0f, while the workspace reduces it's scale (as defined by SpringLoadedState.java). Previously, the code to aggregate animations was assuming hotseat and workspace always had the same scale. MultiScaleProperty.get() was being used to set the starting value of the animation. Previously, it was returning the last aggregated value. However, this value was correct only for the workspace, but not for the hotseat. Returning the current view scale makes it always correct. Bug: 220271046 Test: Dragged icons from hotseat to workspace, and verified animation didn't jump Change-Id: Ic01776c1d8e3967624626ed7c44d194a06295790 --- .../QuickstepAtomicAnimationFactory.java | 5 +-- .../LauncherUnfoldAnimationController.java | 15 ++++---- .../quickstep/util/WorkspaceRevealAnim.java | 21 +++++++----- .../android/launcher3/LauncherAnimUtils.java | 16 ++++----- .../WorkspaceStateTransitionAnimation.java | 34 +++++++++++++------ .../util/MultiScalePropertyFactory.java | 28 +++++++++++---- .../launcher3/util/MultiScalePropertyTest.kt | 5 +-- 7 files changed, 81 insertions(+), 43 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index 75cf5cb3a5..bd7e5de866 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -22,7 +22,7 @@ import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator; +import static com.android.launcher3.WorkspaceStateTransitionAnimation.getWorkspaceSpringScaleAnimator; import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; @@ -172,7 +172,8 @@ public class QuickstepAtomicAnimationFactory extends } else if (fromState == HINT_STATE && toState == NORMAL) { config.setInterpolator(ANIM_DEPTH, DEACCEL_3); if (mHintToNormalDuration == -1) { - ValueAnimator va = getSpringScaleAnimator(mActivity, mActivity.getWorkspace(), + ValueAnimator va = getWorkspaceSpringScaleAnimator(mActivity, + mActivity.getWorkspace(), toState.getWorkspaceScaleAndTranslation(mActivity).scale); mHintToNormalDuration = (int) va.getDuration(); } diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java index 333df10dde..3b5804634a 100644 --- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java +++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java @@ -15,14 +15,14 @@ */ package com.android.quickstep.util; +import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY; import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_UNFOLD_ANIMATION; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY_FACTORY; +import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY; import static com.android.launcher3.Utilities.comp; import android.annotation.Nullable; import android.util.FloatProperty; import android.util.MathUtils; -import android.view.View; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -30,6 +30,7 @@ import androidx.core.view.OneShotPreDrawListener; import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; +import com.android.launcher3.Workspace; import com.android.launcher3.util.HorizontalInsettableView; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener; @@ -44,8 +45,10 @@ public class LauncherUnfoldAnimationController { // Percentage of the width of the quick search bar that will be reduced // from the both sides of the bar when progress is 0 private static final float MAX_WIDTH_INSET_FRACTION = 0.15f; - private static final FloatProperty UNFOLD_SCALE_PROPERTY = - SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_UNFOLD_ANIMATION); + private static final FloatProperty WORKSPACE_SCALE_PROPERTY = + WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_UNFOLD_ANIMATION); + private static final FloatProperty HOTSEAT_SCALE_PROPERTY = + HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_UNFOLD_ANIMATION); private final Launcher mLauncher; @@ -147,8 +150,8 @@ public class LauncherUnfoldAnimationController { } private void setScale(float value) { - UNFOLD_SCALE_PROPERTY.setValue(mLauncher.getWorkspace(), value); - UNFOLD_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); + WORKSPACE_SCALE_PROPERTY.setValue(mLauncher.getWorkspace(), value); + HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value); } } } diff --git a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java index 8659b687c3..5326d2bb66 100644 --- a/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java +++ b/quickstep/src/com/android/quickstep/util/WorkspaceRevealAnim.java @@ -15,8 +15,9 @@ */ package com.android.quickstep.util; +import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY; import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_REVEAL_ANIM; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY_FACTORY; +import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; @@ -32,6 +33,7 @@ import android.util.FloatProperty; import android.view.View; import com.android.launcher3.BaseQuickstepLauncher; +import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Workspace; @@ -51,8 +53,11 @@ public class WorkspaceRevealAnim { // Should be used for animations running alongside this WorkspaceRevealAnim. public static final int DURATION_MS = 350; - private static final FloatProperty REVEAL_SCALE_PROPERTY = - SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_REVEAL_ANIM); + private static final FloatProperty WORKSPACE_SCALE_PROPERTY = + WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_REVEAL_ANIM); + + private static final FloatProperty HOTSEAT_SCALE_PROPERTY = + HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_REVEAL_ANIM); private final float mScaleStart; private final AnimatorSet mAnimators = new AnimatorSet(); @@ -67,8 +72,8 @@ public class WorkspaceRevealAnim { workspace.setPivotToScaleWithSelf(launcher.getHotseat()); // Add reveal animations. - addRevealAnimatorsForView(workspace); - addRevealAnimatorsForView(launcher.getHotseat()); + addRevealAnimatorsForView(workspace, WORKSPACE_SCALE_PROPERTY); + addRevealAnimatorsForView(launcher.getHotseat(), HOTSEAT_SCALE_PROPERTY); // Add overview scrim animation. if (animateOverviewScrim) { @@ -93,8 +98,8 @@ public class WorkspaceRevealAnim { mAnimators.setInterpolator(Interpolators.DECELERATED_EASE); } - private void addRevealAnimatorsForView(View v) { - ObjectAnimator scale = ObjectAnimator.ofFloat(v, REVEAL_SCALE_PROPERTY, mScaleStart, 1f); + private void addRevealAnimatorsForView(T v, FloatProperty scaleProperty) { + ObjectAnimator scale = ObjectAnimator.ofFloat(v, scaleProperty, mScaleStart, 1f); scale.setDuration(DURATION_MS); scale.setInterpolator(Interpolators.DECELERATED_EASE); mAnimators.play(scale); @@ -107,7 +112,7 @@ public class WorkspaceRevealAnim { mAnimators.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - REVEAL_SCALE_PROPERTY.set(v, 1f); + scaleProperty.set(v, 1f); v.setAlpha(1f); } }); diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index 430039276e..c43172cc56 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -67,18 +67,16 @@ public class LauncherAnimUtils { }; /** - * Property to set the scale of workspace and hotseat. The value is based on a combination + * Property to set the scale of workspace. The value is based on a combination * of all the ones set, to have a smooth experience even in the case of overlapping scaling * animation. */ - public static final MultiScalePropertyFactory SCALE_PROPERTY_FACTORY = - new MultiScalePropertyFactory("scale_property") { - @Override - protected void apply(View view, float scale) { - view.setScaleX(scale); - view.setScaleY(scale); - } - }; + public static final MultiScalePropertyFactory WORKSPACE_SCALE_PROPERTY_FACTORY = + new MultiScalePropertyFactory("workspace_scale_property"); + + /** Property to set the scale of hotseat. */ + public static final MultiScalePropertyFactory HOTSEAT_SCALE_PROPERTY_FACTORY = + new MultiScalePropertyFactory("hotseat_scale_property"); public static final int SCALE_INDEX_UNFOLD_ANIMATION = 1; public static final int SCALE_INDEX_UNLOCK_ANIMATION = 2; diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 98e785f47b..d94e84cbac 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -18,11 +18,12 @@ package com.android.launcher3; import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE; +import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY; import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WORKSPACE_STATE; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY_FACTORY; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; +import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY; import static com.android.launcher3.LauncherState.FLAG_HAS_SYS_UI_SCRIM; import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; @@ -64,8 +65,11 @@ import com.android.systemui.plugins.ResourceProvider; */ public class WorkspaceStateTransitionAnimation { - private static final FloatProperty WORKSPACE_STATE_SCALE_PROPERTY = - SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WORKSPACE_STATE); + private static final FloatProperty WORKSPACE_SCALE_PROPERTY = + WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WORKSPACE_STATE); + + private static final FloatProperty HOTSEAT_SCALE_PROPERTY = + HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WORKSPACE_STATE); private final Launcher mLauncher; private final Workspace mWorkspace; @@ -120,9 +124,9 @@ public class WorkspaceStateTransitionAnimation { && fromState == HINT_STATE && state == NORMAL; if (shouldSpring) { ((PendingAnimation) propertySetter).add(getSpringScaleAnimator(mLauncher, - mWorkspace, mNewScale)); + mWorkspace, mNewScale, WORKSPACE_SCALE_PROPERTY)); } else { - propertySetter.setFloat(mWorkspace, WORKSPACE_STATE_SCALE_PROPERTY, mNewScale, + propertySetter.setFloat(mWorkspace, WORKSPACE_SCALE_PROPERTY, mNewScale, scaleInterpolator); } @@ -130,11 +134,12 @@ public class WorkspaceStateTransitionAnimation { float hotseatScale = hotseatScaleAndTranslation.scale; if (shouldSpring) { PendingAnimation pa = (PendingAnimation) propertySetter; - pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale)); + pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale, + HOTSEAT_SCALE_PROPERTY)); } else { Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE, scaleInterpolator); - propertySetter.setFloat(hotseat, WORKSPACE_STATE_SCALE_PROPERTY, hotseatScale, + propertySetter.setFloat(hotseat, HOTSEAT_SCALE_PROPERTY, hotseatScale, hotseatScaleInterpolator); } @@ -197,10 +202,19 @@ public class WorkspaceStateTransitionAnimation { pageAlpha, fadeInterpolator); } + /** + * Returns a spring based animator for the scale property of {@param workspace}. + */ + public static ValueAnimator getWorkspaceSpringScaleAnimator(Launcher launcher, + Workspace workspace, float scale) { + return getSpringScaleAnimator(launcher, workspace, scale, WORKSPACE_SCALE_PROPERTY); + } + /** * Returns a spring based animator for the scale property of {@param v}. */ - public static ValueAnimator getSpringScaleAnimator(Launcher launcher, View v, float scale) { + public static ValueAnimator getSpringScaleAnimator(Launcher launcher, T v, + float scale, FloatProperty property) { ResourceProvider rp = DynamicResource.provider(launcher); float damping = rp.getFloat(R.dimen.hint_scale_damping_ratio); float stiffness = rp.getFloat(R.dimen.hint_scale_stiffness); @@ -211,9 +225,9 @@ public class WorkspaceStateTransitionAnimation { .setDampingRatio(damping) .setMinimumVisibleChange(MIN_VISIBLE_CHANGE_SCALE) .setEndValue(scale) - .setStartValue(WORKSPACE_STATE_SCALE_PROPERTY.get(v)) + .setStartValue(property.get(v)) .setStartVelocity(velocityPxPerS) - .build(v, WORKSPACE_STATE_SCALE_PROPERTY); + .build(v, property); } } \ No newline at end of file diff --git a/src/com/android/launcher3/util/MultiScalePropertyFactory.java b/src/com/android/launcher3/util/MultiScalePropertyFactory.java index f27d0f0d86..a7e6cc8679 100644 --- a/src/com/android/launcher3/util/MultiScalePropertyFactory.java +++ b/src/com/android/launcher3/util/MultiScalePropertyFactory.java @@ -18,6 +18,8 @@ package com.android.launcher3.util; import android.util.ArrayMap; import android.util.FloatProperty; +import android.util.Log; +import android.view.View; import com.android.launcher3.Utilities; @@ -33,8 +35,10 @@ import com.android.launcher3.Utilities; * * @param Type where to apply the property. */ -public abstract class MultiScalePropertyFactory { +public class MultiScalePropertyFactory { + private static final boolean DEBUG = false; + private static final String TAG = "MultiScaleProperty"; private final String mName; private final ArrayMap mProperties = new ArrayMap(); @@ -56,7 +60,6 @@ public abstract class MultiScalePropertyFactory { (k) -> new MultiScaleProperty(index, mName + "_" + index)); } - /** * Each [setValue] will be aggregated with the other properties values created by the * corresponding factory. @@ -91,11 +94,22 @@ public abstract class MultiScalePropertyFactory { mLastAggregatedValue = Utilities.boundToRange(multValue, minValue, maxValue); mValue = newValue; apply(obj, mLastAggregatedValue); + + if (DEBUG) { + Log.d(TAG, "name=" + mName + + " newValue=" + newValue + " mInx=" + mInx + + " aggregated=" + mLastAggregatedValue + " others= " + mProperties); + } } @Override - public Float get(T t) { - return mLastAggregatedValue; + public Float get(T view) { + // The scale of the view should match mLastAggregatedValue. Still, if it has been + // changed without using this property, it can differ. As this get method is usually + // used to set the starting point on an animation, this would result in some jumps + // when the view scale is different than the last aggregated value. To stay on the + // safe side, let's return the real view scale. + return view.getScaleX(); } @Override @@ -104,6 +118,8 @@ public abstract class MultiScalePropertyFactory { } } - /** Applies value to object after setValue method is called. */ - protected abstract void apply(T obj, float value); + protected void apply(View view, float value) { + view.setScaleX(value); + view.setScaleY(value); + } } diff --git a/tests/src/com/android/launcher3/util/MultiScalePropertyTest.kt b/tests/src/com/android/launcher3/util/MultiScalePropertyTest.kt index c4a8db6adf..6099987172 100644 --- a/tests/src/com/android/launcher3/util/MultiScalePropertyTest.kt +++ b/tests/src/com/android/launcher3/util/MultiScalePropertyTest.kt @@ -1,5 +1,6 @@ package com.android.launcher3.util +import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.google.common.truth.Truth.assertThat @@ -14,8 +15,8 @@ class MultiScalePropertyTest { private val received = mutableListOf() private val factory = - object : MultiScalePropertyFactory("Test") { - override fun apply(obj: Int?, value: Float) { + object : MultiScalePropertyFactory("Test") { + override fun apply(obj: View?, value: Float) { received.add(value) } }