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