Add return animations to Toast search results.

This only applies to targets that already use the Animation library for
launch animations.

Bug: 336719605
Bug: 298089923
Flag: com.android.launcher3.enable_container_return_animations
Flag: com.android.systemui.shared.return_animation_framework_library
Test: tested manually with flag on and off
Change-Id: Ib824e78fa8b1b226b32d23d8325f06b496ba5deb
This commit is contained in:
Luca Zuccarini
2024-05-24 14:31:01 +00:00
parent f6d91d2d37
commit 52545edb41
4 changed files with 251 additions and 49 deletions

View File

@@ -43,6 +43,7 @@ import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
import static com.android.launcher3.Flags.enableContainerReturnAnimations;
import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
@@ -68,6 +69,7 @@ import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITION
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import static com.android.quickstep.util.AnimUtils.clampToDuration;
import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
@@ -181,6 +183,9 @@ 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);
@@ -333,17 +338,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
restartedListener.register(onEndCallback::executeAllAndDestroy);
onEndCallback.add(restartedListener::unregister);
mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);
ItemInfo tag = (ItemInfo) v.getTag();
if (tag != null && tag.shouldUseBackgroundAnimation()) {
ContainerAnimationRunner containerAnimationRunner = ContainerAnimationRunner.from(
v, mLauncher, mStartingWindowListener, onEndCallback);
if (containerAnimationRunner != null) {
mAppLaunchRunner = containerAnimationRunner;
}
}
RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(
mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */);
RemoteAnimationRunnerCompat runner = createAppLaunchRunner(v, onEndCallback);
// Note that this duration is a guess as we do not know if the animation will be a
// recents launch or not for sure until we know the opening app targets.
@@ -360,9 +355,94 @@ 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) v.getTag(), options);
// Register the return animation so it can be triggered on back from the app to home.
maybeRegisterAppReturnTransition(v);
return new ActivityOptionsWrapper(options, onEndCallback);
}
/**
* Selects the appropriate type of launch runner for the given view, builds it, and returns it.
* {@link QuickstepTransitionManager#mAppLaunchRunner} is updated as a by-product of this
* method.
*/
private RemoteAnimationRunnerCompat createAppLaunchRunner(View v, RunnableList onEndCallback) {
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;
}
}
mAppLaunchRunner = containerRunner != null
? containerRunner : new AppLaunchAnimationRunner(v, onEndCallback);
return new LauncherAnimationRunner(
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 = mLauncher.getLaunchCookie(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
@@ -1728,6 +1808,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
}
}
private static boolean checkReturnAnimationsFlags() {
return enableContainerReturnAnimations() && returnAnimationFrameworkLibrary();
}
/**
* Remote animation runner for animation from the app to Launcher, including recents.
*/
@@ -1844,38 +1928,45 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
/** The delegate runner that handles the actual animation. */
private final RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> mDelegate;
@Nullable
private final ActivityTransitionAnimator.TransitionCookie mCookie;
private ContainerAnimationRunner(
RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> delegate) {
RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> delegate,
ActivityTransitionAnimator.TransitionCookie cookie) {
mDelegate = delegate;
mCookie = cookie;
}
@Nullable
private static ContainerAnimationRunner from(View v, Launcher launcher,
StartingWindowListener startingWindowListener, RunnableList onEndCallback) {
View viewToUse = findLaunchableViewWithBackground(v);
if (viewToUse == null) {
return null;
ActivityTransitionAnimator.TransitionCookie getCookie() {
return mCookie;
}
@Nullable
static ContainerAnimationRunner fromView(
View v,
ActivityTransitionAnimator.TransitionCookie cookie,
boolean forLaunch,
Launcher launcher,
StartingWindowListener startingWindowListener,
RunnableList onEndCallback) {
if (!forLaunch && !checkReturnAnimationsFlags()) {
throw new IllegalStateException(
"forLaunch cannot be false when the enableContainerReturnAnimations or "
+ "returnAnimationFrameworkLibrary flag is disabled");
}
// The CUJ is logged by the click handler, so we don't log it inside the animation
// library.
ActivityTransitionAnimator.Controller controllerDelegate =
ActivityTransitionAnimator.Controller.fromView(viewToUse, null /* cujType */);
if (controllerDelegate == null) {
return null;
}
// This wrapper allows us to override the default value, telling the controller that the
// current window is below the animating window.
// First the controller is created. This is used by the runner to animate the
// origin/target view.
ActivityTransitionAnimator.Controller controller =
new DelegateTransitionAnimatorController(controllerDelegate) {
@Override
public boolean isBelowAnimatingWindow() {
return true;
}
};
buildController(v, cookie, forLaunch);
if (controller == null) {
return null;
}
// The callback is used to make sure that we use the right color to fade between view
// and the window.
ActivityTransitionAnimator.Callback callback = task -> {
final int backgroundColor =
startingWindowListener.mBackgroundColor == Color.TRANSPARENT
@@ -1894,7 +1985,52 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
return new ContainerAnimationRunner(
new ActivityTransitionAnimator.AnimationDelegate(
MAIN_EXECUTOR, controller, callback, listener));
MAIN_EXECUTOR, controller, callback, listener),
cookie);
}
/**
* Constructs a {@link ActivityTransitionAnimator.Controller} that can be used by a
* {@link ContainerAnimationRunner} to animate a view into an opening window or from a
* closing one.
*/
@Nullable
private static ActivityTransitionAnimator.Controller buildController(
View v, ActivityTransitionAnimator.TransitionCookie cookie, boolean isLaunching) {
View viewToUse = findLaunchableViewWithBackground(v);
if (viewToUse == null) {
return null;
}
// The CUJ is logged by the click handler, so we don't log it inside the animation
// library. TODO: figure out return CUJ.
ActivityTransitionAnimator.Controller controllerDelegate =
ActivityTransitionAnimator.Controller.fromView(viewToUse, null /* cujType */);
if (controllerDelegate == null) {
return null;
}
// This wrapper allows us to override the default value, telling the controller that the
// current window is below the animating window as well as information about the return
// animation.
return new DelegateTransitionAnimatorController(controllerDelegate) {
@Override
public boolean isLaunching() {
return isLaunching;
}
@Override
public boolean isBelowAnimatingWindow() {
return true;
}
@Nullable
@Override
public ActivityTransitionAnimator.TransitionCookie getTransitionCookie() {
return cookie;
}
};
}
/**
@@ -1916,6 +2052,67 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
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,
@@ -1928,6 +2125,11 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener
public void onAnimationCancelled() {
mDelegate.onAnimationCancelled();
}
@Override
public boolean supportsReturnTransition() {
return true;
}
}
/**