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