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