2 * Copyright (C) 2017 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.server.wm;
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
22 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
23 import static android.view.RemoteAnimationTarget.MODE_OPENING;
24 import static android.view.WindowManager.DOCKED_INVALID;
25 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
27 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
28 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
29 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
30 import static com.android.server.wm.BoundsAnimationController.FADE_IN;
31 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
32 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
33 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
35 import android.annotation.IntDef;
36 import android.app.ActivityManager.TaskSnapshot;
37 import android.app.WindowConfiguration;
38 import android.graphics.Point;
39 import android.graphics.Rect;
40 import android.os.Binder;
41 import android.os.IBinder.DeathRecipient;
42 import android.os.RemoteException;
43 import android.os.SystemClock;
44 import android.util.ArraySet;
45 import android.util.Slog;
46 import android.util.SparseBooleanArray;
47 import android.util.SparseIntArray;
48 import android.util.proto.ProtoOutputStream;
49 import android.view.IRecentsAnimationController;
50 import android.view.IRecentsAnimationRunner;
51 import android.view.InputWindowHandle;
52 import android.view.RemoteAnimationTarget;
53 import android.view.SurfaceControl;
54 import android.view.SurfaceControl.Transaction;
56 import com.android.internal.annotations.VisibleForTesting;
57 import com.android.server.LocalServices;
58 import com.android.server.inputmethod.InputMethodManagerInternal;
59 import com.android.server.statusbar.StatusBarManagerInternal;
60 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
61 import com.android.server.wm.utils.InsetUtils;
63 import com.google.android.collect.Sets;
65 import java.io.PrintWriter;
66 import java.util.ArrayList;
69 * Controls a single instance of the remote driven recents animation. In particular, this allows
70 * the calling SystemUI to animate the visible task windows as a part of the transition. The remote
71 * runner is provided an animation controller which allows it to take screenshots and to notify
72 * window manager when the animation is completed. In addition, window manager may also notify the
73 * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
75 public class RecentsAnimationController implements DeathRecipient {
76 private static final String TAG = RecentsAnimationController.class.getSimpleName();
77 private static final long FAILSAFE_DELAY = 1000;
79 public static final int REORDER_KEEP_IN_PLACE = 0;
80 public static final int REORDER_MOVE_TO_TOP = 1;
81 public static final int REORDER_MOVE_TO_ORIGINAL_POSITION = 2;
83 @IntDef(prefix = { "REORDER_MODE_" }, value = {
84 REORDER_KEEP_IN_PLACE,
86 REORDER_MOVE_TO_ORIGINAL_POSITION
88 public @interface ReorderMode {}
90 private final WindowManagerService mService;
91 private final StatusBarManagerInternal mStatusBar;
92 private IRecentsAnimationRunner mRunner;
93 private final RecentsAnimationCallbacks mCallbacks;
94 private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
95 private final int mDisplayId;
96 private final Runnable mFailsafeRunnable = () ->
97 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
99 final Object mLock = new Object();
101 // The recents component app token that is shown behind the visibile tasks
102 private AppWindowToken mTargetAppToken;
103 private int mTargetActivityType;
104 private Rect mMinimizedHomeBounds = new Rect();
106 // We start the RecentsAnimationController in a pending-start state since we need to wait for
107 // the wallpaper/activity to draw before we can give control to the handler to start animating
108 // the visible task surfaces
109 private boolean mPendingStart = true;
111 // Set when the animation has been canceled
112 private boolean mCanceled;
114 // Whether or not the input consumer is enabled. The input consumer must be both registered and
115 // enabled for it to start intercepting touch events.
116 private boolean mInputConsumerEnabled;
118 // Whether or not the recents animation should cause the primary split-screen stack to be
120 private boolean mSplitScreenMinimized;
122 private final Rect mTmpRect = new Rect();
124 private boolean mLinkedToDeathOfRunner;
126 private boolean mCancelWithDeferredScreenshot;
128 private boolean mCancelOnNextTransitionStart;
131 * Animates the screenshot of task that used to be controlled by RecentsAnimation.
132 * @see {@link #cancelOnNextTransitionStart}
134 SurfaceAnimator mRecentScreenshotAnimator;
136 final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
138 public int onAppTransitionStartingLocked(int transit, long duration,
139 long statusBarAnimationStartTime, long statusBarAnimationDuration) {
141 mService.mRoot.getDisplayContent(mDisplayId).mAppTransition
142 .unregisterListener(this);
147 public interface RecentsAnimationCallbacks {
148 /** Callback when recents animation is finished. */
149 void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously,
150 boolean sendUserLeaveHint);
153 private final IRecentsAnimationController mController =
154 new IRecentsAnimationController.Stub() {
157 public TaskSnapshot screenshotTask(int taskId) {
158 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "screenshotTask(" + taskId + "):"
159 + " mCanceled=" + mCanceled);
160 final long token = Binder.clearCallingIdentity();
162 synchronized (mService.getWindowManagerLock()) {
166 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
167 final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
168 final Task task = adapter.mTask;
169 if (task.mTaskId == taskId) {
170 final TaskSnapshotController snapshotController =
171 mService.mTaskSnapshotController;
172 final ArraySet<Task> tasks = Sets.newArraySet(task);
173 snapshotController.snapshotTasks(tasks);
174 snapshotController.addSkipClosingAppSnapshotTasks(tasks);
175 return snapshotController.getSnapshot(taskId, 0 /* userId */,
176 false /* restoreFromDisk */, false /* reducedResolution */);
182 Binder.restoreCallingIdentity(token);
187 public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) {
188 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):"
189 + " mCanceled=" + mCanceled);
190 final long token = Binder.clearCallingIdentity();
192 synchronized (mService.getWindowManagerLock()) {
198 // Note, the callback will handle its own synchronization, do not lock on WM lock
199 // prior to calling the callback
200 mCallbacks.onAnimationFinished(moveHomeToTop
201 ? REORDER_MOVE_TO_TOP
202 : REORDER_MOVE_TO_ORIGINAL_POSITION,
203 true /* runSynchronously */, sendUserLeaveHint);
204 final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
205 dc.mBoundsAnimationController.setAnimationType(FADE_IN);
207 Binder.restoreCallingIdentity(token);
212 public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars)
213 throws RemoteException {
214 final long token = Binder.clearCallingIdentity();
216 synchronized (mService.getWindowManagerLock()) {
217 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
218 final Task task = mPendingAnimations.get(i).mTask;
219 if (task.getActivityType() != mTargetActivityType) {
220 task.setCanAffectSystemUiFlags(behindSystemBars);
223 mService.mWindowPlacerLocked.requestTraversal();
226 Binder.restoreCallingIdentity(token);
231 public void setInputConsumerEnabled(boolean enabled) {
232 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setInputConsumerEnabled(" + enabled + "):"
233 + " mCanceled=" + mCanceled);
234 final long token = Binder.clearCallingIdentity();
236 synchronized (mService.getWindowManagerLock()) {
241 mInputConsumerEnabled = enabled;
242 final InputMonitor inputMonitor =
243 mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
244 inputMonitor.updateInputWindowsLw(true /*force*/);
245 mService.scheduleAnimationLocked();
248 Binder.restoreCallingIdentity(token);
253 public void setSplitScreenMinimized(boolean minimized) {
254 final long token = Binder.clearCallingIdentity();
256 synchronized (mService.getWindowManagerLock()) {
261 mSplitScreenMinimized = minimized;
262 mService.checkSplitScreenMinimizedChanged(true /* animate */);
265 Binder.restoreCallingIdentity(token);
270 public void hideCurrentInputMethod() {
271 final long token = Binder.clearCallingIdentity();
273 final InputMethodManagerInternal inputMethodManagerInternal =
274 LocalServices.getService(InputMethodManagerInternal.class);
275 if (inputMethodManagerInternal != null) {
276 inputMethodManagerInternal.hideCurrentInputMethod();
279 Binder.restoreCallingIdentity(token);
284 public void setCancelWithDeferredScreenshot(boolean screenshot) {
285 synchronized (mLock) {
286 setCancelWithDeferredScreenshotLocked(screenshot);
291 public void cleanupScreenshot() {
292 synchronized (mLock) {
293 if (mRecentScreenshotAnimator != null) {
294 mRecentScreenshotAnimator.cancelAnimation();
295 mRecentScreenshotAnimator = null;
302 * @param remoteAnimationRunner The remote runner which should be notified when the animation is
303 * ready to start or has been canceled
304 * @param callbacks Callbacks to be made when the animation finishes
306 RecentsAnimationController(WindowManagerService service,
307 IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
310 mRunner = remoteAnimationRunner;
311 mCallbacks = callbacks;
312 mDisplayId = displayId;
313 mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
316 public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
317 initialize(mService.mRoot.getDisplayContent(mDisplayId), targetActivityType, recentTaskIds);
321 * Initializes the recents animation controller. This is a separate call from the constructor
322 * because it may call cancelAnimation() which needs to properly clean up the controller
323 * in the window manager.
326 void initialize(DisplayContent dc, int targetActivityType, SparseBooleanArray recentTaskIds) {
327 mTargetActivityType = targetActivityType;
328 dc.mAppTransition.registerListenerLocked(mAppTransitionListener);
330 // Make leashes for each of the visible/target tasks and add it to the recents animation to
332 final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
333 final TaskStack targetStack = dc.getStack(WINDOWING_MODE_UNDEFINED, targetActivityType);
334 if (targetStack != null) {
335 for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
336 final Task t = targetStack.getChildAt(i);
337 if (!visibleTasks.contains(t)) {
342 final int taskCount = visibleTasks.size();
343 for (int i = 0; i < taskCount; i++) {
344 final Task task = visibleTasks.get(i);
345 final WindowConfiguration config = task.getWindowConfiguration();
346 if (config.tasksAreFloating()
347 || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
350 addAnimation(task, !recentTaskIds.get(task.mTaskId));
353 // Skip the animation if there is nothing to animate
354 if (mPendingAnimations.isEmpty()) {
355 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks");
360 linkToDeathOfRunner();
361 } catch (RemoteException e) {
362 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath");
366 // Adjust the wallpaper visibility for the showing target activity
367 final AppWindowToken recentsComponentAppToken = dc.getStack(WINDOWING_MODE_UNDEFINED,
368 targetActivityType).getTopChild().getTopFullscreenAppToken();
369 if (recentsComponentAppToken != null) {
370 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setHomeApp("
371 + recentsComponentAppToken.getName() + ")");
372 mTargetAppToken = recentsComponentAppToken;
373 if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
374 dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
375 dc.setLayoutNeeded();
379 // Save the minimized home height
380 final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility();
381 dc.getDockedDividerController().getHomeStackBoundsInDockedMode(
382 dc.getConfiguration(),
383 dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(),
384 mMinimizedHomeBounds);
386 mService.mWindowPlacerLocked.performSurfacePlacement();
388 // Notify that the animation has started
389 mStatusBar.onRecentsAnimationStateChanged(true /* running */);
393 AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
394 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "addAnimation(" + task.getName() + ")");
395 final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
396 isRecentTaskInvisible);
397 task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
398 task.commitPendingTransaction();
399 mPendingAnimations.add(taskAdapter);
404 void removeAnimation(TaskAnimationAdapter taskAdapter) {
405 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeAnimation("
406 + taskAdapter.mTask.mTaskId + ")");
407 taskAdapter.mTask.setCanAffectSystemUiFlags(true);
408 taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter);
409 mPendingAnimations.remove(taskAdapter);
412 void startAnimation() {
413 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
414 + " mCanceled=" + mCanceled);
415 if (!mPendingStart || mCanceled) {
416 // Skip starting if we've already started or canceled the animation
420 final ArrayList<RemoteAnimationTarget> appAnimations = new ArrayList<>();
421 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
422 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
423 final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationApp();
424 if (target != null) {
425 appAnimations.add(target);
427 removeAnimation(taskAdapter);
431 // Skip the animation if there is nothing to animate
432 if (appAnimations.isEmpty()) {
433 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows");
437 final RemoteAnimationTarget[] appTargets = appAnimations.toArray(
438 new RemoteAnimationTarget[appAnimations.size()]);
439 mPendingStart = false;
441 // Perform layout if it was scheduled before to make sure that we get correct content
442 // insets for the target app window after a rotation
443 final DisplayContent displayContent = mService.mRoot.getDisplayContent(mDisplayId);
444 displayContent.performLayout(false /* initial */, false /* updateInputWindows */);
446 final Rect minimizedHomeBounds = mTargetAppToken != null
447 && mTargetAppToken.inSplitScreenSecondaryWindowingMode()
448 ? mMinimizedHomeBounds
450 final Rect contentInsets;
451 if (mTargetAppToken != null && mTargetAppToken.findMainWindow() != null) {
452 contentInsets = mTargetAppToken.findMainWindow().getContentInsets();
454 // If the window for the activity had not yet been created, use the display insets.
455 mService.getStableInsets(mDisplayId, mTmpRect);
456 contentInsets = mTmpRect;
458 mRunner.onAnimationStart(mController, appTargets, contentInsets, minimizedHomeBounds);
459 if (DEBUG_RECENTS_ANIMATIONS) {
460 Slog.d(TAG, "startAnimation(): Notify animation start:");
461 for (int i = 0; i < mPendingAnimations.size(); i++) {
462 final Task task = mPendingAnimations.get(i).mTask;
463 Slog.d(TAG, "\t" + task.mTaskId);
466 } catch (RemoteException e) {
467 Slog.e(TAG, "Failed to start recents animation", e);
469 final SparseIntArray reasons = new SparseIntArray();
470 reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
471 mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis());
474 void cancelAnimation(@ReorderMode int reorderMode, String reason) {
475 cancelAnimation(reorderMode, false /* runSynchronously */, false /*screenshot */, reason);
478 void cancelAnimationSynchronously(@ReorderMode int reorderMode, String reason) {
479 cancelAnimation(reorderMode, true /* runSynchronously */, false /* screenshot */, reason);
482 void cancelAnimationWithScreenShot() {
483 cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, true /* screenshot */,
484 "stackOrderChanged");
487 private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously,
488 boolean screenshot, String reason) {
489 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason
490 + " runSynchronously=" + runSynchronously);
491 synchronized (mService.getWindowManagerLock()) {
493 // We've already canceled the animation
496 mService.mH.removeCallbacks(mFailsafeRunnable);
500 // Screen shot previous task when next task starts transition.
501 final Task task = mPendingAnimations.get(0).mTask;
502 screenshotRecentTask(task, reorderMode, runSynchronously);
503 mRunner.onAnimationCanceled(true /* deferredWithScreenshot */);
506 mRunner.onAnimationCanceled(false /* deferredWithScreenshot */);
507 } catch (RemoteException e) {
508 Slog.e(TAG, "Failed to cancel recents animation", e);
510 // Clean up and return to the previous app
511 mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
512 false /* sendUserLeaveHint */);
517 * Cancel recents animation when the next app transition starts.
519 * When we cancel the recents animation due to a stack order change, we can't just cancel it
520 * immediately as it would lead to a flicker in Launcher if we just remove the task from the
521 * leash. Instead we screenshot the previous task and replace the child of the leash with the
522 * screenshot, so that Launcher can still control the leash lifecycle & make the next app
523 * transition animate smoothly without flickering.
525 void cancelOnNextTransitionStart() {
526 mCancelOnNextTransitionStart = true;
529 void setCancelWithDeferredScreenshotLocked(boolean screenshot) {
530 mCancelWithDeferredScreenshot = screenshot;
533 boolean shouldCancelWithDeferredScreenshot() {
534 return mCancelWithDeferredScreenshot;
537 void onTransitionStart() {
542 if (mCancelOnNextTransitionStart) {
543 mCancelOnNextTransitionStart = false;
544 cancelAnimationWithScreenShot();
548 void screenshotRecentTask(Task task, @ReorderMode int reorderMode, boolean runSynchronously) {
549 final TaskScreenshotAnimatable animatable = TaskScreenshotAnimatable.create(task);
550 if (animatable != null) {
551 mRecentScreenshotAnimator = new SurfaceAnimator(
554 if (DEBUG_RECENTS_ANIMATIONS) {
555 Slog.d(TAG, "mRecentScreenshotAnimator finish");
557 mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
558 false /* sendUserLeaveHint */);
560 mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
564 void cleanupAnimation(@ReorderMode int reorderMode) {
565 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG,
566 "cleanupAnimation(): Notify animation finished mPendingAnimations="
567 + mPendingAnimations.size() + " reorderMode=" + reorderMode);
568 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
569 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
570 if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
571 taskAdapter.mTask.dontAnimateDimExit();
573 removeAnimation(taskAdapter);
576 // Clear any pending failsafe runnables
577 mService.mH.removeCallbacks(mFailsafeRunnable);
579 // Clear references to the runner
580 unlinkToDeathOfRunner();
584 // Make sure previous animator has cleaned-up.
585 if (mRecentScreenshotAnimator != null) {
586 mRecentScreenshotAnimator.cancelAnimation();
587 mRecentScreenshotAnimator = null;
590 // Update the input windows after the animation is complete
591 final InputMonitor inputMonitor =
592 mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
593 inputMonitor.updateInputWindowsLw(true /*force*/);
595 // We have deferred all notifications to the target app as a part of the recents animation,
596 // so if we are actually transitioning there, notify again here
597 if (mTargetAppToken != null) {
598 if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
599 mService.mRoot.getDisplayContent(mDisplayId)
600 .mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token);
604 // Notify that the animation has ended
605 mStatusBar.onRecentsAnimationStateChanged(false /* running */);
608 void scheduleFailsafe() {
609 mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY);
612 private void linkToDeathOfRunner() throws RemoteException {
613 if (!mLinkedToDeathOfRunner) {
614 mRunner.asBinder().linkToDeath(this, 0);
615 mLinkedToDeathOfRunner = true;
619 private void unlinkToDeathOfRunner() {
620 if (mLinkedToDeathOfRunner) {
621 mRunner.asBinder().unlinkToDeath(this, 0);
622 mLinkedToDeathOfRunner = false;
627 public void binderDied() {
628 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
630 synchronized (mService.getWindowManagerLock()) {
631 // Clear associated input consumers on runner death
632 final InputMonitor inputMonitor =
633 mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor();
634 inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
638 void checkAnimationReady(WallpaperController wallpaperController) {
640 final boolean wallpaperReady = !isTargetOverWallpaper()
641 || (wallpaperController.getWallpaperTarget() != null
642 && wallpaperController.wallpaperTransitionReady());
643 if (wallpaperReady) {
644 mService.getRecentsAnimationController().startAnimation();
649 boolean isSplitScreenMinimized() {
650 return mSplitScreenMinimized;
653 boolean isWallpaperVisible(WindowState w) {
654 return w != null && w.mAppToken != null && mTargetAppToken == w.mAppToken
655 && isTargetOverWallpaper();
659 * @return Whether to use the input consumer to override app input to route home/recents.
661 boolean shouldApplyInputConsumer(AppWindowToken appToken) {
662 // Only apply the input consumer if it is enabled, it is not the target (home/recents)
663 // being revealed with the transition, and we are actively animating the app as a part of
665 return mInputConsumerEnabled && mTargetAppToken != appToken && isAnimatingApp(appToken);
668 boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle,
670 // Update the input consumer touchable region to match the target app main window
671 final WindowState targetAppMainWindow = mTargetAppToken != null
672 ? mTargetAppToken.findMainWindow()
674 if (targetAppMainWindow != null) {
675 targetAppMainWindow.getBounds(mTmpRect);
676 inputWindowHandle.hasFocus = hasFocus;
677 inputWindowHandle.touchableRegion.set(mTmpRect);
683 boolean isTargetApp(AppWindowToken token) {
684 return mTargetAppToken != null && token == mTargetAppToken;
687 private boolean isTargetOverWallpaper() {
688 if (mTargetAppToken == null) {
691 return mTargetAppToken.windowsCanBeWallpaperTarget();
694 boolean isAnimatingTask(Task task) {
695 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
696 if (task == mPendingAnimations.get(i).mTask) {
703 private boolean isAnimatingApp(AppWindowToken appToken) {
704 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
705 final Task task = mPendingAnimations.get(i).mTask;
706 for (int j = task.getChildCount() - 1; j >= 0; j--) {
707 final AppWindowToken app = task.getChildAt(j);
708 if (app == appToken) {
717 class TaskAnimationAdapter implements AnimationAdapter {
719 private final Task mTask;
720 private SurfaceControl mCapturedLeash;
721 private OnAnimationFinishedCallback mCapturedFinishCallback;
722 private final boolean mIsRecentTaskInvisible;
723 private RemoteAnimationTarget mTarget;
724 private final Point mPosition = new Point();
725 private final Rect mBounds = new Rect();
727 TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
729 mIsRecentTaskInvisible = isRecentTaskInvisible;
730 final WindowContainer container = mTask.getParent();
731 container.getRelativeDisplayedPosition(mPosition);
732 mBounds.set(container.getDisplayedBounds());
735 RemoteAnimationTarget createRemoteAnimationApp() {
736 final AppWindowToken topApp = mTask.getTopVisibleAppToken();
737 final WindowState mainWindow = topApp != null
738 ? topApp.findMainWindow()
740 if (mainWindow == null) {
743 final Rect insets = new Rect();
744 mainWindow.getContentInsets(insets);
745 InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
746 final int mode = topApp.getActivityType() == mTargetActivityType
749 mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash,
750 !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
751 insets, mTask.getPrefixOrderIndex(), mPosition, mBounds,
752 mTask.getWindowConfiguration(), mIsRecentTaskInvisible, null, null);
757 public boolean getShowWallpaper() {
762 public int getBackgroundColor() {
767 public void startAnimation(SurfaceControl animationLeash, Transaction t,
768 OnAnimationFinishedCallback finishCallback) {
769 // Restore z-layering, position and stack crop until client has a chance to modify it.
770 t.setLayer(animationLeash, mTask.getPrefixOrderIndex());
771 t.setPosition(animationLeash, mPosition.x, mPosition.y);
772 mTmpRect.set(mBounds);
773 mTmpRect.offsetTo(0, 0);
774 t.setWindowCrop(animationLeash, mTmpRect);
775 mCapturedLeash = animationLeash;
776 mCapturedFinishCallback = finishCallback;
780 public void onAnimationCancelled(SurfaceControl animationLeash) {
781 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled");
785 public long getDurationHint() {
790 public long getStatusBarTransitionsStartTime() {
791 return SystemClock.uptimeMillis();
795 public void dump(PrintWriter pw, String prefix) {
796 pw.print(prefix); pw.println("task=" + mTask);
797 if (mTarget != null) {
798 pw.print(prefix); pw.println("Target:");
799 mTarget.dump(pw, prefix + " ");
801 pw.print(prefix); pw.println("Target: null");
803 pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
804 pw.println("mPosition=" + mPosition);
805 pw.println("mBounds=" + mBounds);
806 pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
810 public void writeToProto(ProtoOutputStream proto) {
811 final long token = proto.start(REMOTE);
812 if (mTarget != null) {
813 mTarget.writeToProto(proto, TARGET);
819 public void dump(PrintWriter pw, String prefix) {
820 final String innerPrefix = prefix + " ";
821 pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
822 pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
823 pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size());
824 pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
825 pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
826 pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
827 pw.print(innerPrefix); pw.println("mTargetAppToken=" + mTargetAppToken);
828 pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());