diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java index e940553a93..a63ba0f8e6 100644 --- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java +++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java @@ -26,7 +26,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.content.Context; import android.os.Handler; -import android.os.RemoteException; import android.util.Log; import android.view.IRemoteAnimationFinishedCallback; import android.view.RemoteAnimationTarget; @@ -210,7 +209,7 @@ public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat { * animation finished runnable. */ @Override - public void onAnimationFinished() throws RemoteException { + public void onAnimationFinished() { mASyncFinishRunnable.run(); } } @@ -240,12 +239,5 @@ public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat { @Override @UiThread default void onAnimationCancelled() {} - - /** - * Returns whether this animation factory supports a tightly coupled return animation. - */ - default boolean supportsReturnTransition() { - return false; - } } } diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 2457cfd41f..da6c05ed34 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -111,6 +111,7 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import android.window.RemoteTransition; import android.window.TransitionFilter; +import android.window.WindowAnimationState; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -141,6 +142,9 @@ import com.android.quickstep.LauncherBackAnimationController; import com.android.quickstep.RemoteAnimationTargets; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.TaskViewUtils; +import com.android.quickstep.util.AlreadyStartedBackAnimState; +import com.android.quickstep.util.AnimatorBackState; +import com.android.quickstep.util.BackAnimState; import com.android.quickstep.util.MultiValueUpdateListener; import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.RectFSpringAnim.DefaultSpringConfig; @@ -176,9 +180,6 @@ import java.util.List; */ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener { - private static final String TRANSITION_COOKIE_PREFIX = - "com.android.launcher3.QuickstepTransitionManager_activityLaunch"; - private static final boolean ENABLE_SHELL_STARTING_SURFACE = SystemProperties.getBoolean("persist.debug.shell_starting_surface", true); @@ -347,14 +348,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback); options.setOnAnimationAbortListener(endCallback); options.setOnAnimationFinishedListener(endCallback); - - IBinder cookie = mAppLaunchRunner.supportsReturnTransition() - ? ((ContainerAnimationRunner) mAppLaunchRunner).getCookie() : null; - addLaunchCookie(cookie, itemInfo, options); - - // Register the return animation so it can be triggered on back from the app to home. - maybeRegisterAppReturnTransition(v); - + options.setLaunchCookie(StableViewInfo.toLaunchCookie(itemInfo)); return new ActivityOptionsWrapper(options, onEndCallback); } @@ -367,21 +361,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener ItemInfo tag = (ItemInfo) v.getTag(); ContainerAnimationRunner containerRunner = null; if (tag != null && tag.shouldUseBackgroundAnimation()) { - // The cookie should only override the default used by launcher if container return - // animations are enabled. - ActivityTransitionAnimator.TransitionCookie cookie = - checkReturnAnimationsFlags() - ? new ActivityTransitionAnimator.TransitionCookie( - TRANSITION_COOKIE_PREFIX + tag.id) - : null; - ContainerAnimationRunner launchAnimationRunner = - ContainerAnimationRunner.fromView( - v, cookie, true /* forLaunch */, mLauncher, mStartingWindowListener, - onEndCallback); - - if (launchAnimationRunner != null) { - containerRunner = launchAnimationRunner; - } + containerRunner = ContainerAnimationRunner.fromView( + v, true /* forLaunch */, mLauncher, mStartingWindowListener, onEndCallback, + null /* windowState */); } mAppLaunchRunner = containerRunner != null @@ -390,51 +372,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */); } - /** - * If container return animations are enabled and the current launch runner is itself a - * {@link ContainerAnimationRunner}, registers a matching return animation that de-registers - * itself after it has run once or is made obsolete by the view going away. - */ - private void maybeRegisterAppReturnTransition(View v) { - if (!checkReturnAnimationsFlags() || !mAppLaunchRunner.supportsReturnTransition()) { - return; - } - - ActivityTransitionAnimator.TransitionCookie cookie = - ((ContainerAnimationRunner) mAppLaunchRunner).getCookie(); - RunnableList onEndCallback = new RunnableList(); - ContainerAnimationRunner runner = - ContainerAnimationRunner.fromView( - v, cookie, false /* forLaunch */, mLauncher, mStartingWindowListener, - onEndCallback); - RemoteTransition transition = - new RemoteTransition( - new LauncherAnimationRunner( - mHandler, runner, true /* startAtFrontOfQueue */ - ).toRemoteTransition() - ); - - SystemUiProxy.INSTANCE.get(mLauncher).registerRemoteTransition( - transition, ContainerAnimationRunner.buildBackToHomeFilter(cookie, mLauncher)); - ContainerAnimationRunner.setUpRemoteAnimationCleanup( - v, transition, onEndCallback, mLauncher); - } - - /** - * Adds a new launch cookie for the activity launch if supported. - * Prioritizes the explicitly provided cookie, falling back on extracting one from the given - * {@link ItemInfo} if necessary. - */ - private void addLaunchCookie(IBinder cookie, ItemInfo info, ActivityOptions options) { - if (cookie == null) { - cookie = StableViewInfo.toLaunchCookie(info); - } - - if (cookie != null) { - options.setLaunchCookie(cookie); - } - } - /** * Whether the launch is a recents app transition and we should do a launch animation * from the recents view. Note that if the remote animation targets are not provided, this @@ -1613,19 +1550,48 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * Creates the {@link RectFSpringAnim} and {@link AnimatorSet} required to animate * the transition. */ - public Pair createWallpaperOpenAnimations( + @NonNull + public BackAnimState createWallpaperOpenAnimations( RemoteAnimationTarget[] appTargets, - RemoteAnimationTarget[] wallpaperTargets, + RemoteAnimationTarget[] wallpapers, + RemoteAnimationTarget[] nonAppTargets, RectF startRect, float startWindowCornerRadius, boolean fromPredictiveBack) { + View launcherView = findLauncherView(appTargets); + if (checkReturnAnimationsFlags() + && launcherView != null + && launcherView.getTag() instanceof ItemInfo info + && info.shouldUseBackgroundAnimation()) { + // Try to create a return animation + RunnableList onEndCallback = new RunnableList(); + WindowAnimationState windowState = new WindowAnimationState(); + windowState.bounds = startRect; + windowState.bottomLeftRadius = windowState.bottomRightRadius = + windowState.topLeftRadius = windowState.topRightRadius = + startWindowCornerRadius; + ContainerAnimationRunner runner = ContainerAnimationRunner.fromView( + launcherView, false /* forLaunch */, mLauncher, mStartingWindowListener, + onEndCallback, windowState); + if (runner != null) { + runner.startAnimation(TRANSIT_CLOSE, + appTargets, wallpapers, nonAppTargets, + new IRemoteAnimationFinishedCallback.Stub() { + @Override + public void onAnimationFinished() { + onEndCallback.executeAllAndDestroy(); + } + }); + return new AlreadyStartedBackAnimState(onEndCallback); + } + } + AnimatorSet anim = new AnimatorSet(); RectFSpringAnim rectFSpringAnim = null; final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible() || launcherIsATargetWithMode(appTargets, MODE_OPENING); - View launcherView = findLauncherView(appTargets); boolean playFallBackAnimation = (launcherView == null && launcherIsForceInvisibleOrOpening) || mLauncher.getWorkspace().isOverlayShown() @@ -1725,7 +1691,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } } - return new Pair(rectFSpringAnim, anim); + return new AnimatorBackState(rectFSpringAnim, anim); } public static int getTaskbarToHomeDuration() { @@ -1775,14 +1741,14 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } } - Pair pair = createWallpaperOpenAnimations( - appTargets, wallpaperTargets, resolveRectF, + BackAnimState bankAnimState = createWallpaperOpenAnimations( + appTargets, wallpaperTargets, nonAppTargets, resolveRectF, QuickStepContract.getWindowCornerRadius(mLauncher), false /* fromPredictiveBack */); TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets, false, null); mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); - result.setAnimation(pair.second, mLauncher); + bankAnimState.applyToAnimationResult(result, mLauncher); } } @@ -1850,29 +1816,19 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener /** The delegate runner that handles the actual animation. */ private final RemoteAnimationDelegate mDelegate; - @Nullable - private final ActivityTransitionAnimator.TransitionCookie mCookie; - private ContainerAnimationRunner( - RemoteAnimationDelegate delegate, - ActivityTransitionAnimator.TransitionCookie cookie) { + RemoteAnimationDelegate delegate) { mDelegate = delegate; - mCookie = cookie; - } - - @Nullable - ActivityTransitionAnimator.TransitionCookie getCookie() { - return mCookie; } @Nullable static ContainerAnimationRunner fromView( View v, - ActivityTransitionAnimator.TransitionCookie cookie, boolean forLaunch, Launcher launcher, StartingWindowListener startingWindowListener, - RunnableList onEndCallback) { + RunnableList onEndCallback, + @Nullable WindowAnimationState windowState) { if (!forLaunch && !checkReturnAnimationsFlags()) { throw new IllegalStateException( "forLaunch cannot be false when the enableContainerReturnAnimations or " @@ -1882,7 +1838,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // First the controller is created. This is used by the runner to animate the // origin/target view. ActivityTransitionAnimator.Controller controller = - buildController(v, cookie, forLaunch); + buildController(v, forLaunch, windowState); if (controller == null) { return null; } @@ -1907,8 +1863,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener return new ContainerAnimationRunner( new ActivityTransitionAnimator.AnimationDelegate( - MAIN_EXECUTOR, controller, callback, listener), - cookie); + MAIN_EXECUTOR, controller, callback, listener)); } /** @@ -1918,7 +1873,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener */ @Nullable private static ActivityTransitionAnimator.Controller buildController( - View v, ActivityTransitionAnimator.TransitionCookie cookie, boolean isLaunching) { + View v, boolean isLaunching, @Nullable WindowAnimationState windowState) { View viewToUse = findLaunchableViewWithBackground(v); if (viewToUse == null) { return null; @@ -1949,8 +1904,8 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener @Nullable @Override - public ActivityTransitionAnimator.TransitionCookie getTransitionCookie() { - return cookie; + public WindowAnimationState getWindowAnimatorState() { + return windowState; } }; } @@ -1964,81 +1919,26 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener View view) { View current = view; while (current.getBackground() == null || !(current instanceof LaunchableView)) { - if (!(current.getParent() instanceof View)) { + if (current.getParent() instanceof View v) { + current = v; + } else { return null; } - - current = (View) current.getParent(); } - return (T) current; } - /** - * Builds the filter used by WM Shell to match app closing transitions (only back, no home - * button/gesture) to the given launch cookie. - */ - static TransitionFilter buildBackToHomeFilter( - ActivityTransitionAnimator.TransitionCookie cookie, Launcher launcher) { - // Closing activity must include the cookie in its list of launch cookies. - TransitionFilter.Requirement appRequirement = new TransitionFilter.Requirement(); - appRequirement.mActivityType = ACTIVITY_TYPE_STANDARD; - appRequirement.mLaunchCookie = cookie; - appRequirement.mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; - // Opening activity must be Launcher. - TransitionFilter.Requirement launcherRequirement = new TransitionFilter.Requirement(); - launcherRequirement.mActivityType = ACTIVITY_TYPE_HOME; - launcherRequirement.mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; - launcherRequirement.mTopActivity = launcher.getComponentName(); - // Transition types CLOSE and TO_BACK match the back button/gesture but not the home - // button/gesture. - TransitionFilter filter = new TransitionFilter(); - filter.mTypeSet = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; - filter.mRequirements = - new TransitionFilter.Requirement[]{appRequirement, launcherRequirement}; - return filter; - } - - /** - * Creates various conditions to ensure that the given transition is cleaned up correctly - * when necessary: - * - if the transition has run, it is the callback that unregisters it; - * - if the associated view is detached before the transition has had an opportunity to run, - * a {@link View.OnAttachStateChangeListener} allows us to do the same (and removes - * itself). - */ - static void setUpRemoteAnimationCleanup( - View v, RemoteTransition transition, RunnableList callback, Launcher launcher) { - View.OnAttachStateChangeListener listener = new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(@NonNull View v) {} - - @Override - public void onViewDetachedFromWindow(@NonNull View v) { - SystemUiProxy.INSTANCE.get(launcher) - .unregisterRemoteTransition(transition); - v.removeOnAttachStateChangeListener(this); - } - }; - - // Remove the animation as soon as it has run once. - callback.add(() -> { - SystemUiProxy.INSTANCE.get(launcher).unregisterRemoteTransition(transition); - if (v != null) { - v.removeOnAttachStateChangeListener(listener); - } - }); - - // Remove the animation when the view is detached from the hierarchy. - // This is so that if back is not invoked (e.g. if we go back home through the home - // gesture) we don't have obsolete transitions staying registered. - v.addOnAttachStateChangeListener(listener); - } - @Override public void onAnimationStart(int transit, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, LauncherAnimationRunner.AnimationResult result) { + startAnimation( + transit, appTargets, wallpaperTargets, nonAppTargets, result); + } + + public void startAnimation(int transit, RemoteAnimationTarget[] appTargets, + RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, + IRemoteAnimationFinishedCallback result) { mDelegate.onAnimationStart( transit, appTargets, wallpaperTargets, nonAppTargets, result); } @@ -2047,11 +1947,6 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener public void onAnimationCancelled() { mDelegate.onAnimationCancelled(); } - - @Override - public boolean supportsReturnTransition() { - return true; - } } /** diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java index 360c2160a7..1124aac457 100644 --- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java +++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java @@ -27,7 +27,6 @@ import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.content.ComponentCallbacks; import android.content.res.Configuration; @@ -38,7 +37,6 @@ import android.graphics.RectF; import android.os.Handler; import android.os.RemoteException; import android.util.Log; -import android.util.Pair; import android.view.Choreographer; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; @@ -63,7 +61,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.taskbar.LauncherTaskbarUIController; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.widget.LauncherAppWidgetHostView; -import com.android.quickstep.util.RectFSpringAnim; +import com.android.quickstep.util.BackAnimState; import com.android.systemui.shared.system.QuickStepContract; import java.lang.ref.WeakReference; @@ -109,8 +107,6 @@ public class LauncherBackAnimationController { private RemoteAnimationTarget mLauncherTarget; private View mLauncherTargetView; private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); - private boolean mSpringAnimationInProgress = false; - private boolean mAnimatorSetInProgress = false; private float mBackProgress = 0; private boolean mBackInProgress = false; private OnBackInvokedCallbackStub mBackCallback; @@ -448,14 +444,15 @@ public class LauncherBackAnimationController { mQuickstepTransitionManager.transferRectToTargetCoordinate( mBackTarget, mCurrentRect, true, resolveRectF); - Pair pair = + BackAnimState backAnim = mQuickstepTransitionManager.createWallpaperOpenAnimations( new RemoteAnimationTarget[]{mBackTarget}, new RemoteAnimationTarget[0], + new RemoteAnimationTarget[0], resolveRectF, cornerRadius, mBackInProgress /* fromPredictiveBack */); - startTransitionAnimations(pair.first, pair.second); + startTransitionAnimations(backAnim); mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL); customizeStatusBarAppearance(true); } @@ -470,8 +467,6 @@ public class LauncherBackAnimationController { mCurrentRect.setEmpty(); mStartRect.setEmpty(); mInitialTouchPos.set(0, 0); - mAnimatorSetInProgress = false; - mSpringAnimationInProgress = false; setLauncherTargetViewVisible(true); mLauncherTargetView = null; // We don't call customizeStatusBarAppearance here to prevent the status bar update with @@ -494,27 +489,8 @@ public class LauncherBackAnimationController { } } - private void startTransitionAnimations(RectFSpringAnim springAnim, AnimatorSet anim) { - mAnimatorSetInProgress = anim != null; - mSpringAnimationInProgress = springAnim != null; - if (springAnim != null) { - springAnim.addAnimatorListener( - new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mSpringAnimationInProgress = false; - tryFinishBackAnimation(); - } - } - ); - } - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mAnimatorSetInProgress = false; - tryFinishBackAnimation(); - } - }); + private void startTransitionAnimations(BackAnimState backAnim) { + backAnim.addOnAnimCompleteCallback(this::finishAnimation); if (mScrimLayer == null) { // Scrim hasn't been attached yet. Let's attach it. addScrimLayer(); @@ -534,7 +510,7 @@ public class LauncherBackAnimationController { } }); mScrimAlphaAnimator.setDuration(SCRIM_FADE_DURATION).start(); - anim.start(); + backAnim.start(); } private void loadResources() { @@ -567,12 +543,6 @@ public class LauncherBackAnimationController { mScrimAlpha = 0; } - private void tryFinishBackAnimation() { - if (!mSpringAnimationInProgress && !mAnimatorSetInProgress) { - finishAnimation(); - } - } - private void customizeStatusBarAppearance(boolean overridingStatusBarFlags) { if (mOverridingStatusBarFlags == overridingStatusBarFlags) { return; diff --git a/quickstep/src/com/android/quickstep/util/BackAnimState.kt b/quickstep/src/com/android/quickstep/util/BackAnimState.kt new file mode 100644 index 0000000000..9009eaa263 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/BackAnimState.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2024 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.quickstep.util + +import android.animation.AnimatorSet +import android.content.Context +import com.android.launcher3.LauncherAnimationRunner.AnimationResult +import com.android.launcher3.anim.AnimatorListeners.forEndCallback +import com.android.launcher3.util.RunnableList + +/** Interface to represent animation for back to Launcher transition */ +interface BackAnimState { + + fun addOnAnimCompleteCallback(r: Runnable) + + fun applyToAnimationResult(result: AnimationResult, c: Context) + + fun start() +} + +class AnimatorBackState(private val springAnim: RectFSpringAnim?, private val anim: AnimatorSet?) : + 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) + animWait.add(r) + } + + override fun applyToAnimationResult(result: AnimationResult, c: Context) { + result.setAnimation(anim, c) + } + + override fun start() { + anim?.start() + } +} + +class AlreadyStartedBackAnimState(private val onEndCallback: RunnableList) : BackAnimState { + + override fun addOnAnimCompleteCallback(r: Runnable) { + onEndCallback.add(r) + } + + override fun applyToAnimationResult(result: AnimationResult, c: Context) { + addOnAnimCompleteCallback(result::onAnimationFinished) + } + + override fun start() {} +}