From c8fd3f23fed58eda58a7b30b0fe9a3953f89b5eb Mon Sep 17 00:00:00 2001 From: Johannes Gallmann Date: Thu, 12 Dec 2024 14:15:26 +0000 Subject: [PATCH] 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 --- aconfig/launcher.aconfig | 12 +++- .../launcher3/QuickstepTransitionManager.java | 61 +++++++++++++------ .../taskbar/LauncherTaskbarUIController.java | 6 +- .../LauncherBackAnimationController.java | 25 +++++++- .../quickstep/LauncherSwipeHandlerV2.java | 4 +- .../android/quickstep/util/BackAnimState.kt | 21 ++++--- .../util/ScalingWorkspaceRevealAnim.kt | 37 +++++------ 7 files changed, 116 insertions(+), 50 deletions(-) diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig index bc49146a07..f0a9cbaf35 100644 --- a/aconfig/launcher.aconfig +++ b/aconfig/launcher.aconfig @@ -543,4 +543,14 @@ flag { 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 2759816fa5..f38693d66b 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1670,7 +1670,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener || 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 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // 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 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener 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 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ? 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 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 7d75286312..ea42d771cb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -217,8 +217,10 @@ public class LauncherTaskbarUIController extends TaskbarUIController { 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 4bd9ffbf45..25b5531d51 100644 --- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java +++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java @@ -51,11 +51,15 @@ import android.window.BackMotionEvent; 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.DisplayController; 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 @@ import java.lang.ref.WeakReference; */ 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 @@ public class LauncherBackAnimationController { 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 @@ public class LauncherBackAnimationController { } } + 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 @@ public class LauncherBackAnimationController { 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 0ddd87bb1c..7dd2f2ee23 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -331,7 +331,7 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler< protected void playScalingRevealAnimation() { if (mContainer != null) { new ScalingWorkspaceRevealAnim(mContainer, mSiblingAnimation, - getWindowTargetRect()).start(); + getWindowTargetRect(), true /* playAlphaReveal */).start(); } } @@ -381,7 +381,7 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler< 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 9009eaa263..4c1e1ffacc 100644 --- a/quickstep/src/com/android/quickstep/util/BackAnimState.kt +++ b/quickstep/src/com/android/quickstep/util/BackAnimState.kt @@ -18,6 +18,7 @@ package com.android.quickstep.util 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 @@ class AnimatorBackState(private val springAnim: RectFSpringAnim?, private val an 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 f719bed5e7..63eae921b0 100644 --- a/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt +++ b/quickstep/src/com/android/quickstep/util/ScalingWorkspaceRevealAnim.kt @@ -54,14 +54,15 @@ class ScalingWorkspaceRevealAnim( 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 @@ class ScalingWorkspaceRevealAnim( 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()