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.server.wm; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; 20 import static android.view.WindowManager.TRANSIT_CHANGE; 21 import static android.view.WindowManager.TRANSIT_CLOSE; 22 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; 23 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; 24 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; 25 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; 26 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; 27 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; 28 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; 29 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; 30 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; 31 import static android.view.WindowManager.TRANSIT_NONE; 32 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; 33 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; 34 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH; 35 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; 36 import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE; 37 import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN; 38 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; 39 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; 40 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; 41 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM; 42 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; 43 import static android.view.WindowManager.TRANSIT_OLD_NONE; 44 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; 45 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; 46 import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; 47 import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE; 48 import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN; 49 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; 50 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND; 51 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK; 52 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT; 53 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; 54 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; 55 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE; 56 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; 57 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN; 58 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_OPEN; 59 import static android.view.WindowManager.TRANSIT_OPEN; 60 import static android.view.WindowManager.TRANSIT_RELAUNCH; 61 import static android.view.WindowManager.TRANSIT_TO_BACK; 62 import static android.view.WindowManager.TRANSIT_TO_FRONT; 63 64 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; 65 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; 66 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; 67 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; 68 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT; 69 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN; 70 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; 71 import static com.android.server.wm.AppTransition.isNormalTransit; 72 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldAttachNavBarToApp; 73 import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldStartNonAppWindowAnimationsForKeyguardExit; 74 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; 75 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; 76 import static com.android.server.wm.WallpaperAnimationAdapter.shouldStartWallpaperAnimation; 77 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; 78 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; 79 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 80 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 81 82 import android.annotation.IntDef; 83 import android.annotation.Nullable; 84 import android.graphics.Rect; 85 import android.os.Trace; 86 import android.util.ArrayMap; 87 import android.util.ArraySet; 88 import android.util.Pair; 89 import android.util.Slog; 90 import android.view.Display; 91 import android.view.RemoteAnimationAdapter; 92 import android.view.RemoteAnimationDefinition; 93 import android.view.WindowManager; 94 import android.view.WindowManager.LayoutParams; 95 import android.view.WindowManager.TransitionFlags; 96 import android.view.WindowManager.TransitionOldType; 97 import android.view.WindowManager.TransitionType; 98 import android.view.animation.Animation; 99 import android.window.ITaskFragmentOrganizer; 100 101 import com.android.internal.annotations.VisibleForTesting; 102 import com.android.internal.protolog.common.ProtoLog; 103 104 import java.lang.annotation.Retention; 105 import java.lang.annotation.RetentionPolicy; 106 import java.util.ArrayList; 107 import java.util.LinkedList; 108 import java.util.function.Consumer; 109 import java.util.function.Predicate; 110 111 /** 112 * Checks for app transition readiness, resolves animation attributes and performs visibility 113 * change for apps that animate as part of an app transition. 114 */ 115 public class AppTransitionController { 116 private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransitionController" : TAG_WM; 117 private final WindowManagerService mService; 118 private final DisplayContent mDisplayContent; 119 private final WallpaperController mWallpaperControllerLocked; 120 private RemoteAnimationDefinition mRemoteAnimationDefinition = null; 121 122 private static final int TYPE_NONE = 0; 123 private static final int TYPE_ACTIVITY = 1; 124 private static final int TYPE_TASK_FRAGMENT = 2; 125 private static final int TYPE_TASK = 3; 126 127 @IntDef(prefix = { "TYPE_" }, value = { 128 TYPE_NONE, 129 TYPE_ACTIVITY, 130 TYPE_TASK_FRAGMENT, 131 TYPE_TASK 132 }) 133 @Retention(RetentionPolicy.SOURCE) 134 @interface TransitContainerType {} 135 136 private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>(); 137 private final ArrayList<WindowContainer> mTempTransitionWindows = new ArrayList<>(); 138 AppTransitionController(WindowManagerService service, DisplayContent displayContent)139 AppTransitionController(WindowManagerService service, DisplayContent displayContent) { 140 mService = service; 141 mDisplayContent = displayContent; 142 mWallpaperControllerLocked = mDisplayContent.mWallpaperController; 143 } 144 registerRemoteAnimations(RemoteAnimationDefinition definition)145 void registerRemoteAnimations(RemoteAnimationDefinition definition) { 146 mRemoteAnimationDefinition = definition; 147 } 148 149 /** 150 * Returns the currently visible window that is associated with the wallpaper in case we are 151 * transitioning from an activity with a wallpaper to one without. 152 */ 153 @Nullable getOldWallpaper()154 private WindowState getOldWallpaper() { 155 final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); 156 final @TransitionType int firstTransit = 157 mDisplayContent.mAppTransition.getFirstAppTransition(); 158 159 final ArraySet<WindowContainer> openingWcs = getAnimationTargets( 160 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, true /* visible */); 161 final boolean showWallpaper = wallpaperTarget != null 162 && (wallpaperTarget.hasWallpaper() 163 // Update task open transition to wallpaper transition when wallpaper is visible. 164 // (i.e.launching app info activity from recent tasks) 165 || ((firstTransit == TRANSIT_OPEN || firstTransit == TRANSIT_TO_FRONT) 166 && (!openingWcs.isEmpty() && openingWcs.valueAt(0).asTask() != null) 167 && mWallpaperControllerLocked.isWallpaperVisible())); 168 // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set, 169 // don't consider upgrading to wallpaper transition. 170 return (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper) 171 ? null : wallpaperTarget; 172 } 173 174 /** 175 * Handle application transition for given display. 176 */ handleAppTransitionReady()177 void handleAppTransitionReady() { 178 mTempTransitionReasons.clear(); 179 if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons) 180 || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons) 181 || !transitionGoodToGoForTaskFragments()) { 182 return; 183 } 184 final boolean isRecentsInOpening = mDisplayContent.mOpeningApps.stream().anyMatch( 185 ConfigurationContainer::isActivityTypeRecents); 186 // In order to avoid visual clutter caused by a conflict between app transition 187 // animation and recents animation, app transition is delayed until recents finishes. 188 // One exceptional case. When 3P launcher is used and a user taps a task screenshot in 189 // task switcher (isRecentsInOpening=true), app transition must start even though 190 // recents is running. Otherwise app transition is blocked until timeout (b/232984498). 191 // When 1P launcher is used, this animation is controlled by the launcher outside of 192 // the app transition, so delaying app transition doesn't cause visible delay. After 193 // recents finishes, app transition is handled just to commit visibility on apps. 194 if (!isRecentsInOpening) { 195 final ArraySet<WindowContainer> participants = new ArraySet<>(); 196 participants.addAll(mDisplayContent.mOpeningApps); 197 participants.addAll(mDisplayContent.mChangingContainers); 198 boolean deferForRecents = false; 199 for (int i = 0; i < participants.size(); i++) { 200 WindowContainer wc = participants.valueAt(i); 201 final ActivityRecord activity = getAppFromContainer(wc); 202 if (activity == null) { 203 continue; 204 } 205 // Don't defer recents animation if one of activity isn't running for it, that one 206 // might be started from quickstep. 207 if (!activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) { 208 deferForRecents = false; 209 break; 210 } 211 deferForRecents = true; 212 } 213 if (deferForRecents) { 214 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 215 "Delaying app transition for recents animation to finish"); 216 return; 217 } 218 } 219 220 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); 221 222 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO"); 223 // TODO(b/205335975): Remove window which stuck in animatingExit status. Find actual cause. 224 mDisplayContent.forAllWindows(WindowState::cleanupAnimatingExitWindow, 225 true /* traverseTopToBottom */); 226 // TODO(new-app-transition): Remove code using appTransition.getAppTransition() 227 final AppTransition appTransition = mDisplayContent.mAppTransition; 228 229 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear(); 230 231 appTransition.removeAppTransitionTimeoutCallbacks(); 232 233 mDisplayContent.mWallpaperMayChange = false; 234 235 int appCount = mDisplayContent.mOpeningApps.size(); 236 for (int i = 0; i < appCount; ++i) { 237 // Clearing the mAnimatingExit flag before entering animation. It's set to true if app 238 // window is removed, or window relayout to invisible. This also affects window 239 // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the 240 // transition selection depends on wallpaper target visibility. 241 mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags(); 242 } 243 appCount = mDisplayContent.mChangingContainers.size(); 244 for (int i = 0; i < appCount; ++i) { 245 // Clearing for same reason as above. 246 final ActivityRecord activity = getAppFromContainer( 247 mDisplayContent.mChangingContainers.valueAtUnchecked(i)); 248 if (activity != null) { 249 activity.clearAnimatingFlags(); 250 } 251 } 252 253 // Adjust wallpaper before we pull the lower/upper target, since pending changes 254 // (like the clearAnimatingFlags() above) might affect wallpaper target result. 255 // Or, the opening app window should be a wallpaper target. 256 mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded( 257 mDisplayContent.mOpeningApps); 258 259 @TransitionOldType final int transit = getTransitCompatType( 260 mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps, 261 mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers, 262 mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(), 263 mDisplayContent.mSkipAppTransitionAnimation); 264 mDisplayContent.mSkipAppTransitionAnimation = false; 265 266 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 267 "handleAppTransitionReady: displayId=%d appTransition={%s}" 268 + " openingApps=[%s] closingApps=[%s] transit=%s", 269 mDisplayContent.mDisplayId, appTransition.toString(), mDisplayContent.mOpeningApps, 270 mDisplayContent.mClosingApps, AppTransition.appTransitionOldToString(transit)); 271 272 // Find the layout params of the top-most application window in the tokens, which is 273 // what will control the animation theme. If all closing windows are obscured, then there is 274 // no need to do an animation. This is the case, for example, when this transition is being 275 // done behind a dream window. 276 final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps, 277 mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers); 278 final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes, 279 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, 280 mDisplayContent.mChangingContainers); 281 final ActivityRecord topOpeningApp = 282 getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */); 283 final ActivityRecord topClosingApp = 284 getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */); 285 final ActivityRecord topChangingApp = 286 getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */); 287 final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity); 288 289 // Check if there is any override 290 if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) { 291 // Unfreeze the windows that were previously frozen for TaskFragment animation. 292 unfreezeEmbeddedChangingWindows(); 293 overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes); 294 } 295 296 final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mClosingApps) 297 || containsVoiceInteraction(mDisplayContent.mOpeningApps); 298 299 final int layoutRedo; 300 mService.mSurfaceAnimationRunner.deferStartingAnimations(); 301 try { 302 applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, 303 animLp, voiceInteraction); 304 handleClosingApps(); 305 handleOpeningApps(); 306 handleChangingApps(transit); 307 handleClosingChangingContainers(); 308 309 appTransition.setLastAppTransition(transit, topOpeningApp, 310 topClosingApp, topChangingApp); 311 312 final int flags = appTransition.getTransitFlags(); 313 layoutRedo = appTransition.goodToGo(transit, topOpeningApp); 314 handleNonAppWindowsInTransition(transit, flags); 315 appTransition.postAnimationCallback(); 316 } finally { 317 appTransition.clear(); 318 mService.mSurfaceAnimationRunner.continueStartingAnimations(); 319 } 320 321 mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent); 322 323 mDisplayContent.mOpeningApps.clear(); 324 mDisplayContent.mClosingApps.clear(); 325 mDisplayContent.mChangingContainers.clear(); 326 mDisplayContent.mUnknownAppVisibilityController.clear(); 327 mDisplayContent.mClosingChangingContainers.clear(); 328 329 // This has changed the visibility of windows, so perform 330 // a new layout to get them all up-to-date. 331 mDisplayContent.setLayoutNeeded(); 332 333 mDisplayContent.computeImeTarget(true /* updateImeTarget */); 334 335 mService.mAtmService.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting( 336 mTempTransitionReasons); 337 338 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 339 340 mDisplayContent.pendingLayoutChanges |= 341 layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; 342 } 343 344 /** 345 * Get old transit type based on the current transit requests. 346 * 347 * @param appTransition {@link AppTransition} for managing app transition state. 348 * @param openingApps {@link ActivityRecord}s which are becoming visible. 349 * @param closingApps {@link ActivityRecord}s which are becoming invisible. 350 * @param changingContainers {@link WindowContainer}s which are changed in configuration. 351 * @param wallpaperTarget If non-null, this is the currently visible window that is associated 352 * with the wallpaper. 353 * @param oldWallpaper The currently visible window that is associated with the wallpaper in 354 * case we are transitioning from an activity with a wallpaper to one 355 * without. Otherwise null. 356 */ getTransitCompatType(AppTransition appTransition, ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation)357 @TransitionOldType static int getTransitCompatType(AppTransition appTransition, 358 ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, 359 ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget, 360 @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) { 361 362 final ActivityRecord topOpeningApp = getTopApp(openingApps, false /* ignoreHidden */); 363 final ActivityRecord topClosingApp = getTopApp(closingApps, true /* ignoreHidden */); 364 365 // Determine if closing and opening app token sets are wallpaper targets, in which case 366 // special animations are needed. 367 final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps) 368 && wallpaperTarget != null; 369 final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps) 370 && wallpaperTarget != null; 371 372 // Keyguard transit has high priority. 373 switch (appTransition.getKeyguardTransition()) { 374 case TRANSIT_KEYGUARD_GOING_AWAY: 375 return openingAppHasWallpaper ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER 376 : TRANSIT_OLD_KEYGUARD_GOING_AWAY; 377 case TRANSIT_KEYGUARD_OCCLUDE: 378 // When there is a closing app, the keyguard has already been occluded by an 379 // activity, and another activity has started on top of that activity, so normal 380 // app transition animation should be used. 381 if (!closingApps.isEmpty()) { 382 return TRANSIT_OLD_ACTIVITY_OPEN; 383 } 384 if (!openingApps.isEmpty() && openingApps.valueAt(0).getActivityType() 385 == ACTIVITY_TYPE_DREAM) { 386 return TRANSIT_OLD_KEYGUARD_OCCLUDE_BY_DREAM; 387 } 388 return TRANSIT_OLD_KEYGUARD_OCCLUDE; 389 case TRANSIT_KEYGUARD_UNOCCLUDE: 390 return TRANSIT_OLD_KEYGUARD_UNOCCLUDE; 391 } 392 393 // Determine whether the top opening and closing activity is a dream activity. If so, this 394 // has higher priority than others except keyguard transit. 395 if (topOpeningApp != null && topOpeningApp.getActivityType() == ACTIVITY_TYPE_DREAM) { 396 return TRANSIT_OLD_DREAM_ACTIVITY_OPEN; 397 } else if (topClosingApp != null 398 && topClosingApp.getActivityType() == ACTIVITY_TYPE_DREAM) { 399 return TRANSIT_OLD_DREAM_ACTIVITY_CLOSE; 400 } 401 402 // This is not keyguard transition and one of the app has request to skip app transition. 403 if (skipAppTransitionAnimation) { 404 return WindowManager.TRANSIT_OLD_UNSET; 405 } 406 @TransitionFlags final int flags = appTransition.getTransitFlags(); 407 @TransitionType final int firstTransit = appTransition.getFirstAppTransition(); 408 409 // Special transitions 410 // TODO(new-app-transitions): Revisit if those can be rewritten by using flags. 411 if (appTransition.containsTransitRequest(TRANSIT_CHANGE) && !changingContainers.isEmpty()) { 412 @TransitContainerType int changingType = 413 getTransitContainerType(changingContainers.valueAt(0)); 414 switch (changingType) { 415 case TYPE_TASK: 416 return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; 417 case TYPE_TASK_FRAGMENT: 418 return TRANSIT_OLD_TASK_FRAGMENT_CHANGE; 419 default: 420 throw new IllegalStateException( 421 "TRANSIT_CHANGE with unrecognized changing type=" + changingType); 422 } 423 } 424 if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) { 425 return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; 426 } 427 if (firstTransit == TRANSIT_NONE) { 428 return TRANSIT_OLD_NONE; 429 } 430 431 /* 432 * There are cases where we open/close a new task/activity, but in reality only a 433 * translucent activity on top of existing activities is opening/closing. For that one, we 434 * have a different animation because non of the task/activity animations actually work well 435 * with translucent apps. 436 */ 437 if (isNormalTransit(firstTransit)) { 438 boolean allOpeningVisible = true; 439 boolean allTranslucentOpeningApps = !openingApps.isEmpty(); 440 for (int i = openingApps.size() - 1; i >= 0; i--) { 441 final ActivityRecord activity = openingApps.valueAt(i); 442 if (!activity.isVisible()) { 443 allOpeningVisible = false; 444 if (activity.fillsParent()) { 445 allTranslucentOpeningApps = false; 446 } 447 } 448 } 449 boolean allTranslucentClosingApps = !closingApps.isEmpty(); 450 for (int i = closingApps.size() - 1; i >= 0; i--) { 451 if (closingApps.valueAt(i).fillsParent()) { 452 allTranslucentClosingApps = false; 453 break; 454 } 455 } 456 457 if (allTranslucentClosingApps && allOpeningVisible) { 458 return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; 459 } 460 if (allTranslucentOpeningApps && closingApps.isEmpty()) { 461 return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; 462 } 463 } 464 465 if (closingAppHasWallpaper && openingAppHasWallpaper) { 466 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!"); 467 switch (firstTransit) { 468 case TRANSIT_OPEN: 469 case TRANSIT_TO_FRONT: 470 return TRANSIT_OLD_WALLPAPER_INTRA_OPEN; 471 case TRANSIT_CLOSE: 472 case TRANSIT_TO_BACK: 473 return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; 474 } 475 } else if (oldWallpaper != null && !openingApps.isEmpty() 476 && !openingApps.contains(oldWallpaper.mActivityRecord) 477 && closingApps.contains(oldWallpaper.mActivityRecord) 478 && topClosingApp == oldWallpaper.mActivityRecord) { 479 // We are transitioning from an activity with a wallpaper to one without. 480 return TRANSIT_OLD_WALLPAPER_CLOSE; 481 } else if (wallpaperTarget != null && wallpaperTarget.isVisible() 482 && openingApps.contains(wallpaperTarget.mActivityRecord) 483 && topOpeningApp == wallpaperTarget.mActivityRecord 484 /* && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE */) { 485 // We are transitioning from an activity without 486 // a wallpaper to now showing the wallpaper 487 return TRANSIT_OLD_WALLPAPER_OPEN; 488 } 489 490 final ArraySet<WindowContainer> openingWcs = getAnimationTargets( 491 openingApps, closingApps, true /* visible */); 492 final ArraySet<WindowContainer> closingWcs = getAnimationTargets( 493 openingApps, closingApps, false /* visible */); 494 final WindowContainer<?> openingContainer = !openingWcs.isEmpty() 495 ? openingWcs.valueAt(0) : null; 496 final WindowContainer<?> closingContainer = !closingWcs.isEmpty() 497 ? closingWcs.valueAt(0) : null; 498 @TransitContainerType int openingType = getTransitContainerType(openingContainer); 499 @TransitContainerType int closingType = getTransitContainerType(closingContainer); 500 if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && openingType == TYPE_TASK) { 501 return TRANSIT_OLD_TASK_TO_FRONT; 502 } 503 if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && closingType == TYPE_TASK) { 504 return TRANSIT_OLD_TASK_TO_BACK; 505 } 506 if (appTransition.containsTransitRequest(TRANSIT_OPEN)) { 507 if (openingType == TYPE_TASK) { 508 return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0 509 ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN; 510 } 511 if (openingType == TYPE_ACTIVITY) { 512 return TRANSIT_OLD_ACTIVITY_OPEN; 513 } 514 if (openingType == TYPE_TASK_FRAGMENT) { 515 return TRANSIT_OLD_TASK_FRAGMENT_OPEN; 516 } 517 } 518 if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) { 519 if (closingType == TYPE_TASK) { 520 return TRANSIT_OLD_TASK_CLOSE; 521 } 522 if (closingType == TYPE_TASK_FRAGMENT) { 523 return TRANSIT_OLD_TASK_FRAGMENT_CLOSE; 524 } 525 if (closingType == TYPE_ACTIVITY) { 526 for (int i = closingApps.size() - 1; i >= 0; i--) { 527 if (closingApps.valueAt(i).visibleIgnoringKeyguard) { 528 return TRANSIT_OLD_ACTIVITY_CLOSE; 529 } 530 } 531 // Skip close activity transition since no closing app can be visible 532 return WindowManager.TRANSIT_OLD_UNSET; 533 } 534 } 535 if (appTransition.containsTransitRequest(TRANSIT_RELAUNCH) 536 && !openingWcs.isEmpty() && !openingApps.isEmpty()) { 537 return TRANSIT_OLD_ACTIVITY_RELAUNCH; 538 } 539 return TRANSIT_OLD_NONE; 540 } 541 542 @TransitContainerType getTransitContainerType(@ullable WindowContainer<?> container)543 private static int getTransitContainerType(@Nullable WindowContainer<?> container) { 544 if (container == null) { 545 return TYPE_NONE; 546 } 547 if (container.asTask() != null) { 548 return TYPE_TASK; 549 } 550 if (container.asTaskFragment() != null) { 551 return TYPE_TASK_FRAGMENT; 552 } 553 if (container.asActivityRecord() != null) { 554 return TYPE_ACTIVITY; 555 } 556 return TYPE_NONE; 557 } 558 559 @Nullable getAnimLp(ActivityRecord activity)560 private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) { 561 final WindowState mainWindow = activity != null ? activity.findMainWindow() : null; 562 return mainWindow != null ? mainWindow.mAttrs : null; 563 } 564 getRemoteAnimationOverride(@ullable WindowContainer container, @TransitionOldType int transit, ArraySet<Integer> activityTypes)565 RemoteAnimationAdapter getRemoteAnimationOverride(@Nullable WindowContainer container, 566 @TransitionOldType int transit, ArraySet<Integer> activityTypes) { 567 if (container != null) { 568 final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition(); 569 if (definition != null) { 570 final RemoteAnimationAdapter adapter = definition.getAdapter(transit, 571 activityTypes); 572 if (adapter != null) { 573 return adapter; 574 } 575 } 576 } 577 return mRemoteAnimationDefinition != null 578 ? mRemoteAnimationDefinition.getAdapter(transit, activityTypes) 579 : null; 580 } 581 unfreezeEmbeddedChangingWindows()582 private void unfreezeEmbeddedChangingWindows() { 583 final ArraySet<WindowContainer> changingContainers = mDisplayContent.mChangingContainers; 584 for (int i = changingContainers.size() - 1; i >= 0; i--) { 585 final WindowContainer wc = changingContainers.valueAt(i); 586 if (wc.isEmbedded()) { 587 wc.mSurfaceFreezer.unfreeze(wc.getSyncTransaction()); 588 } 589 } 590 } 591 transitionMayContainNonAppWindows(@ransitionOldType int transit)592 private boolean transitionMayContainNonAppWindows(@TransitionOldType int transit) { 593 // We don't want to have the client to animate any non-app windows. 594 // Having {@code transit} of those types doesn't mean it will contain non-app windows, but 595 // non-app windows will only be included with those transition types. And we don't currently 596 // have any use case of those for TaskFragment transition. 597 return shouldStartNonAppWindowAnimationsForKeyguardExit(transit) 598 || shouldAttachNavBarToApp(mService, mDisplayContent, transit) 599 || shouldStartWallpaperAnimation(mDisplayContent); 600 } 601 602 /** 603 * Whether the transition contains any embedded {@link TaskFragment} that does not fill the 604 * parent {@link Task} before or after the transition. 605 */ transitionContainsTaskFragmentWithBoundsOverride()606 private boolean transitionContainsTaskFragmentWithBoundsOverride() { 607 for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) { 608 final WindowContainer wc = mDisplayContent.mChangingContainers.valueAt(i); 609 if (wc.isEmbedded()) { 610 // Contains embedded TaskFragment with bounds changed. 611 return true; 612 } 613 } 614 mTempTransitionWindows.clear(); 615 mTempTransitionWindows.addAll(mDisplayContent.mClosingApps); 616 mTempTransitionWindows.addAll(mDisplayContent.mOpeningApps); 617 boolean containsTaskFragmentWithBoundsOverride = false; 618 for (int i = mTempTransitionWindows.size() - 1; i >= 0; i--) { 619 final ActivityRecord r = mTempTransitionWindows.get(i).asActivityRecord(); 620 final TaskFragment tf = r.getTaskFragment(); 621 if (tf != null && tf.isEmbeddedWithBoundsOverride()) { 622 containsTaskFragmentWithBoundsOverride = true; 623 break; 624 } 625 } 626 mTempTransitionWindows.clear(); 627 return containsTaskFragmentWithBoundsOverride; 628 } 629 630 /** 631 * Finds the common parent {@link Task} that is parent of all embedded app windows in the 632 * current transition. 633 * @return {@code null} if app windows in the transition are not children of the same Task, or 634 * if none of the app windows is embedded. 635 */ 636 @Nullable findParentTaskForAllEmbeddedWindows()637 private Task findParentTaskForAllEmbeddedWindows() { 638 mTempTransitionWindows.clear(); 639 mTempTransitionWindows.addAll(mDisplayContent.mClosingApps); 640 mTempTransitionWindows.addAll(mDisplayContent.mOpeningApps); 641 mTempTransitionWindows.addAll(mDisplayContent.mChangingContainers); 642 643 // It should only animated by the organizer if all windows are below the same leaf Task. 644 Task leafTask = null; 645 for (int i = mTempTransitionWindows.size() - 1; i >= 0; i--) { 646 final ActivityRecord r = getAppFromContainer(mTempTransitionWindows.get(i)); 647 if (r == null) { 648 leafTask = null; 649 break; 650 } 651 // There are also cases where the Task contains non-embedded activity, such as launching 652 // split TaskFragments from a non-embedded activity. 653 // The hierarchy may looks like this: 654 // - Task 655 // - Activity 656 // - TaskFragment 657 // - Activity 658 // - TaskFragment 659 // - Activity 660 // We also want to have the organizer handle the transition for such case. 661 final Task task = r.getTask(); 662 // We don't support embedding in PiP, leave the animation to the PipTaskOrganizer. 663 if (task == null || task.inPinnedWindowingMode()) { 664 leafTask = null; 665 break; 666 } 667 // We don't want the organizer to handle transition of other non-embedded Task. 668 if (leafTask != null && leafTask != task) { 669 leafTask = null; 670 break; 671 } 672 final ActivityRecord rootActivity = task.getRootActivity(); 673 // We don't want the organizer to handle transition when the whole app is closing. 674 if (rootActivity == null) { 675 leafTask = null; 676 break; 677 } 678 // We don't want the organizer to handle transition of non-embedded activity of other 679 // app. 680 if (r.getUid() != task.effectiveUid && !r.isEmbedded()) { 681 leafTask = null; 682 break; 683 } 684 leafTask = task; 685 } 686 mTempTransitionWindows.clear(); 687 return leafTask; 688 } 689 690 /** 691 * Finds the common {@link android.window.TaskFragmentOrganizer} that organizes all embedded 692 * {@link TaskFragment} belong to the given {@link Task}. 693 * @return {@code null} if there is no such organizer, or if there are more than one. 694 */ 695 @Nullable findTaskFragmentOrganizer(@ullable Task task)696 private ITaskFragmentOrganizer findTaskFragmentOrganizer(@Nullable Task task) { 697 if (task == null) { 698 return null; 699 } 700 // We don't support remote animation for Task with multiple TaskFragmentOrganizers. 701 final ITaskFragmentOrganizer[] organizer = new ITaskFragmentOrganizer[1]; 702 final boolean hasMultipleOrganizers = task.forAllLeafTaskFragments(taskFragment -> { 703 final ITaskFragmentOrganizer tfOrganizer = taskFragment.getTaskFragmentOrganizer(); 704 if (tfOrganizer == null) { 705 return false; 706 } 707 if (organizer[0] != null && !organizer[0].asBinder().equals(tfOrganizer.asBinder())) { 708 return true; 709 } 710 organizer[0] = tfOrganizer; 711 return false; 712 }); 713 if (hasMultipleOrganizers) { 714 ProtoLog.e(WM_DEBUG_APP_TRANSITIONS, "We don't support remote animation for" 715 + " Task with multiple TaskFragmentOrganizers."); 716 return null; 717 } 718 return organizer[0]; 719 } 720 721 /** 722 * Overrides the pending transition with the remote animation defined by the 723 * {@link ITaskFragmentOrganizer} if all windows in the transition are children of 724 * {@link TaskFragment} that are organized by the same organizer. 725 * 726 * @return {@code true} if the transition is overridden. 727 */ overrideWithTaskFragmentRemoteAnimation(@ransitionOldType int transit, ArraySet<Integer> activityTypes)728 private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit, 729 ArraySet<Integer> activityTypes) { 730 if (transitionMayContainNonAppWindows(transit)) { 731 return false; 732 } 733 if (!transitionContainsTaskFragmentWithBoundsOverride()) { 734 // No need to play TaskFragment remote animation if all embedded TaskFragment in the 735 // transition fill the Task. 736 return false; 737 } 738 739 final Task task = findParentTaskForAllEmbeddedWindows(); 740 final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizer(task); 741 final RemoteAnimationDefinition definition = organizer != null 742 ? mDisplayContent.mAtmService.mTaskFragmentOrganizerController 743 .getRemoteAnimationDefinition(organizer) 744 : null; 745 final RemoteAnimationAdapter adapter = definition != null 746 ? definition.getAdapter(transit, activityTypes) 747 : null; 748 if (adapter == null) { 749 return false; 750 } 751 mDisplayContent.mAppTransition.overridePendingAppTransitionRemote( 752 adapter, false /* sync */, true /*isActivityEmbedding*/); 753 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 754 "Override with TaskFragment remote animation for transit=%s", 755 AppTransition.appTransitionOldToString(transit)); 756 757 final int organizerUid = mDisplayContent.mAtmService.mTaskFragmentOrganizerController 758 .getTaskFragmentOrganizerUid(organizer); 759 final boolean shouldDisableInputForRemoteAnimation = !task.isFullyTrustedEmbedding( 760 organizerUid); 761 final RemoteAnimationController remoteAnimationController = 762 mDisplayContent.mAppTransition.getRemoteAnimationController(); 763 if (shouldDisableInputForRemoteAnimation && remoteAnimationController != null) { 764 // We are going to use client-driven animation, Disable all input on activity windows 765 // during the animation (unless it is fully trusted) to ensure it is safe to allow 766 // client to animate the surfaces. 767 // This is needed for all activity windows in the animation Task. 768 remoteAnimationController.setOnRemoteAnimationReady(() -> { 769 final Consumer<ActivityRecord> updateActivities = 770 activity -> activity.setDropInputForAnimation(true); 771 task.forAllActivities(updateActivities); 772 }); 773 ProtoLog.d(WM_DEBUG_APP_TRANSITIONS, "Task=%d contains embedded TaskFragment." 774 + " Disabled all input during TaskFragment remote animation.", task.mTaskId); 775 } 776 return true; 777 } 778 779 /** 780 * Overrides the pending transition with the remote animation defined for the transition in the 781 * set of defined remote animations in the app window token. 782 */ overrideWithRemoteAnimationIfSet(@ullable ActivityRecord animLpActivity, @TransitionOldType int transit, ArraySet<Integer> activityTypes)783 private void overrideWithRemoteAnimationIfSet(@Nullable ActivityRecord animLpActivity, 784 @TransitionOldType int transit, ArraySet<Integer> activityTypes) { 785 RemoteAnimationAdapter adapter = null; 786 if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) { 787 // The crash transition has higher priority than any involved remote animations. 788 } else if (AppTransition.isKeyguardGoingAwayTransitOld(transit)) { 789 adapter = mRemoteAnimationDefinition != null 790 ? mRemoteAnimationDefinition.getAdapter(transit, activityTypes) 791 : null; 792 } else if (mDisplayContent.mAppTransition.getRemoteAnimationController() == null) { 793 adapter = getRemoteAnimationOverride(animLpActivity, transit, activityTypes); 794 } 795 if (adapter != null) { 796 mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter); 797 } 798 } 799 800 @Nullable findRootTaskFromContainer(WindowContainer wc)801 static Task findRootTaskFromContainer(WindowContainer wc) { 802 return wc.asTaskFragment() != null ? wc.asTaskFragment().getRootTask() 803 : wc.asActivityRecord().getRootTask(); 804 } 805 806 @Nullable getAppFromContainer(WindowContainer wc)807 static ActivityRecord getAppFromContainer(WindowContainer wc) { 808 return wc.asTaskFragment() != null ? wc.asTaskFragment().getTopNonFinishingActivity() 809 : wc.asActivityRecord(); 810 } 811 812 /** 813 * @return The window token that determines the animation theme. 814 */ 815 @Nullable findAnimLayoutParamsToken(@ransitionOldType int transit, ArraySet<Integer> activityTypes, ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, ArraySet<WindowContainer> changingApps)816 private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit, 817 ArraySet<Integer> activityTypes, ArraySet<ActivityRecord> openingApps, 818 ArraySet<ActivityRecord> closingApps, ArraySet<WindowContainer> changingApps) { 819 ActivityRecord result; 820 821 // Remote animations always win, but fullscreen tokens override non-fullscreen tokens. 822 result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, 823 w -> w.getRemoteAnimationDefinition() != null 824 && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes)); 825 if (result != null) { 826 return result; 827 } 828 result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, 829 w -> w.fillsParent() && w.findMainWindow() != null); 830 if (result != null) { 831 return result; 832 } 833 return lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, 834 w -> w.findMainWindow() != null); 835 } 836 837 /** 838 * @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set 839 * of apps in {@code array1}, {@code array2}, and {@code array3}. 840 */ collectActivityTypes(ArraySet<ActivityRecord> array1, ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3)841 private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1, 842 ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3) { 843 final ArraySet<Integer> result = new ArraySet<>(); 844 for (int i = array1.size() - 1; i >= 0; i--) { 845 result.add(array1.valueAt(i).getActivityType()); 846 } 847 for (int i = array2.size() - 1; i >= 0; i--) { 848 result.add(array2.valueAt(i).getActivityType()); 849 } 850 for (int i = array3.size() - 1; i >= 0; i--) { 851 result.add(array3.valueAt(i).getActivityType()); 852 } 853 return result; 854 } 855 lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1, ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3, Predicate<ActivityRecord> filter)856 private static ActivityRecord lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1, 857 ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3, 858 Predicate<ActivityRecord> filter) { 859 final int array2base = array1.size(); 860 final int array3base = array2.size() + array2base; 861 final int count = array3base + array3.size(); 862 int bestPrefixOrderIndex = Integer.MIN_VALUE; 863 ActivityRecord bestToken = null; 864 for (int i = 0; i < count; i++) { 865 final WindowContainer wtoken = i < array2base 866 ? array1.valueAt(i) 867 : (i < array3base 868 ? array2.valueAt(i - array2base) 869 : array3.valueAt(i - array3base)); 870 final int prefixOrderIndex = wtoken.getPrefixOrderIndex(); 871 final ActivityRecord r = getAppFromContainer(wtoken); 872 if (r != null && filter.test(r) && prefixOrderIndex > bestPrefixOrderIndex) { 873 bestPrefixOrderIndex = prefixOrderIndex; 874 bestToken = r; 875 } 876 } 877 return bestToken; 878 } 879 880 private boolean containsVoiceInteraction(ArraySet<ActivityRecord> apps) { 881 for (int i = apps.size() - 1; i >= 0; i--) { 882 if (apps.valueAt(i).mVoiceInteraction) { 883 return true; 884 } 885 } 886 return false; 887 } 888 889 /** 890 * Apply animation to the set of window containers. 891 * 892 * @param wcs The list of {@link WindowContainer}s to which an app transition animation applies. 893 * @param apps The list of {@link ActivityRecord}s being transitioning. 894 * @param transit The current transition type. 895 * @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes 896 * invisible. 897 * @param animLp Layout parameters in which an app transition animation runs. 898 * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice 899 * interaction session driving task. 900 */ applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps, @TransitionOldType int transit, boolean visible, LayoutParams animLp, boolean voiceInteraction)901 private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps, 902 @TransitionOldType int transit, boolean visible, LayoutParams animLp, 903 boolean voiceInteraction) { 904 final int wcsCount = wcs.size(); 905 for (int i = 0; i < wcsCount; i++) { 906 final WindowContainer wc = wcs.valueAt(i); 907 // If app transition animation target is promoted to higher level, SurfaceAnimator 908 // triggers WC#onAnimationFinished only on the promoted target. So we need to take care 909 // of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the 910 // app transition. 911 final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>(); 912 for (int j = 0; j < apps.size(); ++j) { 913 final ActivityRecord app = apps.valueAt(j); 914 if (app.isDescendantOf(wc)) { 915 transitioningDescendants.add(app); 916 } 917 } 918 wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants); 919 } 920 } 921 922 /** 923 * Returns {@code true} if a given {@link WindowContainer} is an embedded Task in 924 * {@link com.android.wm.shell.TaskView}. 925 * 926 * Note that this is a short term workaround to support Android Auto until it migrate to 927 * ShellTransition. This should only be used by {@link #getAnimationTargets}. 928 * 929 * TODO(b/213312721): Remove this predicate and its callers once ShellTransition is enabled. 930 */ isTaskViewTask(WindowContainer wc)931 static boolean isTaskViewTask(WindowContainer wc) { 932 // We use Task#mRemoveWithTaskOrganizer to identify an embedded Task, but this is a hack and 933 // it is not guaranteed to work this logic in the future version. 934 return wc instanceof Task && ((Task) wc).mRemoveWithTaskOrganizer; 935 } 936 937 /** 938 * Find WindowContainers to be animated from a set of opening and closing apps. We will promote 939 * animation targets to higher level in the window hierarchy if possible. 940 * 941 * @param visible {@code true} to get animation targets for opening apps, {@code false} to get 942 * animation targets for closing apps. 943 * @return {@link WindowContainer}s to be animated. 944 */ 945 @VisibleForTesting getAnimationTargets( ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, boolean visible)946 static ArraySet<WindowContainer> getAnimationTargets( 947 ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, 948 boolean visible) { 949 950 // The candidates of animation targets, which might be able to promote to higher level. 951 final LinkedList<WindowContainer> candidates = new LinkedList<>(); 952 final ArraySet<ActivityRecord> apps = visible ? openingApps : closingApps; 953 for (int i = 0; i < apps.size(); ++i) { 954 final ActivityRecord app = apps.valueAt(i); 955 if (app.shouldApplyAnimation(visible)) { 956 candidates.add(app); 957 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 958 "Changing app %s visible=%b performLayout=%b", 959 app, app.isVisible(), false); 960 } 961 } 962 963 final ArraySet<ActivityRecord> otherApps = visible ? closingApps : openingApps; 964 // Ancestors of closing apps while finding animation targets for opening apps, or ancestors 965 // of opening apps while finding animation targets for closing apps. 966 final ArraySet<WindowContainer> otherAncestors = new ArraySet<>(); 967 for (int i = 0; i < otherApps.size(); ++i) { 968 for (WindowContainer wc = otherApps.valueAt(i); wc != null; wc = wc.getParent()) { 969 otherAncestors.add(wc); 970 } 971 } 972 973 // The final animation targets which cannot promote to higher level anymore. 974 final ArraySet<WindowContainer> targets = new ArraySet<>(); 975 final ArrayList<WindowContainer> siblings = new ArrayList<>(); 976 while (!candidates.isEmpty()) { 977 final WindowContainer current = candidates.removeFirst(); 978 final WindowContainer parent = current.getParent(); 979 siblings.clear(); 980 siblings.add(current); 981 boolean canPromote = true; 982 983 if (isTaskViewTask(current)) { 984 // Don't animate an embedded Task in app transition. This is a short term workaround 985 // to prevent conflict of surface hierarchy changes between legacy app transition 986 // and TaskView (b/205189147). 987 // TODO(b/213312721): Remove this once ShellTransition is enabled. 988 continue; 989 } else if (parent == null || !parent.canCreateRemoteAnimationTarget() 990 || !parent.canBeAnimationTarget() 991 // We cannot promote the animation on Task's parent when the task is in 992 // clearing task in case the animating get stuck when performing the opening 993 // task that behind it. 994 || (current.asTask() != null && current.asTask().mInRemoveTask) 995 // We cannot promote the animation to changing window. This may happen when an 996 // activity is open in a TaskFragment that is resizing, while the existing 997 // activity in the TaskFragment is reparented to another TaskFragment. 998 || parent.isChangingAppTransition()) { 999 canPromote = false; 1000 } else { 1001 // In case a descendant of the parent belongs to the other group, we cannot promote 1002 // the animation target from "current" to the parent. 1003 // 1004 // Example: Imagine we're checking if we can animate a Task instead of a set of 1005 // ActivityRecords. In case an activity starts a new activity within a same Task, 1006 // an ActivityRecord of an existing activity belongs to the opening apps, at the 1007 // same time, the other ActivityRecord of a new activity belongs to the closing 1008 // apps. In this case, we cannot promote the animation target to Task level, but 1009 // need to animate each individual activity. 1010 // 1011 // [Task] +- [ActivityRecord1] (in opening apps) 1012 // +- [ActivityRecord2] (in closing apps) 1013 if (otherAncestors.contains(parent)) { 1014 canPromote = false; 1015 } 1016 1017 // If the current window container is task and it have adjacent task, it means 1018 // both tasks will open or close app toghther but we want get their opening or 1019 // closing animation target independently so do not promote. 1020 if (current.asTask() != null 1021 && current.asTask().getAdjacentTaskFragment() != null 1022 && current.asTask().getAdjacentTaskFragment().asTask() != null) { 1023 canPromote = false; 1024 } 1025 1026 // Find all siblings of the current WindowContainer in "candidates", move them into 1027 // a separate list "siblings", and checks if an animation target can be promoted 1028 // to its parent. 1029 // 1030 // We can promote an animation target to its parent if and only if all visible 1031 // siblings will be animating. 1032 // 1033 // Example: Imagine that a Task contains two visible activity record, but only one 1034 // of them is included in the opening apps and the other belongs to neither opening 1035 // or closing apps. This happens when an activity launches another translucent 1036 // activity in the same Task. In this case, we cannot animate Task, but have to 1037 // animate each activity, otherwise an activity behind the translucent activity also 1038 // animates. 1039 // 1040 // [Task] +- [ActivityRecord1] (visible, in opening apps) 1041 // +- [ActivityRecord2] (visible, not in opening apps) 1042 for (int j = 0; j < parent.getChildCount(); ++j) { 1043 final WindowContainer sibling = parent.getChildAt(j); 1044 if (candidates.remove(sibling)) { 1045 if (!isTaskViewTask(sibling)) { 1046 // Don't animate an embedded Task in app transition. This is a short 1047 // term workaround to prevent conflict of surface hierarchy changes 1048 // between legacy app transition and TaskView (b/205189147). 1049 // TODO(b/213312721): Remove this once ShellTransition is enabled. 1050 siblings.add(sibling); 1051 } 1052 } else if (sibling != current && sibling.isVisible()) { 1053 canPromote = false; 1054 } 1055 } 1056 } 1057 1058 if (canPromote) { 1059 candidates.add(parent); 1060 } else { 1061 targets.addAll(siblings); 1062 } 1063 } 1064 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "getAnimationTarget in=%s, out=%s", 1065 apps, targets); 1066 return targets; 1067 } 1068 1069 /** 1070 * Apply an app transition animation based on a set of {@link ActivityRecord} 1071 * 1072 * @param openingApps The list of opening apps to which an app transition animation applies. 1073 * @param closingApps The list of closing apps to which an app transition animation applies. 1074 * @param transit The current transition type. 1075 * @param animLp Layout parameters in which an app transition animation runs. 1076 * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice 1077 * interaction session driving task. 1078 */ applyAnimations(ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit, LayoutParams animLp, boolean voiceInteraction)1079 private void applyAnimations(ArraySet<ActivityRecord> openingApps, 1080 ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit, 1081 LayoutParams animLp, boolean voiceInteraction) { 1082 final RecentsAnimationController rac = mService.getRecentsAnimationController(); 1083 if (transit == WindowManager.TRANSIT_OLD_UNSET 1084 || (openingApps.isEmpty() && closingApps.isEmpty())) { 1085 if (rac != null) { 1086 rac.sendTasksAppeared(); 1087 } 1088 return; 1089 } 1090 1091 if (AppTransition.isActivityTransitOld(transit)) { 1092 final ArrayList<Pair<ActivityRecord, Rect>> closingLetterboxes = new ArrayList(); 1093 for (int i = 0; i < closingApps.size(); ++i) { 1094 ActivityRecord closingApp = closingApps.valueAt(i); 1095 if (closingApp.areBoundsLetterboxed()) { 1096 final Rect insets = closingApp.getLetterboxInsets(); 1097 closingLetterboxes.add(new Pair(closingApp, insets)); 1098 } 1099 } 1100 1101 for (int i = 0; i < openingApps.size(); ++i) { 1102 ActivityRecord openingApp = openingApps.valueAt(i); 1103 if (openingApp.areBoundsLetterboxed()) { 1104 final Rect openingInsets = openingApp.getLetterboxInsets(); 1105 for (Pair<ActivityRecord, Rect> closingLetterbox : closingLetterboxes) { 1106 final Rect closingInsets = closingLetterbox.second; 1107 if (openingInsets.equals(closingInsets)) { 1108 ActivityRecord closingApp = closingLetterbox.first; 1109 openingApp.setNeedsLetterboxedAnimation(true); 1110 closingApp.setNeedsLetterboxedAnimation(true); 1111 } 1112 } 1113 } 1114 } 1115 } 1116 1117 final ArraySet<WindowContainer> openingWcs = getAnimationTargets( 1118 openingApps, closingApps, true /* visible */); 1119 final ArraySet<WindowContainer> closingWcs = getAnimationTargets( 1120 openingApps, closingApps, false /* visible */); 1121 applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp, 1122 voiceInteraction); 1123 applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp, 1124 voiceInteraction); 1125 if (rac != null) { 1126 rac.sendTasksAppeared(); 1127 } 1128 1129 for (int i = 0; i < openingApps.size(); ++i) { 1130 openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false; 1131 } 1132 for (int i = 0; i < closingApps.size(); ++i) { 1133 closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false; 1134 } 1135 1136 final AccessibilityController accessibilityController = 1137 mDisplayContent.mWmService.mAccessibilityController; 1138 if (accessibilityController.hasCallbacks()) { 1139 accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit); 1140 } 1141 } 1142 handleOpeningApps()1143 private void handleOpeningApps() { 1144 final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps; 1145 final int appsCount = openingApps.size(); 1146 1147 for (int i = 0; i < appsCount; i++) { 1148 final ActivityRecord app = openingApps.valueAt(i); 1149 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app); 1150 1151 app.commitVisibility(true /* visible */, false /* performLayout */); 1152 1153 // In case a trampoline activity is used, it can happen that a new ActivityRecord is 1154 // added and a new app transition starts before the previous app transition animation 1155 // ends. So we cannot simply use app.isAnimating(PARENTS) to determine if the app must 1156 // to be added to the list of tokens to be notified of app transition complete. 1157 final WindowContainer wc = app.getAnimatingContainer(PARENTS, 1158 ANIMATION_TYPE_APP_TRANSITION); 1159 if (wc == null || !wc.getAnimationSources().contains(app)) { 1160 // This token isn't going to be animating. Add it to the list of tokens to 1161 // be notified of app transition complete since the notification will not be 1162 // sent be the app window animator. 1163 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(app.token); 1164 } 1165 app.updateReportedVisibilityLocked(); 1166 app.waitingToShow = false; 1167 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, 1168 ">>> OPEN TRANSACTION handleAppTransitionReady()"); 1169 mService.openSurfaceTransaction(); 1170 try { 1171 app.showAllWindowsLocked(); 1172 } finally { 1173 mService.closeSurfaceTransaction("handleAppTransitionReady"); 1174 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, 1175 "<<< CLOSE TRANSACTION handleAppTransitionReady()"); 1176 } 1177 1178 if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) { 1179 app.attachThumbnailAnimation(); 1180 } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) { 1181 app.attachCrossProfileAppsThumbnailAnimation(); 1182 } 1183 } 1184 } 1185 handleClosingApps()1186 private void handleClosingApps() { 1187 final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps; 1188 final int appsCount = closingApps.size(); 1189 1190 for (int i = 0; i < appsCount; i++) { 1191 final ActivityRecord app = closingApps.valueAt(i); 1192 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", app); 1193 1194 app.commitVisibility(false /* visible */, false /* performLayout */); 1195 app.updateReportedVisibilityLocked(); 1196 // Force the allDrawn flag, because we want to start 1197 // this guy's animations regardless of whether it's 1198 // gotten drawn. 1199 app.allDrawn = true; 1200 // Ensure that apps that are mid-starting are also scheduled to have their 1201 // starting windows removed after the animation is complete 1202 if (app.mStartingWindow != null && !app.mStartingWindow.mAnimatingExit) { 1203 app.removeStartingWindow(); 1204 } 1205 1206 if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) { 1207 app.attachThumbnailAnimation(); 1208 } 1209 } 1210 } 1211 handleClosingChangingContainers()1212 private void handleClosingChangingContainers() { 1213 final ArrayMap<WindowContainer, Rect> containers = 1214 mDisplayContent.mClosingChangingContainers; 1215 while (!containers.isEmpty()) { 1216 final WindowContainer container = containers.keyAt(0); 1217 containers.remove(container); 1218 1219 // For closing changing windows that are part of the transition, they should have been 1220 // removed from mClosingChangingContainers in WindowContainer#getAnimationAdapter() 1221 // If the closing changing TaskFragment is not part of the transition, update its 1222 // surface after removing it from mClosingChangingContainers. 1223 final TaskFragment taskFragment = container.asTaskFragment(); 1224 if (taskFragment != null) { 1225 taskFragment.updateOrganizedTaskFragmentSurface(); 1226 } 1227 } 1228 } 1229 handleChangingApps(@ransitionOldType int transit)1230 private void handleChangingApps(@TransitionOldType int transit) { 1231 final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers; 1232 final int appsCount = apps.size(); 1233 for (int i = 0; i < appsCount; i++) { 1234 WindowContainer wc = apps.valueAt(i); 1235 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc); 1236 wc.applyAnimation(null, transit, true, false, null /* sources */); 1237 } 1238 } 1239 handleNonAppWindowsInTransition(@ransitionOldType int transit, int flags)1240 private void handleNonAppWindowsInTransition(@TransitionOldType int transit, int flags) { 1241 if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY 1242 && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) { 1243 if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0 1244 && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0 1245 && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) { 1246 Animation anim = mService.mPolicy.createKeyguardWallpaperExit( 1247 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); 1248 if (anim != null) { 1249 anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); 1250 mDisplayContent.mWallpaperController.startWallpaperAnimation(anim); 1251 } 1252 } 1253 } 1254 if ((transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY 1255 || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) 1256 && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) { 1257 mDisplayContent.startKeyguardExitOnNonAppWindows( 1258 transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER, 1259 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0, 1260 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0); 1261 } 1262 } 1263 transitionGoodToGo(ArraySet<? extends WindowContainer> apps, ArrayMap<WindowContainer, Integer> outReasons)1264 private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps, 1265 ArrayMap<WindowContainer, Integer> outReasons) { 1266 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 1267 "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(), 1268 mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout()); 1269 if (mDisplayContent.mAppTransition.isTimeout()) { 1270 return true; 1271 } 1272 final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent( 1273 Display.DEFAULT_DISPLAY).getRotationAnimation(); 1274 1275 // Imagine the case where we are changing orientation due to an app transition, but a 1276 // previous orientation change is still in progress. We won't process the orientation 1277 // change for our transition because we need to wait for the rotation animation to 1278 // finish. 1279 // If we start the app transition at this point, we will interrupt it halfway with a 1280 // new rotation animation after the old one finally finishes. It's better to defer the 1281 // app transition. 1282 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() 1283 && mDisplayContent.getDisplayRotation().needsUpdate()) { 1284 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 1285 "Delaying app transition for screen rotation animation to finish"); 1286 return false; 1287 } 1288 for (int i = 0; i < apps.size(); i++) { 1289 WindowContainer wc = apps.valueAt(i); 1290 final ActivityRecord activity = getAppFromContainer(wc); 1291 if (activity == null) { 1292 continue; 1293 } 1294 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 1295 "Check opening app=%s: allDrawn=%b startingDisplayed=%b " 1296 + "startingMoved=%b isRelaunching()=%b startingWindow=%s", 1297 activity, activity.allDrawn, activity.isStartingWindowDisplayed(), 1298 activity.startingMoved, activity.isRelaunching(), 1299 activity.mStartingWindow); 1300 final boolean allDrawn = activity.allDrawn && !activity.isRelaunching(); 1301 if (!allDrawn && !activity.isStartingWindowDisplayed() && !activity.startingMoved) { 1302 return false; 1303 } 1304 if (allDrawn) { 1305 outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN); 1306 } else { 1307 outReasons.put(activity, 1308 activity.mStartingData instanceof SplashScreenStartingData 1309 ? APP_TRANSITION_SPLASH_SCREEN 1310 : APP_TRANSITION_SNAPSHOT); 1311 } 1312 } 1313 1314 // We also need to wait for the specs to be fetched, if needed. 1315 if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) { 1316 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "isFetchingAppTransitionSpecs=true"); 1317 return false; 1318 } 1319 1320 if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) { 1321 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "unknownApps is not empty: %s", 1322 mDisplayContent.mUnknownAppVisibilityController.getDebugMessage()); 1323 return false; 1324 } 1325 1326 // If the wallpaper is visible, we need to check it's ready too. 1327 return !mWallpaperControllerLocked.isWallpaperVisible() 1328 || mWallpaperControllerLocked.wallpaperTransitionReady(); 1329 } 1330 transitionGoodToGoForTaskFragments()1331 private boolean transitionGoodToGoForTaskFragments() { 1332 if (mDisplayContent.mAppTransition.isTimeout()) { 1333 return true; 1334 } 1335 1336 // Check all Tasks in this transition. This is needed because new TaskFragment created for 1337 // launching activity may not be in the tracking lists, but we still want to wait for the 1338 // activity launch to start the transition. 1339 final ArraySet<Task> rootTasks = new ArraySet<>(); 1340 for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) { 1341 rootTasks.add(mDisplayContent.mOpeningApps.valueAt(i).getRootTask()); 1342 } 1343 for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) { 1344 rootTasks.add(mDisplayContent.mClosingApps.valueAt(i).getRootTask()); 1345 } 1346 for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) { 1347 rootTasks.add( 1348 findRootTaskFromContainer(mDisplayContent.mChangingContainers.valueAt(i))); 1349 } 1350 1351 // Organized TaskFragment can be empty for two situations: 1352 // 1. New created and is waiting for Activity launch. In this case, we want to wait for 1353 // the Activity launch to trigger the transition. 1354 // 2. Last Activity is just removed. In this case, we want to wait for organizer to 1355 // remove the TaskFragment because it may also want to change other TaskFragments in 1356 // the same transition. 1357 for (int i = rootTasks.size() - 1; i >= 0; i--) { 1358 final Task rootTask = rootTasks.valueAt(i); 1359 if (rootTask == null) { 1360 // It is possible that one activity may have been removed from the hierarchy. No 1361 // need to check for this case. 1362 continue; 1363 } 1364 final boolean notReady = rootTask.forAllLeafTaskFragments(taskFragment -> { 1365 if (!taskFragment.isReadyToTransit()) { 1366 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Organized TaskFragment is not ready= %s", 1367 taskFragment); 1368 return true; 1369 } 1370 return false; 1371 }); 1372 if (notReady) { 1373 return false; 1374 } 1375 } 1376 return true; 1377 } 1378 1379 /** 1380 * Identifies whether the current transition occurs within a single task or not. This is used 1381 * to determine whether animations should be clipped to the task bounds instead of root task 1382 * bounds. 1383 */ 1384 @VisibleForTesting isTransitWithinTask(@ransitionOldType int transit, Task task)1385 boolean isTransitWithinTask(@TransitionOldType int transit, Task task) { 1386 if (task == null 1387 || !mDisplayContent.mChangingContainers.isEmpty()) { 1388 // if there is no task, then we can't constrain to the task. 1389 // if anything is changing, it can animate outside its task. 1390 return false; 1391 } 1392 if (!(transit == TRANSIT_OLD_ACTIVITY_OPEN 1393 || transit == TRANSIT_OLD_ACTIVITY_CLOSE 1394 || transit == TRANSIT_OLD_ACTIVITY_RELAUNCH)) { 1395 // only activity-level transitions will be within-task. 1396 return false; 1397 } 1398 // check that all components are in the task. 1399 for (ActivityRecord activity : mDisplayContent.mOpeningApps) { 1400 Task activityTask = activity.getTask(); 1401 if (activityTask != task) { 1402 return false; 1403 } 1404 } 1405 for (ActivityRecord activity : mDisplayContent.mClosingApps) { 1406 if (activity.getTask() != task) { 1407 return false; 1408 } 1409 } 1410 return true; 1411 } 1412 canBeWallpaperTarget(ArraySet<ActivityRecord> apps)1413 private static boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) { 1414 for (int i = apps.size() - 1; i >= 0; i--) { 1415 if (apps.valueAt(i).windowsCanBeWallpaperTarget()) { 1416 return true; 1417 } 1418 } 1419 return false; 1420 } 1421 1422 /** 1423 * Finds the top app in a list of apps, using its {@link ActivityRecord#getPrefixOrderIndex} to 1424 * compare z-order. 1425 * 1426 * @param apps The list of apps to search. 1427 * @param ignoreInvisible If set to true, ignores apps that are not 1428 * {@link ActivityRecord#isVisible}. 1429 * @return The top {@link ActivityRecord}. 1430 */ getTopApp(ArraySet<? extends WindowContainer> apps, boolean ignoreInvisible)1431 private static ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps, 1432 boolean ignoreInvisible) { 1433 int topPrefixOrderIndex = Integer.MIN_VALUE; 1434 ActivityRecord topApp = null; 1435 for (int i = apps.size() - 1; i >= 0; i--) { 1436 final ActivityRecord app = getAppFromContainer(apps.valueAt(i)); 1437 if (app == null || ignoreInvisible && !app.isVisible()) { 1438 continue; 1439 } 1440 final int prefixOrderIndex = app.getPrefixOrderIndex(); 1441 if (prefixOrderIndex > topPrefixOrderIndex) { 1442 topPrefixOrderIndex = prefixOrderIndex; 1443 topApp = app; 1444 } 1445 } 1446 return topApp; 1447 } 1448 } 1449