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