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