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