blob: e65f3b417429d1416c6a5275ed15c3427bdd3d60 [file] [log] [blame]
lumark588a3e82018-07-20 18:53:54 +08001/*
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
17package com.android.server.wm;
18
19import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
20import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
21import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
22import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE;
23import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
24import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
25import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE;
26import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER;
27import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
28import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
29import static android.view.WindowManager.TRANSIT_NONE;
30import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
31import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE;
32import static android.view.WindowManager.TRANSIT_TASK_OPEN;
33import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
34import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
35import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
36import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
37import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
38import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
39import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
40import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
41
42import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
43import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
44import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SNAPSHOT;
45import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
46import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
47import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
48import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
49import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
50import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
51import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
52import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
53
lumark588a3e82018-07-20 18:53:54 +080054import android.os.Trace;
55import android.util.ArraySet;
56import android.util.Slog;
57import android.util.SparseIntArray;
58import android.view.Display;
59import android.view.RemoteAnimationAdapter;
60import android.view.RemoteAnimationDefinition;
61import android.view.WindowManager;
62import android.view.WindowManager.LayoutParams;
63import android.view.animation.Animation;
64
65import com.android.internal.annotations.VisibleForTesting;
66
67import java.util.function.Predicate;
68
69
70/**
71 * Checks for app transition readiness, resolves animation attributes and performs visibility
72 * change for apps that animate as part of an app transition.
73 */
74public class AppTransitionController {
75 private static final String TAG = TAG_WITH_CLASS_NAME ? "AppTransitionController" : TAG_WM;
76 private final WindowManagerService mService;
77 private final DisplayContent mDisplayContent;
78 private final WallpaperController mWallpaperControllerLocked;
79
80 private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
81
82 AppTransitionController(WindowManagerService service, DisplayContent displayContent) {
83 mService = service;
84 mDisplayContent = displayContent;
wilsonshihc32538e2018-11-07 17:27:34 +080085 mWallpaperControllerLocked = mDisplayContent.mWallpaperController;
lumark588a3e82018-07-20 18:53:54 +080086 }
87
88 /**
89 * Handle application transition for given display.
90 */
91 void handleAppTransitionReady() {
92 final int appsCount = mDisplayContent.mOpeningApps.size();
93 if (!transitionGoodToGo(appsCount, mTempTransitionReasons)) {
94 return;
95 }
96 Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");
97
98 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
99 int transit = mDisplayContent.mAppTransition.getAppTransition();
100 if (mDisplayContent.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) {
101 transit = WindowManager.TRANSIT_UNSET;
102 }
103 mDisplayContent.mSkipAppTransitionAnimation = false;
104 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
105
106 mDisplayContent.mAppTransition.removeAppTransitionTimeoutCallbacks();
107
wilsonshihc32538e2018-11-07 17:27:34 +0800108 mDisplayContent.mWallpaperMayChange = false;
lumark588a3e82018-07-20 18:53:54 +0800109
110 int i;
111 for (i = 0; i < appsCount; i++) {
112 final AppWindowToken wtoken = mDisplayContent.mOpeningApps.valueAt(i);
113 // Clearing the mAnimatingExit flag before entering animation. It's set to true if app
114 // window is removed, or window relayout to invisible. This also affects window
115 // visibility. We need to clear it *before* maybeUpdateTransitToWallpaper() as the
116 // transition selection depends on wallpaper target visibility.
117 wtoken.clearAnimatingFlags();
118 }
119
120 // Adjust wallpaper before we pull the lower/upper target, since pending changes
121 // (like the clearAnimatingFlags() above) might affect wallpaper target result.
122 // Or, the opening app window should be a wallpaper target.
wilsonshihc32538e2018-11-07 17:27:34 +0800123 mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
lumark588a3e82018-07-20 18:53:54 +0800124 mDisplayContent.mOpeningApps);
125
126 // Determine if closing and opening app token sets are wallpaper targets, in which case
127 // special animations are needed.
128 final boolean hasWallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget() != null;
129 final boolean openingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mOpeningApps)
130 && hasWallpaperTarget;
131 final boolean closingAppHasWallpaper = canBeWallpaperTarget(mDisplayContent.mClosingApps)
132 && hasWallpaperTarget;
133
134 transit = maybeUpdateTransitToTranslucentAnim(transit);
135 transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
136 closingAppHasWallpaper);
137
138 // Find the layout params of the top-most application window in the tokens, which is
139 // what will control the animation theme. If all closing windows are obscured, then there is
140 // no need to do an animation. This is the case, for example, when this transition is being
141 // done behind a dream window.
142 final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
143 mDisplayContent.mClosingApps);
144 final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw();
145 final AppWindowToken animLpToken = allowAnimations
146 ? findAnimLayoutParamsToken(transit, activityTypes)
147 : null;
148 final AppWindowToken topOpeningApp = allowAnimations
149 ? getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */)
150 : null;
151 final AppWindowToken topClosingApp = allowAnimations
152 ? getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */)
153 : null;
154 final WindowManager.LayoutParams animLp = getAnimLp(animLpToken);
155 overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes);
156
157 final boolean voiceInteraction = containsVoiceInteraction(mDisplayContent.mOpeningApps)
158 || containsVoiceInteraction(mDisplayContent.mOpeningApps);
159
160 final int layoutRedo;
161 mService.mSurfaceAnimationRunner.deferStartingAnimations();
162 try {
163 processApplicationsAnimatingInPlace(transit);
164
165 handleClosingApps(transit, animLp, voiceInteraction);
166 handleOpeningApps(transit, animLp, voiceInteraction);
167
168 mDisplayContent.mAppTransition.setLastAppTransition(transit, topOpeningApp,
169 topClosingApp);
170
171 final int flags = mDisplayContent.mAppTransition.getTransitFlags();
172 layoutRedo = mDisplayContent.mAppTransition.goodToGo(transit, topOpeningApp,
173 topClosingApp, mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps);
174 handleNonAppWindowsInTransition(transit, flags);
175 mDisplayContent.mAppTransition.postAnimationCallback();
176 mDisplayContent.mAppTransition.clear();
177 } finally {
178 mService.mSurfaceAnimationRunner.continueStartingAnimations();
179 }
180
181 mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
182
183 mDisplayContent.mOpeningApps.clear();
184 mDisplayContent.mClosingApps.clear();
185 mDisplayContent.mUnknownAppVisibilityController.clear();
186
187 // This has changed the visibility of windows, so perform
188 // a new layout to get them all up-to-date.
189 mDisplayContent.setLayoutNeeded();
190
191 mDisplayContent.computeImeTarget(true /* updateImeTarget */);
192
193 mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING,
194 mTempTransitionReasons.clone()).sendToTarget();
195
196 Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
197
198 mDisplayContent.pendingLayoutChanges |=
199 layoutRedo | FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;
200 }
201
202 private static WindowManager.LayoutParams getAnimLp(AppWindowToken wtoken) {
203 final WindowState mainWindow = wtoken != null ? wtoken.findMainWindow() : null;
204 return mainWindow != null ? mainWindow.mAttrs : null;
205 }
206
207 /**
208 * Overrides the pending transition with the remote animation defined for the transition in the
209 * set of defined remote animations in the app window token.
210 */
211 private void overrideWithRemoteAnimationIfSet(AppWindowToken animLpToken, int transit,
212 ArraySet<Integer> activityTypes) {
213 if (transit == TRANSIT_CRASHING_ACTIVITY_CLOSE) {
214 // The crash transition has higher priority than any involved remote animations.
215 return;
216 }
217 if (animLpToken == null) {
218 return;
219 }
220 final RemoteAnimationDefinition definition = animLpToken.getRemoteAnimationDefinition();
221 if (definition == null) {
222 return;
223 }
224 final RemoteAnimationAdapter adapter = definition.getAdapter(transit, activityTypes);
225 if (adapter != null) {
226 animLpToken.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(
227 adapter);
228 }
229 }
230
231 /**
232 * @return The window token that determines the animation theme.
233 */
234 private AppWindowToken findAnimLayoutParamsToken(@WindowManager.TransitionType int transit,
235 ArraySet<Integer> activityTypes) {
236 AppWindowToken result;
237 final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
238 final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
239
240 // Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
241 result = lookForHighestTokenWithFilter(closingApps, openingApps,
242 w -> w.getRemoteAnimationDefinition() != null
243 && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
244 if (result != null) {
245 return result;
246 }
247 result = lookForHighestTokenWithFilter(closingApps, openingApps,
248 w -> w.fillsParent() && w.findMainWindow() != null);
249 if (result != null) {
250 return result;
251 }
252 return lookForHighestTokenWithFilter(closingApps, openingApps,
253 w -> w.findMainWindow() != null);
254 }
255
256 /**
257 * @return The set of {@link WindowConfiguration.ActivityType}s contained in the set of apps in
258 * {@code array1} and {@code array2}.
259 */
260 private static ArraySet<Integer> collectActivityTypes(ArraySet<AppWindowToken> array1,
261 ArraySet<AppWindowToken> array2) {
262 final ArraySet<Integer> result = new ArraySet<>();
263 for (int i = array1.size() - 1; i >= 0; i--) {
264 result.add(array1.valueAt(i).getActivityType());
265 }
266 for (int i = array2.size() - 1; i >= 0; i--) {
267 result.add(array2.valueAt(i).getActivityType());
268 }
269 return result;
270 }
271
272 private static AppWindowToken lookForHighestTokenWithFilter(ArraySet<AppWindowToken> array1,
273 ArraySet<AppWindowToken> array2, Predicate<AppWindowToken> filter) {
274 final int array1count = array1.size();
275 final int count = array1count + array2.size();
276 int bestPrefixOrderIndex = Integer.MIN_VALUE;
277 AppWindowToken bestToken = null;
278 for (int i = 0; i < count; i++) {
279 final AppWindowToken wtoken = i < array1count
280 ? array1.valueAt(i)
281 : array2.valueAt(i - array1count);
282 final int prefixOrderIndex = wtoken.getPrefixOrderIndex();
283 if (filter.test(wtoken) && prefixOrderIndex > bestPrefixOrderIndex) {
284 bestPrefixOrderIndex = prefixOrderIndex;
285 bestToken = wtoken;
286 }
287 }
288 return bestToken;
289 }
290
291 private boolean containsVoiceInteraction(ArraySet<AppWindowToken> apps) {
292 for (int i = apps.size() - 1; i >= 0; i--) {
293 if (apps.valueAt(i).mVoiceInteraction) {
294 return true;
295 }
296 }
297 return false;
298 }
299
300 private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
301 final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
302 final int appsCount = openingApps.size();
303 for (int i = 0; i < appsCount; i++) {
304 AppWindowToken wtoken = openingApps.valueAt(i);
305 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken);
306
307 if (!wtoken.setVisibility(animLp, true, transit, false, voiceInteraction)) {
308 // This token isn't going to be animating. Add it to the list of tokens to
309 // be notified of app transition complete since the notification will not be
310 // sent be the app window animator.
311 mDisplayContent.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);
312 }
313 wtoken.updateReportedVisibilityLocked();
314 wtoken.waitingToShow = false;
315 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
316 ">>> OPEN TRANSACTION handleAppTransitionReady()");
317 mService.openSurfaceTransaction();
318 try {
319 wtoken.showAllWindowsLocked();
320 } finally {
321 mService.closeSurfaceTransaction("handleAppTransitionReady");
322 if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
323 "<<< CLOSE TRANSACTION handleAppTransitionReady()");
324 }
325
326 if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) {
327 wtoken.attachThumbnailAnimation();
328 } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
329 wtoken.attachCrossProfileAppsThumbnailAnimation();
330 }
331 }
332 }
333
334 private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) {
335 final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
336 final int appsCount = closingApps.size();
337 for (int i = 0; i < appsCount; i++) {
338 AppWindowToken wtoken = closingApps.valueAt(i);
339
340 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken);
341 // TODO: Do we need to add to mNoAnimationNotifyOnTransitionFinished like above if not
342 // animating?
343 wtoken.setVisibility(animLp, false, transit, false, voiceInteraction);
344 wtoken.updateReportedVisibilityLocked();
345 // Force the allDrawn flag, because we want to start
346 // this guy's animations regardless of whether it's
347 // gotten drawn.
348 wtoken.allDrawn = true;
349 wtoken.deferClearAllDrawn = false;
350 // Ensure that apps that are mid-starting are also scheduled to have their
351 // starting windows removed after the animation is complete
352 if (wtoken.startingWindow != null && !wtoken.startingWindow.mAnimatingExit
353 && wtoken.getController() != null) {
354 wtoken.getController().removeStartingWindow();
355 }
356
357 if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
358 wtoken.attachThumbnailAnimation();
359 }
360 }
361 }
362
363 private void handleNonAppWindowsInTransition(int transit, int flags) {
364 if (transit == TRANSIT_KEYGUARD_GOING_AWAY) {
365 if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0
366 && (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION) == 0) {
367 Animation anim = mService.mPolicy.createKeyguardWallpaperExit(
368 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
369 if (anim != null) {
370 mDisplayContent.mWallpaperController.startWallpaperAnimation(anim);
371 }
372 }
373 }
374 if (transit == TRANSIT_KEYGUARD_GOING_AWAY
375 || transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
376 mDisplayContent.startKeyguardExitOnNonAppWindows(
377 transit == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
378 (flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE) != 0);
379 }
380 }
381
382 private boolean transitionGoodToGo(int appsCount, SparseIntArray outReasons) {
383 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
384 "Checking " + appsCount + " opening apps (frozen="
385 + mService.mDisplayFrozen + " timeout="
386 + mDisplayContent.mAppTransition.isTimeout() + ")...");
387 final ScreenRotationAnimation screenRotationAnimation =
388 mService.mAnimator.getScreenRotationAnimationLocked(
389 Display.DEFAULT_DISPLAY);
390
391 outReasons.clear();
392 if (!mDisplayContent.mAppTransition.isTimeout()) {
393 // Imagine the case where we are changing orientation due to an app transition, but a
394 // previous orientation change is still in progress. We won't process the orientation
395 // change for our transition because we need to wait for the rotation animation to
396 // finish.
397 // If we start the app transition at this point, we will interrupt it halfway with a
398 // new rotation animation after the old one finally finishes. It's better to defer the
399 // app transition.
400 if (screenRotationAnimation != null && screenRotationAnimation.isAnimating() &&
401 mService.getDefaultDisplayContentLocked().rotationNeedsUpdate()) {
402 if (DEBUG_APP_TRANSITIONS) {
403 Slog.v(TAG, "Delaying app transition for screen rotation animation to finish");
404 }
405 return false;
406 }
407 for (int i = 0; i < appsCount; i++) {
408 AppWindowToken wtoken = mDisplayContent.mOpeningApps.valueAt(i);
409 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
410 "Check opening app=" + wtoken + ": allDrawn="
411 + wtoken.allDrawn + " startingDisplayed="
412 + wtoken.startingDisplayed + " startingMoved="
413 + wtoken.startingMoved + " isRelaunching()="
414 + wtoken.isRelaunching() + " startingWindow="
415 + wtoken.startingWindow);
416
417
418 final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching();
419 if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
420 return false;
421 }
422 final int windowingMode = wtoken.getWindowingMode();
423 if (allDrawn) {
424 outReasons.put(windowingMode, APP_TRANSITION_WINDOWS_DRAWN);
425 } else {
426 outReasons.put(windowingMode,
427 wtoken.startingData instanceof SplashScreenStartingData
428 ? APP_TRANSITION_SPLASH_SCREEN
429 : APP_TRANSITION_SNAPSHOT);
430 }
431 }
432
433 // We also need to wait for the specs to be fetched, if needed.
434 if (mDisplayContent.mAppTransition.isFetchingAppTransitionsSpecs()) {
435 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "isFetchingAppTransitionSpecs=true");
436 return false;
437 }
438
439 if (!mDisplayContent.mUnknownAppVisibilityController.allResolved()) {
440 if (DEBUG_APP_TRANSITIONS) {
441 Slog.v(TAG, "unknownApps is not empty: "
442 + mDisplayContent.mUnknownAppVisibilityController.getDebugMessage());
443 }
444 return false;
445 }
446
447 // If the wallpaper is visible, we need to check it's ready too.
448 boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
449 mWallpaperControllerLocked.wallpaperTransitionReady();
450 if (wallpaperReady) {
451 return true;
452 }
453 return false;
454 }
455 return true;
456 }
457
458 private int maybeUpdateTransitToWallpaper(int transit, boolean openingAppHasWallpaper,
459 boolean closingAppHasWallpaper) {
460 // Given no app transition pass it through instead of a wallpaper transition.
461 // Never convert the crashing transition.
462 // Never update the transition for the wallpaper if we are just docking from recents
463 if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE
464 || transit == TRANSIT_DOCK_TASK_FROM_RECENTS) {
465 return transit;
466 }
467
468 final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
469 final boolean showWallpaper = wallpaperTarget != null
470 && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
471 // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
472 // don't consider upgrading to wallpaper transition.
473 final WindowState oldWallpaper =
474 (mWallpaperControllerLocked.isWallpaperTargetAnimating() || !showWallpaper)
475 ? null
476 : wallpaperTarget;
477 final ArraySet<AppWindowToken> openingApps = mDisplayContent.mOpeningApps;
478 final ArraySet<AppWindowToken> closingApps = mDisplayContent.mClosingApps;
479 final AppWindowToken topOpeningApp = getTopApp(mDisplayContent.mOpeningApps,
480 false /* ignoreHidden */);
481 final AppWindowToken topClosingApp = getTopApp(mDisplayContent.mClosingApps,
482 true /* ignoreHidden */);
483
484 boolean openingCanBeWallpaperTarget = canBeWallpaperTarget(openingApps);
485 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
486 "New wallpaper target=" + wallpaperTarget
487 + ", oldWallpaper=" + oldWallpaper
488 + ", openingApps=" + openingApps
489 + ", closingApps=" + closingApps);
490
491 if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) {
492 transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER;
493 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
494 "New transit: " + AppTransition.appTransitionToString(transit));
495 }
496 // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic
497 // relies on the fact that we always execute a Keyguard transition after preparing one.
498 else if (!isKeyguardGoingAwayTransit(transit)) {
499 if (closingAppHasWallpaper && openingAppHasWallpaper) {
500 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!");
501 switch (transit) {
502 case TRANSIT_ACTIVITY_OPEN:
503 case TRANSIT_TASK_OPEN:
504 case TRANSIT_TASK_TO_FRONT:
505 transit = TRANSIT_WALLPAPER_INTRA_OPEN;
506 break;
507 case TRANSIT_ACTIVITY_CLOSE:
508 case TRANSIT_TASK_CLOSE:
509 case TRANSIT_TASK_TO_BACK:
510 transit = TRANSIT_WALLPAPER_INTRA_CLOSE;
511 break;
512 }
513 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
514 "New transit: " + AppTransition.appTransitionToString(transit));
515 } else if (oldWallpaper != null && !mDisplayContent.mOpeningApps.isEmpty()
516 && !openingApps.contains(oldWallpaper.mAppToken)
517 && closingApps.contains(oldWallpaper.mAppToken)
518 && topClosingApp == oldWallpaper.mAppToken) {
519 // We are transitioning from an activity with a wallpaper to one without.
520 transit = TRANSIT_WALLPAPER_CLOSE;
521 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
522 + AppTransition.appTransitionToString(transit));
523 } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()
524 && openingApps.contains(wallpaperTarget.mAppToken)
525 && topOpeningApp == wallpaperTarget.mAppToken
526 && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) {
527 // We are transitioning from an activity without
528 // a wallpaper to now showing the wallpaper
529 transit = TRANSIT_WALLPAPER_OPEN;
530 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: "
531 + AppTransition.appTransitionToString(transit));
532 }
533 }
534 return transit;
535 }
536
537 /**
538 * There are cases where we open/close a new task/activity, but in reality only a translucent
539 * activity on top of existing activities is opening/closing. For that one, we have a different
540 * animation because non of the task/activity animations actually work well with translucent
541 * apps.
542 *
543 * @param transit The current transition type.
544 * @return The current transition type or
545 * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/
546 * {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
547 * situation.
548 */
549 @VisibleForTesting
550 int maybeUpdateTransitToTranslucentAnim(int transit) {
551 final boolean taskOrActivity = AppTransition.isTaskTransit(transit)
552 || AppTransition.isActivityTransit(transit);
553 boolean allOpeningVisible = true;
554 boolean allTranslucentOpeningApps = !mDisplayContent.mOpeningApps.isEmpty();
555 for (int i = mDisplayContent.mOpeningApps.size() - 1; i >= 0; i--) {
556 final AppWindowToken token = mDisplayContent.mOpeningApps.valueAt(i);
557 if (!token.isVisible()) {
558 allOpeningVisible = false;
559 if (token.fillsParent()) {
560 allTranslucentOpeningApps = false;
561 }
562 }
563 }
564 boolean allTranslucentClosingApps = !mDisplayContent.mClosingApps.isEmpty();
565 for (int i = mDisplayContent.mClosingApps.size() - 1; i >= 0; i--) {
566 if (mDisplayContent.mClosingApps.valueAt(i).fillsParent()) {
567 allTranslucentClosingApps = false;
568 break;
569 }
570 }
571
572 if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) {
573 return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
574 }
575 if (taskOrActivity && allTranslucentOpeningApps && mDisplayContent.mClosingApps.isEmpty()) {
576 return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
577 }
578 return transit;
579 }
580
581 private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) {
582 for (int i = apps.size() - 1; i >= 0; i--) {
583 if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
584 return true;
585 }
586 }
587 return false;
588 }
589
590 /**
591 * Finds the top app in a list of apps, using its {@link AppWindowToken#getPrefixOrderIndex} to
592 * compare z-order.
593 *
594 * @param apps The list of apps to search.
595 * @param ignoreHidden If set to true, ignores apps that are {@link AppWindowToken#isHidden}.
596 * @return The top {@link AppWindowToken}.
597 */
598 private AppWindowToken getTopApp(ArraySet<AppWindowToken> apps, boolean ignoreHidden) {
599 int topPrefixOrderIndex = Integer.MIN_VALUE;
600 AppWindowToken topApp = null;
601 for (int i = apps.size() - 1; i >= 0; i--) {
602 final AppWindowToken app = apps.valueAt(i);
603 if (ignoreHidden && app.isHidden()) {
604 continue;
605 }
606 final int prefixOrderIndex = app.getPrefixOrderIndex();
607 if (prefixOrderIndex > topPrefixOrderIndex) {
608 topPrefixOrderIndex = prefixOrderIndex;
609 topApp = app;
610 }
611 }
612 return topApp;
613 }
614
615 private void processApplicationsAnimatingInPlace(int transit) {
616 if (transit == TRANSIT_TASK_IN_PLACE) {
617 // Find the focused window
618 final WindowState win = mDisplayContent.findFocusedWindow();
619 if (win != null) {
620 final AppWindowToken wtoken = win.mAppToken;
621 if (DEBUG_APP_TRANSITIONS)
622 Slog.v(TAG, "Now animating app in place " + wtoken);
623 wtoken.cancelAnimation();
624 wtoken.applyAnimationLocked(null, transit, false, false);
625 wtoken.updateReportedVisibilityLocked();
626 wtoken.showAllWindowsLocked();
627 }
628 }
629 }
630}