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