• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
22 import static android.app.role.RoleManager.ROLE_HOME;
23 import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
24 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
25 import static android.view.RemoteAnimationTarget.MODE_OPENING;
26 import static android.view.Surface.ROTATION_0;
27 import static android.view.Surface.ROTATION_180;
28 import static android.view.WindowManager.TRANSIT_CLOSE;
29 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
30 import static android.view.WindowManager.TRANSIT_OPEN;
31 import static android.view.WindowManager.TRANSIT_TO_BACK;
32 import static android.view.WindowManager.TRANSIT_TO_FRONT;
33 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
34 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
35 import static android.window.TransitionFilter.CONTAINER_ORDER_TOP;
36 
37 import static com.android.app.animation.Interpolators.ACCELERATE_1_5;
38 import static com.android.app.animation.Interpolators.AGGRESSIVE_EASE;
39 import static com.android.app.animation.Interpolators.DECELERATE_1_5;
40 import static com.android.app.animation.Interpolators.DECELERATE_1_7;
41 import static com.android.app.animation.Interpolators.EXAGGERATED_EASE;
42 import static com.android.app.animation.Interpolators.LINEAR;
43 import static com.android.launcher3.BaseActivity.EVENT_DESTROYED;
44 import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
45 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
46 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
47 import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
48 import static com.android.launcher3.Flags.enableContainerReturnAnimations;
49 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
50 import static com.android.launcher3.Flags.syncAppLaunchWithTaskbarStash;
51 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
52 import static com.android.launcher3.LauncherState.ALL_APPS;
53 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
54 import static com.android.launcher3.LauncherState.NORMAL;
55 import static com.android.launcher3.LauncherState.OVERVIEW;
56 import static com.android.launcher3.Utilities.mapBoundToRange;
57 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
58 import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE;
59 import static com.android.launcher3.util.DisplayController.isTransientTaskbar;
60 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
61 import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
62 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
63 import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
64 import static com.android.launcher3.views.FloatingIconView.getFloatingIconView;
65 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
66 import static com.android.quickstep.util.AnimUtils.clampToDuration;
67 import static com.android.quickstep.util.AnimUtils.completeRunnableListCallback;
68 import static com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary;
69 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
70 import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
71 import static com.android.wm.shell.Flags.enableDynamicInsetsForAppLaunch;
72 
73 import android.animation.Animator;
74 import android.animation.AnimatorListenerAdapter;
75 import android.animation.AnimatorSet;
76 import android.animation.ObjectAnimator;
77 import android.animation.ValueAnimator;
78 import android.app.ActivityOptions;
79 import android.app.WindowConfiguration;
80 import android.app.role.RoleManager;
81 import android.content.ComponentName;
82 import android.content.Context;
83 import android.content.res.Resources;
84 import android.graphics.Color;
85 import android.graphics.Matrix;
86 import android.graphics.Point;
87 import android.graphics.PointF;
88 import android.graphics.Rect;
89 import android.graphics.RectF;
90 import android.graphics.drawable.Drawable;
91 import android.os.Handler;
92 import android.os.IBinder;
93 import android.os.IRemoteCallback;
94 import android.os.Looper;
95 import android.os.SystemProperties;
96 import android.os.UserHandle;
97 import android.provider.Settings;
98 import android.util.Log;
99 import android.util.Pair;
100 import android.util.Size;
101 import android.view.CrossWindowBlurListeners;
102 import android.view.IRemoteAnimationFinishedCallback;
103 import android.view.RemoteAnimationAdapter;
104 import android.view.RemoteAnimationDefinition;
105 import android.view.RemoteAnimationTarget;
106 import android.view.SurfaceControl;
107 import android.view.View;
108 import android.view.ViewRootImpl;
109 import android.view.ViewTreeObserver;
110 import android.view.WindowManager;
111 import android.view.animation.AnimationUtils;
112 import android.view.animation.Interpolator;
113 import android.view.animation.PathInterpolator;
114 import android.window.DesktopModeFlags;
115 import android.window.RemoteTransition;
116 import android.window.TransitionFilter;
117 import android.window.WindowAnimationState;
118 
119 import androidx.annotation.NonNull;
120 import androidx.annotation.Nullable;
121 import androidx.core.graphics.ColorUtils;
122 
123 import com.android.app.animation.Animations;
124 import com.android.internal.jank.Cuj;
125 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
126 import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
127 import com.android.launcher3.anim.AnimationSuccessListener;
128 import com.android.launcher3.anim.AnimatorListeners;
129 import com.android.launcher3.compat.AccessibilityManagerCompat;
130 import com.android.launcher3.dragndrop.DragLayer;
131 import com.android.launcher3.icons.FastBitmapDrawable;
132 import com.android.launcher3.model.data.ItemInfo;
133 import com.android.launcher3.shortcuts.DeepShortcutView;
134 import com.android.launcher3.statehandlers.DepthController;
135 import com.android.launcher3.taskbar.LauncherTaskbarUIController;
136 import com.android.launcher3.testing.shared.ResourceUtils;
137 import com.android.launcher3.touch.PagedOrientationHandler;
138 import com.android.launcher3.uioverrides.QuickstepLauncher;
139 import com.android.launcher3.util.ActivityOptionsWrapper;
140 import com.android.launcher3.util.DynamicResource;
141 import com.android.launcher3.util.RunnableList;
142 import com.android.launcher3.util.StableViewInfo;
143 import com.android.launcher3.views.FloatingIconView;
144 import com.android.launcher3.widget.LauncherAppWidgetHostView;
145 import com.android.quickstep.LauncherBackAnimationController;
146 import com.android.quickstep.RemoteAnimationTargets;
147 import com.android.quickstep.SystemUiProxy;
148 import com.android.quickstep.TaskViewUtils;
149 import com.android.quickstep.util.AlreadyStartedBackAnimState;
150 import com.android.quickstep.util.AnimatorBackState;
151 import com.android.quickstep.util.BackAnimState;
152 import com.android.quickstep.util.MultiValueUpdateListener;
153 import com.android.quickstep.util.RectFSpringAnim;
154 import com.android.quickstep.util.RectFSpringAnim.DefaultSpringConfig;
155 import com.android.quickstep.util.RectFSpringAnim.TaskbarHotseatSpringConfig;
156 import com.android.quickstep.util.ScalingWorkspaceRevealAnim;
157 import com.android.quickstep.util.StaggeredWorkspaceAnim;
158 import com.android.quickstep.util.SurfaceTransaction;
159 import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
160 import com.android.quickstep.util.SurfaceTransactionApplier;
161 import com.android.quickstep.util.TaskRestartedDuringLaunchListener;
162 import com.android.quickstep.util.WorkspaceRevealAnim;
163 import com.android.quickstep.views.FloatingWidgetView;
164 import com.android.quickstep.views.RecentsView;
165 import com.android.systemui.animation.ActivityTransitionAnimator;
166 import com.android.systemui.animation.DelegateTransitionAnimatorController;
167 import com.android.systemui.animation.LaunchableView;
168 import com.android.systemui.animation.RemoteAnimationDelegate;
169 import com.android.systemui.animation.RemoteAnimationRunnerCompat;
170 import com.android.systemui.shared.system.BlurUtils;
171 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
172 import com.android.systemui.shared.system.QuickStepContract;
173 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
174 import com.android.wm.shell.startingsurface.IStartingWindowListener;
175 
176 import java.io.PrintWriter;
177 import java.lang.ref.WeakReference;
178 import java.util.ArrayList;
179 import java.util.Arrays;
180 import java.util.Collections;
181 import java.util.LinkedHashMap;
182 import java.util.List;
183 import java.util.Map.Entry;
184 
185 /**
186  * Manages the opening and closing app transitions from Launcher
187  */
188 public class QuickstepTransitionManager implements OnDeviceProfileChangeListener {
189     private static final String TAG = "QuickstepTransitionManager";
190 
191     private static final boolean ENABLE_SHELL_STARTING_SURFACE =
192             SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
193 
194     /** Duration of status bar animations. */
195     public static final int STATUS_BAR_TRANSITION_DURATION = 120;
196 
197     /**
198      * Since our animations decelerate heavily when finishing, we want to start status bar
199      * animations x ms before the ending.
200      */
201     public static final int STATUS_BAR_TRANSITION_PRE_DELAY = 96;
202 
203     public static final long APP_LAUNCH_DURATION = 500;
204 
205     private static final long APP_LAUNCH_ALPHA_DURATION = 50;
206     private static final long APP_LAUNCH_ALPHA_START_DELAY = 25;
207 
208     public static final int ANIMATION_NAV_FADE_IN_DURATION = 266;
209     public static final int ANIMATION_NAV_FADE_OUT_DURATION = 133;
210     public static final long ANIMATION_DELAY_NAV_FADE_IN =
211             APP_LAUNCH_DURATION - ANIMATION_NAV_FADE_IN_DURATION;
212     public static final Interpolator NAV_FADE_IN_INTERPOLATOR =
213             new PathInterpolator(0f, 0f, 0f, 1f);
214     public static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
215             new PathInterpolator(0.2f, 0f, 1f, 1f);
216 
217     public static final int RECENTS_LAUNCH_DURATION = 336;
218     private static final int LAUNCHER_RESUME_START_DELAY = 100;
219     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
220     public static final int SPLIT_LAUNCH_DURATION = 370;
221     public static final int SPLIT_DIVIDER_ANIM_DURATION = 100;
222 
223     public static final int CONTENT_ALPHA_DURATION = 217;
224     public static final int TRANSIENT_TASKBAR_TRANSITION_DURATION = 417;
225     public static final int PINNED_TASKBAR_TRANSITION_DURATION = 600;
226     public static final int TASKBAR_TO_APP_DURATION = 600;
227     // TODO(b/236145847): Tune TASKBAR_TO_HOME_DURATION to 383 after conflict with unlock animation
228     // is solved.
229     private static final int TASKBAR_TO_HOME_DURATION_FAST = 300;
230     private static final int TASKBAR_TO_HOME_DURATION_SLOW = 1000;
231     protected static final int CONTENT_SCALE_DURATION = 350;
232 
233     private static final int MAX_NUM_TASKS = 5;
234 
235     // Cross-fade duration between App Widget and App
236     private static final int WIDGET_CROSSFADE_DURATION_MILLIS = 125;
237 
238     protected final QuickstepLauncher mLauncher;
239     protected final DragLayer mDragLayer;
240 
241     protected final Handler mHandler;
242 
243     private final float mClosingWindowTransY;
244     private final float mClosingFreeformWindowTransY;
245     private final float mMaxShadowRadius;
246 
247     private final StartingWindowListener mStartingWindowListener =
248             new StartingWindowListener(this);
249 
250     // TODO(b/397690719): Investigate the memory leak from TaskStackChangeListeners#mImpl
251     // This is a temporary fix of memory leak b/397690719. We track registered
252     // {@link TaskRestartedDuringLaunchListener}, and remove them on activity destroy.
253     private final List<TaskRestartedDuringLaunchListener> mRegisteredTaskStackChangeListener =
254             new ArrayList<>();
255 
256     private DeviceProfile mDeviceProfile;
257 
258     // Strong refs to runners which are cleared when the launcher activity is destroyed
259     private RemoteAnimationFactory mWallpaperOpenRunner;
260     private RemoteAnimationFactory mAppLaunchRunner;
261 
262     private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
263     private RemoteTransition mLauncherOpenTransition;
264 
265     private final RemoteAnimationCoordinateTransfer mCoordinateTransfer;
266 
267     private LauncherBackAnimationController mBackAnimationController;
268     private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
269         @Override
270         public void onAnimationStart(Animator animation) {
271             mLauncher.addForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
272         }
273 
274         @Override
275         public void onAnimationEnd(Animator animation) {
276             mLauncher.clearForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
277         }
278     };
279 
280     // Pairs of window starting type and starting window background color for starting tasks
281     // Will never be larger than MAX_NUM_TASKS
282     private LinkedHashMap<Integer, Pair<Integer, Integer>> mTaskStartParams;
283 
284     private final Interpolator mOpeningXInterpolator;
285     private final Interpolator mOpeningInterpolator;
286 
287     private final SystemUiProxy mSystemUiProxy;
288 
QuickstepTransitionManager(Context context)289     public QuickstepTransitionManager(Context context) {
290         mLauncher = Launcher.cast(Launcher.getLauncher(context));
291         mDragLayer = mLauncher.getDragLayer();
292         mHandler = new Handler(Looper.getMainLooper());
293         mDeviceProfile = mLauncher.getDeviceProfile();
294         mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
295 
296         Resources res = mLauncher.getResources();
297         mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
298         mClosingFreeformWindowTransY =
299                 res.getDimensionPixelSize(R.dimen.closing_freeform_window_trans_y);
300         mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
301 
302         mLauncher.addOnDeviceProfileChangeListener(this);
303         mSystemUiProxy = SystemUiProxy.INSTANCE.get(mLauncher);
304 
305         if (ENABLE_SHELL_STARTING_SURFACE) {
306             mTaskStartParams = new LinkedHashMap<>(MAX_NUM_TASKS) {
307                 @Override
308                 protected boolean removeEldestEntry(Entry<Integer, Pair<Integer, Integer>> entry) {
309                     return size() > MAX_NUM_TASKS;
310                 }
311             };
312 
313             mSystemUiProxy.setStartingWindowListener(mStartingWindowListener);
314         }
315 
316         mOpeningXInterpolator = AnimationUtils.loadInterpolator(context, R.interpolator.app_open_x);
317         mOpeningInterpolator = AnimationUtils.loadInterpolator(context,
318                 R.interpolator.emphasized_interpolator);
319         mCoordinateTransfer = new RemoteAnimationCoordinateTransfer(mLauncher);
320     }
321 
322     @Override
onDeviceProfileChanged(DeviceProfile dp)323     public void onDeviceProfileChanged(DeviceProfile dp) {
324         mDeviceProfile = dp;
325     }
326 
327     /**
328      * @return ActivityOptions with remote animations that controls how the window of the opening
329      * targets are displayed.
330      */
getActivityLaunchOptions(View v, ItemInfo itemInfo)331     public ActivityOptionsWrapper getActivityLaunchOptions(View v, ItemInfo itemInfo) {
332         boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
333         RunnableList onEndCallback = new RunnableList();
334 
335         // Handle the case where an already visible task is launched which results in no transition
336         TaskRestartedDuringLaunchListener restartedListener =
337                 new TaskRestartedDuringLaunchListener();
338         restartedListener.register(onEndCallback::executeAllAndDestroy);
339         mRegisteredTaskStackChangeListener.add(restartedListener);
340         onEndCallback.add(new Runnable() {
341             @Override
342             public void run() {
343                 restartedListener.unregister();
344                 mRegisteredTaskStackChangeListener.remove(restartedListener);
345             }
346         });
347 
348         RemoteAnimationRunnerCompat runner = createAppLaunchRunner(v, onEndCallback);
349 
350         // Note that this duration is a guess as we do not know if the animation will be a
351         // recents launch or not for sure until we know the opening app targets.
352         long duration = fromRecents
353                 ? RECENTS_LAUNCH_DURATION
354                 : APP_LAUNCH_DURATION;
355 
356         long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
357                 - STATUS_BAR_TRANSITION_PRE_DELAY;
358         ActivityOptions options = ActivityOptions.makeRemoteAnimation(
359                 new RemoteAnimationAdapter(runner, duration, statusBarTransitionDelay),
360                 new RemoteTransition(runner.toRemoteTransition(),
361                         mLauncher.getIApplicationThread(), "QuickstepLaunch"));
362         IRemoteCallback endCallback = completeRunnableListCallback(onEndCallback, mLauncher);
363         options.setOnAnimationAbortListener(endCallback);
364         options.setOnAnimationFinishedListener(endCallback);
365         options.setLaunchCookie(StableViewInfo.toLaunchCookie(itemInfo));
366 
367         // Prepare taskbar for animation synchronization. This needs to happen here before any
368         // app transition is created.
369         LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
370         if (syncAppLaunchWithTaskbarStash()
371                 && enableScalingRevealHomeAnimation()
372                 && taskbarController != null) {
373             taskbarController.setIgnoreInAppFlagForSync(true);
374             mLauncher.addEventCallback(EVENT_DESTROYED, onEndCallback::executeAllAndDestroy);
375             onEndCallback.add(() -> {
376                 taskbarController.setIgnoreInAppFlagForSync(false);
377             });
378         }
379 
380         return new ActivityOptionsWrapper(options, onEndCallback);
381     }
382 
383     /**
384      * Selects the appropriate type of launch runner for the given view, builds it, and returns it.
385      * {@link QuickstepTransitionManager#mAppLaunchRunner} is updated as a by-product of this
386      * method.
387      */
createAppLaunchRunner(View v, RunnableList onEndCallback)388     private RemoteAnimationRunnerCompat createAppLaunchRunner(View v, RunnableList onEndCallback) {
389         ItemInfo tag = (ItemInfo) v.getTag();
390         ContainerAnimationRunner containerRunner = null;
391         if (tag != null && tag.shouldUseBackgroundAnimation()) {
392             containerRunner = ContainerAnimationRunner.fromView(
393                     v, true /* forLaunch */, mLauncher, mStartingWindowListener, onEndCallback,
394                     null /* windowState */);
395         }
396 
397         mAppLaunchRunner = containerRunner != null
398                 ? containerRunner : new AppLaunchAnimationRunner(v, onEndCallback);
399         return new LauncherAnimationRunner(
400                 mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */);
401     }
402 
403     /**
404      * Whether the launch is a recents app transition and we should do a launch animation
405      * from the recents view. Note that if the remote animation targets are not provided, this
406      * may not always be correct as we may resolve the opening app to a task when the animation
407      * starts.
408      *
409      * @param v       the view to launch from
410      * @param targets apps that are opening/closing
411      * @return true if the app is launching from recents, false if it most likely is not
412      */
isLaunchingFromRecents(@onNull View v, @Nullable RemoteAnimationTarget[] targets)413     protected boolean isLaunchingFromRecents(@NonNull View v,
414             @Nullable RemoteAnimationTarget[] targets) {
415         return mLauncher.getStateManager().getState().isRecentsViewVisible
416                 && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
417     }
418 
419     /**
420      * Composes the animations for a launch from the recents list.
421      *
422      * @param anim            the animator set to add to
423      * @param v               the launching view
424      * @param appTargets      the apps that are opening/closing
425      * @param launcherClosing true if the launcher app is closing
426      */
composeRecentsLaunchAnimator(@onNull AnimatorSet anim, @NonNull View v, @NonNull RemoteAnimationTarget[] appTargets, @NonNull RemoteAnimationTarget[] wallpaperTargets, @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing)427     protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
428             @NonNull RemoteAnimationTarget[] appTargets,
429             @NonNull RemoteAnimationTarget[] wallpaperTargets,
430             @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing) {
431         TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
432                 nonAppTargets, launcherClosing, mLauncher.getStateManager(),
433                 mLauncher.getOverviewPanel(), mLauncher.getDepthController(),
434                 /* transitionInfo= */ null);
435     }
436 
areAllTargetsTranslucent(@onNull RemoteAnimationTarget[] targets)437     private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTarget[] targets) {
438         boolean isAllOpeningTargetTrs = true;
439         for (int i = 0; i < targets.length; i++) {
440             RemoteAnimationTarget target = targets[i];
441             if (target.mode == MODE_OPENING) {
442                 isAllOpeningTargetTrs &= target.isTranslucent;
443             }
444             if (!isAllOpeningTargetTrs) break;
445         }
446         return isAllOpeningTargetTrs;
447     }
448 
449     /**
450      * Compose the animations for a launch from the app icon.
451      *
452      * @param anim            the animation to add to
453      * @param v               the launching view with the icon
454      * @param appTargets      the list of opening/closing apps
455      * @param launcherClosing true if launcher is closing
456      */
composeIconLaunchAnimator(@onNull AnimatorSet anim, @NonNull View v, @NonNull RemoteAnimationTarget[] appTargets, @NonNull RemoteAnimationTarget[] wallpaperTargets, @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing)457     private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
458             @NonNull RemoteAnimationTarget[] appTargets,
459             @NonNull RemoteAnimationTarget[] wallpaperTargets,
460             @NonNull RemoteAnimationTarget[] nonAppTargets,
461             boolean launcherClosing) {
462         // Set the state animation first so that any state listeners are called
463         // before our internal listeners.
464         mLauncher.getStateManager().setCurrentAnimation(anim);
465 
466         // Note: the targetBounds are relative to the launcher
467         int startDelay = getSingleFrameMs(mLauncher);
468         Animator windowAnimator = getOpeningWindowAnimators(
469                 v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing);
470         windowAnimator.setStartDelay(startDelay);
471         anim.play(windowAnimator);
472         if (launcherClosing) {
473             // Delay animation by a frame to avoid jank.
474             Pair<AnimatorSet, Runnable> launcherContentAnimator =
475                     getLauncherContentAnimator(true /* isAppOpening */, startDelay, false);
476             anim.play(launcherContentAnimator.first);
477             anim.addListener(new AnimatorListenerAdapter() {
478                 @Override
479                 public void onAnimationEnd(Animator animation) {
480                     launcherContentAnimator.second.run();
481                 }
482             });
483         }
484     }
485 
composeWidgetLaunchAnimator( @onNull AnimatorSet anim, @NonNull LauncherAppWidgetHostView v, @NonNull RemoteAnimationTarget[] appTargets, @NonNull RemoteAnimationTarget[] wallpaperTargets, @NonNull RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing)486     private void composeWidgetLaunchAnimator(
487             @NonNull AnimatorSet anim,
488             @NonNull LauncherAppWidgetHostView v,
489             @NonNull RemoteAnimationTarget[] appTargets,
490             @NonNull RemoteAnimationTarget[] wallpaperTargets,
491             @NonNull RemoteAnimationTarget[] nonAppTargets,
492             boolean launcherClosing) {
493         mLauncher.getStateManager().setCurrentAnimation(anim);
494         anim.play(getOpeningWindowAnimatorsForWidget(
495                 v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing));
496     }
497 
498     /**
499      * Return the window bounds of the opening target.
500      * In multiwindow mode, we need to get the final size of the opening app window target to help
501      * figure out where the floating view should animate to.
502      */
getWindowTargetBounds(@onNull RemoteAnimationTarget[] appTargets, int rotationChange)503     private Rect getWindowTargetBounds(@NonNull RemoteAnimationTarget[] appTargets,
504             int rotationChange) {
505         RemoteAnimationTarget target = null;
506         for (RemoteAnimationTarget t : appTargets) {
507             if (t.mode != MODE_OPENING) continue;
508             target = t;
509             break;
510         }
511         if (target == null) return new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
512         final Rect bounds = new Rect(target.screenSpaceBounds);
513         if (target.localBounds != null) {
514             bounds.set(target.localBounds);
515         } else {
516             bounds.offsetTo(target.position.x, target.position.y);
517         }
518         if (rotationChange != 0) {
519             if ((rotationChange % 2) == 1) {
520                 // undoing rotation, so our "original" parent size is actually flipped
521                 Utilities.rotateBounds(bounds, mDeviceProfile.heightPx, mDeviceProfile.widthPx,
522                         4 - rotationChange);
523             } else {
524                 Utilities.rotateBounds(bounds, mDeviceProfile.widthPx, mDeviceProfile.heightPx,
525                         4 - rotationChange);
526             }
527         }
528         return bounds;
529     }
530 
531     /** Dump debug logs to bug report. */
dump(@onNull String prefix, @NonNull PrintWriter printWriter)532     public void dump(@NonNull String prefix, @NonNull PrintWriter printWriter) {}
533 
534     /**
535      * Content is everything on screen except the background and the floating view (if any).
536      *
537      * @param isAppOpening     True when this is called when an app is opening.
538      *                         False when this is called when an app is closing.
539      * @param startDelay       Start delay duration.
540      * @param skipAllAppsScale True if we want to avoid scaling All Apps
541      */
getLauncherContentAnimator(boolean isAppOpening, int startDelay, boolean skipAllAppsScale)542     private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
543             int startDelay, boolean skipAllAppsScale) {
544         AnimatorSet launcherAnimator = new AnimatorSet();
545         Runnable endListener;
546 
547         float[] alphas = isAppOpening
548                 ? new float[]{1, 0}
549                 : new float[]{0, 1};
550 
551         float[] scales = isAppOpening
552                 ? new float[]{1, mDeviceProfile.workspaceContentScale}
553                 : new float[]{mDeviceProfile.workspaceContentScale, 1};
554 
555         // Pause expensive view updates as they can lead to layer thrashing and skipped frames.
556         mLauncher.pauseExpensiveViewUpdates();
557 
558         if (mLauncher.isInState(ALL_APPS)) {
559             // All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
560             final View appsView = mLauncher.getAppsView();
561             final float startAlpha = appsView.getAlpha();
562             final float startScale = SCALE_PROPERTY.get(appsView);
563             if (mDeviceProfile.isTablet) {
564                 // AllApps should not fade at all in tablets.
565                 alphas = new float[]{1, 1};
566             }
567             appsView.setAlpha(alphas[0]);
568 
569             ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
570             alpha.setDuration(CONTENT_ALPHA_DURATION);
571             alpha.setInterpolator(LINEAR);
572             appsView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
573             alpha.addListener(new AnimatorListenerAdapter() {
574                 @Override
575                 public void onAnimationEnd(Animator animation) {
576                     appsView.setLayerType(View.LAYER_TYPE_NONE, null);
577                 }
578             });
579 
580             if (!skipAllAppsScale) {
581                 SCALE_PROPERTY.set(appsView, scales[0]);
582                 ObjectAnimator scale = ObjectAnimator.ofFloat(appsView, SCALE_PROPERTY, scales);
583                 scale.setInterpolator(AGGRESSIVE_EASE);
584                 scale.setDuration(CONTENT_SCALE_DURATION);
585                 launcherAnimator.play(scale);
586             }
587 
588             launcherAnimator.play(alpha);
589 
590             endListener = () -> {
591                 appsView.setAlpha(startAlpha);
592                 SCALE_PROPERTY.set(appsView, startScale);
593                 appsView.setLayerType(View.LAYER_TYPE_NONE, null);
594                 mLauncher.resumeExpensiveViewUpdates();
595             };
596         } else if (mLauncher.isInState(OVERVIEW)) {
597             endListener = composeViewContentAnimator(launcherAnimator, alphas, scales);
598         } else {
599             List<View> viewsToAnimate = new ArrayList<>();
600             Workspace<?> workspace = mLauncher.getWorkspace();
601             if (Flags.coordinateWorkspaceScale()) {
602                 viewsToAnimate.add(workspace);
603             } else {
604                 workspace.forEachVisiblePage(
605                         view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets()));
606             }
607 
608             Hotseat hotseat = mLauncher.getHotseat();
609             // Do not scale hotseat as a whole when taskbar is present, and scale QSB only if it's
610             // not inline.
611             if (mDeviceProfile.isTaskbarPresent) {
612                 if (!mDeviceProfile.isQsbInline) {
613                     viewsToAnimate.add(hotseat.getQsb());
614                 }
615             } else {
616                 viewsToAnimate.add(hotseat);
617             }
618 
619             viewsToAnimate.forEach(view -> {
620                 view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
621 
622                 float[] scale = scales;
623                 if (Flags.coordinateWorkspaceScale()) {
624                     // Start the animation from the current value, instead of assuming the views are
625                     // in their resting state, so interrupted animations merge seamlessly.
626                     // TODO(b/367591368): ideally these animations would be refactored to be
627                     //  controlled centrally so each instances doesn't need to care about this
628                     //  coordination.
629                     scale = new float[]{view.getScaleX(), scales[1]};
630 
631                     // Cancel any ongoing animations. This is necessary to avoid a conflict between
632                     // e.g. the unfinished animation triggered when closing an app back to Home and
633                     // this animation caused by a launch.
634                     Animations.Companion.cancelOngoingAnimation(view);
635                     // Make sure to cache the current animation, so it can be properly interrupted.
636                     Animations.Companion.setOngoingAnimation(view, launcherAnimator);
637                 }
638 
639                 ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(view, SCALE_PROPERTY, scale)
640                         .setDuration(CONTENT_SCALE_DURATION);
641                 scaleAnim.setInterpolator(DECELERATE_1_5);
642                 launcherAnimator.play(scaleAnim);
643             });
644 
645             endListener = () -> {
646                 viewsToAnimate.forEach(view -> {
647                     SCALE_PROPERTY.set(view, 1f);
648                     view.setLayerType(View.LAYER_TYPE_NONE, null);
649 
650                     if (Flags.coordinateWorkspaceScale()) {
651                         // Reset the cached animation.
652                         Animations.Companion.setOngoingAnimation(view, null /* animation */);
653                     }
654                 });
655                 mLauncher.resumeExpensiveViewUpdates();
656             };
657         }
658 
659         launcherAnimator.setStartDelay(startDelay);
660         return new Pair<>(launcherAnimator, endListener);
661     }
662 
663     /**
664      * Compose recents view alpha and translation Y animation when launcher opens/closes apps.
665      *
666      * @param anim   the animator set to add to
667      * @param alphas the alphas to animate to over time
668      * @param scales the scale values to animator to over time
669      * @return listener to run when the animation ends
670      */
composeViewContentAnimator(@onNull AnimatorSet anim, float[] alphas, float[] scales)671     protected Runnable composeViewContentAnimator(@NonNull AnimatorSet anim,
672             float[] alphas, float[] scales) {
673         RecentsView overview = mLauncher.getOverviewPanel();
674         ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
675                 RecentsView.CONTENT_ALPHA, alphas);
676         alpha.setDuration(CONTENT_ALPHA_DURATION);
677         alpha.setInterpolator(LINEAR);
678         anim.play(alpha);
679         overview.setFreezeViewVisibility(true);
680 
681         ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(overview, SCALE_PROPERTY, scales);
682         scaleAnim.setInterpolator(AGGRESSIVE_EASE);
683         scaleAnim.setDuration(CONTENT_SCALE_DURATION);
684         anim.play(scaleAnim);
685 
686         return () -> {
687             overview.setFreezeViewVisibility(false);
688             SCALE_PROPERTY.set(overview, 1f);
689             mLauncher.getStateManager().reapplyState();
690             mLauncher.resumeExpensiveViewUpdates();
691         };
692     }
693 
shouldCropToInset(RemoteAnimationTarget target)694     private boolean shouldCropToInset(RemoteAnimationTarget target) {
695         return enableDynamicInsetsForAppLaunch()
696                 && mDeviceProfile.isTaskbarPresentInApps
697                 && target != null && !target.willShowImeOnTarget
698                 && !isTransientTaskbar(mLauncher);
699     }
700 
701     /**
702      * @return Animator that controls the window of the opening targets from app icons.
703      */
getOpeningWindowAnimators(View v, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing)704     private Animator getOpeningWindowAnimators(View v,
705             RemoteAnimationTarget[] appTargets,
706             RemoteAnimationTarget[] wallpaperTargets,
707             RemoteAnimationTarget[] nonAppTargets,
708             boolean launcherClosing) {
709         RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
710                 wallpaperTargets, nonAppTargets, MODE_OPENING);
711         int rotationChange = getRotationChange(appTargets);
712         Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
713         final int[] bottomInsetPos = new int[]{
714                 mSystemUiProxy.getHomeVisibilityState().getNavbarInsetPosition()};
715         final RemoteAnimationTarget target = openingTargets.getFirstAppTarget();
716         final boolean cropToInset = shouldCropToInset(target);
717         if (cropToInset) {
718             // Animate to above the taskbar.
719             windowTargetBounds.bottom = Math.min(bottomInsetPos[0],
720                     windowTargetBounds.bottom);
721         }
722         boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets);
723 
724         RectF launcherIconBounds = new RectF();
725         FloatingIconView floatingView = getFloatingIconView(mLauncher, v,
726                 (mLauncher.getTaskbarUIController() == null || !isTransientTaskbar(mLauncher))
727                         ? null
728                         : mLauncher.getTaskbarUIController().findMatchingView(v),
729                 null /* fadeOutView */, !appTargetsAreTranslucent, launcherIconBounds,
730                 true /* isOpening */);
731         Rect crop = new Rect();
732         Matrix matrix = new Matrix();
733 
734         SurfaceTransactionApplier surfaceApplier =
735                 new SurfaceTransactionApplier(floatingView);
736         openingTargets.addReleaseCheck(surfaceApplier);
737         RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
738 
739         int[] dragLayerBounds = new int[2];
740         mDragLayer.getLocationOnScreen(dragLayerBounds);
741 
742         final boolean hasSplashScreen;
743         if (ENABLE_SHELL_STARTING_SURFACE) {
744             int taskId = openingTargets.getFirstAppTargetTaskId();
745             Pair<Integer, Integer> defaultParams = Pair.create(STARTING_WINDOW_TYPE_NONE, 0);
746             Pair<Integer, Integer> taskParams =
747                     mTaskStartParams.getOrDefault(taskId, defaultParams);
748             mTaskStartParams.remove(taskId);
749             hasSplashScreen = taskParams.first == STARTING_WINDOW_TYPE_SPLASH_SCREEN;
750         } else {
751             hasSplashScreen = false;
752         }
753 
754         AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile,
755                 windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1],
756                 hasSplashScreen, floatingView.isDifferentFromAppIcon());
757         int left = prop.cropCenterXStart - prop.cropWidthStart / 2;
758         int top = prop.cropCenterYStart - prop.cropHeightStart / 2;
759         int right = left + prop.cropWidthStart;
760         int bottom = top + prop.cropHeightStart;
761         // Set the crop here so we can calculate the corner radius below.
762         crop.set(left, top, right, bottom);
763 
764         RectF floatingIconBounds = new RectF();
765         RectF tmpRectF = new RectF();
766         Point tmpPos = new Point();
767 
768         AnimatorSet animatorSet = new AnimatorSet();
769         ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
770         appAnimator.setDuration(APP_LAUNCH_DURATION);
771         appAnimator.setInterpolator(LINEAR);
772         appAnimator.addListener(floatingView);
773         appAnimator.addListener(new AnimatorListenerAdapter() {
774             @Override
775             public void onAnimationStart(Animator animation) {
776                 LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
777                 if (taskbarController != null && taskbarController.shouldShowEduOnAppLaunch()) {
778                     // LAUNCHER_TASKBAR_EDUCATION_SHOWING is set to true here, when the education
779                     // flow is about to start, to avoid a race condition with other components
780                     // that would show something else to the user as soon as the app is opened.
781                     Settings.Secure.putInt(mLauncher.getContentResolver(),
782                             LAUNCHER_TASKBAR_EDUCATION_SHOWING, 1);
783                 }
784             }
785 
786             @Override
787             public void onAnimationEnd(Animator animation) {
788                 if (v instanceof BubbleTextView) {
789                     ((BubbleTextView) v).setStayPressed(false);
790                 }
791                 LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
792                 if (taskbarController != null) {
793                     taskbarController.showEduOnAppLaunch();
794                 }
795                 openingTargets.release();
796             }
797         });
798 
799         final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
800                 ? Math.max(crop.width(), crop.height()) / 2f
801                 : 0f;
802         final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
803                 ? 0 : getWindowCornerRadius(mLauncher);
804         final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;
805 
806         MultiValueUpdateListener listener = new MultiValueUpdateListener() {
807             FloatProp mDx = new FloatProp(0, prop.dX, mOpeningXInterpolator);
808             FloatProp mDy = new FloatProp(0, prop.dY, mOpeningInterpolator);
809 
810             FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
811                     prop.finalAppIconScale, mOpeningInterpolator);
812             FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f,
813                     clampToDuration(LINEAR, APP_LAUNCH_ALPHA_START_DELAY, APP_LAUNCH_ALPHA_DURATION,
814                             APP_LAUNCH_DURATION));
815 
816             FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius,
817                     mOpeningInterpolator);
818             FloatProp mShadowRadius = new FloatProp(0, finalShadowRadius,
819                     mOpeningInterpolator);
820 
821             FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd,
822                     mOpeningInterpolator);
823             FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
824                     mOpeningInterpolator);
825             FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd,
826                     mOpeningInterpolator);
827             FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd,
828                     mOpeningInterpolator);
829 
830             FloatProp mNavFadeOut = new FloatProp(1f, 0f, clampToDuration(
831                     NAV_FADE_OUT_INTERPOLATOR, 0, ANIMATION_NAV_FADE_OUT_DURATION,
832                     APP_LAUNCH_DURATION));
833             FloatProp mNavFadeIn = new FloatProp(0f, 1f, clampToDuration(
834                     NAV_FADE_IN_INTERPOLATOR, ANIMATION_DELAY_NAV_FADE_IN,
835                     ANIMATION_NAV_FADE_IN_DURATION, APP_LAUNCH_DURATION));
836 
837             @Override
838             public void onUpdate(float percent, boolean initOnly) {
839                 if (cropToInset && bottomInsetPos[0] != mSystemUiProxy.getHomeVisibilityState()
840                         .getNavbarInsetPosition()) {
841                     final RemoteAnimationTarget target = openingTargets.getFirstAppTarget();
842                     bottomInsetPos[0] = mSystemUiProxy.getHomeVisibilityState()
843                             .getNavbarInsetPosition();
844                     final Rect bounds = target != null
845                             ? target.screenSpaceBounds : windowTargetBounds;
846                     // Animate to above the taskbar.
847                     int bottomLevel = Math.min(bottomInsetPos[0], bounds.bottom);
848                     windowTargetBounds.bottom = bottomLevel;
849                     final int endHeight = bottomLevel - bounds.top;
850 
851                     AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(),
852                             mDeviceProfile, windowTargetBounds, launcherIconBounds, v,
853                             dragLayerBounds[0], dragLayerBounds[1], hasSplashScreen,
854                             floatingView.isDifferentFromAppIcon());
855                     mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
856                             mOpeningInterpolator);
857                     mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd,
858                             mOpeningInterpolator);
859                     mDy = new FloatProp(0, prop.dY, mOpeningInterpolator);
860                     mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
861                             prop.finalAppIconScale, mOpeningInterpolator);
862                     float interpolatedPercent = mOpeningInterpolator.getInterpolation(percent);
863                     mCropRectHeight.value = Utilities.mapRange(interpolatedPercent,
864                             prop.cropHeightStart, prop.cropHeightEnd);
865                     mCropRectCenterY.value = Utilities.mapRange(interpolatedPercent,
866                             prop.cropCenterYStart, prop.cropCenterYEnd);
867                     mDy.value = Utilities.mapRange(interpolatedPercent, 0, prop.dY);
868                     mIconScaleToFitScreen.value = Utilities.mapRange(interpolatedPercent,
869                             prop.initialAppIconScale, prop.finalAppIconScale);
870                 }
871 
872                 // Calculate the size of the scaled icon.
873                 float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value;
874                 float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value;
875 
876                 int left = (int) (mCropRectCenterX.value - mCropRectWidth.value / 2);
877                 int top = (int) (mCropRectCenterY.value - mCropRectHeight.value / 2);
878                 int right = (int) (left + mCropRectWidth.value);
879                 int bottom = (int) (top + mCropRectHeight.value);
880                 crop.set(left, top, right, bottom);
881 
882                 final int windowCropWidth = crop.width();
883                 final int windowCropHeight = crop.height();
884                 if (rotationChange != 0) {
885                     Utilities.rotateBounds(crop, mDeviceProfile.widthPx,
886                             mDeviceProfile.heightPx, rotationChange);
887                 }
888 
889                 // Scale the size of the icon to match the size of the window crop.
890                 float scaleX = iconWidth / windowCropWidth;
891                 float scaleY = iconHeight / windowCropHeight;
892                 float scale = Math.min(1f, Math.max(scaleX, scaleY));
893 
894                 float scaledCropWidth = windowCropWidth * scale;
895                 float scaledCropHeight = windowCropHeight * scale;
896                 float offsetX = (scaledCropWidth - iconWidth) / 2;
897                 float offsetY = (scaledCropHeight - iconHeight) / 2;
898 
899                 // Calculate the window position to match the icon position.
900                 tmpRectF.set(launcherIconBounds);
901                 tmpRectF.offset(dragLayerBounds[0], dragLayerBounds[1]);
902                 tmpRectF.offset(mDx.value, mDy.value);
903                 Utilities.scaleRectFAboutCenter(tmpRectF, mIconScaleToFitScreen.value);
904                 float windowTransX0 = tmpRectF.left - offsetX - crop.left * scale;
905                 float windowTransY0 = tmpRectF.top - offsetY - crop.top * scale;
906 
907                 // Calculate the icon position.
908                 floatingIconBounds.set(launcherIconBounds);
909                 floatingIconBounds.offset(mDx.value, mDy.value);
910                 Utilities.scaleRectFAboutCenter(floatingIconBounds, mIconScaleToFitScreen.value);
911                 floatingIconBounds.left -= offsetX;
912                 floatingIconBounds.top -= offsetY;
913                 floatingIconBounds.right += offsetX;
914                 floatingIconBounds.bottom += offsetY;
915 
916                 if (initOnly) {
917                     // For the init pass, we want full alpha since the window is not yet ready.
918                     floatingView.update(1f, floatingIconBounds, percent, 0f,
919                             mWindowRadius.value * scale, true /* isOpening */);
920                     return;
921                 }
922 
923                 SurfaceTransaction transaction = new SurfaceTransaction();
924 
925                 for (int i = appTargets.length - 1; i >= 0; i--) {
926                     RemoteAnimationTarget target = appTargets[i];
927                     SurfaceProperties builder = transaction.forSurface(target.leash);
928 
929                     if (target.mode == MODE_OPENING) {
930                         matrix.setScale(scale, scale);
931                         if (rotationChange == 1) {
932                             matrix.postTranslate(windowTransY0,
933                                     mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth));
934                         } else if (rotationChange == 2) {
935                             matrix.postTranslate(
936                                     mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth),
937                                     mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight));
938                         } else if (rotationChange == 3) {
939                             matrix.postTranslate(
940                                     mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight),
941                                     windowTransX0);
942                         } else {
943                             matrix.postTranslate(windowTransX0, windowTransY0);
944                         }
945 
946                         floatingView.update(mIconAlpha.value, floatingIconBounds, percent, 0f,
947                                 mWindowRadius.value * scale, true /* isOpening */);
948                         builder.setMatrix(matrix)
949                                 .setWindowCrop(crop)
950                                 .setAlpha(1f - mIconAlpha.value)
951                                 .setCornerRadius(mWindowRadius.value)
952                                 .setShadowRadius(mShadowRadius.value);
953                     } else if (target.mode == MODE_CLOSING) {
954                         if (target.localBounds != null) {
955                             tmpPos.set(target.localBounds.left, target.localBounds.top);
956                         } else {
957                             tmpPos.set(target.position.x, target.position.y);
958                         }
959                         final Rect crop = new Rect(target.screenSpaceBounds);
960                         crop.offsetTo(0, 0);
961 
962                         if ((rotationChange % 2) == 1) {
963                             int tmp = crop.right;
964                             crop.right = crop.bottom;
965                             crop.bottom = tmp;
966                             tmp = tmpPos.x;
967                             tmpPos.x = tmpPos.y;
968                             tmpPos.y = tmp;
969                         }
970                         matrix.setTranslate(tmpPos.x, tmpPos.y);
971                         builder.setMatrix(matrix)
972                                 .setWindowCrop(crop)
973                                 .setAlpha(1f);
974                     }
975                 }
976 
977                 if (navBarTarget != null) {
978                     SurfaceProperties navBuilder =
979                             transaction.forSurface(navBarTarget.leash);
980                     if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
981                         matrix.setScale(scale, scale);
982                         matrix.postTranslate(windowTransX0, windowTransY0);
983                         navBuilder.setMatrix(matrix)
984                                 .setWindowCrop(crop)
985                                 .setAlpha(mNavFadeIn.value);
986                     } else {
987                         navBuilder.setAlpha(mNavFadeOut.value);
988                     }
989                 }
990                 surfaceApplier.scheduleApply(transaction);
991             }
992         };
993         appAnimator.addUpdateListener(listener);
994         // Since we added a start delay, call update here to init the FloatingIconView properly.
995         listener.onUpdate(0, true /* initOnly */);
996 
997         // If app targets are translucent, do not animate the background as it causes a visible
998         // flicker when it resets itself at the end of its animation.
999         if (appTargetsAreTranslucent || !launcherClosing) {
1000             animatorSet.play(appAnimator);
1001         } else {
1002             animatorSet.playTogether(appAnimator, getBackgroundAnimator());
1003         }
1004         return animatorSet;
1005     }
1006 
getOpeningWindowAnimatorsForWidget(LauncherAppWidgetHostView v, RemoteAnimationTarget[] appTargets, RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing)1007     private Animator getOpeningWindowAnimatorsForWidget(LauncherAppWidgetHostView v,
1008             RemoteAnimationTarget[] appTargets,
1009             RemoteAnimationTarget[] wallpaperTargets,
1010             RemoteAnimationTarget[] nonAppTargets, boolean launcherClosing) {
1011         Rect windowTargetBounds = getWindowTargetBounds(appTargets, getRotationChange(appTargets));
1012         boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets);
1013 
1014         final RectF widgetBackgroundBounds = new RectF();
1015         final Rect appWindowCrop = new Rect();
1016         final Matrix matrix = new Matrix();
1017         RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
1018                 wallpaperTargets, nonAppTargets, MODE_OPENING);
1019 
1020         RemoteAnimationTarget openingTarget = openingTargets.getFirstAppTarget();
1021         int fallbackBackgroundColor = 0;
1022         if (openingTarget != null && ENABLE_SHELL_STARTING_SURFACE) {
1023             fallbackBackgroundColor = mTaskStartParams.containsKey(openingTarget.taskId)
1024                     ? mTaskStartParams.get(openingTarget.taskId).second : 0;
1025             mTaskStartParams.remove(openingTarget.taskId);
1026         }
1027         if (fallbackBackgroundColor == 0) {
1028             fallbackBackgroundColor =
1029                     FloatingWidgetView.getDefaultBackgroundColor(mLauncher, openingTarget);
1030         }
1031 
1032         final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
1033                 ? 0 : getWindowCornerRadius(mLauncher);
1034         final FloatingWidgetView floatingView = FloatingWidgetView.getFloatingWidgetView(mLauncher,
1035                 v, widgetBackgroundBounds,
1036                 new Size(windowTargetBounds.width(), windowTargetBounds.height()),
1037                 finalWindowRadius, appTargetsAreTranslucent, fallbackBackgroundColor);
1038         final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
1039                 ? floatingView.getInitialCornerRadius() : 0;
1040 
1041         SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView);
1042         openingTargets.addReleaseCheck(surfaceApplier);
1043 
1044         RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
1045 
1046         AnimatorSet animatorSet = new AnimatorSet();
1047         ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
1048         appAnimator.setDuration(APP_LAUNCH_DURATION);
1049         appAnimator.setInterpolator(LINEAR);
1050         appAnimator.addListener(floatingView);
1051         appAnimator.addListener(new AnimatorListenerAdapter() {
1052             @Override
1053             public void onAnimationEnd(Animator animation) {
1054                 openingTargets.release();
1055             }
1056         });
1057         floatingView.setFastFinishRunnable(animatorSet::end);
1058 
1059         appAnimator.addUpdateListener(new MultiValueUpdateListener() {
1060             float mAppWindowScale = 1;
1061             final FloatProp mWidgetForegroundAlpha = new FloatProp(1, 0, clampToDuration(
1062                     LINEAR, 0, WIDGET_CROSSFADE_DURATION_MILLIS / 2, APP_LAUNCH_DURATION));
1063 
1064             final FloatProp mWidgetFallbackBackgroundAlpha = new FloatProp(0, 1,
1065                     clampToDuration(LINEAR, 0, 75, APP_LAUNCH_DURATION));
1066             final FloatProp mPreviewAlpha = new FloatProp(0, 1, clampToDuration(
1067                     LINEAR,
1068                     WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* delay */,
1069                     WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* duration */,
1070                     APP_LAUNCH_DURATION));
1071             final FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius,
1072                     mOpeningInterpolator);
1073             final FloatProp mCornerRadiusProgress = new FloatProp(0, 1, mOpeningInterpolator);
1074 
1075             // Window & widget background positioning bounds
1076             final FloatProp mDx = new FloatProp(widgetBackgroundBounds.centerX(),
1077                     windowTargetBounds.centerX(), mOpeningXInterpolator);
1078             final FloatProp mDy = new FloatProp(widgetBackgroundBounds.centerY(),
1079                     windowTargetBounds.centerY(), mOpeningInterpolator);
1080             final FloatProp mWidth = new FloatProp(widgetBackgroundBounds.width(),
1081                     windowTargetBounds.width(), mOpeningInterpolator);
1082             final FloatProp mHeight = new FloatProp(widgetBackgroundBounds.height(),
1083                     windowTargetBounds.height(), mOpeningInterpolator);
1084 
1085             final FloatProp mNavFadeOut = new FloatProp(1f, 0f, clampToDuration(
1086                     NAV_FADE_OUT_INTERPOLATOR, 0, ANIMATION_NAV_FADE_OUT_DURATION,
1087                     APP_LAUNCH_DURATION));
1088             final FloatProp mNavFadeIn = new FloatProp(0f, 1f, clampToDuration(
1089                     NAV_FADE_IN_INTERPOLATOR, ANIMATION_DELAY_NAV_FADE_IN,
1090                     ANIMATION_NAV_FADE_IN_DURATION, APP_LAUNCH_DURATION));
1091 
1092             @Override
1093             public void onUpdate(float percent, boolean initOnly) {
1094                 widgetBackgroundBounds.set(mDx.value - mWidth.value / 2f,
1095                         mDy.value - mHeight.value / 2f, mDx.value + mWidth.value / 2f,
1096                         mDy.value + mHeight.value / 2f);
1097                 // Set app window scaling factor to match widget background width
1098                 mAppWindowScale = widgetBackgroundBounds.width() / windowTargetBounds.width();
1099                 // Crop scaled app window to match widget
1100                 appWindowCrop.set(0 /* left */, 0 /* top */,
1101                         windowTargetBounds.width() /* right */,
1102                         Math.round(widgetBackgroundBounds.height() / mAppWindowScale) /* bottom */);
1103                 matrix.setTranslate(widgetBackgroundBounds.left, widgetBackgroundBounds.top);
1104                 matrix.postScale(mAppWindowScale, mAppWindowScale, widgetBackgroundBounds.left,
1105                         widgetBackgroundBounds.top);
1106 
1107                 SurfaceTransaction transaction = new SurfaceTransaction();
1108                 float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
1109                 for (int i = appTargets.length - 1; i >= 0; i--) {
1110                     RemoteAnimationTarget target = appTargets[i];
1111                     SurfaceProperties builder = transaction.forSurface(target.leash);
1112                     if (target.mode == MODE_OPENING) {
1113                         floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
1114                                 mWidgetForegroundAlpha.value, mWidgetFallbackBackgroundAlpha.value,
1115                                 mCornerRadiusProgress.value);
1116                         builder.setMatrix(matrix)
1117                                 .setWindowCrop(appWindowCrop)
1118                                 .setAlpha(mPreviewAlpha.value)
1119                                 .setCornerRadius(mWindowRadius.value / mAppWindowScale);
1120                     }
1121                 }
1122 
1123                 if (navBarTarget != null) {
1124                     SurfaceProperties navBuilder = transaction.forSurface(navBarTarget.leash);
1125                     if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
1126                         navBuilder.setMatrix(matrix)
1127                                 .setWindowCrop(appWindowCrop)
1128                                 .setAlpha(mNavFadeIn.value);
1129                     } else {
1130                         navBuilder.setAlpha(mNavFadeOut.value);
1131                     }
1132                 }
1133                 surfaceApplier.scheduleApply(transaction);
1134             }
1135         });
1136 
1137         // If app targets are translucent, do not animate the background as it causes a visible
1138         // flicker when it resets itself at the end of its animation.
1139         if (appTargetsAreTranslucent || !launcherClosing) {
1140             animatorSet.play(appAnimator);
1141         } else {
1142             animatorSet.playTogether(appAnimator, getBackgroundAnimator());
1143         }
1144         return animatorSet;
1145     }
1146 
1147     /**
1148      * Returns animator that controls depth/blur of the background.
1149      */
getBackgroundAnimator()1150     private ObjectAnimator getBackgroundAnimator() {
1151         // When launching an app from overview that doesn't map to a task, we still want to just
1152         // blur the wallpaper instead of the launcher surface as well
1153         boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW
1154                 && BlurUtils.supportsBlursOnWindows();
1155 
1156         LaunchDepthController depthController = new LaunchDepthController(mLauncher);
1157         ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController.stateDepth,
1158                         MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mLauncher))
1159                         .setDuration(APP_LAUNCH_DURATION);
1160 
1161         if (allowBlurringLauncher) {
1162             // Create a temporary effect layer, that lives on top of launcher, so we can apply
1163             // the blur to it. The EffectLayer will be fullscreen, which will help with caching
1164             // optimizations on the SurfaceFlinger side:
1165             // - Results would be able to be cached as a texture
1166             // - There won't be texture allocation overhead, because EffectLayers don't have
1167             //   buffers
1168             ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
1169             SurfaceControl parent = viewRootImpl != null
1170                     ? viewRootImpl.getSurfaceControl()
1171                     : null;
1172             SurfaceControl dimLayer = new SurfaceControl.Builder()
1173                     .setName("Blur layer")
1174                     .setParent(parent)
1175                     .setOpaque(false)
1176                     .setHidden(false)
1177                     .setEffectLayer()
1178                     .build();
1179 
1180             backgroundRadiusAnim.addListener(AnimatorListeners.forEndCallback(() ->
1181                     new SurfaceControl.Transaction().remove(dimLayer).apply()));
1182         }
1183 
1184         backgroundRadiusAnim.addListener(
1185                 AnimatorListeners.forEndCallback(() -> {
1186                     // reset the depth to match the main depth controller's depth
1187                     depthController.stateDepth
1188                             .setValue(mLauncher.getDepthController().stateDepth.getValue());
1189                     depthController.dispose();
1190                 }));
1191 
1192         return backgroundRadiusAnim;
1193     }
1194 
1195     /**
1196      * Registers remote animations used when closing apps to home screen.
1197      */
registerRemoteAnimations()1198     public void registerRemoteAnimations() {
1199         if (SEPARATE_RECENTS_ACTIVITY.get()) {
1200             return;
1201         }
1202         RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
1203         addRemoteAnimations(definition);
1204         mLauncher.registerRemoteAnimations(definition);
1205     }
1206 
1207     /**
1208      * Adds remote animations to a {@link RemoteAnimationDefinition}. May be overridden to add
1209      * additional animations.
1210      */
addRemoteAnimations(RemoteAnimationDefinition definition)1211     private void addRemoteAnimations(RemoteAnimationDefinition definition) {
1212         mWallpaperOpenRunner = new WallpaperOpenLauncherAnimationRunner();
1213         definition.addRemoteAnimation(WindowManager.TRANSIT_OLD_WALLPAPER_OPEN,
1214                 WindowConfiguration.ACTIVITY_TYPE_STANDARD,
1215                 new RemoteAnimationAdapter(
1216                         new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
1217                                 false /* startAtFrontOfQueue */),
1218                         CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
1219     }
1220 
1221     /**
1222      * Registers remote animations used when closing apps to home screen.
1223      */
registerRemoteTransitions()1224     public void registerRemoteTransitions() {
1225         SystemUiProxy.INSTANCE.get(mLauncher).shareTransactionQueue();
1226         if (SEPARATE_RECENTS_ACTIVITY.get()) {
1227             return;
1228         }
1229 
1230         mWallpaperOpenTransitionRunner = new WallpaperOpenLauncherAnimationRunner();
1231         mLauncherOpenTransition = new RemoteTransition(
1232                 new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
1233                         false /* startAtFrontOfQueue */).toRemoteTransition(),
1234                 mLauncher.getIApplicationThread(), "QuickstepLaunchHome");
1235 
1236         TransitionFilter homeCheck = new TransitionFilter();
1237         // No need to handle the transition that also dismisses keyguard.
1238         homeCheck.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
1239 
1240         homeCheck.mRequirements =
1241                 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement(),
1242                         new TransitionFilter.Requirement(),
1243                         new TransitionFilter.Requirement()};
1244 
1245         homeCheck.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME;
1246         homeCheck.mRequirements[0].mTopActivity = mLauncher.getComponentName();
1247         homeCheck.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
1248         homeCheck.mRequirements[0].mOrder = CONTAINER_ORDER_TOP;
1249 
1250         homeCheck.mRequirements[1].mActivityType = ACTIVITY_TYPE_STANDARD;
1251         homeCheck.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
1252 
1253         homeCheck.mRequirements[2].mNot = true;
1254         homeCheck.mRequirements[2].mCustomAnimation = true;
1255         homeCheck.mRequirements[2].mMustBeTask = true;
1256         homeCheck.mRequirements[2].mMustBeIndependent = true;
1257 
1258         SystemUiProxy.INSTANCE.get(mLauncher)
1259                 .registerRemoteTransition(mLauncherOpenTransition, homeCheck);
1260         if (mBackAnimationController != null) {
1261             mBackAnimationController.registerComponentCallbacks();
1262             if (isHomeRoleHeld()) {
1263                 mBackAnimationController.registerBackCallbacks(mHandler);
1264             }
1265         }
1266     }
1267 
onActivityDestroyed()1268     public void onActivityDestroyed() {
1269         unregisterRemoteAnimations();
1270         unregisterRemoteTransitions();
1271         mLauncher.removeOnDeviceProfileChangeListener(this);
1272         SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(null);
1273         if (BuildConfig.IS_STUDIO_BUILD && !mRegisteredTaskStackChangeListener.isEmpty()) {
1274             Log.e(TAG, "IllegalState: Failed to run onEndCallback created from"
1275                     + " getActivityLaunchOptions()");
1276         }
1277         mRegisteredTaskStackChangeListener.forEach(TaskRestartedDuringLaunchListener::unregister);
1278         mRegisteredTaskStackChangeListener.clear();
1279     }
1280 
1281     /**
1282      * Called when the overview-target changes. Updates the back callback registration state.
1283      */
onOverviewTargetChange()1284     public void onOverviewTargetChange() {
1285         if (isHomeRoleHeld()) {
1286             mBackAnimationController.registerBackCallbacks(mHandler);
1287         } else {
1288             mBackAnimationController.unregisterBackCallbacks();
1289         }
1290     }
1291 
isHomeRoleHeld()1292     private boolean isHomeRoleHeld() {
1293         RoleManager roleManager = mLauncher.getSystemService(RoleManager.class);
1294         return roleManager == null || roleManager.isRoleHeld(ROLE_HOME);
1295     }
1296 
unregisterRemoteAnimations()1297     private void unregisterRemoteAnimations() {
1298         if (SEPARATE_RECENTS_ACTIVITY.get()) {
1299             return;
1300         }
1301         mLauncher.unregisterRemoteAnimations();
1302 
1303         // Also clear strong references to the runners registered with the remote animation
1304         // definition so we don't have to wait for the system gc
1305         mWallpaperOpenRunner = null;
1306         mAppLaunchRunner = null;
1307     }
1308 
unregisterRemoteTransitions()1309     protected void unregisterRemoteTransitions() {
1310         SystemUiProxy.INSTANCE.get(mLauncher).unshareTransactionQueue();
1311         if (SEPARATE_RECENTS_ACTIVITY.get()) {
1312             return;
1313         }
1314         if (mLauncherOpenTransition == null) return;
1315         SystemUiProxy.INSTANCE.get(mLauncher).unregisterRemoteTransition(
1316                 mLauncherOpenTransition);
1317         mLauncherOpenTransition = null;
1318         mWallpaperOpenTransitionRunner = null;
1319         if (mBackAnimationController != null) {
1320             mBackAnimationController.unregisterBackCallbacks();
1321             mBackAnimationController.unregisterComponentCallbacks();
1322             mBackAnimationController = null;
1323         }
1324     }
1325 
launcherIsATargetWithMode(RemoteAnimationTarget[] targets, int mode)1326     private boolean launcherIsATargetWithMode(RemoteAnimationTarget[] targets, int mode) {
1327         for (RemoteAnimationTarget target : targets) {
1328             if (target.mode == mode && target.taskInfo != null
1329                     // Compare component name instead of task-id because transitions will promote
1330                     // the target up to the root task while getTaskId returns the leaf.
1331                     && target.taskInfo.topActivity != null
1332                     && target.taskInfo.topActivity.equals(mLauncher.getComponentName())) {
1333                 return true;
1334             }
1335         }
1336         return false;
1337     }
1338 
shouldPlayFallbackClosingAnimation(RemoteAnimationTarget[] targets)1339     private boolean shouldPlayFallbackClosingAnimation(RemoteAnimationTarget[] targets) {
1340         int numTargets = 0;
1341         for (RemoteAnimationTarget target : targets) {
1342             if (target.mode == MODE_CLOSING) {
1343                 numTargets++;
1344                 if (numTargets > 1 || target.windowConfiguration.getWindowingMode()
1345                         == WINDOWING_MODE_MULTI_WINDOW) {
1346                     return true;
1347                 }
1348             }
1349         }
1350         return false;
1351     }
1352 
getRotationChange(RemoteAnimationTarget[] appTargets)1353     private static int getRotationChange(RemoteAnimationTarget[] appTargets) {
1354         int rotationChange = 0;
1355         for (RemoteAnimationTarget target : appTargets) {
1356             if (Math.abs(target.rotationChange) > Math.abs(rotationChange)) {
1357                 rotationChange = target.rotationChange;
1358             }
1359         }
1360         return rotationChange;
1361     }
1362 
1363     /**
1364      * Returns view on launcher that corresponds to the closing app in the list of app targets
1365      */
findLauncherView(RemoteAnimationTarget[] appTargets)1366     public @Nullable View findLauncherView(RemoteAnimationTarget[] appTargets) {
1367         for (RemoteAnimationTarget appTarget : appTargets) {
1368             if (appTarget.mode == MODE_CLOSING) {
1369                 View launcherView = findLauncherView(appTarget);
1370                 if (launcherView != null) {
1371                     return launcherView;
1372                 }
1373             }
1374         }
1375         return null;
1376     }
1377 
1378     /**
1379      * Returns view on launcher that corresponds to the {@param runningTaskTarget}.
1380      */
findLauncherView(RemoteAnimationTarget runningTaskTarget)1381     private @Nullable View findLauncherView(RemoteAnimationTarget runningTaskTarget) {
1382         if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) {
1383             return null;
1384         }
1385 
1386         final ComponentName[] taskInfoActivities = new ComponentName[]{
1387                 runningTaskTarget.taskInfo.baseActivity,
1388                 runningTaskTarget.taskInfo.origActivity,
1389                 runningTaskTarget.taskInfo.realActivity,
1390                 runningTaskTarget.taskInfo.topActivity};
1391 
1392         String packageName = null;
1393         for (ComponentName component : taskInfoActivities) {
1394             if (component != null && component.getPackageName() != null) {
1395                 packageName = component.getPackageName();
1396                 break;
1397             }
1398         }
1399 
1400         if (packageName == null) {
1401             return null;
1402         }
1403 
1404         // Find the associated item info for the launch cookie (if available), note that predicted
1405         // apps actually have an id of -1, so use another default id here
1406         final List<IBinder> launchCookies = runningTaskTarget.taskInfo.launchCookies == null
1407                 ? Collections.EMPTY_LIST
1408                 : runningTaskTarget.taskInfo.launchCookies;
1409 
1410         return mLauncher.getFirstVisibleElementForAppClose(
1411                 StableViewInfo.fromLaunchCookies(launchCookies), packageName,
1412                 UserHandle.of(runningTaskTarget.taskInfo.userId));
1413     }
1414 
getDefaultWindowTargetRect()1415     private @NonNull RectF getDefaultWindowTargetRect() {
1416         RecentsView recentsView = mLauncher.getOverviewPanel();
1417         PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
1418         DeviceProfile dp = mLauncher.getDeviceProfile();
1419         final int halfIconSize = dp.iconSizePx / 2;
1420         float primaryDimension = orientationHandler
1421                 .getPrimaryValue(dp.availableWidthPx, dp.availableHeightPx);
1422         float secondaryDimension = orientationHandler
1423                 .getSecondaryValue(dp.availableWidthPx, dp.availableHeightPx);
1424         final float targetX = primaryDimension / 2f;
1425         final float targetY = secondaryDimension - dp.hotseatBarSizePx;
1426         return new RectF(targetX - halfIconSize, targetY - halfIconSize,
1427                 targetX + halfIconSize, targetY + halfIconSize);
1428     }
1429 
1430     /**
1431      * Closing animator that animates the window into its final location on the workspace.
1432      */
getClosingWindowAnimators(AnimatorSet animation, RemoteAnimationTarget[] targets, View launcherView, PointF velocityPxPerS, RectF closingWindowStartRectF, float startWindowCornerRadius)1433     protected RectFSpringAnim getClosingWindowAnimators(AnimatorSet animation,
1434             RemoteAnimationTarget[] targets, View launcherView, PointF velocityPxPerS,
1435             RectF closingWindowStartRectF, float startWindowCornerRadius) {
1436         FloatingIconView floatingIconView = null;
1437         FloatingWidgetView floatingWidget = null;
1438         RectF targetRect = new RectF();
1439 
1440         RemoteAnimationTarget runningTaskTarget = null;
1441         boolean isTransluscent = false;
1442         for (RemoteAnimationTarget target : targets) {
1443             if (target.mode == MODE_CLOSING) {
1444                 runningTaskTarget = target;
1445                 isTransluscent = runningTaskTarget.isTranslucent;
1446                 break;
1447             }
1448         }
1449 
1450         // Get floating view and target rect.
1451         boolean isInHotseat = false;
1452         if (launcherView instanceof LauncherAppWidgetHostView) {
1453             Size windowSize = new Size(mDeviceProfile.availableWidthPx,
1454                     mDeviceProfile.availableHeightPx);
1455             int fallbackBackgroundColor =
1456                     FloatingWidgetView.getDefaultBackgroundColor(mLauncher, runningTaskTarget);
1457             floatingWidget = FloatingWidgetView.getFloatingWidgetView(mLauncher,
1458                     (LauncherAppWidgetHostView) launcherView, targetRect, windowSize,
1459                     mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher),
1460                     isTransluscent, fallbackBackgroundColor);
1461         } else if (launcherView != null && !RemoveAnimationSettingsTracker.INSTANCE.get(
1462                 mLauncher).isRemoveAnimationEnabled()) {
1463             floatingIconView = getFloatingIconView(mLauncher, launcherView, null,
1464                     mLauncher.getTaskbarUIController() == null
1465                             ? null
1466                             : mLauncher.getTaskbarUIController().findMatchingView(launcherView),
1467                     true /* hideOriginal */, targetRect, false /* isOpening */);
1468             if (launcherView.getTag() instanceof ItemInfo itemInfo) {
1469                 isInHotseat = itemInfo.isInHotseat();
1470                 if (isInHotseat) {
1471                     int dx = mLauncher.getHotseatItemTranslationX(itemInfo);
1472                     targetRect.offset(dx, 0);
1473                 }
1474             }
1475         } else {
1476             targetRect.set(getDefaultWindowTargetRect());
1477         }
1478 
1479         boolean useTaskbarHotseatParams = mDeviceProfile.isTaskbarPresent && isInHotseat;
1480         RectFSpringAnim anim = new RectFSpringAnim(useTaskbarHotseatParams
1481                 ? new TaskbarHotseatSpringConfig(mLauncher, closingWindowStartRectF, targetRect)
1482                 : new DefaultSpringConfig(mLauncher, mDeviceProfile, closingWindowStartRectF,
1483                         targetRect));
1484 
1485         // Hook up floating views to the closing window animators.
1486         // note the coordinate of closingWindowStartRect is based on launcher
1487         Rect closingWindowStartRect = new Rect();
1488         closingWindowStartRectF.round(closingWindowStartRect);
1489         Rect closingWindowOriginalRect =
1490                 new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
1491         if (floatingIconView != null) {
1492             anim.addAnimatorListener(floatingIconView);
1493             floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
1494             floatingIconView.setFastFinishRunnable(anim::end);
1495             FloatingIconView finalFloatingIconView = floatingIconView;
1496 
1497             // We want the window alpha to be 0 once this threshold is met, so that the
1498             // FolderIconView can be seen morphing into the icon shape.
1499             final float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
1500 
1501             RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
1502                     closingWindowStartRect, closingWindowOriginalRect, startWindowCornerRadius) {
1503                 @Override
1504                 public void onUpdate(RectF currentRectF, float progress) {
1505                     finalFloatingIconView.update(1f, currentRectF, progress, windowAlphaThreshold,
1506                             getCornerRadius(progress), false);
1507 
1508                     super.onUpdate(currentRectF, progress);
1509                 }
1510             };
1511             anim.addOnUpdateListener(runner);
1512         } else if (floatingWidget != null) {
1513             anim.addAnimatorListener(floatingWidget);
1514             floatingWidget.setOnTargetChangeListener(anim::onTargetPositionChanged);
1515             floatingWidget.setFastFinishRunnable(anim::end);
1516 
1517             final float floatingWidgetAlpha = isTransluscent ? 0 : 1;
1518             FloatingWidgetView finalFloatingWidget = floatingWidget;
1519             RectFSpringAnim.OnUpdateListener runner = new SpringAnimRunner(targets, targetRect,
1520                     closingWindowStartRect, closingWindowOriginalRect, startWindowCornerRadius) {
1521                 @Override
1522                 public void onUpdate(RectF currentRectF, float progress) {
1523                     final float fallbackBackgroundAlpha =
1524                             1 - mapBoundToRange(progress, 0.8f, 1, 0, 1, EXAGGERATED_EASE);
1525                     final float foregroundAlpha =
1526                             mapBoundToRange(progress, 0.5f, 1, 0, 1, EXAGGERATED_EASE);
1527                     finalFloatingWidget.update(currentRectF, floatingWidgetAlpha, foregroundAlpha,
1528                             fallbackBackgroundAlpha, 1 - progress);
1529 
1530                     super.onUpdate(currentRectF, progress);
1531                 }
1532             };
1533             anim.addOnUpdateListener(runner);
1534         } else {
1535             // If no floating icon or widget is present, animate the to the default window
1536             // target rect.
1537             anim.addOnUpdateListener(new SpringAnimRunner(
1538                     targets, targetRect, closingWindowStartRect, closingWindowOriginalRect,
1539                     startWindowCornerRadius));
1540         }
1541 
1542         // Use a fixed velocity to start the animation.
1543         animation.addListener(new AnimatorListenerAdapter() {
1544             @Override
1545             public void onAnimationStart(Animator animation) {
1546                 anim.start(mLauncher, mDeviceProfile, velocityPxPerS);
1547             }
1548         });
1549         return anim;
1550     }
1551 
1552     /**
1553      * Closing window animator that moves the window down and offscreen.
1554      */
getFallbackClosingWindowAnimators(RemoteAnimationTarget[] appTargets)1555     private Animator getFallbackClosingWindowAnimators(RemoteAnimationTarget[] appTargets) {
1556         final int rotationChange = getRotationChange(appTargets);
1557         SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
1558         Matrix matrix = new Matrix();
1559         Point tmpPos = new Point();
1560         Rect tmpRect = new Rect();
1561         ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
1562         int duration = CLOSING_TRANSITION_DURATION_MS;
1563         float windowCornerRadius = mDeviceProfile.isMultiWindowMode
1564                 ? 0 : getWindowCornerRadius(mLauncher);
1565         float startShadowRadius = areAllTargetsTranslucent(appTargets) ? 0 : mMaxShadowRadius;
1566         closingAnimator.setDuration(duration);
1567         boolean isFreeform = isFreeformAnimation(appTargets);
1568         float translateY = isFreeform ? mClosingFreeformWindowTransY : mClosingWindowTransY;
1569         float endScale = isFreeform ? 0.95f : 1f;
1570         Interpolator alphaInterpolator = isFreeform
1571                 ? clampToDuration(LINEAR, 0, 100, duration)
1572                 : clampToDuration(LINEAR, 25, 125, duration);
1573         closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
1574             FloatProp mDy = new FloatProp(0, translateY, DECELERATE_1_7);
1575             FloatProp mScale = new FloatProp(1f, endScale, DECELERATE_1_7);
1576             FloatProp mAlpha = new FloatProp(1f, 0f, alphaInterpolator);
1577             FloatProp mShadowRadius = new FloatProp(startShadowRadius, 0, DECELERATE_1_7);
1578 
1579             @Override
1580             public void onUpdate(float percent, boolean initOnly) {
1581                 SurfaceTransaction transaction = new SurfaceTransaction();
1582                 for (int i = appTargets.length - 1; i >= 0; i--) {
1583                     RemoteAnimationTarget target = appTargets[i];
1584                     SurfaceProperties builder = transaction.forSurface(target.leash);
1585 
1586                     if (target.screenSpaceBounds != null) {
1587                         tmpPos.set(target.screenSpaceBounds.left, target.screenSpaceBounds.top);
1588                     } else {
1589                         tmpPos.set(target.position.x, target.position.y);
1590                     }
1591 
1592                     final Rect crop = new Rect(target.localBounds);
1593                     crop.offsetTo(0, 0);
1594                     if (target.mode == MODE_CLOSING) {
1595                         tmpRect.set(target.screenSpaceBounds);
1596                         if ((rotationChange % 2) != 0) {
1597                             final int right = crop.right;
1598                             crop.right = crop.bottom;
1599                             crop.bottom = right;
1600                         }
1601                         matrix.setScale(mScale.value, mScale.value,
1602                                 tmpRect.centerX(),
1603                                 tmpRect.centerY());
1604                         matrix.postTranslate(0, mDy.value);
1605                         matrix.postTranslate(tmpPos.x, tmpPos.y);
1606                         builder.setMatrix(matrix)
1607                                 .setWindowCrop(crop)
1608                                 .setAlpha(mAlpha.value)
1609                                 .setCornerRadius(windowCornerRadius)
1610                                 .setShadowRadius(mShadowRadius.value);
1611                     } else if (target.mode == MODE_OPENING) {
1612                         matrix.setTranslate(tmpPos.x, tmpPos.y);
1613                         builder.setMatrix(matrix)
1614                                 .setWindowCrop(crop)
1615                                 .setAlpha(1f);
1616                     }
1617                 }
1618                 surfaceApplier.scheduleApply(transaction);
1619             }
1620         });
1621 
1622         return closingAnimator;
1623     }
1624 
isFreeformAnimation(RemoteAnimationTarget[] appTargets)1625     private boolean isFreeformAnimation(RemoteAnimationTarget[] appTargets) {
1626         return DesktopModeStatus.canEnterDesktopMode(mLauncher.getApplicationContext())
1627                 && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX.isTrue()
1628                 && Arrays.stream(appTargets)
1629                         .anyMatch(app -> app.taskInfo != null && app.taskInfo.isFreeform());
1630     }
1631 
addCujInstrumentation(Animator anim, int cuj)1632     private void addCujInstrumentation(Animator anim, int cuj) {
1633         anim.addListener(getCujAnimationSuccessListener(cuj));
1634     }
1635 
addCujInstrumentation(RectFSpringAnim anim, int cuj)1636     private void addCujInstrumentation(RectFSpringAnim anim, int cuj) {
1637         anim.addAnimatorListener(getCujAnimationSuccessListener(cuj));
1638     }
1639 
getCujAnimationSuccessListener(int cuj)1640     private AnimationSuccessListener getCujAnimationSuccessListener(int cuj) {
1641         return new AnimationSuccessListener() {
1642             @Override
1643             public void onAnimationStart(Animator animation) {
1644                 mDragLayer.getViewTreeObserver().addOnDrawListener(
1645                         new ViewTreeObserver.OnDrawListener() {
1646                             boolean mHandled = false;
1647 
1648                             @Override
1649                             public void onDraw() {
1650                                 if (mHandled) {
1651                                     return;
1652                                 }
1653                                 mHandled = true;
1654 
1655                                 InteractionJankMonitorWrapper.begin(mDragLayer, cuj);
1656 
1657                                 mDragLayer.post(() ->
1658                                         mDragLayer.getViewTreeObserver().removeOnDrawListener(
1659                                                 this));
1660                             }
1661                         });
1662                 super.onAnimationStart(animation);
1663             }
1664 
1665             @Override
1666             public void onAnimationCancel(Animator animation) {
1667                 super.onAnimationCancel(animation);
1668                 InteractionJankMonitorWrapper.cancel(cuj);
1669             }
1670 
1671             @Override
1672             public void onAnimationSuccess(Animator animator) {
1673                 InteractionJankMonitorWrapper.end(cuj);
1674             }
1675         };
1676     }
1677 
1678     /**
1679      * Creates the {@link RectFSpringAnim} and {@link AnimatorSet} required to animate
1680      * the transition.
1681      */
1682     @NonNull
1683     public BackAnimState createWallpaperOpenAnimations(
1684             RemoteAnimationTarget[] appTargets,
1685             RemoteAnimationTarget[] wallpapers,
1686             RemoteAnimationTarget[] nonAppTargets,
1687             RectF startRect,
1688             float startWindowCornerRadius,
1689             boolean fromPredictiveBack) {
1690         View launcherView = findLauncherView(appTargets);
1691         if (checkReturnAnimationsFlags()
1692                 && launcherView != null
1693                 && launcherView.getTag() instanceof ItemInfo info
1694                 && info.shouldUseBackgroundAnimation()) {
1695             // Try to create a return animation
1696             RunnableList onEndCallback = new RunnableList();
1697             WindowAnimationState windowState = new WindowAnimationState();
1698             windowState.bounds = startRect;
1699             windowState.bottomLeftRadius = windowState.bottomRightRadius =
1700                     windowState.topLeftRadius = windowState.topRightRadius =
1701                             startWindowCornerRadius;
1702             ContainerAnimationRunner runner = ContainerAnimationRunner.fromView(
1703                     launcherView, false /* forLaunch */, mLauncher, mStartingWindowListener,
1704                     onEndCallback, windowState);
1705             if (runner != null) {
1706                 runner.startAnimation(TRANSIT_CLOSE,
1707                         appTargets, wallpapers, nonAppTargets,
1708                         new IRemoteAnimationFinishedCallback.Stub() {
1709                             @Override
1710                             public void onAnimationFinished() {
1711                                 onEndCallback.executeAllAndDestroy();
1712                             }
1713                         });
1714                 return new AlreadyStartedBackAnimState(onEndCallback);
1715             }
1716         }
1717 
1718         AnimatorSet anim = new AnimatorSet();
1719         RectFSpringAnim rectFSpringAnim = null;
1720 
1721         final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible()
1722                 || launcherIsATargetWithMode(appTargets, MODE_OPENING);
1723 
1724         boolean playFallBackAnimation = (launcherView == null
1725                 && launcherIsForceInvisibleOrOpening)
1726                 || mLauncher.getWorkspace().isOverlayShown()
1727                 || shouldPlayFallbackClosingAnimation(appTargets);
1728 
1729         boolean playWorkspaceReveal = true;
1730         if (!Flags.predictiveBackToHomePolish()) {
1731             playWorkspaceReveal = !fromPredictiveBack;
1732         }
1733         boolean skipAllAppsScale = false;
1734         if (!playFallBackAnimation) {
1735             PointF velocity;
1736             if (enableScalingRevealHomeAnimation()) {
1737                 velocity = new PointF();
1738             } else {
1739                 // Use a fixed velocity to start the animation.
1740                 float velocityPxPerS = DynamicResource.provider(mLauncher)
1741                         .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s);
1742                 velocity = new PointF(0, -velocityPxPerS);
1743             }
1744             rectFSpringAnim = getClosingWindowAnimators(
1745                     anim, appTargets, launcherView, velocity, startRect,
1746                     startWindowCornerRadius);
1747             if (mLauncher.isInState(LauncherState.ALL_APPS)) {
1748                 // Skip scaling all apps, otherwise FloatingIconView will get wrong
1749                 // layout bounds.
1750                 skipAllAppsScale = true;
1751             } else if (Flags.predictiveBackToHomePolish() || !fromPredictiveBack) {
1752                 if (enableScalingRevealHomeAnimation()) {
1753                     anim.play(
1754                             new ScalingWorkspaceRevealAnim(mLauncher, rectFSpringAnim,
1755                                     rectFSpringAnim.getTargetRect(),
1756                                     !fromPredictiveBack /* playAlphaReveal */).getAnimators());
1757                 } else {
1758                     anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
1759                             true /* animateOverviewScrim */, launcherView).getAnimators());
1760                 }
1761 
1762                 if (!areAllTargetsTranslucent(appTargets)) {
1763                     anim.play(ObjectAnimator.ofFloat(mLauncher.getDepthController().stateDepth,
1764                             MULTI_PROPERTY_VALUE,
1765                             BACKGROUND_APP.getDepth(mLauncher), NORMAL.getDepth(mLauncher)));
1766                 }
1767 
1768                 // We play StaggeredWorkspaceAnim as a part of the closing window animation.
1769                 playWorkspaceReveal = false;
1770             }
1771         } else {
1772             anim.play(getFallbackClosingWindowAnimators(appTargets));
1773         }
1774 
1775         if (Flags.predictiveBackToHomePolish()) {
1776             AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
1777                 @Override
1778                 public void onAnimationEnd(Animator animation) {
1779                     super.onAnimationEnd(animation);
1780                     AccessibilityManagerCompat.sendTestProtocolEventToTest(
1781                             mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
1782                 }
1783             };
1784             if (rectFSpringAnim != null) {
1785                 rectFSpringAnim.addAnimatorListener(endListener);
1786             } else {
1787                 anim.addListener(endListener);
1788             }
1789         }
1790 
1791         // Normally, we run the launcher content animation when we are transitioning
1792         // home, but if home is already visible, then we don't want to animate the
1793         // contents of launcher unless we know that we are animating home as a result
1794         // of the home button press with quickstep, which will result in launcher being
1795         // started on touch down, prior to the animation home (and won't be in the
1796         // targets list because it is already visible). In that case, we force
1797         // invisibility on touch down, and only reset it after the animation to home
1798         // is initialized.
1799         boolean legacyFromPredictiveBack =
1800                 !Flags.predictiveBackToHomePolish() && fromPredictiveBack;
1801         if (launcherIsForceInvisibleOrOpening || legacyFromPredictiveBack) {
1802             if (rectFSpringAnim != null && anim.getChildAnimations().isEmpty()) {
1803                 addCujInstrumentation(rectFSpringAnim, Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
1804             } else {
1805                 if (isFreeformAnimation(appTargets)) {
1806                     addCujInstrumentation(anim,
1807                             Cuj.CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE);
1808                 }
1809                 addCujInstrumentation(anim, playFallBackAnimation
1810                         ? Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK
1811                         : Cuj.CUJ_LAUNCHER_APP_CLOSE_TO_HOME);
1812             }
1813             if (!Flags.predictiveBackToHomePolish()) {
1814                 AnimatorListenerAdapter endListener = new AnimatorListenerAdapter() {
1815                     @Override
1816                     public void onAnimationEnd(Animator animation) {
1817                         super.onAnimationEnd(animation);
1818                         AccessibilityManagerCompat.sendTestProtocolEventToTest(
1819                                 mLauncher, WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE);
1820                     }
1821                 };
1822                 if (fromPredictiveBack && rectFSpringAnim != null) {
1823                     rectFSpringAnim.addAnimatorListener(endListener);
1824                 } else {
1825                     anim.addListener(endListener);
1826                 }
1827             }
1828 
1829             // Only register the content animation for cancellation when state changes
1830             mLauncher.getStateManager().setCurrentAnimation(anim);
1831 
1832             if (mLauncher.isInState(LauncherState.ALL_APPS) && !legacyFromPredictiveBack) {
1833                 Pair<AnimatorSet, Runnable> contentAnimator =
1834                         getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY,
1835                                 skipAllAppsScale);
1836                 anim.play(contentAnimator.first);
1837                 anim.addListener(new AnimatorListenerAdapter() {
1838                     @Override
1839                     public void onAnimationEnd(Animator animation) {
1840                         contentAnimator.second.run();
1841                     }
1842                 });
1843             } else if (playWorkspaceReveal) {
1844                     anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
1845             }
1846         }
1847 
1848         return new AnimatorBackState(rectFSpringAnim, anim);
1849     }
1850 
1851     /** Get animation duration for taskbar for going to home. */
1852     public static int getTaskbarToHomeDuration(boolean isPinnedTaskbarAndNotInDesktopMode) {
1853         return getTaskbarToHomeDuration(false, isPinnedTaskbarAndNotInDesktopMode);
1854     }
1855 
1856     /**
1857      * Get animation duration for taskbar for going to home.
1858      *
1859      * @param shouldOverrideToFastAnimation should overwrite scaling reveal home animation duration
1860      */
1861     public static int getTaskbarToHomeDuration(boolean shouldOverrideToFastAnimation,
1862             boolean isPinnedTaskbarAndNotInDesktopMode) {
1863         if (isPinnedTaskbarAndNotInDesktopMode) {
1864             return PINNED_TASKBAR_TRANSITION_DURATION;
1865         } else if (enableScalingRevealHomeAnimation() && !shouldOverrideToFastAnimation) {
1866             return TASKBAR_TO_HOME_DURATION_SLOW;
1867         } else {
1868             return TASKBAR_TO_HOME_DURATION_FAST;
1869         }
1870     }
1871 
1872     private static boolean checkReturnAnimationsFlags() {
1873         return enableContainerReturnAnimations() && returnAnimationFrameworkLibrary();
1874     }
1875 
1876     /**
1877      * Remote animation runner for animation from the app to Launcher, including recents.
1878      */
1879     protected class WallpaperOpenLauncherAnimationRunner implements RemoteAnimationFactory {
1880 
1881         @Override
1882         public void onAnimationStart(int transit,
1883                 RemoteAnimationTarget[] appTargets,
1884                 RemoteAnimationTarget[] wallpaperTargets,
1885                 RemoteAnimationTarget[] nonAppTargets,
1886                 LauncherAnimationRunner.AnimationResult result) {
1887             if (mLauncher.isDestroyed()) {
1888                 AnimatorSet anim = new AnimatorSet();
1889                 anim.play(getFallbackClosingWindowAnimators(appTargets));
1890                 result.setAnimation(anim, mLauncher.getApplicationContext());
1891                 return;
1892             }
1893 
1894             if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
1895                 mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
1896                 mLauncher.getStateManager().moveToRestState();
1897             }
1898 
1899             RectF windowTargetBounds =
1900                     new RectF(getWindowTargetBounds(appTargets, getRotationChange(appTargets)));
1901 
1902             final RectF resolveRectF = new RectF(windowTargetBounds);
1903             for (RemoteAnimationTarget t : appTargets) {
1904                 if (t.mode == MODE_CLOSING) {
1905                     transferRectToTargetCoordinate(
1906                             t, windowTargetBounds, true, resolveRectF);
1907                     break;
1908                 }
1909             }
1910 
1911             BackAnimState bankAnimState = createWallpaperOpenAnimations(
1912                     appTargets, wallpaperTargets, nonAppTargets, resolveRectF,
1913                     QuickStepContract.getWindowCornerRadius(mLauncher),
1914                     false /* fromPredictiveBack */);
1915 
1916             TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets, false, null);
1917             mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
1918             bankAnimState.applyToAnimationResult(result, mLauncher);
1919         }
1920     }
1921 
1922     /**
1923      * Remote animation runner for animation to launch an app.
1924      */
1925     private class AppLaunchAnimationRunner implements RemoteAnimationFactory {
1926 
1927         private final View mV;
1928         private final RunnableList mOnEndCallback;
1929 
1930         AppLaunchAnimationRunner(View v, RunnableList onEndCallback) {
1931             mV = v;
1932             mOnEndCallback = onEndCallback;
1933         }
1934 
1935         @Override
1936         public void onAnimationStart(int transit,
1937                 RemoteAnimationTarget[] appTargets,
1938                 RemoteAnimationTarget[] wallpaperTargets,
1939                 RemoteAnimationTarget[] nonAppTargets,
1940                 LauncherAnimationRunner.AnimationResult result) {
1941             AnimatorSet anim = new AnimatorSet();
1942             boolean launcherClosing =
1943                     launcherIsATargetWithMode(appTargets, MODE_CLOSING);
1944 
1945             final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView;
1946             final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
1947             final boolean skipFirstFrame;
1948             if (launchingFromWidget) {
1949                 composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
1950                         wallpaperTargets, nonAppTargets, launcherClosing);
1951                 addCujInstrumentation(anim, Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET);
1952                 skipFirstFrame = true;
1953             } else if (launchingFromRecents) {
1954                 composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
1955                         launcherClosing);
1956                 addCujInstrumentation(
1957                         anim, Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_RECENTS);
1958                 skipFirstFrame = true;
1959             } else {
1960                 composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
1961                         launcherClosing);
1962                 addCujInstrumentation(anim, Cuj.CUJ_LAUNCHER_APP_LAUNCH_FROM_ICON);
1963                 skipFirstFrame = false;
1964             }
1965 
1966             if (launcherClosing) {
1967                 anim.addListener(mForceInvisibleListener);
1968             }
1969 
1970             // Syncs the app launch animation and taskbar stash animation (if exists).
1971             if (syncAppLaunchWithTaskbarStash() && enableScalingRevealHomeAnimation()) {
1972                 LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
1973                 if (taskbarController != null) {
1974                     taskbarController.setIgnoreInAppFlagForSync(false);
1975 
1976                     if (launcherClosing) {
1977                         Animator taskbar = taskbarController.createAnimToApp();
1978                         if (taskbar != null) {
1979                             anim.play(taskbar);
1980                         }
1981                     }
1982                 }
1983             }
1984 
1985             result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
1986                     skipFirstFrame);
1987         }
1988 
1989         @Override
1990         public void onAnimationCancelled() {
1991             mOnEndCallback.executeAllAndDestroy();
1992         }
1993     }
1994 
1995     /** Remote animation runner to launch an app using System UI's animation library. */
1996     private static class ContainerAnimationRunner implements RemoteAnimationFactory {
1997 
1998         /** The delegate runner that handles the actual animation. */
1999         private final RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> mDelegate;
2000 
2001         private ContainerAnimationRunner(
2002                 RemoteAnimationDelegate<IRemoteAnimationFinishedCallback> delegate) {
2003             mDelegate = delegate;
2004         }
2005 
2006         @Nullable
2007         static ContainerAnimationRunner fromView(
2008                 View v,
2009                 boolean forLaunch,
2010                 Launcher launcher,
2011                 StartingWindowListener startingWindowListener,
2012                 RunnableList onEndCallback,
2013                 @Nullable WindowAnimationState windowState) {
2014             if (!forLaunch && !checkReturnAnimationsFlags()) {
2015                 throw new IllegalStateException(
2016                         "forLaunch cannot be false when the enableContainerReturnAnimations or "
2017                                 + "returnAnimationFrameworkLibrary flag is disabled");
2018             }
2019 
2020             // First the controller is created. This is used by the runner to animate the
2021             // origin/target view.
2022             ActivityTransitionAnimator.Controller controller =
2023                     buildController(v, forLaunch, windowState);
2024             if (controller == null) {
2025                 return null;
2026             }
2027 
2028             // The callback is used to make sure that we use the right color to fade between view
2029             // and the window.
2030             ActivityTransitionAnimator.Callback callback = task -> {
2031                 final int backgroundColor =
2032                         startingWindowListener.mBackgroundColor == Color.TRANSPARENT
2033                                 ? launcher.getScrimView().getBackgroundColor()
2034                                 : startingWindowListener.mBackgroundColor;
2035                 return ColorUtils.setAlphaComponent(backgroundColor, 255);
2036             };
2037 
2038             ActivityTransitionAnimator.Listener listener =
2039                     new ActivityTransitionAnimator.Listener() {
2040                         @Override
2041                         public void onTransitionAnimationEnd() {
2042                             onEndCallback.executeAllAndDestroy();
2043                         }
2044                     };
2045 
2046             return new ContainerAnimationRunner(
2047                     new ActivityTransitionAnimator.AnimationDelegate(
2048                             MAIN_EXECUTOR, controller, callback, listener));
2049         }
2050 
2051         /**
2052          * Constructs a {@link ActivityTransitionAnimator.Controller} that can be used by a
2053          * {@link ContainerAnimationRunner} to animate a view into an opening window or from a
2054          * closing one.
2055          */
2056         @Nullable
2057         private static ActivityTransitionAnimator.Controller buildController(
2058                 View v, boolean isLaunching, @Nullable WindowAnimationState windowState) {
2059             View viewToUse = findLaunchableViewWithBackground(v);
2060             if (viewToUse == null) {
2061                 return null;
2062             }
2063 
2064             // The CUJ is logged by the click handler, so we don't log it inside the animation
2065             // library. TODO: figure out return CUJ.
2066             ActivityTransitionAnimator.Controller controllerDelegate =
2067                     ActivityTransitionAnimator.Controller.fromView(viewToUse, null /* cujType */);
2068 
2069             if (controllerDelegate == null) {
2070                 return null;
2071             }
2072 
2073             // This wrapper allows us to override the default value, telling the controller that the
2074             // current window is below the animating window as well as information about the return
2075             // animation.
2076             return new DelegateTransitionAnimatorController(controllerDelegate) {
2077                 @Override
2078                 public boolean isLaunching() {
2079                     return isLaunching;
2080                 }
2081 
2082                 @Override
2083                 public boolean isBelowAnimatingWindow() {
2084                     return true;
2085                 }
2086 
2087                 @Nullable
2088                 @Override
2089                 public WindowAnimationState getWindowAnimatorState() {
2090                     return windowState;
2091                 }
2092             };
2093         }
2094 
2095         /**
2096          * Finds the closest parent of [view] (inclusive) that implements {@link LaunchableView} and
2097          * has a background drawable.
2098          */
2099         @Nullable
2100         private static <T extends View & LaunchableView> T findLaunchableViewWithBackground(
2101                 View view) {
2102             View current = view;
2103             while (current.getBackground() == null || !(current instanceof LaunchableView)) {
2104                 if (current.getParent() instanceof View v) {
2105                     current = v;
2106                 } else {
2107                     return null;
2108                 }
2109             }
2110             return (T) current;
2111         }
2112 
2113         @Override
2114         public void onAnimationStart(int transit, RemoteAnimationTarget[] appTargets,
2115                 RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets,
2116                 LauncherAnimationRunner.AnimationResult result) {
2117             startAnimation(
2118                     transit, appTargets, wallpaperTargets, nonAppTargets, result);
2119         }
2120 
2121         public void startAnimation(int transit, RemoteAnimationTarget[] appTargets,
2122                 RemoteAnimationTarget[] wallpaperTargets, RemoteAnimationTarget[] nonAppTargets,
2123                 IRemoteAnimationFinishedCallback result) {
2124             mDelegate.onAnimationStart(
2125                     transit, appTargets, wallpaperTargets, nonAppTargets, result);
2126         }
2127 
2128         @Override
2129         public void onAnimationCancelled() {
2130             mDelegate.onAnimationCancelled();
2131         }
2132     }
2133 
2134     /**
2135      * Class that holds all the variables for the app open animation.
2136      */
2137     static class AnimOpenProperties {
2138 
2139         public final int cropCenterXStart;
2140         public final int cropCenterYStart;
2141         public final int cropWidthStart;
2142         public final int cropHeightStart;
2143 
2144         public final int cropCenterXEnd;
2145         public final int cropCenterYEnd;
2146         public final int cropWidthEnd;
2147         public final int cropHeightEnd;
2148 
2149         public final float dX;
2150         public final float dY;
2151 
2152         public final float initialAppIconScale;
2153         public final float finalAppIconScale;
2154 
2155         public final float iconAlphaStart;
2156 
2157         AnimOpenProperties(Resources r, DeviceProfile dp, Rect windowTargetBounds,
2158                 RectF launcherIconBounds, View view, int dragLayerLeft, int dragLayerTop,
2159                 boolean hasSplashScreen, boolean hasDifferentAppIcon) {
2160             // Scale the app icon to take up the entire screen. This simplifies the math when
2161             // animating the app window position / scale.
2162             float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
2163             float maxScaleX = smallestSize / launcherIconBounds.width();
2164             float maxScaleY = smallestSize / launcherIconBounds.height();
2165             float iconStartScale = 1f;
2166             if (view instanceof BubbleTextView && !(view.getParent() instanceof DeepShortcutView)) {
2167                 Drawable dr = ((BubbleTextView) view).getIcon();
2168                 if (dr instanceof FastBitmapDrawable) {
2169                     iconStartScale = ((FastBitmapDrawable) dr).getAnimatedScale();
2170                 }
2171             }
2172 
2173             initialAppIconScale = iconStartScale;
2174             finalAppIconScale = Math.max(maxScaleX, maxScaleY);
2175 
2176             // Animate the app icon to the center of the window bounds in screen coordinates.
2177             float centerX = windowTargetBounds.centerX() - dragLayerLeft;
2178             float centerY = windowTargetBounds.centerY() - dragLayerTop;
2179 
2180             dX = centerX - launcherIconBounds.centerX();
2181             dY = centerY - launcherIconBounds.centerY();
2182 
2183             iconAlphaStart = hasSplashScreen && !hasDifferentAppIcon ? 0 : 1f;
2184 
2185             final int windowIconSize = ResourceUtils.getDimenByName("starting_surface_icon_size",
2186                     r, 108);
2187 
2188             cropCenterXStart = windowTargetBounds.centerX();
2189             cropCenterYStart = windowTargetBounds.centerY();
2190 
2191             cropWidthStart = windowIconSize;
2192             cropHeightStart = windowIconSize;
2193 
2194             cropWidthEnd = windowTargetBounds.width();
2195             cropHeightEnd = windowTargetBounds.height();
2196 
2197             cropCenterXEnd = windowTargetBounds.centerX();
2198             cropCenterYEnd = windowTargetBounds.centerY();
2199         }
2200     }
2201 
2202     private static class StartingWindowListener extends IStartingWindowListener.Stub {
2203         private final WeakReference<QuickstepTransitionManager> mTransitionManagerRef;
2204         private int mBackgroundColor;
2205 
2206         private StartingWindowListener(QuickstepTransitionManager transitionManager) {
2207             mTransitionManagerRef = new WeakReference<>(transitionManager);
2208         }
2209 
2210         @Override
2211         public void onTaskLaunching(int taskId, int supportedType, int color) {
2212             QuickstepTransitionManager transitionManager = mTransitionManagerRef.get();
2213             if (transitionManager != null) {
2214                 transitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
2215             }
2216             mBackgroundColor = color;
2217         }
2218     }
2219 
2220     /**
2221      * Transfer the rectangle to another coordinate if needed.
2222      *
2223      * @param toLauncher which one is the anchor of this transfer, if true then transfer from
2224      *                   animation target to launcher, false transfer from launcher to animation
2225      *                   target.
2226      */
2227     public void transferRectToTargetCoordinate(RemoteAnimationTarget target, RectF currentRect,
2228             boolean toLauncher, RectF resultRect) {
2229         mCoordinateTransfer.transferRectToTargetCoordinate(
2230                 target, currentRect, toLauncher, resultRect);
2231     }
2232 
2233     private static class RemoteAnimationCoordinateTransfer {
2234         private final QuickstepLauncher mLauncher;
2235         private final Rect mDisplayRect = new Rect();
2236         private final Rect mTmpResult = new Rect();
2237 
2238         RemoteAnimationCoordinateTransfer(QuickstepLauncher launcher) {
2239             mLauncher = launcher;
2240         }
2241 
2242         void transferRectToTargetCoordinate(RemoteAnimationTarget target, RectF currentRect,
2243                 boolean toLauncher, RectF resultRect) {
2244             final int taskRotation = target.windowConfiguration.getRotation();
2245             final DeviceProfile profile = mLauncher.getDeviceProfile();
2246 
2247             final int rotationDelta = toLauncher
2248                     ? android.util.RotationUtils.deltaRotation(taskRotation, profile.rotationHint)
2249                     : android.util.RotationUtils.deltaRotation(profile.rotationHint, taskRotation);
2250             if (rotationDelta != ROTATION_0) {
2251                 // Get original display size when task is on top but with different rotation
2252                 if (rotationDelta % 2 != 0 && toLauncher && (profile.rotationHint == ROTATION_0
2253                         || profile.rotationHint == ROTATION_180)) {
2254                     mDisplayRect.set(0, 0, profile.heightPx, profile.widthPx);
2255                 } else {
2256                     mDisplayRect.set(0, 0, profile.widthPx, profile.heightPx);
2257                 }
2258                 currentRect.round(mTmpResult);
2259                 android.util.RotationUtils.rotateBounds(mTmpResult, mDisplayRect, rotationDelta);
2260                 resultRect.set(mTmpResult);
2261             } else {
2262                 resultRect.set(currentRect);
2263             }
2264         }
2265     }
2266 
2267     /**
2268      * RectFSpringAnim update listener to be used for app to home animation.
2269      */
2270     private class SpringAnimRunner implements RectFSpringAnim.OnUpdateListener {
2271         private final RemoteAnimationTarget[] mAppTargets;
2272         private final Matrix mMatrix = new Matrix();
2273         private final Point mTmpPos = new Point();
2274         private final RectF mCurrentRectF = new RectF();
2275         private final float mStartRadius;
2276         private final float mEndRadius;
2277         private final SurfaceTransactionApplier mSurfaceApplier;
2278         private final Rect mWindowStartBounds = new Rect();
2279         private final Rect mWindowOriginalBounds = new Rect();
2280 
2281         private final Rect mTmpRect = new Rect();
2282 
2283         /**
2284          * Constructor for SpringAnimRunner
2285          *
2286          * @param appTargets                the list of opening/closing apps
2287          * @param targetRect                target rectangle
2288          * @param closingWindowStartRect    start position of the window when the spring animation
2289          *                                  is started. In the predictive back to home case this
2290          *                                  will be smaller than closingWindowOriginalRect because
2291          *                                  the window is already scaled by the user gesture
2292          * @param closingWindowOriginalRect Original unscaled window rect
2293          * @param startWindowCornerRadius   corner radius of window at the start position
2294          */
2295         SpringAnimRunner(RemoteAnimationTarget[] appTargets, RectF targetRect,
2296                 Rect closingWindowStartRect, Rect closingWindowOriginalRect,
2297                 float startWindowCornerRadius) {
2298             mAppTargets = appTargets;
2299             mStartRadius = startWindowCornerRadius;
2300             mEndRadius = Math.max(1, targetRect.width()) / 2f;
2301             mSurfaceApplier = new SurfaceTransactionApplier(mDragLayer);
2302             mWindowStartBounds.set(closingWindowStartRect);
2303             mWindowOriginalBounds.set(closingWindowOriginalRect);
2304 
2305             // transfer the coordinate based on animation target.
2306             if (mAppTargets != null) {
2307                 for (RemoteAnimationTarget t : mAppTargets) {
2308                     if (t.mode == MODE_CLOSING) {
2309                         final RectF transferRect = new RectF(mWindowStartBounds);
2310                         final RectF result = new RectF();
2311                         transferRectToTargetCoordinate(t, transferRect, false, result);
2312                         result.round(mWindowStartBounds);
2313 
2314                         transferRect.set(closingWindowOriginalRect);
2315                         transferRectToTargetCoordinate(t, transferRect, false, result);
2316                         result.round(mWindowOriginalBounds);
2317                         break;
2318                     }
2319                 }
2320             }
2321         }
2322 
2323         public float getCornerRadius(float progress) {
2324             return Utilities.mapRange(progress, mStartRadius, mEndRadius);
2325         }
2326 
2327         @Override
2328         public void onUpdate(RectF currentRectF, float progress) {
2329             SurfaceTransaction transaction = new SurfaceTransaction();
2330             for (int i = mAppTargets.length - 1; i >= 0; i--) {
2331                 RemoteAnimationTarget target = mAppTargets[i];
2332                 SurfaceProperties builder = transaction.forSurface(target.leash);
2333 
2334                 if (target.localBounds != null) {
2335                     mTmpPos.set(target.localBounds.left, target.localBounds.top);
2336                 } else {
2337                     mTmpPos.set(target.position.x, target.position.y);
2338                 }
2339 
2340                 if (target.mode == MODE_CLOSING) {
2341                     transferRectToTargetCoordinate(target, currentRectF, false, mCurrentRectF);
2342 
2343                     // Scale the target window to match the currentRectF.
2344                     final float scale;
2345 
2346                     // We need to infer the crop (we crop the window to match the currentRectF).
2347                     if (mWindowStartBounds.height() > mWindowStartBounds.width()) {
2348                         scale = Math.min(1f, mCurrentRectF.width() / mWindowOriginalBounds.width());
2349 
2350                         int unscaledHeight = (int) (mCurrentRectF.height() * (1f / scale));
2351                         int croppedHeight = mWindowStartBounds.height() - unscaledHeight;
2352                         mTmpRect.set(0, 0, mWindowOriginalBounds.width(),
2353                                 mWindowStartBounds.height() - croppedHeight);
2354                     } else {
2355                         scale = Math.min(1f, mCurrentRectF.height()
2356                                 / mWindowOriginalBounds.height());
2357 
2358                         int unscaledWidth = (int) (mCurrentRectF.width() * (1f / scale));
2359                         int croppedWidth = mWindowStartBounds.width() - unscaledWidth;
2360                         mTmpRect.set(0, 0, mWindowStartBounds.width() - croppedWidth,
2361                                 mWindowOriginalBounds.height());
2362                     }
2363 
2364                     // Match size and position of currentRect.
2365                     mMatrix.setScale(scale, scale);
2366                     mMatrix.postTranslate(mCurrentRectF.left, mCurrentRectF.top);
2367 
2368                     builder.setMatrix(mMatrix)
2369                             .setWindowCrop(mTmpRect)
2370                             .setAlpha(getWindowAlpha(progress))
2371                             .setCornerRadius(getCornerRadius(progress) / scale);
2372                 } else if (target.mode == MODE_OPENING) {
2373                     mMatrix.setTranslate(mTmpPos.x, mTmpPos.y);
2374                     builder.setMatrix(mMatrix)
2375                             .setAlpha(1f);
2376                 }
2377             }
2378             mSurfaceApplier.scheduleApply(transaction);
2379         }
2380 
2381         protected float getWindowAlpha(float progress) {
2382             // Alpha interpolates between [1, 0] between progress values [start, end]
2383             final float start = 0f;
2384             final float end = 0.85f;
2385 
2386             if (progress <= start) {
2387                 return 1f;
2388             }
2389             if (progress >= end) {
2390                 return 0f;
2391             }
2392             return Utilities.mapToRange(progress, start, end, 1, 0, ACCELERATE_1_5);
2393         }
2394     }
2395 
2396     private static class LaunchDepthController extends DepthController {
2397         LaunchDepthController(QuickstepLauncher launcher) {
2398             super(launcher);
2399             setCrossWindowBlursEnabled(
2400                     CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled());
2401             // Make sure that the starting value matches the current depth set by the main
2402             // controller.
2403             stateDepth.setValue(launcher.getDepthController().stateDepth.getValue());
2404         }
2405     }
2406 }
2407