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