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.view.WindowManager.TRANSIT_CHANGE; 20 import static android.view.WindowManager.TRANSIT_CLOSE; 21 import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; 22 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; 23 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; 24 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; 25 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; 26 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; 27 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; 28 import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; 29 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; 30 import static android.view.WindowManager.TRANSIT_NONE; 31 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; 32 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; 33 import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH; 34 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; 35 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; 36 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; 37 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; 38 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; 39 import static android.view.WindowManager.TRANSIT_OLD_NONE; 40 import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; 41 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; 42 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN; 43 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND; 44 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_BACK; 45 import static android.view.WindowManager.TRANSIT_OLD_TASK_TO_FRONT; 46 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; 47 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; 48 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_CLOSE; 49 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; 50 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN; 51 import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_OPEN; 52 import static android.view.WindowManager.TRANSIT_OPEN; 53 import static android.view.WindowManager.TRANSIT_RELAUNCH; 54 import static android.view.WindowManager.TRANSIT_TO_BACK; 55 import static android.view.WindowManager.TRANSIT_TO_FRONT; 56 57 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; 58 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM; 59 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; 60 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; 61 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT; 62 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN; 63 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; 64 import static com.android.server.wm.AppTransition.isNormalTransit; 65 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; 66 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; 67 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; 68 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 69 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 70 71 import android.annotation.Nullable; 72 import android.os.Trace; 73 import android.util.ArrayMap; 74 import android.util.ArraySet; 75 import android.util.Slog; 76 import android.view.Display; 77 import android.view.RemoteAnimationAdapter; 78 import android.view.RemoteAnimationDefinition; 79 import android.view.WindowManager; 80 import android.view.WindowManager.LayoutParams; 81 import android.view.WindowManager.TransitionFlags; 82 import android.view.WindowManager.TransitionOldType; 83 import android.view.WindowManager.TransitionType; 84 import android.view.animation.Animation; 85 86 import com.android.internal.annotations.VisibleForTesting; 87 import com.android.internal.protolog.common.ProtoLog; 88 89 import java.util.ArrayList; 90 import java.util.LinkedList; 91 import java.util.function.Predicate; 92 93 /** 94 * Checks for app transition readiness, resolves animation attributes and performs visibility 95 * change for apps that animate as part of an app transition. 96 */ 97 public class AppTransitionController { 98 private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransitionController" : TAG_WM; 99 private final WindowManagerService mService; 100 private final DisplayContent mDisplayContent; 101 private final WallpaperController mWallpaperControllerLocked; 102 private RemoteAnimationDefinition mRemoteAnimationDefinition = null; 103 private static final int KEYGUARD_GOING_AWAY_ANIMATION_DURATION = 400; 104 105 private final ArrayMap<WindowContainer, Integer> mTempTransitionReasons = new ArrayMap<>(); 106 AppTransitionController(WindowManagerService service, DisplayContent displayContent)107 AppTransitionController(WindowManagerService service, DisplayContent displayContent) { 108 mService = service; 109 mDisplayContent = displayContent; 110 mWallpaperControllerLocked = mDisplayContent.mWallpaperController; 111 } 112 registerRemoteAnimations(RemoteAnimationDefinition definition)113 void registerRemoteAnimations(RemoteAnimationDefinition definition) { 114 mRemoteAnimationDefinition = definition; 115 } 116 117 /** 118 * Returns the currently visible window that is associated with the wallpaper in case we are 119 * transitioning from an activity with a wallpaper to one without. 120 */ getOldWallpaper()121 private @Nullable WindowState getOldWallpaper() { 122 final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget(); 123 final @TransitionType int firstTransit = 124 mDisplayContent.mAppTransition.getFirstAppTransition(); 125 126 final ArraySet<WindowContainer> openingWcs = getAnimationTargets( 127 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, true /* visible */); 128 final boolean showWallpaper = wallpaperTarget != null 129 && (wallpaperTarget.hasWallpaper() 130 // Update task open transition to wallpaper transition when wallpaper is visible. 131 // (i.e.launching app info activity from recent tasks) 132 || ((firstTransit == TRANSIT_OPEN || firstTransit == TRANSIT_TO_FRONT) 133 && (!openingWcs.isEmpty() && openingWcs.valueAt(0).asTask() != null) 134 && mWallpaperControllerLocked.isWallpaperVisible())); 135 // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set, 136 // don't consider upgrading to wallpaper transition. 137 return (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper) 138 ? null : wallpaperTarget; 139 } 140 141 /** 142 * Handle application transition for given display. 143 */ handleAppTransitionReady()144 void handleAppTransitionReady() { 145 mTempTransitionReasons.clear(); 146 if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons) 147 || !transitionGoodToGo(mDisplayContent.mChangingContainers, 148 mTempTransitionReasons)) { 149 return; 150 } 151 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady"); 152 153 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO"); 154 // TODO(new-app-transition): Remove code using appTransition.getAppTransition() 155 final AppTransition appTransition = mDisplayContent.mAppTransition; 156 157 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear(); 158 159 appTransition.removeAppTransitionTimeoutCallbacks(); 160 161 mDisplayContent.mWallpaperMayChange = false; 162 163 int appCount = mDisplayContent.mOpeningApps.size(); 164 for (int i = 0; i < appCount; ++i) { 165 // Clearing the mAnimatingExit flag before entering animation. It's set to true if app 166 // window is removed, or window relayout to invisible. This also affects window 167 // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the 168 // transition selection depends on wallpaper target visibility. 169 mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags(); 170 } 171 appCount = mDisplayContent.mChangingContainers.size(); 172 for (int i = 0; i < appCount; ++i) { 173 // Clearing for same reason as above. 174 final ActivityRecord activity = getAppFromContainer( 175 mDisplayContent.mChangingContainers.valueAtUnchecked(i)); 176 if (activity != null) { 177 activity.clearAnimatingFlags(); 178 } 179 } 180 181 // Adjust wallpaper before we pull the lower/upper target, since pending changes 182 // (like the clearAnimatingFlags() above) might affect wallpaper target result. 183 // Or, the opening app window should be a wallpaper target. 184 mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded( 185 mDisplayContent.mOpeningApps); 186 187 final @TransitionOldType int transit = getTransitCompatType( 188 mDisplayContent.mAppTransition, 189 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, 190 mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(), 191 mDisplayContent.mSkipAppTransitionAnimation); 192 mDisplayContent.mSkipAppTransitionAnimation = false; 193 194 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 195 "handleAppTransitionReady: displayId=%d appTransition={%s}" 196 + " openingApps=[%s] closingApps=[%s] transit=%s", 197 mDisplayContent.mDisplayId, 198 appTransition.toString(), 199 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, 200 AppTransition.appTransitionOldToString(transit)); 201 202 // Find the layout params of the top-most application window in the tokens, which is 203 // what will control the animation theme. If all closing windows are obscured, then there is 204 // no need to do an animation. This is the case, for example, when this transition is being 205 // done behind a dream window. 206 final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps, 207 mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers); 208 final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes); 209 final ActivityRecord topOpeningApp = 210 getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */); 211 final ActivityRecord topClosingApp = 212 getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */); 213 final ActivityRecord topChangingApp = 214 getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */); 215 final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity); 216 overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes); 217 218 final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps) 219 || containsVoiceInteraction(mDisplayContent.mOpeningApps); 220 221 final int layoutRedo; 222 mService.mSurfaceAnimationRunner.deferStartingAnimations(); 223 try { 224 applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, 225 animLp, voiceInteraction); 226 handleClosingApps(); 227 handleOpeningApps(); 228 handleChangingApps(transit); 229 230 appTransition.setLastAppTransition(transit, topOpeningApp, 231 topClosingApp, topChangingApp); 232 233 final int flags = appTransition.getTransitFlags(); 234 layoutRedo = appTransition.goodToGo(transit, topOpeningApp); 235 handleNonAppWindowsInTransition(transit, flags); 236 appTransition.postAnimationCallback(); 237 appTransition.clear(); 238 } finally { 239 mService.mSurfaceAnimationRunner.continueStartingAnimations(); 240 } 241 242 mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent); 243 244 mDisplayContent.mOpeningApps.clear(); 245 mDisplayContent.mClosingApps.clear(); 246 mDisplayContent.mChangingContainers.clear(); 247 mDisplayContent.mUnknownAppVisibilityController.clear(); 248 249 // This has changed the visibility of windows, so perform 250 // a new layout to get them all up-to-date. 251 mDisplayContent.setLayoutNeeded(); 252 253 mDisplayContent.computeImeTarget(true /* updateImeTarget */); 254 255 mService.mAtmService.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting( 256 mTempTransitionReasons); 257 258 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); 259 260 mDisplayContent.pendingLayoutChanges |= 261 layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG; 262 } 263 264 /** 265 * Get old transit type based on the current transit requests. 266 * 267 * @param appTransition {@link AppTransition} for managing app transition state. 268 * @param openingApps {@link ActivityRecord}s which are becoming visible. 269 * @param closingApps {@link ActivityRecord}s which are becoming invisible. 270 * @param wallpaperTarget If non-null, this is the currently visible window that is associated 271 * with the wallpaper. 272 * @param oldWallpaper The currently visible window that is associated with the wallpaper in 273 * case we are transitioning from an activity with a wallpaper to one 274 * without. Otherwise null. 275 */ getTransitCompatType(AppTransition appTransition, ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation)276 static @TransitionOldType int getTransitCompatType(AppTransition appTransition, 277 ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, 278 @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper, 279 boolean skipAppTransitionAnimation) { 280 281 // Determine if closing and opening app token sets are wallpaper targets, in which case 282 // special animations are needed. 283 final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps) 284 && wallpaperTarget != null; 285 final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps) 286 && wallpaperTarget != null; 287 288 // Keyguard transit has highest priority. 289 switch (appTransition.getKeyguardTransition()) { 290 case TRANSIT_KEYGUARD_GOING_AWAY: 291 return openingAppHasWallpaper ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER 292 : TRANSIT_OLD_KEYGUARD_GOING_AWAY; 293 case TRANSIT_KEYGUARD_OCCLUDE: 294 // When there is a closing app, the keyguard has already been occluded by an 295 // activity, and another activity has started on top of that activity, so normal 296 // app transition animation should be used. 297 return closingApps.isEmpty() ? TRANSIT_OLD_KEYGUARD_OCCLUDE 298 : TRANSIT_OLD_ACTIVITY_OPEN; 299 case TRANSIT_KEYGUARD_UNOCCLUDE: 300 return TRANSIT_OLD_KEYGUARD_UNOCCLUDE; 301 } 302 303 // This is not keyguard transition and one of the app has request to skip app transition. 304 if (skipAppTransitionAnimation) { 305 return WindowManager.TRANSIT_OLD_UNSET; 306 } 307 final @TransitionFlags int flags = appTransition.getTransitFlags(); 308 final @TransitionType int firstTransit = appTransition.getFirstAppTransition(); 309 310 // Special transitions 311 // TODO(new-app-transitions): Revisit if those can be rewritten by using flags. 312 if (appTransition.containsTransitRequest(TRANSIT_CHANGE)) { 313 return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; 314 } 315 if ((flags & TRANSIT_FLAG_APP_CRASHED) != 0) { 316 return TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; 317 } 318 if (firstTransit == TRANSIT_NONE) { 319 return TRANSIT_OLD_NONE; 320 } 321 322 /* 323 * There are cases where we open/close a new task/activity, but in reality only a 324 * translucent activity on top of existing activities is opening/closing. For that one, we 325 * have a different animation because non of the task/activity animations actually work well 326 * with translucent apps. 327 */ 328 if (isNormalTransit(firstTransit)) { 329 boolean allOpeningVisible = true; 330 boolean allTranslucentOpeningApps = !openingApps.isEmpty(); 331 for (int i = openingApps.size() - 1; i >= 0; i--) { 332 final ActivityRecord activity = openingApps.valueAt(i); 333 if (!activity.isVisible()) { 334 allOpeningVisible = false; 335 if (activity.fillsParent()) { 336 allTranslucentOpeningApps = false; 337 } 338 } 339 } 340 boolean allTranslucentClosingApps = !closingApps.isEmpty(); 341 for (int i = closingApps.size() - 1; i >= 0; i--) { 342 if (closingApps.valueAt(i).fillsParent()) { 343 allTranslucentClosingApps = false; 344 break; 345 } 346 } 347 348 if (allTranslucentClosingApps && allOpeningVisible) { 349 return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; 350 } 351 if (allTranslucentOpeningApps && closingApps.isEmpty()) { 352 return TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; 353 } 354 } 355 356 final ActivityRecord topOpeningApp = getTopApp(openingApps, 357 false /* ignoreHidden */); 358 final ActivityRecord topClosingApp = getTopApp(closingApps, 359 true /* ignoreHidden */); 360 361 if (closingAppHasWallpaper && openingAppHasWallpaper) { 362 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!"); 363 switch (firstTransit) { 364 case TRANSIT_OPEN: 365 case TRANSIT_TO_FRONT: 366 return TRANSIT_OLD_WALLPAPER_INTRA_OPEN; 367 case TRANSIT_CLOSE: 368 case TRANSIT_TO_BACK: 369 return TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; 370 } 371 } else if (oldWallpaper != null && !openingApps.isEmpty() 372 && !openingApps.contains(oldWallpaper.mActivityRecord) 373 && closingApps.contains(oldWallpaper.mActivityRecord) 374 && topClosingApp == oldWallpaper.mActivityRecord) { 375 // We are transitioning from an activity with a wallpaper to one without. 376 return TRANSIT_OLD_WALLPAPER_CLOSE; 377 } else if (wallpaperTarget != null && wallpaperTarget.isVisible() 378 && openingApps.contains(wallpaperTarget.mActivityRecord) 379 && topOpeningApp == wallpaperTarget.mActivityRecord 380 /* && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE */) { 381 // We are transitioning from an activity without 382 // a wallpaper to now showing the wallpaper 383 return TRANSIT_OLD_WALLPAPER_OPEN; 384 } 385 386 final ArraySet<WindowContainer> openingWcs = getAnimationTargets( 387 openingApps, closingApps, true /* visible */); 388 final ArraySet<WindowContainer> closingWcs = getAnimationTargets( 389 openingApps, closingApps, false /* visible */); 390 final boolean isActivityOpening = !openingWcs.isEmpty() 391 && openingWcs.valueAt(0).asActivityRecord() != null; 392 final boolean isActivityClosing = !closingWcs.isEmpty() 393 && closingWcs.valueAt(0).asActivityRecord() != null; 394 final boolean isTaskOpening = !openingWcs.isEmpty() && !isActivityOpening; 395 final boolean isTaskClosing = !closingWcs.isEmpty() && !isActivityClosing; 396 397 if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && isTaskOpening) { 398 return TRANSIT_OLD_TASK_TO_FRONT; 399 } 400 if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && isTaskClosing) { 401 return TRANSIT_OLD_TASK_TO_BACK; 402 } 403 if (appTransition.containsTransitRequest(TRANSIT_OPEN)) { 404 if (isTaskOpening) { 405 return (appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0 406 ? TRANSIT_OLD_TASK_OPEN_BEHIND : TRANSIT_OLD_TASK_OPEN; 407 } 408 if (isActivityOpening) { 409 return TRANSIT_OLD_ACTIVITY_OPEN; 410 } 411 } 412 if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) { 413 if (isTaskClosing) { 414 return TRANSIT_OLD_TASK_CLOSE; 415 } 416 if (isActivityClosing) { 417 for (int i = closingApps.size() - 1; i >= 0; i--) { 418 if (closingApps.valueAt(i).visibleIgnoringKeyguard) { 419 return TRANSIT_OLD_ACTIVITY_CLOSE; 420 } 421 } 422 // Skip close activity transition since no closing app can be visible 423 return WindowManager.TRANSIT_OLD_UNSET; 424 } 425 } 426 if (appTransition.containsTransitRequest(TRANSIT_RELAUNCH) 427 && !openingWcs.isEmpty() && !openingApps.isEmpty()) { 428 return TRANSIT_OLD_ACTIVITY_RELAUNCH; 429 } 430 return TRANSIT_OLD_NONE; 431 } 432 getAnimLp(ActivityRecord activity)433 private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) { 434 final WindowState mainWindow = activity != null ? activity.findMainWindow() : null; 435 return mainWindow != null ? mainWindow.mAttrs : null; 436 } 437 getRemoteAnimationOverride(@ullable WindowContainer container, @TransitionOldType int transit, ArraySet<Integer> activityTypes)438 RemoteAnimationAdapter getRemoteAnimationOverride(@Nullable WindowContainer container, 439 @TransitionOldType int transit, ArraySet<Integer> activityTypes) { 440 if (container != null) { 441 final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition(); 442 if (definition != null) { 443 final RemoteAnimationAdapter adapter = definition.getAdapter(transit, 444 activityTypes); 445 if (adapter != null) { 446 return adapter; 447 } 448 } 449 } 450 return mRemoteAnimationDefinition != null 451 ? mRemoteAnimationDefinition.getAdapter(transit, activityTypes) 452 : null; 453 } 454 455 /** 456 * Overrides the pending transition with the remote animation defined for the transition in the 457 * set of defined remote animations in the app window token. 458 */ overrideWithRemoteAnimationIfSet(@ullable ActivityRecord animLpActivity, @TransitionOldType int transit, ArraySet<Integer> activityTypes)459 private void overrideWithRemoteAnimationIfSet(@Nullable ActivityRecord animLpActivity, 460 @TransitionOldType int transit, ArraySet<Integer> activityTypes) { 461 if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) { 462 // The crash transition has higher priority than any involved remote animations. 463 return; 464 } 465 final RemoteAnimationAdapter adapter = 466 getRemoteAnimationOverride(animLpActivity, transit, activityTypes); 467 if (adapter != null) { 468 mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter); 469 } 470 } 471 getAppFromContainer(WindowContainer wc)472 static ActivityRecord getAppFromContainer(WindowContainer wc) { 473 return wc.asTask() != null ? wc.asTask().getTopNonFinishingActivity() 474 : wc.asActivityRecord(); 475 } 476 477 /** 478 * @return The window token that determines the animation theme. 479 */ findAnimLayoutParamsToken(@ransitionOldType int transit, ArraySet<Integer> activityTypes)480 private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit, 481 ArraySet<Integer> activityTypes) { 482 ActivityRecord result; 483 final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps; 484 final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps; 485 final ArraySet<WindowContainer> changingApps = mDisplayContent.mChangingContainers; 486 487 // Remote animations always win, but fullscreen tokens override non-fullscreen tokens. 488 result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, 489 w -> w.getRemoteAnimationDefinition() != null 490 && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes)); 491 if (result != null) { 492 return getAppFromContainer(result); 493 } 494 result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, 495 w -> w.fillsParent() && w.findMainWindow() != null); 496 if (result != null) { 497 return result; 498 } 499 return lookForHighestTokenWithFilter(closingApps, openingApps, changingApps, 500 w -> w.findMainWindow() != null); 501 } 502 503 /** 504 * @return The set of {@link android.app.WindowConfiguration.ActivityType}s contained in the set 505 * of apps in {@code array1}, {@code array2}, and {@code array3}. 506 */ collectActivityTypes(ArraySet<ActivityRecord> array1, ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3)507 private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1, 508 ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3) { 509 final ArraySet<Integer> result = new ArraySet<>(); 510 for (int i = array1.size() - 1; i >= 0; i--) { 511 result.add(array1.valueAt(i).getActivityType()); 512 } 513 for (int i = array2.size() - 1; i >= 0; i--) { 514 result.add(array2.valueAt(i).getActivityType()); 515 } 516 for (int i = array3.size() - 1; i >= 0; i--) { 517 result.add(array3.valueAt(i).getActivityType()); 518 } 519 return result; 520 } 521 lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1, ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3, Predicate<ActivityRecord> filter)522 private static ActivityRecord lookForHighestTokenWithFilter(ArraySet<ActivityRecord> array1, 523 ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3, 524 Predicate<ActivityRecord> filter) { 525 final int array2base = array1.size(); 526 final int array3base = array2.size() + array2base; 527 final int count = array3base + array3.size(); 528 int bestPrefixOrderIndex = Integer.MIN_VALUE; 529 ActivityRecord bestToken = null; 530 for (int i = 0; i < count; i++) { 531 final WindowContainer wtoken = i < array2base 532 ? array1.valueAt(i) 533 : (i < array3base 534 ? array2.valueAt(i - array2base) 535 : array3.valueAt(i - array3base)); 536 final int prefixOrderIndex = wtoken.getPrefixOrderIndex(); 537 final ActivityRecord r = getAppFromContainer(wtoken); 538 if (r != null && filter.test(r) && prefixOrderIndex > bestPrefixOrderIndex) { 539 bestPrefixOrderIndex = prefixOrderIndex; 540 bestToken = r; 541 } 542 } 543 return bestToken; 544 } 545 546 private boolean containsVoiceInteraction(ArraySet<ActivityRecord> apps) { 547 for (int i = apps.size() - 1; i >= 0; i--) { 548 if (apps.valueAt(i).mVoiceInteraction) { 549 return true; 550 } 551 } 552 return false; 553 } 554 555 /** 556 * Apply animation to the set of window containers. 557 * 558 * @param wcs The list of {@link WindowContainer}s to which an app transition animation applies. 559 * @param apps The list of {@link ActivityRecord}s being transitioning. 560 * @param transit The current transition type. 561 * @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes 562 * invisible. 563 * @param animLp Layout parameters in which an app transition animation runs. 564 * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice 565 * interaction session driving task. 566 */ applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps, @TransitionOldType int transit, boolean visible, LayoutParams animLp, boolean voiceInteraction)567 private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps, 568 @TransitionOldType int transit, boolean visible, LayoutParams animLp, 569 boolean voiceInteraction) { 570 final int wcsCount = wcs.size(); 571 for (int i = 0; i < wcsCount; i++) { 572 final WindowContainer wc = wcs.valueAt(i); 573 // If app transition animation target is promoted to higher level, SurfaceAnimator 574 // triggers WC#onAnimationFinished only on the promoted target. So we need to take care 575 // of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the 576 // app transition. 577 final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>(); 578 for (int j = 0; j < apps.size(); ++j) { 579 final ActivityRecord app = apps.valueAt(j); 580 if (app.isDescendantOf(wc)) { 581 transitioningDescendants.add(app); 582 } 583 } 584 wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants); 585 } 586 } 587 588 /** 589 * Find WindowContainers to be animated from a set of opening and closing apps. We will promote 590 * animation targets to higher level in the window hierarchy if possible. 591 * 592 * @param visible {@code true} to get animation targets for opening apps, {@code false} to get 593 * animation targets for closing apps. 594 * @return {@link WindowContainer}s to be animated. 595 */ 596 @VisibleForTesting getAnimationTargets( ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, boolean visible)597 static ArraySet<WindowContainer> getAnimationTargets( 598 ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, 599 boolean visible) { 600 601 // The candidates of animation targets, which might be able to promote to higher level. 602 final LinkedList<WindowContainer> candidates = new LinkedList<>(); 603 final ArraySet<ActivityRecord> apps = visible ? openingApps : closingApps; 604 for (int i = 0; i < apps.size(); ++i) { 605 final ActivityRecord app = apps.valueAt(i); 606 if (app.shouldApplyAnimation(visible)) { 607 candidates.add(app); 608 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 609 "Changing app %s visible=%b performLayout=%b", 610 app, app.isVisible(), false); 611 } 612 } 613 614 final ArraySet<ActivityRecord> otherApps = visible ? closingApps : openingApps; 615 // Ancestors of closing apps while finding animation targets for opening apps, or ancestors 616 // of opening apps while finding animation targets for closing apps. 617 final ArraySet<WindowContainer> otherAncestors = new ArraySet<>(); 618 for (int i = 0; i < otherApps.size(); ++i) { 619 for (WindowContainer wc = otherApps.valueAt(i); wc != null; wc = wc.getParent()) { 620 otherAncestors.add(wc); 621 } 622 } 623 624 // The final animation targets which cannot promote to higher level anymore. 625 final ArraySet<WindowContainer> targets = new ArraySet<>(); 626 final ArrayList<WindowContainer> siblings = new ArrayList<>(); 627 while (!candidates.isEmpty()) { 628 final WindowContainer current = candidates.removeFirst(); 629 final WindowContainer parent = current.getParent(); 630 siblings.clear(); 631 siblings.add(current); 632 boolean canPromote = true; 633 634 if (parent == null || !parent.canCreateRemoteAnimationTarget() 635 // We cannot promote the animation on Task's parent when the task is in 636 // clearing task in case the animating get stuck when performing the opening 637 // task that behind it. 638 || (current.asTask() != null && current.asTask().mInRemoveTask)) { 639 canPromote = false; 640 } else { 641 // In case a descendant of the parent belongs to the other group, we cannot promote 642 // the animation target from "current" to the parent. 643 // 644 // Example: Imagine we're checking if we can animate a Task instead of a set of 645 // ActivityRecords. In case an activity starts a new activity within a same Task, 646 // an ActivityRecord of an existing activity belongs to the opening apps, at the 647 // same time, the other ActivityRecord of a new activity belongs to the closing 648 // apps. In this case, we cannot promote the animation target to Task level, but 649 // need to animate each individual activity. 650 // 651 // [Task] +- [ActivityRecord1] (in opening apps) 652 // +- [ActivityRecord2] (in closing apps) 653 if (otherAncestors.contains(parent)) { 654 canPromote = false; 655 } 656 657 // Find all siblings of the current WindowContainer in "candidates", move them into 658 // a separate list "siblings", and checks if an animation target can be promoted 659 // to its parent. 660 // 661 // We can promote an animation target to its parent if and only if all visible 662 // siblings will be animating. 663 // 664 // Example: Imagine that a Task contains two visible activity record, but only one 665 // of them is included in the opening apps and the other belongs to neither opening 666 // or closing apps. This happens when an activity launches another translucent 667 // activity in the same Task. In this case, we cannot animate Task, but have to 668 // animate each activity, otherwise an activity behind the translucent activity also 669 // animates. 670 // 671 // [Task] +- [ActivityRecord1] (visible, in opening apps) 672 // +- [ActivityRecord2] (visible, not in opening apps) 673 for (int j = 0; j < parent.getChildCount(); ++j) { 674 final WindowContainer sibling = parent.getChildAt(j); 675 if (candidates.remove(sibling)) { 676 siblings.add(sibling); 677 } else if (sibling != current && sibling.isVisible()) { 678 canPromote = false; 679 } 680 } 681 } 682 683 if (canPromote) { 684 candidates.add(parent); 685 } else { 686 targets.addAll(siblings); 687 } 688 } 689 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "getAnimationTarget in=%s, out=%s", 690 apps, targets); 691 return targets; 692 } 693 694 /** 695 * Apply an app transition animation based on a set of {@link ActivityRecord} 696 * 697 * @param openingApps The list of opening apps to which an app transition animation applies. 698 * @param closingApps The list of closing apps to which an app transition animation applies. 699 * @param transit The current transition type. 700 * @param animLp Layout parameters in which an app transition animation runs. 701 * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice 702 * interaction session driving task. 703 */ applyAnimations(ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit, LayoutParams animLp, boolean voiceInteraction)704 private void applyAnimations(ArraySet<ActivityRecord> openingApps, 705 ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit, 706 LayoutParams animLp, boolean voiceInteraction) { 707 if (transit == WindowManager.TRANSIT_OLD_UNSET 708 || (openingApps.isEmpty() && closingApps.isEmpty())) { 709 return; 710 } 711 712 final ArraySet<WindowContainer> openingWcs = getAnimationTargets( 713 openingApps, closingApps, true /* visible */); 714 final ArraySet<WindowContainer> closingWcs = getAnimationTargets( 715 openingApps, closingApps, false /* visible */); 716 applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp, 717 voiceInteraction); 718 applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp, 719 voiceInteraction); 720 721 for (int i = 0; i < openingApps.size(); ++i) { 722 openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false; 723 } 724 for (int i = 0; i < closingApps.size(); ++i) { 725 closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false; 726 } 727 728 final AccessibilityController accessibilityController = 729 mDisplayContent.mWmService.mAccessibilityController; 730 if (accessibilityController != null) { 731 accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit); 732 } 733 } 734 handleOpeningApps()735 private void handleOpeningApps() { 736 final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps; 737 final int appsCount = openingApps.size(); 738 739 for (int i = 0; i < appsCount; i++) { 740 final ActivityRecord app = openingApps.valueAt(i); 741 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app); 742 743 app.commitVisibility(true /* visible */, false /* performLayout */); 744 745 // In case a trampoline activity is used, it can happen that a new ActivityRecord is 746 // added and a new app transition starts before the previous app transition animation 747 // ends. So we cannot simply use app.isAnimating(PARENTS) to determine if the app must 748 // to be added to the list of tokens to be notified of app transition complete. 749 final WindowContainer wc = app.getAnimatingContainer(PARENTS, 750 ANIMATION_TYPE_APP_TRANSITION); 751 if (wc == null || !wc.getAnimationSources().contains(app)) { 752 // This token isn't going to be animating. Add it to the list of tokens to 753 // be notified of app transition complete since the notification will not be 754 // sent be the app window animator. 755 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(app.token); 756 } 757 app.updateReportedVisibilityLocked(); 758 app.waitingToShow = false; 759 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, 760 ">>> OPEN TRANSACTION handleAppTransitionReady()"); 761 mService.openSurfaceTransaction(); 762 try { 763 app.showAllWindowsLocked(); 764 } finally { 765 mService.closeSurfaceTransaction("handleAppTransitionReady"); 766 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, 767 "<<< CLOSE TRANSACTION handleAppTransitionReady()"); 768 } 769 770 if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) { 771 app.attachThumbnailAnimation(); 772 } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) { 773 app.attachCrossProfileAppsThumbnailAnimation(); 774 } 775 } 776 } 777 handleClosingApps()778 private void handleClosingApps() { 779 final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps; 780 final int appsCount = closingApps.size(); 781 782 for (int i = 0; i < appsCount; i++) { 783 final ActivityRecord app = closingApps.valueAt(i); 784 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", app); 785 786 app.commitVisibility(false /* visible */, false /* performLayout */); 787 app.updateReportedVisibilityLocked(); 788 // Force the allDrawn flag, because we want to start 789 // this guy's animations regardless of whether it's 790 // gotten drawn. 791 app.allDrawn = true; 792 // Ensure that apps that are mid-starting are also scheduled to have their 793 // starting windows removed after the animation is complete 794 if (app.mStartingWindow != null && !app.mStartingWindow.mAnimatingExit) { 795 app.removeStartingWindow(); 796 } 797 798 if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) { 799 app.attachThumbnailAnimation(); 800 } 801 } 802 } 803 handleChangingApps(@ransitionOldType int transit)804 private void handleChangingApps(@TransitionOldType int transit) { 805 final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers; 806 final int appsCount = apps.size(); 807 for (int i = 0; i < appsCount; i++) { 808 WindowContainer wc = apps.valueAt(i); 809 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc); 810 wc.applyAnimation(null, transit, true, false, null /* sources */); 811 } 812 } 813 handleNonAppWindowsInTransition(@ransitionOldType int transit, int flags)814 private void handleNonAppWindowsInTransition(@TransitionOldType int transit, int flags) { 815 if (transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY 816 && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) { 817 if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0 818 && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0 819 && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) == 0) { 820 Animation anim = mService.mPolicy.createKeyguardWallpaperExit( 821 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0); 822 if (anim != null) { 823 anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked()); 824 mDisplayContent.mWallpaperController.startWallpaperAnimation(anim); 825 } 826 } 827 } 828 if ((transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY 829 || transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER) 830 && !WindowManagerService.sEnableRemoteKeyguardGoingAwayAnimation) { 831 mDisplayContent.startKeyguardExitOnNonAppWindows( 832 transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER, 833 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0, 834 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION) != 0); 835 } 836 } 837 transitionGoodToGo(ArraySet<? extends WindowContainer> apps, ArrayMap<WindowContainer, Integer> outReasons)838 private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps, 839 ArrayMap<WindowContainer, Integer> outReasons) { 840 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 841 "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(), 842 mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout()); 843 844 final ScreenRotationAnimation screenRotationAnimation = mService.mRoot.getDisplayContent( 845 Display.DEFAULT_DISPLAY).getRotationAnimation(); 846 847 if (!mDisplayContent.mAppTransition.isTimeout()) { 848 // Imagine the case where we are changing orientation due to an app transition, but a 849 // previous orientation change is still in progress. We won't process the orientation 850 // change for our transition because we need to wait for the rotation animation to 851 // finish. 852 // If we start the app transition at this point, we will interrupt it halfway with a 853 // new rotation animation after the old one finally finishes. It's better to defer the 854 // app transition. 855 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() 856 && mDisplayContent.getDisplayRotation().needsUpdate()) { 857 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 858 "Delaying app transition for screen rotation animation to finish"); 859 return false; 860 } 861 for (int i = 0; i < apps.size(); i++) { 862 WindowContainer wc = apps.valueAt(i); 863 final ActivityRecord activity = getAppFromContainer(wc); 864 if (activity == null) { 865 continue; 866 } 867 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, 868 "Check opening app=%s: allDrawn=%b startingDisplayed=%b " 869 + "startingMoved=%b isRelaunching()=%b startingWindow=%s", 870 activity, activity.allDrawn, activity.startingDisplayed, 871 activity.startingMoved, activity.isRelaunching(), 872 activity.mStartingWindow); 873 874 875 final boolean allDrawn = activity.allDrawn && !activity.isRelaunching(); 876 if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) { 877 return false; 878 } 879 if (allDrawn) { 880 outReasons.put(activity, APP_TRANSITION_WINDOWS_DRAWN); 881 } else { 882 outReasons.put(activity, 883 activity.mStartingData instanceof SplashScreenStartingData 884 ? APP_TRANSITION_SPLASH_SCREEN 885 : APP_TRANSITION_SNAPSHOT); 886 } 887 } 888 889 // We also need to wait for the specs to be fetched, if needed. 890 if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) { 891 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "isFetchingAppTransitionSpecs=true"); 892 return false; 893 } 894 895 if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) { 896 ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "unknownApps is not empty: %s", 897 mDisplayContent.mUnknownAppVisibilityController.getDebugMessage()); 898 return false; 899 } 900 901 // If the wallpaper is visible, we need to check it's ready too. 902 boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() || 903 mWallpaperControllerLocked.wallpaperTransitionReady(); 904 if (wallpaperReady) { 905 return true; 906 } 907 return false; 908 } 909 return true; 910 } 911 912 /** 913 * Identifies whether the current transition occurs within a single task or not. This is used 914 * to determine whether animations should be clipped to the task bounds instead of root task 915 * bounds. 916 */ 917 @VisibleForTesting isTransitWithinTask(@ransitionOldType int transit, Task task)918 boolean isTransitWithinTask(@TransitionOldType int transit, Task task) { 919 if (task == null 920 || !mDisplayContent.mChangingContainers.isEmpty()) { 921 // if there is no task, then we can't constrain to the task. 922 // if anything is changing, it can animate outside its task. 923 return false; 924 } 925 if (!(transit == TRANSIT_OLD_ACTIVITY_OPEN 926 || transit == TRANSIT_OLD_ACTIVITY_CLOSE 927 || transit == TRANSIT_OLD_ACTIVITY_RELAUNCH)) { 928 // only activity-level transitions will be within-task. 929 return false; 930 } 931 // check that all components are in the task. 932 for (ActivityRecord activity : mDisplayContent.mOpeningApps) { 933 Task activityTask = activity.getTask(); 934 if (activityTask != task) { 935 return false; 936 } 937 } 938 for (ActivityRecord activity : mDisplayContent.mClosingApps) { 939 if (activity.getTask() != task) { 940 return false; 941 } 942 } 943 return true; 944 } 945 canBeWallpaperTarget(ArraySet<ActivityRecord> apps)946 private static boolean canBeWallpaperTarget(ArraySet<ActivityRecord> apps) { 947 for (int i = apps.size() - 1; i >= 0; i--) { 948 if (apps.valueAt(i).windowsCanBeWallpaperTarget()) { 949 return true; 950 } 951 } 952 return false; 953 } 954 955 /** 956 * Finds the top app in a list of apps, using its {@link ActivityRecord#getPrefixOrderIndex} to 957 * compare z-order. 958 * 959 * @param apps The list of apps to search. 960 * @param ignoreInvisible If set to true, ignores apps that are not 961 * {@link ActivityRecord#isVisible}. 962 * @return The top {@link ActivityRecord}. 963 */ getTopApp(ArraySet<? extends WindowContainer> apps, boolean ignoreInvisible)964 private static ActivityRecord getTopApp(ArraySet<? extends WindowContainer> apps, 965 boolean ignoreInvisible) { 966 int topPrefixOrderIndex = Integer.MIN_VALUE; 967 ActivityRecord topApp = null; 968 for (int i = apps.size() - 1; i >= 0; i--) { 969 final ActivityRecord app = getAppFromContainer(apps.valueAt(i)); 970 if (app == null || ignoreInvisible && !app.isVisible()) { 971 continue; 972 } 973 final int prefixOrderIndex = app.getPrefixOrderIndex(); 974 if (prefixOrderIndex > topPrefixOrderIndex) { 975 topPrefixOrderIndex = prefixOrderIndex; 976 topApp = app; 977 } 978 } 979 return topApp; 980 } 981 } 982