1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.wm; 18 19 import static android.app.ActivityManagerInternal.APP_TRANSITION_RECENTS_ANIM; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 23 import static android.view.RemoteAnimationTarget.MODE_CLOSING; 24 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; 25 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; 26 import static com.android.server.wm.AnimationAdapterProto.REMOTE; 27 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; 28 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS; 29 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING; 30 31 import android.annotation.IntDef; 32 import android.app.ActivityManager.TaskSnapshot; 33 import android.app.WindowConfiguration; 34 import android.graphics.Point; 35 import android.graphics.Rect; 36 import android.os.Binder; 37 import android.os.IBinder.DeathRecipient; 38 import android.os.RemoteException; 39 import android.os.SystemClock; 40 import android.util.ArraySet; 41 import android.util.Slog; 42 import android.util.SparseBooleanArray; 43 import android.util.SparseIntArray; 44 import android.util.proto.ProtoOutputStream; 45 import android.view.IRecentsAnimationController; 46 import android.view.IRecentsAnimationRunner; 47 import android.view.RemoteAnimationTarget; 48 import android.view.SurfaceControl; 49 import android.view.SurfaceControl.Transaction; 50 import android.view.inputmethod.InputMethodManagerInternal; 51 import com.android.internal.annotations.VisibleForTesting; 52 import com.android.server.LocalServices; 53 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 54 import com.android.server.wm.utils.InsetUtils; 55 import com.google.android.collect.Sets; 56 import java.io.PrintWriter; 57 import java.util.ArrayList; 58 59 /** 60 * Controls a single instance of the remote driven recents animation. In particular, this allows 61 * the calling SystemUI to animate the visible task windows as a part of the transition. The remote 62 * runner is provided an animation controller which allows it to take screenshots and to notify 63 * window manager when the animation is completed. In addition, window manager may also notify the 64 * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.) 65 */ 66 public class RecentsAnimationController implements DeathRecipient { 67 private static final String TAG = RecentsAnimationController.class.getSimpleName(); 68 private static final long FAILSAFE_DELAY = 1000; 69 70 public static final int REORDER_KEEP_IN_PLACE = 0; 71 public static final int REORDER_MOVE_TO_TOP = 1; 72 public static final int REORDER_MOVE_TO_ORIGINAL_POSITION = 2; 73 74 @IntDef(prefix = { "REORDER_MODE_" }, value = { 75 REORDER_KEEP_IN_PLACE, 76 REORDER_MOVE_TO_TOP, 77 REORDER_MOVE_TO_ORIGINAL_POSITION 78 }) 79 public @interface ReorderMode {} 80 81 private final WindowManagerService mService; 82 private IRecentsAnimationRunner mRunner; 83 private final RecentsAnimationCallbacks mCallbacks; 84 private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>(); 85 private final int mDisplayId; 86 private final Runnable mFailsafeRunnable = () -> 87 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable"); 88 89 // The recents component app token that is shown behind the visibile tasks 90 private AppWindowToken mTargetAppToken; 91 private Rect mMinimizedHomeBounds = new Rect(); 92 93 // We start the RecentsAnimationController in a pending-start state since we need to wait for 94 // the wallpaper/activity to draw before we can give control to the handler to start animating 95 // the visible task surfaces 96 private boolean mPendingStart = true; 97 98 // Set when the animation has been canceled 99 private boolean mCanceled; 100 101 // Whether or not the input consumer is enabled. The input consumer must be both registered and 102 // enabled for it to start intercepting touch events. 103 private boolean mInputConsumerEnabled; 104 105 // Whether or not the recents animation should cause the primary split-screen stack to be 106 // minimized 107 private boolean mSplitScreenMinimized; 108 109 private final Rect mTmpRect = new Rect(); 110 111 private boolean mLinkedToDeathOfRunner; 112 113 public interface RecentsAnimationCallbacks { onAnimationFinished(@eorderMode int reorderMode, boolean runSychronously)114 void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously); 115 } 116 117 private final IRecentsAnimationController mController = 118 new IRecentsAnimationController.Stub() { 119 120 @Override 121 public TaskSnapshot screenshotTask(int taskId) { 122 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "screenshotTask(" + taskId + "):" 123 + " mCanceled=" + mCanceled); 124 final long token = Binder.clearCallingIdentity(); 125 try { 126 synchronized (mService.getWindowManagerLock()) { 127 if (mCanceled) { 128 return null; 129 } 130 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 131 final TaskAnimationAdapter adapter = mPendingAnimations.get(i); 132 final Task task = adapter.mTask; 133 if (task.mTaskId == taskId) { 134 final TaskSnapshotController snapshotController = 135 mService.mTaskSnapshotController; 136 final ArraySet<Task> tasks = Sets.newArraySet(task); 137 snapshotController.snapshotTasks(tasks); 138 snapshotController.addSkipClosingAppSnapshotTasks(tasks); 139 return snapshotController.getSnapshot(taskId, 0 /* userId */, 140 false /* restoreFromDisk */, false /* reducedResolution */); 141 } 142 } 143 return null; 144 } 145 } finally { 146 Binder.restoreCallingIdentity(token); 147 } 148 } 149 150 @Override 151 public void finish(boolean moveHomeToTop) { 152 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):" 153 + " mCanceled=" + mCanceled); 154 final long token = Binder.clearCallingIdentity(); 155 try { 156 synchronized (mService.getWindowManagerLock()) { 157 if (mCanceled) { 158 return; 159 } 160 } 161 162 // Note, the callback will handle its own synchronization, do not lock on WM lock 163 // prior to calling the callback 164 mCallbacks.onAnimationFinished(moveHomeToTop 165 ? REORDER_MOVE_TO_TOP 166 : REORDER_MOVE_TO_ORIGINAL_POSITION, 167 true /* runSynchronously */); 168 } finally { 169 Binder.restoreCallingIdentity(token); 170 } 171 } 172 173 @Override 174 public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) 175 throws RemoteException { 176 final long token = Binder.clearCallingIdentity(); 177 try { 178 synchronized (mService.getWindowManagerLock()) { 179 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 180 mPendingAnimations.get(i).mTask.setCanAffectSystemUiFlags(behindSystemBars); 181 } 182 mService.mWindowPlacerLocked.requestTraversal(); 183 } 184 } finally { 185 Binder.restoreCallingIdentity(token); 186 } 187 } 188 189 @Override 190 public void setInputConsumerEnabled(boolean enabled) { 191 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setInputConsumerEnabled(" + enabled + "):" 192 + " mCanceled=" + mCanceled); 193 final long token = Binder.clearCallingIdentity(); 194 try { 195 synchronized (mService.getWindowManagerLock()) { 196 if (mCanceled) { 197 return; 198 } 199 200 mInputConsumerEnabled = enabled; 201 mService.mInputMonitor.updateInputWindowsLw(true /*force*/); 202 mService.scheduleAnimationLocked(); 203 } 204 } finally { 205 Binder.restoreCallingIdentity(token); 206 } 207 } 208 209 @Override 210 public void setSplitScreenMinimized(boolean minimized) { 211 final long token = Binder.clearCallingIdentity(); 212 try { 213 synchronized (mService.getWindowManagerLock()) { 214 if (mCanceled) { 215 return; 216 } 217 218 mSplitScreenMinimized = minimized; 219 mService.checkSplitScreenMinimizedChanged(true /* animate */); 220 } 221 } finally { 222 Binder.restoreCallingIdentity(token); 223 } 224 } 225 226 @Override 227 public void hideCurrentInputMethod() { 228 final long token = Binder.clearCallingIdentity(); 229 try { 230 final InputMethodManagerInternal inputMethodManagerInternal = 231 LocalServices.getService(InputMethodManagerInternal.class); 232 if (inputMethodManagerInternal != null) { 233 inputMethodManagerInternal.hideCurrentInputMethod(); 234 } 235 } finally { 236 Binder.restoreCallingIdentity(token); 237 } 238 } 239 }; 240 241 /** 242 * @param remoteAnimationRunner The remote runner which should be notified when the animation is 243 * ready to start or has been canceled 244 * @param callbacks Callbacks to be made when the animation finishes 245 */ RecentsAnimationController(WindowManagerService service, IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks, int displayId)246 RecentsAnimationController(WindowManagerService service, 247 IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks, 248 int displayId) { 249 mService = service; 250 mRunner = remoteAnimationRunner; 251 mCallbacks = callbacks; 252 mDisplayId = displayId; 253 } 254 255 /** 256 * Initializes the recents animation controller. This is a separate call from the constructor 257 * because it may call cancelAnimation() which needs to properly clean up the controller 258 * in the window manager. 259 */ initialize(int targetActivityType, SparseBooleanArray recentTaskIds)260 public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) { 261 // Make leashes for each of the visible tasks and add it to the recents animation to be 262 // started 263 final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); 264 final ArrayList<Task> visibleTasks = dc.getVisibleTasks(); 265 final int taskCount = visibleTasks.size(); 266 for (int i = 0; i < taskCount; i++) { 267 final Task task = visibleTasks.get(i); 268 final WindowConfiguration config = task.getWindowConfiguration(); 269 if (config.tasksAreFloating() 270 || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY 271 || config.getActivityType() == targetActivityType) { 272 continue; 273 } 274 addAnimation(task, !recentTaskIds.get(task.mTaskId)); 275 } 276 277 // Skip the animation if there is nothing to animate 278 if (mPendingAnimations.isEmpty()) { 279 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks"); 280 return; 281 } 282 283 try { 284 linkToDeathOfRunner(); 285 } catch (RemoteException e) { 286 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath"); 287 return; 288 } 289 290 // Adjust the wallpaper visibility for the showing target activity 291 final AppWindowToken recentsComponentAppToken = dc.getStack(WINDOWING_MODE_UNDEFINED, 292 targetActivityType).getTopChild().getTopFullscreenAppToken(); 293 if (recentsComponentAppToken != null) { 294 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setHomeApp(" 295 + recentsComponentAppToken.getName() + ")"); 296 mTargetAppToken = recentsComponentAppToken; 297 if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) { 298 dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; 299 dc.setLayoutNeeded(); 300 } 301 } 302 303 // Save the minimized home height 304 dc.getDockedDividerController().getHomeStackBoundsInDockedMode(mMinimizedHomeBounds); 305 306 mService.mWindowPlacerLocked.performSurfacePlacement(); 307 } 308 309 @VisibleForTesting addAnimation(Task task, boolean isRecentTaskInvisible)310 AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) { 311 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "addAnimation(" + task.getName() + ")"); 312 // TODO: Refactor this to use the task's animator 313 final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */, 314 mService); 315 final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task, 316 isRecentTaskInvisible); 317 anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */); 318 task.commitPendingTransaction(); 319 mPendingAnimations.add(taskAdapter); 320 return taskAdapter; 321 } 322 323 @VisibleForTesting removeAnimation(TaskAnimationAdapter taskAdapter)324 void removeAnimation(TaskAnimationAdapter taskAdapter) { 325 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeAnimation(" 326 + taskAdapter.mTask.mTaskId + ")"); 327 taskAdapter.mTask.setCanAffectSystemUiFlags(true); 328 taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter); 329 mPendingAnimations.remove(taskAdapter); 330 } 331 startAnimation()332 void startAnimation() { 333 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart 334 + " mCanceled=" + mCanceled); 335 if (!mPendingStart || mCanceled) { 336 // Skip starting if we've already started or canceled the animation 337 return; 338 } 339 try { 340 final ArrayList<RemoteAnimationTarget> appAnimations = new ArrayList<>(); 341 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 342 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); 343 final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationApp(); 344 if (target != null) { 345 appAnimations.add(target); 346 } else { 347 removeAnimation(taskAdapter); 348 } 349 } 350 351 // Skip the animation if there is nothing to animate 352 if (appAnimations.isEmpty()) { 353 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows"); 354 return; 355 } 356 357 final RemoteAnimationTarget[] appTargets = appAnimations.toArray( 358 new RemoteAnimationTarget[appAnimations.size()]); 359 mPendingStart = false; 360 361 final Rect minimizedHomeBounds = mTargetAppToken != null 362 && mTargetAppToken.inSplitScreenSecondaryWindowingMode() 363 ? mMinimizedHomeBounds 364 : null; 365 final Rect contentInsets = mTargetAppToken != null 366 && mTargetAppToken.findMainWindow() != null 367 ? mTargetAppToken.findMainWindow().mContentInsets 368 : null; 369 mRunner.onAnimationStart(mController, appTargets, contentInsets, 370 minimizedHomeBounds); 371 if (DEBUG_RECENTS_ANIMATIONS) { 372 Slog.d(TAG, "startAnimation(): Notify animation start:"); 373 for (int i = 0; i < mPendingAnimations.size(); i++) { 374 final Task task = mPendingAnimations.get(i).mTask; 375 Slog.d(TAG, "\t" + task.mTaskId); 376 } 377 } 378 } catch (RemoteException e) { 379 Slog.e(TAG, "Failed to start recents animation", e); 380 } 381 final SparseIntArray reasons = new SparseIntArray(); 382 reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM); 383 mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, 384 reasons).sendToTarget(); 385 } 386 cancelAnimation(@eorderMode int reorderMode, String reason)387 void cancelAnimation(@ReorderMode int reorderMode, String reason) { 388 cancelAnimation(reorderMode, false /* runSynchronously */, reason); 389 } 390 cancelAnimationSynchronously(@eorderMode int reorderMode, String reason)391 void cancelAnimationSynchronously(@ReorderMode int reorderMode, String reason) { 392 cancelAnimation(reorderMode, true /* runSynchronously */, reason); 393 } 394 cancelAnimation(@eorderMode int reorderMode, boolean runSynchronously, String reason)395 private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously, 396 String reason) { 397 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason 398 + " runSynchronously=" + runSynchronously); 399 synchronized (mService.getWindowManagerLock()) { 400 if (mCanceled) { 401 // We've already canceled the animation 402 return; 403 } 404 mService.mH.removeCallbacks(mFailsafeRunnable); 405 mCanceled = true; 406 try { 407 mRunner.onAnimationCanceled(); 408 } catch (RemoteException e) { 409 Slog.e(TAG, "Failed to cancel recents animation", e); 410 } 411 } 412 413 // Clean up and return to the previous app 414 mCallbacks.onAnimationFinished(reorderMode, runSynchronously); 415 } 416 cleanupAnimation(@eorderMode int reorderMode)417 void cleanupAnimation(@ReorderMode int reorderMode) { 418 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, 419 "cleanupAnimation(): Notify animation finished mPendingAnimations=" 420 + mPendingAnimations.size() + " reorderMode=" + reorderMode); 421 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 422 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); 423 if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { 424 taskAdapter.mTask.dontAnimateDimExit(); 425 } 426 removeAnimation(taskAdapter); 427 } 428 429 // Clear any pending failsafe runnables 430 mService.mH.removeCallbacks(mFailsafeRunnable); 431 432 // Clear references to the runner 433 unlinkToDeathOfRunner(); 434 mRunner = null; 435 mCanceled = true; 436 437 // Clear associated input consumers 438 mService.mInputMonitor.updateInputWindowsLw(true /*force*/); 439 mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); 440 441 // We have deferred all notifications to the target app as a part of the recents animation, 442 // so if we are actually transitioning there, notify again here 443 if (mTargetAppToken != null) { 444 if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { 445 mService.mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token); 446 } 447 } 448 } 449 scheduleFailsafe()450 void scheduleFailsafe() { 451 mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY); 452 } 453 linkToDeathOfRunner()454 private void linkToDeathOfRunner() throws RemoteException { 455 if (!mLinkedToDeathOfRunner) { 456 mRunner.asBinder().linkToDeath(this, 0); 457 mLinkedToDeathOfRunner = true; 458 } 459 } 460 unlinkToDeathOfRunner()461 private void unlinkToDeathOfRunner() { 462 if (mLinkedToDeathOfRunner) { 463 mRunner.asBinder().unlinkToDeath(this, 0); 464 mLinkedToDeathOfRunner = false; 465 } 466 } 467 468 @Override binderDied()469 public void binderDied() { 470 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied"); 471 } 472 checkAnimationReady(WallpaperController wallpaperController)473 void checkAnimationReady(WallpaperController wallpaperController) { 474 if (mPendingStart) { 475 final boolean wallpaperReady = !isTargetOverWallpaper() 476 || (wallpaperController.getWallpaperTarget() != null 477 && wallpaperController.wallpaperTransitionReady()); 478 if (wallpaperReady) { 479 mService.getRecentsAnimationController().startAnimation(); 480 } 481 } 482 } 483 isSplitScreenMinimized()484 boolean isSplitScreenMinimized() { 485 return mSplitScreenMinimized; 486 } 487 isWallpaperVisible(WindowState w)488 boolean isWallpaperVisible(WindowState w) { 489 return w != null && w.mAppToken != null && mTargetAppToken == w.mAppToken 490 && isTargetOverWallpaper(); 491 } 492 hasInputConsumerForApp(AppWindowToken appToken)493 boolean hasInputConsumerForApp(AppWindowToken appToken) { 494 return mInputConsumerEnabled && isAnimatingApp(appToken); 495 } 496 updateInputConsumerForApp(InputConsumerImpl recentsAnimationInputConsumer, boolean hasFocus)497 boolean updateInputConsumerForApp(InputConsumerImpl recentsAnimationInputConsumer, 498 boolean hasFocus) { 499 // Update the input consumer touchable region to match the target app main window 500 final WindowState targetAppMainWindow = mTargetAppToken != null 501 ? mTargetAppToken.findMainWindow() 502 : null; 503 if (targetAppMainWindow != null) { 504 targetAppMainWindow.getBounds(mTmpRect); 505 recentsAnimationInputConsumer.mWindowHandle.hasFocus = hasFocus; 506 recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect); 507 return true; 508 } 509 return false; 510 } 511 isTargetApp(AppWindowToken token)512 boolean isTargetApp(AppWindowToken token) { 513 return mTargetAppToken != null && token == mTargetAppToken; 514 } 515 isTargetOverWallpaper()516 private boolean isTargetOverWallpaper() { 517 if (mTargetAppToken == null) { 518 return false; 519 } 520 return mTargetAppToken.windowsCanBeWallpaperTarget(); 521 } 522 isAnimatingTask(Task task)523 boolean isAnimatingTask(Task task) { 524 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 525 if (task == mPendingAnimations.get(i).mTask) { 526 return true; 527 } 528 } 529 return false; 530 } 531 isAnimatingApp(AppWindowToken appToken)532 private boolean isAnimatingApp(AppWindowToken appToken) { 533 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 534 final Task task = mPendingAnimations.get(i).mTask; 535 for (int j = task.getChildCount() - 1; j >= 0; j--) { 536 final AppWindowToken app = task.getChildAt(j); 537 if (app == appToken) { 538 return true; 539 } 540 } 541 } 542 return false; 543 } 544 545 @VisibleForTesting 546 class TaskAnimationAdapter implements AnimationAdapter { 547 548 private final Task mTask; 549 private SurfaceControl mCapturedLeash; 550 private OnAnimationFinishedCallback mCapturedFinishCallback; 551 private final boolean mIsRecentTaskInvisible; 552 private RemoteAnimationTarget mTarget; 553 private final Point mPosition = new Point(); 554 private final Rect mBounds = new Rect(); 555 TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible)556 TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { 557 mTask = task; 558 mIsRecentTaskInvisible = isRecentTaskInvisible; 559 final WindowContainer container = mTask.getParent(); 560 container.getRelativePosition(mPosition); 561 container.getBounds(mBounds); 562 } 563 createRemoteAnimationApp()564 RemoteAnimationTarget createRemoteAnimationApp() { 565 final AppWindowToken topApp = mTask.getTopVisibleAppToken(); 566 final WindowState mainWindow = topApp != null 567 ? topApp.findMainWindow() 568 : null; 569 if (mainWindow == null) { 570 return null; 571 } 572 final Rect insets = new Rect(mainWindow.mContentInsets); 573 InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets()); 574 mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash, 575 !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect, 576 insets, mTask.getPrefixOrderIndex(), mPosition, mBounds, 577 mTask.getWindowConfiguration(), mIsRecentTaskInvisible); 578 return mTarget; 579 } 580 581 @Override getDetachWallpaper()582 public boolean getDetachWallpaper() { 583 return false; 584 } 585 586 @Override getShowWallpaper()587 public boolean getShowWallpaper() { 588 return false; 589 } 590 591 @Override getBackgroundColor()592 public int getBackgroundColor() { 593 return 0; 594 } 595 596 @Override startAnimation(SurfaceControl animationLeash, Transaction t, OnAnimationFinishedCallback finishCallback)597 public void startAnimation(SurfaceControl animationLeash, Transaction t, 598 OnAnimationFinishedCallback finishCallback) { 599 // Restore z-layering, position and stack crop until client has a chance to modify it. 600 t.setLayer(animationLeash, mTask.getPrefixOrderIndex()); 601 t.setPosition(animationLeash, mPosition.x, mPosition.y); 602 mTmpRect.set(mBounds); 603 mTmpRect.offsetTo(0, 0); 604 t.setWindowCrop(animationLeash, mTmpRect); 605 mCapturedLeash = animationLeash; 606 mCapturedFinishCallback = finishCallback; 607 } 608 609 @Override onAnimationCancelled(SurfaceControl animationLeash)610 public void onAnimationCancelled(SurfaceControl animationLeash) { 611 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled"); 612 } 613 614 @Override getDurationHint()615 public long getDurationHint() { 616 return 0; 617 } 618 619 @Override getStatusBarTransitionsStartTime()620 public long getStatusBarTransitionsStartTime() { 621 return SystemClock.uptimeMillis(); 622 } 623 624 @Override dump(PrintWriter pw, String prefix)625 public void dump(PrintWriter pw, String prefix) { 626 pw.print(prefix); pw.println("task=" + mTask); 627 if (mTarget != null) { 628 pw.print(prefix); pw.println("Target:"); 629 mTarget.dump(pw, prefix + " "); 630 } else { 631 pw.print(prefix); pw.println("Target: null"); 632 } 633 pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); 634 pw.println("mPosition=" + mPosition); 635 pw.println("mBounds=" + mBounds); 636 pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); 637 } 638 639 @Override writeToProto(ProtoOutputStream proto)640 public void writeToProto(ProtoOutputStream proto) { 641 final long token = proto.start(REMOTE); 642 if (mTarget != null) { 643 mTarget.writeToProto(proto, TARGET); 644 } 645 proto.end(token); 646 } 647 } 648 dump(PrintWriter pw, String prefix)649 public void dump(PrintWriter pw, String prefix) { 650 final String innerPrefix = prefix + " "; 651 pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":"); 652 pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart); 653 pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled); 654 pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled); 655 pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized); 656 pw.print(innerPrefix); pw.println("mTargetAppToken=" + mTargetAppToken); 657 pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper()); 658 } 659 } 660