Play workspace reveal animation for predictive back-to-home
Bug: 382453424
Test: Manual, i.e. tested back-to-home and back-to-allapps animation on phones, tablets and foldables, including interruptions.
Flag: com.android.launcher3.predictive_back_to_home_polish
Change-Id: I092a74ab2340828e18067ca15c7019c44d30f40b
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index bc49146..f0a9cba 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -543,4 +543,14 @@
namespace: "launcher"
description: "Enable launcher icon shape customizations"
bug: "348708061"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "predictive_back_to_home_polish"
+ namespace: "launcher"
+ description: "Enables workspace reveal animation for predictive back-to-home"
+ bug: "382453424"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 2759816..f38693d 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1670,7 +1670,10 @@
|| mLauncher.getWorkspace().isOverlayShown()
|| shouldPlayFallbackClosingAnimation(appTargets);
- boolean playWorkspaceReveal = !fromPredictiveBack;
+ boolean playWorkspaceReveal = true;
+ if (!Flags.predictiveBackToHomePolish()) {
+ playWorkspaceReveal = !fromPredictiveBack;
+ }
boolean skipAllAppsScale = false;
if (!playFallBackAnimation) {
PointF velocity;
@@ -1689,12 +1692,12 @@
// Skip scaling all apps, otherwise FloatingIconView will get wrong
// layout bounds.
skipAllAppsScale = true;
- } else if (!fromPredictiveBack) {
+ } else if (Flags.predictiveBackToHomePolish() || !fromPredictiveBack) {
if (enableScalingRevealHomeAnimation()) {
anim.play(
- new ScalingWorkspaceRevealAnim(
- mLauncher, rectFSpringAnim,
- rectFSpringAnim.getTargetRect()).getAnimators());
+ new ScalingWorkspaceRevealAnim(mLauncher, rectFSpringAnim,
+ rectFSpringAnim.getTargetRect(),
+ !fromPredictiveBack /* playAlphaReveal */).getAnimators());
} else {
anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
true /* animateOverviewScrim */, launcherView).getAnimators());
@@ -1713,15 +1716,7 @@
anim.play(getFallbackClosingWindowAnimators(appTargets));
}
- // Normally, we run the launcher content animation when we are transitioning
- // home, but if home is already visible, then we don't want to animate the
- // contents of launcher unless we know that we are animating home as a result
- // of the home button press with quickstep, which will result in launcher being
- // started on touch down, prior to the animation home (and won't be in the
- // targets list because it is already visible). In that case, we force
- // invisibility on touch down, and only reset it after the animation to home
- // is initialized.
- if (launcherIsForceInvisibleOrOpening || fromPredictiveBack) {
+ if (Flags.predictiveBackToHomePolish()) {
AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -1730,7 +1725,24 @@
mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
}
};
+ if (rectFSpringAnim != null) {
+ rectFSpringAnim.addAnimatorListener(endListener);
+ } else {
+ anim.addListener(endListener);
+ }
+ }
+ // Normally, we run the launcher content animation when we are transitioning
+ // home, but if home is already visible, then we don't want to animate the
+ // contents of launcher unless we know that we are animating home as a result
+ // of the home button press with quickstep, which will result in launcher being
+ // started on touch down, prior to the animation home (and won't be in the
+ // targets list because it is already visible). In that case, we force
+ // invisibility on touch down, and only reset it after the animation to home
+ // is initialized.
+ boolean legacyFromPredictiveBack =
+ !Flags.predictiveBackToHomePolish() && fromPredictiveBack;
+ if (launcherIsForceInvisibleOrOpening || legacyFromPredictiveBack) {
if (rectFSpringAnim != null && anim.getChildAnimations().isEmpty()) {
addCujInstrumentation(rectFSpringAnim, Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
} else {
@@ -1738,17 +1750,26 @@
? Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK
: Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
}
-
- if (fromPredictiveBack && rectFSpringAnim != null) {
- rectFSpringAnim.addAnimatorListener(endListener);
- } else {
- anim.addListener(endListener);
+ if (!Flags.predictiveBackToHomePolish()) {
+ AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ AccessibilityManagerCompat.sendTestProtocolEventToTest(
+ mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
+ }
+ };
+ if (fromPredictiveBack && rectFSpringAnim != null) {
+ rectFSpringAnim.addAnimatorListener(endListener);
+ } else {
+ anim.addListener(endListener);
+ }
}
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
- if (mLauncher.isInState(LauncherState.ALL_APPS) && !fromPredictiveBack) {
+ if (mLauncher.isInState(LauncherState.ALL_APPS) && !legacyFromPredictiveBack) {
Pair<AnimatorSet, Runnable> contentAnimator =
getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
skipAllAppsScale);
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 7d75286..ea42d77 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -217,8 +217,10 @@
private int getTaskbarAnimationDuration(boolean isVisible) {
// fast animation duration since we will not be playing workspace reveal animation.
- boolean shouldOverrideToFastAnimation =
- !isHotseatIconOnTopWhenAligned() || mLauncher.getPredictiveBackToHomeInProgress();
+ boolean shouldOverrideToFastAnimation = !isHotseatIconOnTopWhenAligned();
+ if (!Flags.predictiveBackToHomePolish()) {
+ shouldOverrideToFastAnimation |= mLauncher.getPredictiveBackToHomeInProgress();
+ }
boolean isPinnedTaskbar = DisplayController.isPinnedTaskbar(mLauncher);
if (isVisible || isPinnedTaskbar) {
return getTaskbarToHomeDuration(shouldOverrideToFastAnimation, isPinnedTaskbar);
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 4bd9ffb..25b5531 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -51,11 +51,15 @@
import android.window.BackProgressAnimator;
import android.window.IBackAnimationHandoffHandler;
import android.window.IOnBackInvokedCallback;
+
+import com.android.app.animation.Animations;
import com.android.app.animation.Interpolators;
import com.android.internal.policy.SystemBarUtils;
import com.android.internal.view.AppearanceRegion;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Flags;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.QuickstepTransitionManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -65,6 +69,7 @@
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.quickstep.util.BackAnimState;
+import com.android.quickstep.util.ScalingWorkspaceRevealAnim;
import com.android.systemui.shared.system.QuickStepContract;
import java.lang.ref.WeakReference;
@@ -87,7 +92,8 @@
*/
public class LauncherBackAnimationController {
private static final int SCRIM_FADE_DURATION = 233;
- private static final float MIN_WINDOW_SCALE = 0.85f;
+ private static final float MIN_WINDOW_SCALE =
+ Flags.predictiveBackToHomePolish() ? 0.75f : 0.85f;
private static final float MAX_SCRIM_ALPHA_DARK = 0.8f;
private static final float MAX_SCRIM_ALPHA_LIGHT = 0.2f;
@@ -314,6 +320,12 @@
new RemoteAnimationTarget[]{ mBackTarget });
setLauncherTargetViewVisible(false);
mCurrentRect.set(mStartRect);
+ if (Flags.predictiveBackToHomePolish() && !mLauncher.getWorkspace().isOverlayShown()
+ && !mLauncher.isInState(LauncherState.ALL_APPS)) {
+ Animations.cancelOngoingAnimation(mLauncher.getWorkspace());
+ Animations.cancelOngoingAnimation(mLauncher.getHotseat());
+ setLauncherScale(ScalingWorkspaceRevealAnim.MIN_SIZE);
+ }
if (mScrimLayer == null) {
addScrimLayer();
}
@@ -328,6 +340,13 @@
}
}
+ private void setLauncherScale(float scale) {
+ mLauncher.getWorkspace().setScaleX(scale);
+ mLauncher.getWorkspace().setScaleY(scale);
+ mLauncher.getHotseat().setScaleX(scale);
+ mLauncher.getHotseat().setScaleY(scale);
+ }
+
void addScrimLayer() {
SurfaceControl parent = mLauncherTarget != null ? mLauncherTarget.leash : null;
if (parent == null || !parent.isValid()) {
@@ -500,6 +519,10 @@
if (mScrimLayer != null) {
removeScrimLayer();
}
+ if (Flags.predictiveBackToHomePolish() && !mLauncher.getWorkspace().isOverlayShown()
+ && !mLauncher.isInState(LauncherState.ALL_APPS)) {
+ setLauncherScale(ScalingWorkspaceRevealAnim.MAX_SIZE);
+ }
}
private void startTransitionAnimations(BackAnimState backAnim) {
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 0ddd87b..7dd2f2e 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -331,7 +331,7 @@
protected void playScalingRevealAnimation() {
if (mContainer != null) {
new ScalingWorkspaceRevealAnim(mContainer, mSiblingAnimation,
- getWindowTargetRect()).start();
+ getWindowTargetRect(), true /* playAlphaReveal */).start();
}
}
@@ -381,7 +381,7 @@
if (mContainer != null) {
new ScalingWorkspaceRevealAnim(
mContainer, null /* siblingAnimation */,
- null /* windowTargetRect */).start();
+ null /* windowTargetRect */, true /* playAlphaReveal */).start();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/util/BackAnimState.kt b/quickstep/src/com/android/quickstep/util/BackAnimState.kt
index 9009eaa..4c1e1ff 100644
--- a/quickstep/src/com/android/quickstep/util/BackAnimState.kt
+++ b/quickstep/src/com/android/quickstep/util/BackAnimState.kt
@@ -18,6 +18,7 @@
import android.animation.AnimatorSet
import android.content.Context
+import com.android.launcher3.Flags
import com.android.launcher3.LauncherAnimationRunner.AnimationResult
import com.android.launcher3.anim.AnimatorListeners.forEndCallback
import com.android.launcher3.util.RunnableList
@@ -36,14 +37,20 @@
BackAnimState {
override fun addOnAnimCompleteCallback(r: Runnable) {
- val springAnimWait = RunnableList()
- springAnim?.addAnimatorListener(forEndCallback(springAnimWait::executeAllAndDestroy))
- ?: springAnimWait.executeAllAndDestroy()
-
val animWait = RunnableList()
- anim?.addListener(
- forEndCallback(Runnable { springAnimWait.add(animWait::executeAllAndDestroy) })
- ) ?: springAnimWait.add(animWait::executeAllAndDestroy)
+ if (Flags.predictiveBackToHomePolish()) {
+ springAnim?.addAnimatorListener(forEndCallback(animWait::executeAllAndDestroy))
+ ?: anim?.addListener(forEndCallback(animWait::executeAllAndDestroy))
+ ?: animWait.executeAllAndDestroy()
+ } else {
+ val springAnimWait = RunnableList()
+ springAnim?.addAnimatorListener(forEndCallback(springAnimWait::executeAllAndDestroy))
+ ?: springAnimWait.executeAllAndDestroy()
+
+ anim?.addListener(
+ forEndCallback(Runnable { springAnimWait.add(animWait::executeAllAndDestroy) })
+ ) ?: springAnimWait.add(animWait::executeAllAndDestroy)
+ }
animWait.add(r)
}
diff --git a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
index f719bed..63eae92 100644
--- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
+++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt
@@ -54,14 +54,15 @@
private val launcher: QuickstepLauncher,
siblingAnimation: RectFSpringAnim?,
windowTargetRect: RectF?,
+ playAlphaReveal: Boolean = true,
) {
companion object {
private const val FADE_DURATION_MS = 200L
private const val SCALE_DURATION_MS = 1000L
private const val MAX_ALPHA = 1f
private const val MIN_ALPHA = 0f
- private const val MAX_SIZE = 1f
- private const val MIN_SIZE = 0.85f
+ internal const val MAX_SIZE = 1f
+ internal const val MIN_SIZE = 0.85f
/**
* Custom interpolator for both the home and wallpaper scaling. Necessary because EMPHASIZED
@@ -132,21 +133,23 @@
SCALE_INTERPOLATOR,
)
- // Fade in quickly at the beginning of the animation, so the content doesn't look like it's
- // popping into existence out of nowhere.
- val fadeClamp = FADE_DURATION_MS.toFloat() / SCALE_DURATION_MS
- workspace.alpha = MIN_ALPHA
- animation.setViewAlpha(
- workspace,
- MAX_ALPHA,
- Interpolators.clampToProgress(LINEAR, 0f, fadeClamp),
- )
- hotseat.alpha = MIN_ALPHA
- animation.setViewAlpha(
- hotseat,
- MAX_ALPHA,
- Interpolators.clampToProgress(LINEAR, 0f, fadeClamp),
- )
+ if (playAlphaReveal) {
+ // Fade in quickly at the beginning of the animation, so the content doesn't look like
+ // it's popping into existence out of nowhere.
+ val fadeClamp = FADE_DURATION_MS.toFloat() / SCALE_DURATION_MS
+ workspace.alpha = MIN_ALPHA
+ animation.setViewAlpha(
+ workspace,
+ MAX_ALPHA,
+ Interpolators.clampToProgress(LINEAR, 0f, fadeClamp),
+ )
+ hotseat.alpha = MIN_ALPHA
+ animation.setViewAlpha(
+ hotseat,
+ MAX_ALPHA,
+ Interpolators.clampToProgress(LINEAR, 0f, fadeClamp),
+ )
+ }
val transitionConfig = StateAnimationConfig()