• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3;
18 
19 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
20 import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
21 
22 import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
23 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
24 import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
25 import static com.android.launcher3.BaseActivity.PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION;
26 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
27 import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
28 import static com.android.launcher3.LauncherState.ALL_APPS;
29 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
30 import static com.android.launcher3.LauncherState.OVERVIEW;
31 import static com.android.launcher3.Utilities.postAsyncCallback;
32 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
33 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
34 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
35 import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
36 import static com.android.launcher3.anim.Interpolators.LINEAR;
37 import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH;
38 import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
39 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
40 import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
41 import static com.android.launcher3.statehandlers.DepthController.DEPTH;
42 import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
43 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
44 import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
45 import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
46 import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
47 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
48 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
49 
50 import android.animation.Animator;
51 import android.animation.AnimatorListenerAdapter;
52 import android.animation.AnimatorSet;
53 import android.animation.ObjectAnimator;
54 import android.animation.ValueAnimator;
55 import android.content.Context;
56 import android.content.pm.PackageManager;
57 import android.content.res.Resources;
58 import android.graphics.Color;
59 import android.graphics.Matrix;
60 import android.graphics.Point;
61 import android.graphics.Rect;
62 import android.graphics.RectF;
63 import android.graphics.drawable.ColorDrawable;
64 import android.graphics.drawable.Drawable;
65 import android.os.CancellationSignal;
66 import android.os.Handler;
67 import android.os.Looper;
68 import android.os.SystemProperties;
69 import android.util.Pair;
70 import android.util.Size;
71 import android.view.SurfaceControl;
72 import android.view.View;
73 import android.view.ViewRootImpl;
74 import android.view.ViewTreeObserver;
75 import android.view.animation.Interpolator;
76 import android.view.animation.PathInterpolator;
77 
78 import androidx.annotation.NonNull;
79 import androidx.annotation.Nullable;
80 import androidx.core.graphics.ColorUtils;
81 
82 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
83 import com.android.launcher3.LauncherAnimationRunner.RemoteAnimationFactory;
84 import com.android.launcher3.anim.AnimationSuccessListener;
85 import com.android.launcher3.dragndrop.DragLayer;
86 import com.android.launcher3.icons.FastBitmapDrawable;
87 import com.android.launcher3.shortcuts.DeepShortcutView;
88 import com.android.launcher3.statehandlers.DepthController;
89 import com.android.launcher3.util.ActivityOptionsWrapper;
90 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
91 import com.android.launcher3.util.RunnableList;
92 import com.android.launcher3.util.Themes;
93 import com.android.launcher3.views.FloatingIconView;
94 import com.android.launcher3.views.ScrimView;
95 import com.android.launcher3.widget.LauncherAppWidgetHostView;
96 import com.android.quickstep.RemoteAnimationTargets;
97 import com.android.quickstep.SystemUiProxy;
98 import com.android.quickstep.TaskViewUtils;
99 import com.android.quickstep.util.MultiValueUpdateListener;
100 import com.android.quickstep.util.RemoteAnimationProvider;
101 import com.android.quickstep.util.SurfaceTransactionApplier;
102 import com.android.quickstep.util.WorkspaceRevealAnim;
103 import com.android.quickstep.views.FloatingWidgetView;
104 import com.android.quickstep.views.RecentsView;
105 import com.android.systemui.shared.system.ActivityCompat;
106 import com.android.systemui.shared.system.ActivityOptionsCompat;
107 import com.android.systemui.shared.system.BlurUtils;
108 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
109 import com.android.systemui.shared.system.QuickStepContract;
110 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
111 import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
112 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
113 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
114 import com.android.systemui.shared.system.RemoteTransitionCompat;
115 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
116 import com.android.systemui.shared.system.WindowManagerWrapper;
117 import com.android.wm.shell.startingsurface.IStartingWindowListener;
118 
119 import java.util.ArrayList;
120 import java.util.LinkedHashMap;
121 import java.util.List;
122 
123 /**
124  * Manages the opening and closing app transitions from Launcher
125  */
126 public class QuickstepTransitionManager implements OnDeviceProfileChangeListener {
127 
128     private static final String TAG = "QuickstepTransition";
129 
130     private static final boolean ENABLE_SHELL_STARTING_SURFACE =
131             SystemProperties.getBoolean("persist.debug.shell_starting_surface", true);
132 
133     /** Duration of status bar animations. */
134     public static final int STATUS_BAR_TRANSITION_DURATION = 120;
135 
136     /**
137      * Since our animations decelerate heavily when finishing, we want to start status bar
138      * animations x ms before the ending.
139      */
140     public static final int STATUS_BAR_TRANSITION_PRE_DELAY = 96;
141 
142     private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
143             "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
144 
145     private static final long APP_LAUNCH_DURATION = 450;
146     // Use a shorter duration for x or y translation to create a curve effect
147     private static final long APP_LAUNCH_CURVED_DURATION = 250;
148     private static final long APP_LAUNCH_ALPHA_DURATION = 50;
149     private static final long APP_LAUNCH_ALPHA_START_DELAY = 25;
150 
151     // We scale the durations for the downward app launch animations (minus the scale animation).
152     private static final float APP_LAUNCH_DOWN_DUR_SCALE_FACTOR = 0.8f;
153     private static final long APP_LAUNCH_DOWN_DURATION =
154             (long) (APP_LAUNCH_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
155     private static final long APP_LAUNCH_DOWN_CURVED_DURATION =
156             (long) (APP_LAUNCH_CURVED_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
157     private static final long APP_LAUNCH_ALPHA_DOWN_DURATION =
158             (long) (APP_LAUNCH_ALPHA_DURATION * APP_LAUNCH_DOWN_DUR_SCALE_FACTOR);
159 
160     public static final int ANIMATION_NAV_FADE_IN_DURATION = 266;
161     public static final int ANIMATION_NAV_FADE_OUT_DURATION = 133;
162     public static final long ANIMATION_DELAY_NAV_FADE_IN =
163             APP_LAUNCH_DURATION - ANIMATION_NAV_FADE_IN_DURATION;
164     public static final Interpolator NAV_FADE_IN_INTERPOLATOR =
165             new PathInterpolator(0f, 0f, 0f, 1f);
166     public static final Interpolator NAV_FADE_OUT_INTERPOLATOR =
167             new PathInterpolator(0.2f, 0f, 1f, 1f);
168 
169     private static final long CROP_DURATION = 375;
170     private static final long RADIUS_DURATION = 375;
171 
172     public static final int RECENTS_LAUNCH_DURATION = 336;
173     private static final int LAUNCHER_RESUME_START_DELAY = 100;
174     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
175 
176     public static final int CONTENT_ALPHA_DURATION = 217;
177     protected static final int CONTENT_SCALE_DURATION = 350;
178     protected static final int CONTENT_SCRIM_DURATION = 350;
179 
180     private static final int MAX_NUM_TASKS = 5;
181 
182     // Cross-fade duration between App Widget and App
183     private static final int WIDGET_CROSSFADE_DURATION_MILLIS = 125;
184 
185     protected final BaseQuickstepLauncher mLauncher;
186 
187     private final DragLayer mDragLayer;
188     private final AlphaProperty mDragLayerAlpha;
189 
190     final Handler mHandler;
191 
192     private final float mContentScale;
193     private final float mClosingWindowTransY;
194     private final float mMaxShadowRadius;
195 
196     private final StartingWindowListener mStartingWindowListener = new StartingWindowListener();
197 
198     private DeviceProfile mDeviceProfile;
199 
200     private RemoteAnimationProvider mRemoteAnimationProvider;
201     // Strong refs to runners which are cleared when the launcher activity is destroyed
202     private RemoteAnimationFactory mWallpaperOpenRunner;
203     private RemoteAnimationFactory mAppLaunchRunner;
204     private RemoteAnimationFactory mKeyguardGoingAwayRunner;
205 
206     private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
207     private RemoteTransitionCompat mLauncherOpenTransition;
208 
209     private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
210         @Override
211         public void onAnimationStart(Animator animation) {
212             mLauncher.addForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
213         }
214 
215         @Override
216         public void onAnimationEnd(Animator animation) {
217             mLauncher.clearForceInvisibleFlag(INVISIBLE_BY_APP_TRANSITIONS);
218         }
219     };
220 
221     // Pairs of window starting type and starting window background color for starting tasks
222     // Will never be larger than MAX_NUM_TASKS
223     private LinkedHashMap<Integer, Pair<Integer, Integer>> mTaskStartParams;
224 
QuickstepTransitionManager(Context context)225     public QuickstepTransitionManager(Context context) {
226         mLauncher = Launcher.cast(Launcher.getLauncher(context));
227         mDragLayer = mLauncher.getDragLayer();
228         mDragLayerAlpha = mDragLayer.getAlphaProperty(ALPHA_INDEX_TRANSITIONS);
229         mHandler = new Handler(Looper.getMainLooper());
230         mDeviceProfile = mLauncher.getDeviceProfile();
231 
232         Resources res = mLauncher.getResources();
233         mContentScale = res.getFloat(R.dimen.content_scale);
234         mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
235         mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
236 
237         mLauncher.addOnDeviceProfileChangeListener(this);
238 
239         if (supportsSSplashScreen()) {
240             mTaskStartParams = new LinkedHashMap<Integer, Pair<Integer, Integer>>(MAX_NUM_TASKS) {
241                 @Override
242                 protected boolean removeEldestEntry(Entry<Integer, Pair<Integer, Integer>> entry) {
243                     return size() > MAX_NUM_TASKS;
244                 }
245             };
246 
247             mStartingWindowListener.setTransitionManager(this);
248             SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(
249                     mStartingWindowListener);
250         }
251     }
252 
253     @Override
onDeviceProfileChanged(DeviceProfile dp)254     public void onDeviceProfileChanged(DeviceProfile dp) {
255         mDeviceProfile = dp;
256     }
257 
258     /**
259      * @return ActivityOptions with remote animations that controls how the window of the opening
260      * targets are displayed.
261      */
getActivityLaunchOptions(View v)262     public ActivityOptionsWrapper getActivityLaunchOptions(View v) {
263         boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
264         RunnableList onEndCallback = new RunnableList();
265         mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);
266         RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(
267                 mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */);
268 
269         // Note that this duration is a guess as we do not know if the animation will be a
270         // recents launch or not for sure until we know the opening app targets.
271         long duration = fromRecents
272                 ? RECENTS_LAUNCH_DURATION
273                 : APP_LAUNCH_DURATION;
274 
275         long statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION
276                 - STATUS_BAR_TRANSITION_PRE_DELAY;
277         RemoteAnimationAdapterCompat adapterCompat =
278                 new RemoteAnimationAdapterCompat(runner, duration, statusBarTransitionDelay);
279         return new ActivityOptionsWrapper(
280                 ActivityOptionsCompat.makeRemoteAnimation(adapterCompat), onEndCallback);
281     }
282 
283     /**
284      * Whether the launch is a recents app transition and we should do a launch animation
285      * from the recents view. Note that if the remote animation targets are not provided, this
286      * may not always be correct as we may resolve the opening app to a task when the animation
287      * starts.
288      *
289      * @param v       the view to launch from
290      * @param targets apps that are opening/closing
291      * @return true if the app is launching from recents, false if it most likely is not
292      */
isLaunchingFromRecents(@onNull View v, @Nullable RemoteAnimationTargetCompat[] targets)293     protected boolean isLaunchingFromRecents(@NonNull View v,
294             @Nullable RemoteAnimationTargetCompat[] targets) {
295         return mLauncher.getStateManager().getState().overviewUi
296                 && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
297     }
298 
299     /**
300      * Composes the animations for a launch from the recents list.
301      *
302      * @param anim            the animator set to add to
303      * @param v               the launching view
304      * @param appTargets      the apps that are opening/closing
305      * @param launcherClosing true if the launcher app is closing
306      */
composeRecentsLaunchAnimator(@onNull AnimatorSet anim, @NonNull View v, @NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing)307     protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
308             @NonNull RemoteAnimationTargetCompat[] appTargets,
309             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
310             @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing) {
311         TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
312                 nonAppTargets, launcherClosing, mLauncher.getStateManager(),
313                 mLauncher.getOverviewPanel(), mLauncher.getDepthController());
314     }
315 
areAllTargetsTranslucent(@onNull RemoteAnimationTargetCompat[] targets)316     private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTargetCompat[] targets) {
317         boolean isAllOpeningTargetTrs = true;
318         for (int i = 0; i < targets.length; i++) {
319             RemoteAnimationTargetCompat target = targets[i];
320             if (target.mode == MODE_OPENING) {
321                 isAllOpeningTargetTrs &= target.isTranslucent;
322             }
323             if (!isAllOpeningTargetTrs) break;
324         }
325         return isAllOpeningTargetTrs;
326     }
327 
328     /**
329      * Compose the animations for a launch from the app icon.
330      *
331      * @param anim            the animation to add to
332      * @param v               the launching view with the icon
333      * @param appTargets      the list of opening/closing apps
334      * @param launcherClosing true if launcher is closing
335      */
composeIconLaunchAnimator(@onNull AnimatorSet anim, @NonNull View v, @NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing)336     private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
337             @NonNull RemoteAnimationTargetCompat[] appTargets,
338             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
339             @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
340             boolean launcherClosing) {
341         // Set the state animation first so that any state listeners are called
342         // before our internal listeners.
343         mLauncher.getStateManager().setCurrentAnimation(anim);
344 
345         final int rotationChange = getRotationChange(appTargets);
346         // Note: the targetBounds are relative to the launcher
347         int startDelay = getSingleFrameMs(mLauncher);
348         Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
349         Animator windowAnimator = getOpeningWindowAnimators(v, appTargets, wallpaperTargets,
350                 nonAppTargets, windowTargetBounds, areAllTargetsTranslucent(appTargets),
351                 rotationChange);
352         windowAnimator.setStartDelay(startDelay);
353         anim.play(windowAnimator);
354         if (launcherClosing) {
355             // Delay animation by a frame to avoid jank.
356             Pair<AnimatorSet, Runnable> launcherContentAnimator =
357                     getLauncherContentAnimator(true /* isAppOpening */, startDelay);
358             anim.play(launcherContentAnimator.first);
359             anim.addListener(new AnimatorListenerAdapter() {
360                 @Override
361                 public void onAnimationEnd(Animator animation) {
362                     launcherContentAnimator.second.run();
363                 }
364             });
365         } else {
366             anim.addListener(new AnimatorListenerAdapter() {
367                 @Override
368                 public void onAnimationStart(Animator animation) {
369                     mLauncher.addOnResumeCallback(() ->
370                             ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
371                                     mLauncher.getStateManager().getState().getDepth(
372                                             mLauncher)).start());
373                 }
374             });
375         }
376     }
377 
composeWidgetLaunchAnimator( @onNull AnimatorSet anim, @NonNull LauncherAppWidgetHostView v, @NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, @NonNull RemoteAnimationTargetCompat[] nonAppTargets)378     private void composeWidgetLaunchAnimator(
379             @NonNull AnimatorSet anim,
380             @NonNull LauncherAppWidgetHostView v,
381             @NonNull RemoteAnimationTargetCompat[] appTargets,
382             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
383             @NonNull RemoteAnimationTargetCompat[] nonAppTargets) {
384         mLauncher.getStateManager().setCurrentAnimation(anim);
385 
386         Rect windowTargetBounds = getWindowTargetBounds(appTargets, getRotationChange(appTargets));
387         anim.play(getOpeningWindowAnimatorsForWidget(v, appTargets, wallpaperTargets, nonAppTargets,
388                 windowTargetBounds, areAllTargetsTranslucent(appTargets)));
389 
390         anim.addListener(new AnimatorListenerAdapter() {
391             @Override
392             public void onAnimationStart(Animator animation) {
393                 mLauncher.addOnResumeCallback(() ->
394                         ObjectAnimator.ofFloat(mLauncher.getDepthController(), DEPTH,
395                                 mLauncher.getStateManager().getState().getDepth(
396                                         mLauncher)).start());
397             }
398         });
399     }
400 
401     /**
402      * Return the window bounds of the opening target.
403      * In multiwindow mode, we need to get the final size of the opening app window target to help
404      * figure out where the floating view should animate to.
405      */
getWindowTargetBounds(@onNull RemoteAnimationTargetCompat[] appTargets, int rotationChange)406     private Rect getWindowTargetBounds(@NonNull RemoteAnimationTargetCompat[] appTargets,
407             int rotationChange) {
408         RemoteAnimationTargetCompat target = null;
409         for (RemoteAnimationTargetCompat t : appTargets) {
410             if (t.mode != MODE_OPENING) continue;
411             target = t;
412             break;
413         }
414         if (target == null) return new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx);
415         final Rect bounds = new Rect(target.screenSpaceBounds);
416         if (target.localBounds != null) {
417             bounds.set(target.localBounds);
418         } else {
419             bounds.offsetTo(target.position.x, target.position.y);
420         }
421         if (rotationChange != 0) {
422             if ((rotationChange % 2) == 1) {
423                 // undoing rotation, so our "original" parent size is actually flipped
424                 Utilities.rotateBounds(bounds, mDeviceProfile.heightPx, mDeviceProfile.widthPx,
425                         4 - rotationChange);
426             } else {
427                 Utilities.rotateBounds(bounds, mDeviceProfile.widthPx, mDeviceProfile.heightPx,
428                         4 - rotationChange);
429             }
430         }
431         return bounds;
432     }
433 
setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider, CancellationSignal cancellationSignal)434     public void setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider,
435             CancellationSignal cancellationSignal) {
436         mRemoteAnimationProvider = animationProvider;
437         cancellationSignal.setOnCancelListener(() -> {
438             if (animationProvider == mRemoteAnimationProvider) {
439                 mRemoteAnimationProvider = null;
440             }
441         });
442     }
443 
444     /**
445      * Content is everything on screen except the background and the floating view (if any).
446      *
447      * @param isAppOpening True when this is called when an app is opening.
448      *                     False when this is called when an app is closing.
449      * @param startDelay   Start delay duration.
450      */
getLauncherContentAnimator(boolean isAppOpening, int startDelay)451     private Pair<AnimatorSet, Runnable> getLauncherContentAnimator(boolean isAppOpening,
452             int startDelay) {
453         AnimatorSet launcherAnimator = new AnimatorSet();
454         Runnable endListener;
455 
456         float[] alphas = isAppOpening
457                 ? new float[]{1, 0}
458                 : new float[]{0, 1};
459 
460         float[] scales = isAppOpening
461                 ? new float[]{1, mContentScale}
462                 : new float[]{mContentScale, 1};
463 
464         if (mLauncher.isInState(ALL_APPS)) {
465             // All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
466             final View appsView = mLauncher.getAppsView();
467             final float startAlpha = appsView.getAlpha();
468             final float startScale = SCALE_PROPERTY.get(appsView);
469             appsView.setAlpha(alphas[0]);
470             SCALE_PROPERTY.set(appsView, scales[0]);
471 
472             ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
473             alpha.setDuration(CONTENT_ALPHA_DURATION);
474             alpha.setInterpolator(LINEAR);
475             appsView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
476             alpha.addListener(new AnimatorListenerAdapter() {
477                 @Override
478                 public void onAnimationEnd(Animator animation) {
479                     appsView.setLayerType(View.LAYER_TYPE_NONE, null);
480                 }
481             });
482             ObjectAnimator scale = ObjectAnimator.ofFloat(appsView, SCALE_PROPERTY, scales);
483             scale.setInterpolator(AGGRESSIVE_EASE);
484             scale.setDuration(CONTENT_SCALE_DURATION);
485 
486             launcherAnimator.play(alpha);
487             launcherAnimator.play(scale);
488 
489             endListener = () -> {
490                 appsView.setAlpha(startAlpha);
491                 SCALE_PROPERTY.set(appsView, startScale);
492                 appsView.setLayerType(View.LAYER_TYPE_NONE, null);
493             };
494         } else if (mLauncher.isInState(OVERVIEW)) {
495             endListener = composeViewContentAnimator(launcherAnimator, alphas, scales);
496         } else {
497             List<View> viewsToAnimate = new ArrayList<>();
498             Workspace workspace = mLauncher.getWorkspace();
499             workspace.forEachVisiblePage(
500                     view -> viewsToAnimate.add(((CellLayout) view).getShortcutsAndWidgets()));
501 
502             viewsToAnimate.add(mLauncher.getHotseat());
503 
504             viewsToAnimate.forEach(view -> {
505                 view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
506 
507                 ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(view, SCALE_PROPERTY, scales)
508                         .setDuration(CONTENT_SCALE_DURATION);
509                 scaleAnim.setInterpolator(DEACCEL_1_5);
510                 launcherAnimator.play(scaleAnim);
511             });
512 
513             final boolean scrimEnabled = ENABLE_SCRIM_FOR_APP_LAUNCH.get();
514             if (scrimEnabled) {
515                 int scrimColor = Themes.getAttrColor(mLauncher, R.attr.overviewScrimColor);
516                 int scrimColorTrans = ColorUtils.setAlphaComponent(scrimColor, 0);
517                 int[] colors = isAppOpening
518                         ? new int[]{scrimColorTrans, scrimColor}
519                         : new int[]{scrimColor, scrimColorTrans};
520                 ScrimView scrimView = mLauncher.getScrimView();
521                 if (scrimView.getBackground() instanceof ColorDrawable) {
522                     scrimView.setBackgroundColor(colors[0]);
523 
524                     ObjectAnimator scrim = ObjectAnimator.ofArgb(scrimView, VIEW_BACKGROUND_COLOR,
525                             colors);
526                     scrim.setDuration(CONTENT_SCRIM_DURATION);
527                     scrim.setInterpolator(DEACCEL_1_5);
528                     launcherAnimator.play(scrim);
529                 }
530             }
531 
532             // Pause page indicator animations as they lead to layer trashing.
533             mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
534 
535             endListener = () -> {
536                 viewsToAnimate.forEach(view -> {
537                     SCALE_PROPERTY.set(view, 1f);
538                     view.setLayerType(View.LAYER_TYPE_NONE, null);
539                 });
540                 if (scrimEnabled) {
541                     mLauncher.getScrimView().setBackgroundColor(Color.TRANSPARENT);
542                 }
543                 mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
544             };
545         }
546 
547         launcherAnimator.setStartDelay(startDelay);
548         return new Pair<>(launcherAnimator, endListener);
549     }
550 
551     /**
552      * Compose recents view alpha and translation Y animation when launcher opens/closes apps.
553      *
554      * @param anim   the animator set to add to
555      * @param alphas the alphas to animate to over time
556      * @param scales the scale values to animator to over time
557      * @return listener to run when the animation ends
558      */
composeViewContentAnimator(@onNull AnimatorSet anim, float[] alphas, float[] scales)559     protected Runnable composeViewContentAnimator(@NonNull AnimatorSet anim,
560             float[] alphas, float[] scales) {
561         RecentsView overview = mLauncher.getOverviewPanel();
562         ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
563                 RecentsView.CONTENT_ALPHA, alphas);
564         alpha.setDuration(CONTENT_ALPHA_DURATION);
565         alpha.setInterpolator(LINEAR);
566         anim.play(alpha);
567         overview.setFreezeViewVisibility(true);
568 
569         ObjectAnimator scaleAnim = ObjectAnimator.ofFloat(overview, SCALE_PROPERTY, scales);
570         scaleAnim.setInterpolator(AGGRESSIVE_EASE);
571         scaleAnim.setDuration(CONTENT_SCALE_DURATION);
572         anim.play(scaleAnim);
573 
574         return () -> {
575             overview.setFreezeViewVisibility(false);
576             SCALE_PROPERTY.set(overview, 1f);
577             mLauncher.getStateManager().reapplyState();
578         };
579     }
580 
581     /**
582      * @return Animator that controls the window of the opening targets from app icons.
583      */
getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets, RemoteAnimationTargetCompat[] nonAppTargets, Rect windowTargetBounds, boolean appTargetsAreTranslucent, int rotationChange)584     private Animator getOpeningWindowAnimators(View v,
585             RemoteAnimationTargetCompat[] appTargets,
586             RemoteAnimationTargetCompat[] wallpaperTargets,
587             RemoteAnimationTargetCompat[] nonAppTargets,
588             Rect windowTargetBounds, boolean appTargetsAreTranslucent, int rotationChange) {
589         RectF launcherIconBounds = new RectF();
590         FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
591                 !appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);
592         Rect crop = new Rect();
593         Matrix matrix = new Matrix();
594 
595         RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
596                 wallpaperTargets, nonAppTargets, MODE_OPENING);
597         SurfaceTransactionApplier surfaceApplier =
598                 new SurfaceTransactionApplier(floatingView);
599         openingTargets.addReleaseCheck(surfaceApplier);
600         RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
601 
602         int[] dragLayerBounds = new int[2];
603         mDragLayer.getLocationOnScreen(dragLayerBounds);
604 
605         final boolean hasSplashScreen;
606         if (supportsSSplashScreen()) {
607             int taskId = openingTargets.getFirstAppTargetTaskId();
608             Pair<Integer, Integer> defaultParams = Pair.create(STARTING_WINDOW_TYPE_NONE, 0);
609             Pair<Integer, Integer> taskParams =
610                     mTaskStartParams.getOrDefault(taskId, defaultParams);
611             mTaskStartParams.remove(taskId);
612             hasSplashScreen = taskParams.first == STARTING_WINDOW_TYPE_SPLASH_SCREEN;
613         } else {
614             hasSplashScreen = false;
615         }
616 
617         AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile,
618                 windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1],
619                 hasSplashScreen, floatingView.isDifferentFromAppIcon());
620         int left = (int) (prop.cropCenterXStart - prop.cropWidthStart / 2);
621         int top = (int) (prop.cropCenterYStart - prop.cropHeightStart / 2);
622         int right = (int) (left + prop.cropWidthStart);
623         int bottom = (int) (top + prop.cropHeightStart);
624         // Set the crop here so we can calculate the corner radius below.
625         crop.set(left, top, right, bottom);
626 
627         RectF floatingIconBounds = new RectF();
628         RectF tmpRectF = new RectF();
629         Point tmpPos = new Point();
630 
631         AnimatorSet animatorSet = new AnimatorSet();
632         ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
633         appAnimator.setDuration(APP_LAUNCH_DURATION);
634         appAnimator.setInterpolator(LINEAR);
635         appAnimator.addListener(floatingView);
636         appAnimator.addListener(new AnimatorListenerAdapter() {
637             @Override
638             public void onAnimationEnd(Animator animation) {
639                 if (v instanceof BubbleTextView) {
640                     ((BubbleTextView) v).setStayPressed(false);
641                 }
642                 openingTargets.release();
643             }
644         });
645 
646         final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
647                 ? Math.max(crop.width(), crop.height()) / 2f
648                 : 0f;
649         final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
650                 ? 0 : getWindowCornerRadius(mLauncher.getResources());
651         final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;
652 
653         MultiValueUpdateListener listener = new MultiValueUpdateListener() {
654             FloatProp mDx = new FloatProp(0, prop.dX, 0, prop.xDuration, AGGRESSIVE_EASE);
655             FloatProp mDy = new FloatProp(0, prop.dY, 0, prop.yDuration, AGGRESSIVE_EASE);
656 
657             FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
658                     prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE);
659             FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f,
660                     APP_LAUNCH_ALPHA_START_DELAY, prop.alphaDuration, LINEAR);
661 
662             FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius, 0,
663                     RADIUS_DURATION, EXAGGERATED_EASE);
664             FloatProp mShadowRadius = new FloatProp(0, finalShadowRadius, 0,
665                     APP_LAUNCH_DURATION, EXAGGERATED_EASE);
666 
667             FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd,
668                     0, CROP_DURATION, EXAGGERATED_EASE);
669             FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
670                     0, CROP_DURATION, EXAGGERATED_EASE);
671             FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd, 0,
672                     CROP_DURATION, EXAGGERATED_EASE);
673             FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0,
674                     CROP_DURATION, EXAGGERATED_EASE);
675 
676             FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
677                     NAV_FADE_OUT_INTERPOLATOR);
678             FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN,
679                     ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
680 
681             @Override
682             public void onUpdate(float percent, boolean initOnly) {
683                 // Calculate the size of the scaled icon.
684                 float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value;
685                 float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value;
686 
687                 int left = (int) (mCropRectCenterX.value - mCropRectWidth.value / 2);
688                 int top = (int) (mCropRectCenterY.value - mCropRectHeight.value / 2);
689                 int right = (int) (left + mCropRectWidth.value);
690                 int bottom = (int) (top + mCropRectHeight.value);
691                 crop.set(left, top, right, bottom);
692 
693                 final int windowCropWidth = crop.width();
694                 final int windowCropHeight = crop.height();
695                 if (rotationChange != 0) {
696                     Utilities.rotateBounds(crop, mDeviceProfile.widthPx,
697                             mDeviceProfile.heightPx, rotationChange);
698                 }
699 
700                 // Scale the size of the icon to match the size of the window crop.
701                 float scaleX = iconWidth / windowCropWidth;
702                 float scaleY = iconHeight / windowCropHeight;
703                 float scale = Math.min(1f, Math.max(scaleX, scaleY));
704 
705                 float scaledCropWidth = windowCropWidth * scale;
706                 float scaledCropHeight = windowCropHeight * scale;
707                 float offsetX = (scaledCropWidth - iconWidth) / 2;
708                 float offsetY = (scaledCropHeight - iconHeight) / 2;
709 
710                 // Calculate the window position to match the icon position.
711                 tmpRectF.set(launcherIconBounds);
712                 tmpRectF.offset(dragLayerBounds[0], dragLayerBounds[1]);
713                 tmpRectF.offset(mDx.value, mDy.value);
714                 Utilities.scaleRectFAboutCenter(tmpRectF, mIconScaleToFitScreen.value);
715                 float windowTransX0 = tmpRectF.left - offsetX - crop.left * scale;
716                 float windowTransY0 = tmpRectF.top - offsetY - crop.top * scale;
717 
718                 // Calculate the icon position.
719                 floatingIconBounds.set(launcherIconBounds);
720                 floatingIconBounds.offset(mDx.value, mDy.value);
721                 Utilities.scaleRectFAboutCenter(floatingIconBounds, mIconScaleToFitScreen.value);
722                 floatingIconBounds.left -= offsetX;
723                 floatingIconBounds.top -= offsetY;
724                 floatingIconBounds.right += offsetX;
725                 floatingIconBounds.bottom += offsetY;
726 
727                 if (initOnly) {
728                     // For the init pass, we want full alpha since the window is not yet ready.
729                     floatingView.update(1f, 255, floatingIconBounds, percent, 0f,
730                             mWindowRadius.value * scale, true /* isOpening */);
731                     return;
732                 }
733 
734                 ArrayList<SurfaceParams> params = new ArrayList<>();
735                 for (int i = appTargets.length - 1; i >= 0; i--) {
736                     RemoteAnimationTargetCompat target = appTargets[i];
737                     SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
738 
739                     if (target.mode == MODE_OPENING) {
740                         matrix.setScale(scale, scale);
741                         if (rotationChange == 1) {
742                             matrix.postTranslate(windowTransY0,
743                                     mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth));
744                         } else if (rotationChange == 2) {
745                             matrix.postTranslate(
746                                     mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth),
747                                     mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight));
748                         } else if (rotationChange == 3) {
749                             matrix.postTranslate(
750                                     mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight),
751                                     windowTransX0);
752                         } else {
753                             matrix.postTranslate(windowTransX0, windowTransY0);
754                         }
755 
756                         floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
757                                 mWindowRadius.value * scale, true /* isOpening */);
758                         builder.withMatrix(matrix)
759                                 .withWindowCrop(crop)
760                                 .withAlpha(1f - mIconAlpha.value)
761                                 .withCornerRadius(mWindowRadius.value)
762                                 .withShadowRadius(mShadowRadius.value);
763                     } else if (target.mode == MODE_CLOSING) {
764                         if (target.localBounds != null) {
765                             final Rect localBounds = target.localBounds;
766                             tmpPos.set(target.localBounds.left, target.localBounds.top);
767                         } else {
768                             tmpPos.set(target.position.x, target.position.y);
769                         }
770                         final Rect crop = new Rect(target.screenSpaceBounds);
771                         crop.offsetTo(0, 0);
772 
773                         if ((rotationChange % 2) == 1) {
774                             int tmp = crop.right;
775                             crop.right = crop.bottom;
776                             crop.bottom = tmp;
777                             tmp = tmpPos.x;
778                             tmpPos.x = tmpPos.y;
779                             tmpPos.y = tmp;
780                         }
781                         matrix.setTranslate(tmpPos.x, tmpPos.y);
782                         builder.withMatrix(matrix)
783                                 .withWindowCrop(crop)
784                                 .withAlpha(1f);
785                     }
786                     params.add(builder.build());
787                 }
788 
789                 if (navBarTarget != null) {
790                     final SurfaceParams.Builder navBuilder =
791                             new SurfaceParams.Builder(navBarTarget.leash);
792                     if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
793                         matrix.setScale(scale, scale);
794                         matrix.postTranslate(windowTransX0, windowTransY0);
795                         navBuilder.withMatrix(matrix)
796                                 .withWindowCrop(crop)
797                                 .withAlpha(mNavFadeIn.value);
798                     } else {
799                         navBuilder.withAlpha(mNavFadeOut.value);
800                     }
801                     params.add(navBuilder.build());
802                 }
803 
804                 surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
805             }
806         };
807         appAnimator.addUpdateListener(listener);
808         // Since we added a start delay, call update here to init the FloatingIconView properly.
809         listener.onUpdate(0, true /* initOnly */);
810 
811         animatorSet.playTogether(appAnimator, getBackgroundAnimator(appTargets));
812         return animatorSet;
813     }
814 
getOpeningWindowAnimatorsForWidget(LauncherAppWidgetHostView v, RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets, RemoteAnimationTargetCompat[] nonAppTargets, Rect windowTargetBounds, boolean appTargetsAreTranslucent)815     private Animator getOpeningWindowAnimatorsForWidget(LauncherAppWidgetHostView v,
816             RemoteAnimationTargetCompat[] appTargets,
817             RemoteAnimationTargetCompat[] wallpaperTargets,
818             RemoteAnimationTargetCompat[] nonAppTargets, Rect windowTargetBounds,
819             boolean appTargetsAreTranslucent) {
820         final RectF widgetBackgroundBounds = new RectF();
821         final Rect appWindowCrop = new Rect();
822         final Matrix matrix = new Matrix();
823         RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
824                 wallpaperTargets, nonAppTargets, MODE_OPENING);
825 
826         RemoteAnimationTargetCompat openingTarget = openingTargets.getFirstAppTarget();
827         int fallbackBackgroundColor = 0;
828         if (openingTarget != null && supportsSSplashScreen()) {
829             fallbackBackgroundColor = mTaskStartParams.containsKey(openingTarget.taskId)
830                     ? mTaskStartParams.get(openingTarget.taskId).second : 0;
831             mTaskStartParams.remove(openingTarget.taskId);
832         }
833         if (fallbackBackgroundColor == 0) {
834             fallbackBackgroundColor =
835                     FloatingWidgetView.getDefaultBackgroundColor(mLauncher, openingTarget);
836         }
837 
838         final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
839                 ? 0 : getWindowCornerRadius(mLauncher.getResources());
840         final FloatingWidgetView floatingView = FloatingWidgetView.getFloatingWidgetView(mLauncher,
841                 v, widgetBackgroundBounds,
842                 new Size(windowTargetBounds.width(), windowTargetBounds.height()),
843                 finalWindowRadius, appTargetsAreTranslucent, fallbackBackgroundColor);
844         final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
845                 ? floatingView.getInitialCornerRadius() : 0;
846 
847         SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(floatingView);
848         openingTargets.addReleaseCheck(surfaceApplier);
849 
850         RemoteAnimationTargetCompat navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();
851 
852         AnimatorSet animatorSet = new AnimatorSet();
853         ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
854         appAnimator.setDuration(APP_LAUNCH_DURATION);
855         appAnimator.setInterpolator(LINEAR);
856         appAnimator.addListener(floatingView);
857         appAnimator.addListener(new AnimatorListenerAdapter() {
858             @Override
859             public void onAnimationEnd(Animator animation) {
860                 openingTargets.release();
861             }
862         });
863         floatingView.setFastFinishRunnable(animatorSet::end);
864 
865         appAnimator.addUpdateListener(new MultiValueUpdateListener() {
866             float mAppWindowScale = 1;
867             final FloatProp mWidgetForegroundAlpha = new FloatProp(1 /* start */,
868                     0 /* end */, 0 /* delay */,
869                     WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* duration */, LINEAR);
870             final FloatProp mWidgetFallbackBackgroundAlpha = new FloatProp(0 /* start */,
871                     1 /* end */, 0 /* delay */, 75 /* duration */, LINEAR);
872             final FloatProp mPreviewAlpha = new FloatProp(0 /* start */, 1 /* end */,
873                     WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* delay */,
874                     WIDGET_CROSSFADE_DURATION_MILLIS / 2 /* duration */, LINEAR);
875             final FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius,
876                     0 /* start */, RADIUS_DURATION, LINEAR);
877             final FloatProp mCornerRadiusProgress = new FloatProp(0, 1, 0, RADIUS_DURATION, LINEAR);
878 
879             // Window & widget background positioning bounds
880             final FloatProp mDx = new FloatProp(widgetBackgroundBounds.centerX(),
881                     windowTargetBounds.centerX(), 0 /* delay */, APP_LAUNCH_CURVED_DURATION,
882                     EXAGGERATED_EASE);
883             final FloatProp mDy = new FloatProp(widgetBackgroundBounds.centerY(),
884                     windowTargetBounds.centerY(), 0 /* delay */, APP_LAUNCH_DURATION,
885                     EXAGGERATED_EASE);
886             final FloatProp mWidth = new FloatProp(widgetBackgroundBounds.width(),
887                     windowTargetBounds.width(), 0 /* delay */, APP_LAUNCH_DURATION,
888                     EXAGGERATED_EASE);
889             final FloatProp mHeight = new FloatProp(widgetBackgroundBounds.height(),
890                     windowTargetBounds.height(), 0 /* delay */, APP_LAUNCH_DURATION,
891                     EXAGGERATED_EASE);
892 
893             final FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
894                     NAV_FADE_OUT_INTERPOLATOR);
895             final FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN,
896                     ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
897 
898             @Override
899             public void onUpdate(float percent, boolean initOnly) {
900                 widgetBackgroundBounds.set(mDx.value - mWidth.value / 2f,
901                         mDy.value - mHeight.value / 2f, mDx.value + mWidth.value / 2f,
902                         mDy.value + mHeight.value / 2f);
903                 // Set app window scaling factor to match widget background width
904                 mAppWindowScale = widgetBackgroundBounds.width() / windowTargetBounds.width();
905                 // Crop scaled app window to match widget
906                 appWindowCrop.set(0 /* left */, 0 /* top */,
907                         Math.round(windowTargetBounds.width()) /* right */,
908                         Math.round(widgetBackgroundBounds.height() / mAppWindowScale) /* bottom */);
909                 matrix.setTranslate(widgetBackgroundBounds.left, widgetBackgroundBounds.top);
910                 matrix.postScale(mAppWindowScale, mAppWindowScale, widgetBackgroundBounds.left,
911                         widgetBackgroundBounds.top);
912 
913                 ArrayList<SurfaceParams> params = new ArrayList<>();
914                 float floatingViewAlpha = appTargetsAreTranslucent ? 1 - mPreviewAlpha.value : 1;
915                 for (int i = appTargets.length - 1; i >= 0; i--) {
916                     RemoteAnimationTargetCompat target = appTargets[i];
917                     SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
918                     if (target.mode == MODE_OPENING) {
919                         floatingView.update(widgetBackgroundBounds, floatingViewAlpha,
920                                 mWidgetForegroundAlpha.value, mWidgetFallbackBackgroundAlpha.value,
921                                 mCornerRadiusProgress.value);
922                         builder.withMatrix(matrix)
923                                 .withWindowCrop(appWindowCrop)
924                                 .withAlpha(mPreviewAlpha.value)
925                                 .withCornerRadius(mWindowRadius.value / mAppWindowScale);
926                     }
927                     params.add(builder.build());
928                 }
929 
930                 if (navBarTarget != null) {
931                     final SurfaceParams.Builder navBuilder =
932                             new SurfaceParams.Builder(navBarTarget.leash);
933                     if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
934                         navBuilder.withMatrix(matrix)
935                                 .withWindowCrop(appWindowCrop)
936                                 .withAlpha(mNavFadeIn.value);
937                     } else {
938                         navBuilder.withAlpha(mNavFadeOut.value);
939                     }
940                     params.add(navBuilder.build());
941                 }
942 
943                 surfaceApplier.scheduleApply(params.toArray(new SurfaceParams[params.size()]));
944             }
945         });
946 
947         animatorSet.playTogether(appAnimator, getBackgroundAnimator(appTargets));
948         return animatorSet;
949     }
950 
getBackgroundAnimator(RemoteAnimationTargetCompat[] appTargets)951     private ObjectAnimator getBackgroundAnimator(RemoteAnimationTargetCompat[] appTargets) {
952         // When launching an app from overview that doesn't map to a task, we still want to just
953         // blur the wallpaper instead of the launcher surface as well
954         boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW;
955         DepthController depthController = mLauncher.getDepthController();
956         ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController, DEPTH,
957                 BACKGROUND_APP.getDepth(mLauncher))
958                 .setDuration(APP_LAUNCH_DURATION);
959         if (allowBlurringLauncher) {
960             final SurfaceControl dimLayer;
961             if (BlurUtils.supportsBlursOnWindows()) {
962                 // Create a temporary effect layer, that lives on top of launcher, so we can apply
963                 // the blur to it. The EffectLayer will be fullscreen, which will help with caching
964                 // optimizations on the SurfaceFlinger side:
965                 // - Results would be able to be cached as a texture
966                 // - There won't be texture allocation overhead, because EffectLayers don't have
967                 //   buffers
968                 ViewRootImpl viewRootImpl = mLauncher.getDragLayer().getViewRootImpl();
969                 SurfaceControl parent = viewRootImpl != null
970                         ? viewRootImpl.getSurfaceControl()
971                         : null;
972                 dimLayer = new SurfaceControl.Builder()
973                         .setName("Blur layer")
974                         .setParent(parent)
975                         .setOpaque(false)
976                         .setHidden(false)
977                         .setEffectLayer()
978                         .build();
979             } else {
980                 dimLayer = null;
981             }
982 
983             depthController.setSurface(dimLayer);
984             backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
985                 @Override
986                 public void onAnimationStart(Animator animation) {
987                     depthController.setIsInLaunchTransition(true);
988                 }
989 
990                 @Override
991                 public void onAnimationEnd(Animator animation) {
992                     depthController.setIsInLaunchTransition(false);
993                     depthController.setSurface(null);
994                     if (dimLayer != null) {
995                         new SurfaceControl.Transaction()
996                                 .remove(dimLayer)
997                                 .apply();
998                     }
999                 }
1000             });
1001         }
1002         return backgroundRadiusAnim;
1003     }
1004 
1005     /**
1006      * Registers remote animations used when closing apps to home screen.
1007      */
registerRemoteAnimations()1008     public void registerRemoteAnimations() {
1009         if (SEPARATE_RECENTS_ACTIVITY.get()) {
1010             return;
1011         }
1012         if (hasControlRemoteAppTransitionPermission()) {
1013             mWallpaperOpenRunner = createWallpaperOpenRunner(false /* fromUnlock */);
1014 
1015             RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
1016             definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
1017                     WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
1018                     new RemoteAnimationAdapterCompat(
1019                             new LauncherAnimationRunner(mHandler, mWallpaperOpenRunner,
1020                                     false /* startAtFrontOfQueue */),
1021                             CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
1022 
1023             if (KEYGUARD_ANIMATION.get()) {
1024                 mKeyguardGoingAwayRunner = createWallpaperOpenRunner(true /* fromUnlock */);
1025                 definition.addRemoteAnimation(
1026                         WindowManagerWrapper.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
1027                         new RemoteAnimationAdapterCompat(
1028                                 new LauncherAnimationRunner(
1029                                         mHandler, mKeyguardGoingAwayRunner,
1030                                         true /* startAtFrontOfQueue */),
1031                                 CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
1032             }
1033 
1034             new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
1035         }
1036     }
1037 
1038     /**
1039      * Registers remote animations used when closing apps to home screen.
1040      */
registerRemoteTransitions()1041     public void registerRemoteTransitions() {
1042         if (SEPARATE_RECENTS_ACTIVITY.get()) {
1043             return;
1044         }
1045         if (hasControlRemoteAppTransitionPermission()) {
1046             mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
1047             mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
1048                     new LauncherAnimationRunner(mHandler, mWallpaperOpenTransitionRunner,
1049                             false /* startAtFrontOfQueue */));
1050             mLauncherOpenTransition.addHomeOpenCheck();
1051             SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
1052         }
1053     }
1054 
onActivityDestroyed()1055     public void onActivityDestroyed() {
1056         unregisterRemoteAnimations();
1057         unregisterRemoteTransitions();
1058         mStartingWindowListener.setTransitionManager(null);
1059         SystemUiProxy.INSTANCE.getNoCreate().setStartingWindowListener(null);
1060     }
1061 
unregisterRemoteAnimations()1062     private void unregisterRemoteAnimations() {
1063         if (SEPARATE_RECENTS_ACTIVITY.get()) {
1064             return;
1065         }
1066         if (hasControlRemoteAppTransitionPermission()) {
1067             new ActivityCompat(mLauncher).unregisterRemoteAnimations();
1068 
1069             // Also clear strong references to the runners registered with the remote animation
1070             // definition so we don't have to wait for the system gc
1071             mWallpaperOpenRunner = null;
1072             mAppLaunchRunner = null;
1073             mKeyguardGoingAwayRunner = null;
1074         }
1075     }
1076 
unregisterRemoteTransitions()1077     private void unregisterRemoteTransitions() {
1078         if (SEPARATE_RECENTS_ACTIVITY.get()) {
1079             return;
1080         }
1081         if (hasControlRemoteAppTransitionPermission()) {
1082             if (mLauncherOpenTransition == null) return;
1083             SystemUiProxy.INSTANCE.getNoCreate().unregisterRemoteTransition(
1084                     mLauncherOpenTransition);
1085             mLauncherOpenTransition = null;
1086             mWallpaperOpenTransitionRunner = null;
1087         }
1088     }
1089 
launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode)1090     private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
1091         return taskIsATargetWithMode(targets, mLauncher.getTaskId(), mode);
1092     }
1093 
1094     /**
1095      * @return Runner that plays when user goes to Launcher
1096      * ie. pressing home, swiping up from nav bar.
1097      */
createWallpaperOpenRunner(boolean fromUnlock)1098     RemoteAnimationFactory createWallpaperOpenRunner(boolean fromUnlock) {
1099         return new WallpaperOpenLauncherAnimationRunner(mHandler, fromUnlock);
1100     }
1101 
1102     /**
1103      * Animator that controls the transformations of the windows when unlocking the device.
1104      */
getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets)1105     private Animator getUnlockWindowAnimator(RemoteAnimationTargetCompat[] appTargets,
1106             RemoteAnimationTargetCompat[] wallpaperTargets) {
1107         SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
1108         ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
1109         unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
1110         float cornerRadius = mDeviceProfile.isMultiWindowMode ? 0 :
1111                 QuickStepContract.getWindowCornerRadius(mLauncher.getResources());
1112         unlockAnimator.addListener(new AnimatorListenerAdapter() {
1113             @Override
1114             public void onAnimationStart(Animator animation) {
1115                 SurfaceParams[] params = new SurfaceParams[appTargets.length];
1116                 for (int i = appTargets.length - 1; i >= 0; i--) {
1117                     RemoteAnimationTargetCompat target = appTargets[i];
1118                     params[i] = new SurfaceParams.Builder(target.leash)
1119                             .withAlpha(1f)
1120                             .withWindowCrop(target.screenSpaceBounds)
1121                             .withCornerRadius(cornerRadius)
1122                             .build();
1123                 }
1124                 surfaceApplier.scheduleApply(params);
1125             }
1126         });
1127         return unlockAnimator;
1128     }
1129 
getRotationChange(RemoteAnimationTargetCompat[] appTargets)1130     private static int getRotationChange(RemoteAnimationTargetCompat[] appTargets) {
1131         int rotationChange = 0;
1132         for (RemoteAnimationTargetCompat target : appTargets) {
1133             if (Math.abs(target.rotationChange) > Math.abs(rotationChange)) {
1134                 rotationChange = target.rotationChange;
1135             }
1136         }
1137         return rotationChange;
1138     }
1139 
1140     /**
1141      * Animator that controls the transformations of the windows the targets that are closing.
1142      */
getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets)1143     private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] appTargets,
1144             RemoteAnimationTargetCompat[] wallpaperTargets) {
1145         final int rotationChange = getRotationChange(appTargets);
1146         SurfaceTransactionApplier surfaceApplier = new SurfaceTransactionApplier(mDragLayer);
1147         Matrix matrix = new Matrix();
1148         Point tmpPos = new Point();
1149         Rect tmpRect = new Rect();
1150         ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
1151         int duration = CLOSING_TRANSITION_DURATION_MS;
1152         float windowCornerRadius = mDeviceProfile.isMultiWindowMode
1153                 ? 0 : getWindowCornerRadius(mLauncher.getResources());
1154         float startShadowRadius = areAllTargetsTranslucent(appTargets) ? 0 : mMaxShadowRadius;
1155         closingAnimator.setDuration(duration);
1156         closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
1157             FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
1158             FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
1159             FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
1160             FloatProp mShadowRadius = new FloatProp(startShadowRadius, 0, 0, duration,
1161                     DEACCEL_1_7);
1162 
1163             @Override
1164             public void onUpdate(float percent, boolean initOnly) {
1165                 SurfaceParams[] params = new SurfaceParams[appTargets.length];
1166                 for (int i = appTargets.length - 1; i >= 0; i--) {
1167                     RemoteAnimationTargetCompat target = appTargets[i];
1168                     SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
1169 
1170                     if (target.localBounds != null) {
1171                         tmpPos.set(target.localBounds.left, target.localBounds.top);
1172                     } else {
1173                         tmpPos.set(target.position.x, target.position.y);
1174                     }
1175 
1176                     final Rect crop = new Rect(target.screenSpaceBounds);
1177                     crop.offsetTo(0, 0);
1178                     if (target.mode == MODE_CLOSING) {
1179                         tmpRect.set(target.screenSpaceBounds);
1180                         if ((rotationChange % 2) != 0) {
1181                             final int right = crop.right;
1182                             crop.right = crop.bottom;
1183                             crop.bottom = right;
1184                         }
1185                         matrix.setScale(mScale.value, mScale.value,
1186                                 tmpRect.centerX(),
1187                                 tmpRect.centerY());
1188                         matrix.postTranslate(0, mDy.value);
1189                         matrix.postTranslate(tmpPos.x, tmpPos.y);
1190                         builder.withMatrix(matrix)
1191                                 .withWindowCrop(crop)
1192                                 .withAlpha(mAlpha.value)
1193                                 .withCornerRadius(windowCornerRadius)
1194                                 .withShadowRadius(mShadowRadius.value);
1195                     } else if (target.mode == MODE_OPENING) {
1196                         matrix.setTranslate(tmpPos.x, tmpPos.y);
1197                         builder.withMatrix(matrix)
1198                                 .withWindowCrop(crop)
1199                                 .withAlpha(1f);
1200                     }
1201                     params[i] = builder.build();
1202                 }
1203                 surfaceApplier.scheduleApply(params);
1204             }
1205         });
1206 
1207         return closingAnimator;
1208     }
1209 
supportsSSplashScreen()1210     private boolean supportsSSplashScreen() {
1211         return hasControlRemoteAppTransitionPermission()
1212                 && Utilities.ATLEAST_S
1213                 && ENABLE_SHELL_STARTING_SURFACE;
1214     }
1215 
1216     /**
1217      * Returns true if we have permission to control remote app transisions
1218      */
hasControlRemoteAppTransitionPermission()1219     public boolean hasControlRemoteAppTransitionPermission() {
1220         return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
1221                 == PackageManager.PERMISSION_GRANTED;
1222     }
1223 
addCujInstrumentation(Animator anim, int cuj)1224     private void addCujInstrumentation(Animator anim, int cuj) {
1225         anim.addListener(new AnimationSuccessListener() {
1226             @Override
1227             public void onAnimationStart(Animator animation) {
1228                 mDragLayer.getViewTreeObserver().addOnDrawListener(
1229                         new ViewTreeObserver.OnDrawListener() {
1230                             boolean mHandled = false;
1231 
1232                             @Override
1233                             public void onDraw() {
1234                                 if (mHandled) {
1235                                     return;
1236                                 }
1237                                 mHandled = true;
1238 
1239                                 InteractionJankMonitorWrapper.begin(mDragLayer, cuj);
1240 
1241                                 mDragLayer.post(() ->
1242                                         mDragLayer.getViewTreeObserver().removeOnDrawListener(
1243                                                 this));
1244                             }
1245                         });
1246                 super.onAnimationStart(animation);
1247             }
1248 
1249             @Override
1250             public void onAnimationCancel(Animator animation) {
1251                 super.onAnimationCancel(animation);
1252                 InteractionJankMonitorWrapper.cancel(cuj);
1253             }
1254 
1255             @Override
1256             public void onAnimationSuccess(Animator animator) {
1257                 InteractionJankMonitorWrapper.end(cuj);
1258             }
1259         });
1260     }
1261 
1262     /**
1263      * Remote animation runner for animation from the app to Launcher, including recents.
1264      */
1265     protected class WallpaperOpenLauncherAnimationRunner implements RemoteAnimationFactory {
1266 
1267         private final Handler mHandler;
1268         private final boolean mFromUnlock;
1269 
WallpaperOpenLauncherAnimationRunner(Handler handler, boolean fromUnlock)1270         public WallpaperOpenLauncherAnimationRunner(Handler handler, boolean fromUnlock) {
1271             mHandler = handler;
1272             mFromUnlock = fromUnlock;
1273         }
1274 
1275         @Override
onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets, RemoteAnimationTargetCompat[] nonAppTargets, LauncherAnimationRunner.AnimationResult result)1276         public void onCreateAnimation(int transit,
1277                 RemoteAnimationTargetCompat[] appTargets,
1278                 RemoteAnimationTargetCompat[] wallpaperTargets,
1279                 RemoteAnimationTargetCompat[] nonAppTargets,
1280                 LauncherAnimationRunner.AnimationResult result) {
1281             if (mLauncher.isDestroyed()) {
1282                 AnimatorSet anim = new AnimatorSet();
1283                 anim.play(getClosingWindowAnimators(appTargets, wallpaperTargets));
1284                 result.setAnimation(anim, mLauncher.getApplicationContext());
1285                 return;
1286             }
1287 
1288             if (!mLauncher.hasBeenResumed()) {
1289                 // If launcher is not resumed, wait until new async-frame after resume
1290                 mLauncher.addOnResumeCallback(() ->
1291                         postAsyncCallback(mHandler, () ->
1292                                 onCreateAnimation(transit, appTargets, wallpaperTargets,
1293                                         nonAppTargets, result)));
1294                 return;
1295             }
1296 
1297             if (mLauncher.hasSomeInvisibleFlag(PENDING_INVISIBLE_BY_WALLPAPER_ANIMATION)) {
1298                 mLauncher.addForceInvisibleFlag(INVISIBLE_BY_PENDING_FLAGS);
1299                 mLauncher.getStateManager().moveToRestState();
1300             }
1301 
1302             AnimatorSet anim = null;
1303             RemoteAnimationProvider provider = mRemoteAnimationProvider;
1304             if (provider != null) {
1305                 anim = provider.createWindowAnimation(appTargets, wallpaperTargets);
1306             }
1307 
1308             if (anim == null) {
1309                 anim = new AnimatorSet();
1310                 anim.play(mFromUnlock
1311                         ? getUnlockWindowAnimator(appTargets, wallpaperTargets)
1312                         : getClosingWindowAnimators(appTargets, wallpaperTargets));
1313 
1314                 // Normally, we run the launcher content animation when we are transitioning
1315                 // home, but if home is already visible, then we don't want to animate the
1316                 // contents of launcher unless we know that we are animating home as a result
1317                 // of the home button press with quickstep, which will result in launcher being
1318                 // started on touch down, prior to the animation home (and won't be in the
1319                 // targets list because it is already visible). In that case, we force
1320                 // invisibility on touch down, and only reset it after the animation to home
1321                 // is initialized.
1322                 if (launcherIsATargetWithMode(appTargets, MODE_OPENING)
1323                         || mLauncher.isForceInvisible()) {
1324                     addCujInstrumentation(
1325                             anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
1326                     // Only register the content animation for cancellation when state changes
1327                     mLauncher.getStateManager().setCurrentAnimation(anim);
1328 
1329                     if (mLauncher.isInState(LauncherState.ALL_APPS)) {
1330                         Pair<AnimatorSet, Runnable> contentAnimator =
1331                                 getLauncherContentAnimator(false, LAUNCHER_RESUME_START_DELAY);
1332                         anim.play(contentAnimator.first);
1333                         anim.addListener(new AnimatorListenerAdapter() {
1334                             @Override
1335                             public void onAnimationEnd(Animator animation) {
1336                                 contentAnimator.second.run();
1337                             }
1338                         });
1339                     } else {
1340                         anim.play(new WorkspaceRevealAnim(mLauncher, false).getAnimators());
1341                     }
1342                 }
1343             }
1344 
1345             mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
1346             result.setAnimation(anim, mLauncher);
1347         }
1348     }
1349 
1350     /**
1351      * Remote animation runner for animation to launch an app.
1352      */
1353     private class AppLaunchAnimationRunner implements RemoteAnimationFactory {
1354 
1355         private final View mV;
1356         private final RunnableList mOnEndCallback;
1357 
AppLaunchAnimationRunner(View v, RunnableList onEndCallback)1358         AppLaunchAnimationRunner(View v, RunnableList onEndCallback) {
1359             mV = v;
1360             mOnEndCallback = onEndCallback;
1361         }
1362 
1363         @Override
onCreateAnimation(int transit, RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] wallpaperTargets, RemoteAnimationTargetCompat[] nonAppTargets, LauncherAnimationRunner.AnimationResult result)1364         public void onCreateAnimation(int transit,
1365                 RemoteAnimationTargetCompat[] appTargets,
1366                 RemoteAnimationTargetCompat[] wallpaperTargets,
1367                 RemoteAnimationTargetCompat[] nonAppTargets,
1368                 LauncherAnimationRunner.AnimationResult result) {
1369             AnimatorSet anim = new AnimatorSet();
1370             boolean launcherClosing =
1371                     launcherIsATargetWithMode(appTargets, MODE_CLOSING);
1372 
1373             final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView;
1374             final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
1375             final boolean skipFirstFrame;
1376             if (launchingFromWidget) {
1377                 composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
1378                         wallpaperTargets, nonAppTargets);
1379                 addCujInstrumentation(
1380                         anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_WIDGET);
1381                 skipFirstFrame = true;
1382             } else if (launchingFromRecents) {
1383                 composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
1384                         launcherClosing);
1385                 addCujInstrumentation(
1386                         anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS);
1387                 skipFirstFrame = true;
1388             } else {
1389                 composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
1390                         launcherClosing);
1391                 addCujInstrumentation(anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_ICON);
1392                 skipFirstFrame = false;
1393             }
1394 
1395             if (launcherClosing) {
1396                 anim.addListener(mForceInvisibleListener);
1397             }
1398 
1399             result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
1400                     skipFirstFrame);
1401         }
1402 
1403         @Override
onAnimationCancelled()1404         public void onAnimationCancelled() {
1405             mOnEndCallback.executeAllAndDestroy();
1406         }
1407     }
1408 
1409     /**
1410      * Class that holds all the variables for the app open animation.
1411      */
1412     static class AnimOpenProperties {
1413 
1414         public final int cropCenterXStart;
1415         public final int cropCenterYStart;
1416         public final int cropWidthStart;
1417         public final int cropHeightStart;
1418 
1419         public final int cropCenterXEnd;
1420         public final int cropCenterYEnd;
1421         public final int cropWidthEnd;
1422         public final int cropHeightEnd;
1423 
1424         public final float dX;
1425         public final float dY;
1426 
1427         public final long xDuration;
1428         public final long yDuration;
1429         public final long alphaDuration;
1430 
1431         public final float initialAppIconScale;
1432         public final float finalAppIconScale;
1433 
1434         public final float iconAlphaStart;
1435 
AnimOpenProperties(Resources r, DeviceProfile dp, Rect windowTargetBounds, RectF launcherIconBounds, View view, int dragLayerLeft, int dragLayerTop, boolean hasSplashScreen, boolean hasDifferentAppIcon)1436         AnimOpenProperties(Resources r, DeviceProfile dp, Rect windowTargetBounds,
1437                 RectF launcherIconBounds, View view, int dragLayerLeft, int dragLayerTop,
1438                 boolean hasSplashScreen, boolean hasDifferentAppIcon) {
1439             // Scale the app icon to take up the entire screen. This simplifies the math when
1440             // animating the app window position / scale.
1441             float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
1442             float maxScaleX = smallestSize / launcherIconBounds.width();
1443             float maxScaleY = smallestSize / launcherIconBounds.height();
1444             float iconStartScale = 1f;
1445             if (view instanceof BubbleTextView && !(view.getParent() instanceof DeepShortcutView)) {
1446                 Drawable dr = ((BubbleTextView) view).getIcon();
1447                 if (dr instanceof FastBitmapDrawable) {
1448                     iconStartScale = ((FastBitmapDrawable) dr).getAnimatedScale();
1449                 }
1450             }
1451 
1452             initialAppIconScale = iconStartScale;
1453             finalAppIconScale = Math.max(maxScaleX, maxScaleY);
1454 
1455             // Animate the app icon to the center of the window bounds in screen coordinates.
1456             float centerX = windowTargetBounds.centerX() - dragLayerLeft;
1457             float centerY = windowTargetBounds.centerY() - dragLayerTop;
1458 
1459             dX = centerX - launcherIconBounds.centerX();
1460             dY = centerY - launcherIconBounds.centerY();
1461 
1462             boolean useUpwardAnimation = launcherIconBounds.top > centerY
1463                     || Math.abs(dY) < dp.cellHeightPx;
1464             xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION
1465                     : APP_LAUNCH_DOWN_DURATION;
1466             yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION
1467                     : APP_LAUNCH_DOWN_CURVED_DURATION;
1468             alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
1469                     : APP_LAUNCH_ALPHA_DOWN_DURATION;
1470             iconAlphaStart = hasSplashScreen && !hasDifferentAppIcon ? 0 : 1f;
1471 
1472             final int windowIconSize = ResourceUtils.getDimenByName("starting_surface_icon_size",
1473                     r, 108);
1474 
1475             cropCenterXStart = windowTargetBounds.centerX();
1476             cropCenterYStart = windowTargetBounds.centerY();
1477 
1478             cropWidthStart = windowIconSize;
1479             cropHeightStart = windowIconSize;
1480 
1481             cropWidthEnd = windowTargetBounds.width();
1482             cropHeightEnd = windowTargetBounds.height();
1483 
1484             cropCenterXEnd = windowTargetBounds.centerX();
1485             cropCenterYEnd = windowTargetBounds.centerY();
1486         }
1487     }
1488 
1489     private static class StartingWindowListener extends IStartingWindowListener.Stub {
1490         private QuickstepTransitionManager mTransitionManager;
1491 
1492         public void setTransitionManager(QuickstepTransitionManager transitionManager) {
1493             mTransitionManager = transitionManager;
1494         }
1495 
1496         @Override
1497         public void onTaskLaunching(int taskId, int supportedType, int color) {
1498             mTransitionManager.mTaskStartParams.put(taskId, Pair.create(supportedType, color));
1499         }
1500     }
1501 }
1502