1 /* 2 * Copyright (C) 2013 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.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT; 20 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; 22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 23 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 24 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 25 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP; 26 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 27 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 28 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 29 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 30 import static android.view.Display.DEFAULT_DISPLAY; 31 import static android.view.WindowManager.DOCKED_BOTTOM; 32 import static android.view.WindowManager.DOCKED_INVALID; 33 import static android.view.WindowManager.DOCKED_LEFT; 34 import static android.view.WindowManager.DOCKED_RIGHT; 35 import static android.view.WindowManager.DOCKED_TOP; 36 37 import static com.android.server.wm.BoundsAnimationController.FADE_IN; 38 import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS; 39 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END; 40 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START; 41 import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState; 42 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; 43 import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS; 44 import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME; 45 import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT; 46 import static com.android.server.wm.StackProto.ADJUST_IME_AMOUNT; 47 import static com.android.server.wm.StackProto.ANIMATING_BOUNDS; 48 import static com.android.server.wm.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING; 49 import static com.android.server.wm.StackProto.BOUNDS; 50 import static com.android.server.wm.StackProto.DEFER_REMOVAL; 51 import static com.android.server.wm.StackProto.FILLS_PARENT; 52 import static com.android.server.wm.StackProto.ID; 53 import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT; 54 import static com.android.server.wm.StackProto.TASKS; 55 import static com.android.server.wm.StackProto.WINDOW_CONTAINER; 56 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; 57 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; 58 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 59 60 import android.annotation.CallSuper; 61 import android.app.RemoteAction; 62 import android.content.res.Configuration; 63 import android.graphics.Point; 64 import android.graphics.Rect; 65 import android.graphics.Region; 66 import android.os.RemoteException; 67 import android.util.DisplayMetrics; 68 import android.util.EventLog; 69 import android.util.Slog; 70 import android.util.proto.ProtoOutputStream; 71 import android.view.DisplayCutout; 72 import android.view.DisplayInfo; 73 import android.view.SurfaceControl; 74 75 import com.android.internal.annotations.VisibleForTesting; 76 import com.android.internal.policy.DividerSnapAlgorithm; 77 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; 78 import com.android.internal.policy.DockedDividerUtils; 79 import com.android.server.EventLogTags; 80 81 import java.io.PrintWriter; 82 import java.util.List; 83 84 public class TaskStack extends WindowContainer<Task> implements 85 BoundsAnimationTarget, ConfigurationContainerListener { 86 /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to 87 * restrict IME adjustment so that a min portion of top stack remains visible.*/ 88 private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f; 89 90 /** Dimming amount for non-focused stack when stacks are IME-adjusted. */ 91 private static final float IME_ADJUST_DIM_AMOUNT = 0.25f; 92 93 /** Unique identifier */ 94 final int mStackId; 95 96 /** For comparison with DisplayContent bounds. */ 97 private Rect mTmpRect = new Rect(); 98 private Rect mTmpRect2 = new Rect(); 99 private Rect mTmpRect3 = new Rect(); 100 101 /** For Pinned stack controlling. */ 102 private Rect mTmpFromBounds = new Rect(); 103 private Rect mTmpToBounds = new Rect(); 104 105 /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */ 106 private final Rect mAdjustedBounds = new Rect(); 107 108 /** 109 * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they 110 * represent the state when the animation has ended. 111 */ 112 private final Rect mFullyAdjustedImeBounds = new Rect(); 113 114 private SurfaceControl mAnimationBackgroundSurface; 115 private boolean mAnimationBackgroundSurfaceIsShown = false; 116 117 /** The particular window with an Animation with non-zero background color. */ 118 private WindowStateAnimator mAnimationBackgroundAnimator; 119 120 /** Application tokens that are exiting, but still on screen for animations. */ 121 final AppTokenList mExitingAppTokens = new AppTokenList(); 122 final AppTokenList mTmpAppTokens = new AppTokenList(); 123 124 /** Detach this stack from its display when animation completes. */ 125 // TODO: maybe tie this to WindowContainer#removeChild some how... 126 boolean mDeferRemoval; 127 128 private final Rect mTmpAdjustedBounds = new Rect(); 129 private boolean mAdjustedForIme; 130 private boolean mImeGoingAway; 131 private WindowState mImeWin; 132 private float mMinimizeAmount; 133 private float mAdjustImeAmount; 134 private float mAdjustDividerAmount; 135 private final int mDockedStackMinimizeThickness; 136 137 // If this is true, we are in the bounds animating mode. The task will be down or upscaled to 138 // perfectly fit the region it would have been cropped to. We may also avoid certain logic we 139 // would otherwise apply while resizing, while resizing in the bounds animating mode. 140 private boolean mBoundsAnimating = false; 141 // Set when an animation has been requested but has not yet started from the UI thread. This is 142 // cleared when the animation actually starts. 143 private boolean mBoundsAnimatingRequested = false; 144 private boolean mBoundsAnimatingToFullscreen = false; 145 private boolean mCancelCurrentBoundsAnimation = false; 146 private Rect mBoundsAnimationTarget = new Rect(); 147 private Rect mBoundsAnimationSourceHintBounds = new Rect(); 148 private @BoundsAnimationController.AnimationType int mAnimationType; 149 150 Rect mPreAnimationBounds = new Rect(); 151 152 private Dimmer mDimmer = new Dimmer(this); 153 154 // TODO: remove after unification. 155 ActivityStack mActivityStack; 156 157 /** 158 * For {@link #prepareSurfaces}. 159 */ 160 final Rect mTmpDimBoundsRect = new Rect(); 161 private final Point mLastSurfaceSize = new Point(); 162 163 private final AnimatingAppWindowTokenRegistry mAnimatingAppWindowTokenRegistry = 164 new AnimatingAppWindowTokenRegistry(); 165 TaskStack(WindowManagerService service, int stackId, ActivityStack activityStack)166 TaskStack(WindowManagerService service, int stackId, ActivityStack activityStack) { 167 super(service); 168 mStackId = stackId; 169 mActivityStack = activityStack; 170 activityStack.registerConfigurationChangeListener(this); 171 mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize( 172 com.android.internal.R.dimen.docked_stack_minimize_thickness); 173 EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId); 174 } 175 findHomeTask()176 Task findHomeTask() { 177 if (!isActivityTypeHome() || mChildren.isEmpty()) { 178 return null; 179 } 180 return mChildren.get(mChildren.size() - 1); 181 } 182 prepareFreezingTaskBounds()183 void prepareFreezingTaskBounds() { 184 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 185 final Task task = mChildren.get(taskNdx); 186 task.prepareFreezingBounds(); 187 } 188 } 189 190 /** 191 * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from 192 * the normal task bounds. 193 * 194 * @param bounds The adjusted bounds. 195 */ setAdjustedBounds(Rect bounds)196 private void setAdjustedBounds(Rect bounds) { 197 if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) { 198 return; 199 } 200 201 mAdjustedBounds.set(bounds); 202 final boolean adjusted = !mAdjustedBounds.isEmpty(); 203 Rect insetBounds = null; 204 if (adjusted && isAdjustedForMinimizedDockedStack()) { 205 insetBounds = getRawBounds(); 206 } else if (adjusted && mAdjustedForIme) { 207 if (mImeGoingAway) { 208 insetBounds = getRawBounds(); 209 } else { 210 insetBounds = mFullyAdjustedImeBounds; 211 } 212 } 213 alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : getRawBounds(), insetBounds); 214 mDisplayContent.setLayoutNeeded(); 215 216 updateSurfaceBounds(); 217 } 218 alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds)219 private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) { 220 if (matchParentBounds()) { 221 return; 222 } 223 224 final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP; 225 226 // Update bounds of containing tasks. 227 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 228 final Task task = mChildren.get(taskNdx); 229 task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom); 230 } 231 } 232 updateAnimationBackgroundBounds()233 private void updateAnimationBackgroundBounds() { 234 if (mAnimationBackgroundSurface == null) { 235 return; 236 } 237 getRawBounds(mTmpRect); 238 final Rect stackBounds = getBounds(); 239 getPendingTransaction() 240 .setWindowCrop(mAnimationBackgroundSurface, mTmpRect.width(), mTmpRect.height()) 241 .setPosition(mAnimationBackgroundSurface, mTmpRect.left - stackBounds.left, 242 mTmpRect.top - stackBounds.top); 243 scheduleAnimation(); 244 } 245 hideAnimationSurface()246 private void hideAnimationSurface() { 247 if (mAnimationBackgroundSurface == null) { 248 return; 249 } 250 getPendingTransaction().hide(mAnimationBackgroundSurface); 251 mAnimationBackgroundSurfaceIsShown = false; 252 scheduleAnimation(); 253 } 254 showAnimationSurface(float alpha)255 private void showAnimationSurface(float alpha) { 256 if (mAnimationBackgroundSurface == null) { 257 return; 258 } 259 getPendingTransaction().setLayer(mAnimationBackgroundSurface, Integer.MIN_VALUE) 260 .setAlpha(mAnimationBackgroundSurface, alpha) 261 .show(mAnimationBackgroundSurface); 262 mAnimationBackgroundSurfaceIsShown = true; 263 scheduleAnimation(); 264 } 265 266 @Override setBounds(Rect bounds)267 public int setBounds(Rect bounds) { 268 return setBounds(getRequestedOverrideBounds(), bounds); 269 } 270 setBounds(Rect existing, Rect bounds)271 private int setBounds(Rect existing, Rect bounds) { 272 if (equivalentBounds(existing, bounds)) { 273 return BOUNDS_CHANGE_NONE; 274 } 275 276 final int result = super.setBounds(bounds); 277 278 if (getParent() != null) { 279 updateAnimationBackgroundBounds(); 280 } 281 282 updateAdjustedBounds(); 283 284 updateSurfaceBounds(); 285 return result; 286 } 287 288 /** Bounds of the stack without adjusting for other factors in the system like visibility 289 * of docked stack. 290 * Most callers should be using {@link ConfigurationContainer#getRequestedOverrideBounds} a 291 * it takes into consideration other system factors. */ getRawBounds(Rect out)292 void getRawBounds(Rect out) { 293 out.set(getRawBounds()); 294 } 295 getRawBounds()296 Rect getRawBounds() { 297 return super.getBounds(); 298 } 299 300 @Override getBounds(Rect bounds)301 public void getBounds(Rect bounds) { 302 bounds.set(getBounds()); 303 } 304 305 @Override getBounds()306 public Rect getBounds() { 307 // If we're currently adjusting for IME or minimized docked stack, we use the adjusted 308 // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked 309 // stack is visible since it is already what we want to represent to the rest of the 310 // system. 311 if (!mAdjustedBounds.isEmpty()) { 312 return mAdjustedBounds; 313 } else { 314 return super.getBounds(); 315 } 316 } 317 318 /** 319 * Sets the bounds animation target bounds ahead of an animation. This can't currently be done 320 * in onAnimationStart() since that is started on the UiThread. 321 */ setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen)322 private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, 323 boolean toFullscreen) { 324 mBoundsAnimatingRequested = true; 325 mBoundsAnimatingToFullscreen = toFullscreen; 326 if (destBounds != null) { 327 mBoundsAnimationTarget.set(destBounds); 328 } else { 329 mBoundsAnimationTarget.setEmpty(); 330 } 331 if (sourceHintBounds != null) { 332 mBoundsAnimationSourceHintBounds.set(sourceHintBounds); 333 } else if (!mBoundsAnimating) { 334 // If the bounds are already animating, we don't want to reset the source hint. This is 335 // because the source hint is sent when starting the animation from the client that 336 // requested to enter pip. Other requests can adjust the pip bounds during an animation, 337 // but could accidentally reset the source hint bounds. 338 mBoundsAnimationSourceHintBounds.setEmpty(); 339 } 340 341 mPreAnimationBounds.set(getRawBounds()); 342 } 343 344 /** 345 * @return the final bounds for the bounds animation. 346 */ getFinalAnimationBounds(Rect outBounds)347 void getFinalAnimationBounds(Rect outBounds) { 348 outBounds.set(mBoundsAnimationTarget); 349 } 350 351 /** 352 * @return the final source bounds for the bounds animation. 353 */ getFinalAnimationSourceHintBounds(Rect outBounds)354 void getFinalAnimationSourceHintBounds(Rect outBounds) { 355 outBounds.set(mBoundsAnimationSourceHintBounds); 356 } 357 358 /** 359 * @return the final animation bounds if the task stack is currently being animated, or the 360 * current stack bounds otherwise. 361 */ getAnimationOrCurrentBounds(Rect outBounds)362 void getAnimationOrCurrentBounds(Rect outBounds) { 363 if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) { 364 getFinalAnimationBounds(outBounds); 365 return; 366 } 367 getBounds(outBounds); 368 } 369 370 /** Bounds of the stack with other system factors taken into consideration. */ getDimBounds(Rect out)371 public void getDimBounds(Rect out) { 372 getBounds(out); 373 } 374 375 /** 376 * Updates the passed-in {@code inOutBounds} based on the current state of the 377 * pinned controller. This gets run *after* the override configuration is updated, so it's 378 * safe to rely on the controller's state in here (though eventually this dependence should 379 * be removed). 380 * 381 * This does NOT modify this TaskStack's configuration. However, it does, for the time-being, 382 * update pinned controller state. 383 * 384 * @param inOutBounds the bounds to update (both input and output). 385 * @return true if bounds were updated to some non-empty value. 386 */ calculatePinnedBoundsForConfigChange(Rect inOutBounds)387 boolean calculatePinnedBoundsForConfigChange(Rect inOutBounds) { 388 boolean animating = false; 389 if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) { 390 animating = true; 391 getFinalAnimationBounds(mTmpRect2); 392 } else { 393 mTmpRect2.set(inOutBounds); 394 } 395 boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged( 396 mTmpRect2, mTmpRect3); 397 if (updated) { 398 inOutBounds.set(mTmpRect3); 399 400 // The final boundary is updated while there is an existing boundary animation. Let's 401 // cancel this animation to prevent the obsolete animation overwritten updated bounds. 402 if (animating && !inOutBounds.equals(mBoundsAnimationTarget)) { 403 final DisplayContent displayContent = getDisplayContent(); 404 displayContent.mBoundsAnimationController.getHandler().post(() -> 405 displayContent.mBoundsAnimationController.cancel(this)); 406 } 407 // Once we've set the bounds based on the rotation of the old bounds in the new 408 // orientation, clear the animation target bounds since they are obsolete, and 409 // cancel any currently running animations 410 mBoundsAnimationTarget.setEmpty(); 411 mBoundsAnimationSourceHintBounds.setEmpty(); 412 mCancelCurrentBoundsAnimation = true; 413 } 414 return updated; 415 } 416 417 /** 418 * Updates the passed-in {@code inOutBounds} based on the current state of the 419 * docked controller. This gets run *after* the override configuration is updated, so it's 420 * safe to rely on the controller's state in here (though eventually this dependence should 421 * be removed). 422 * 423 * This does NOT modify this TaskStack's configuration. However, it does, for the time-being, 424 * update docked controller state. 425 * 426 * @param parentConfig the parent configuration for reference. 427 * @param inOutBounds the bounds to update (both input and output). 428 */ calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds)429 void calculateDockedBoundsForConfigChange(Configuration parentConfig, Rect inOutBounds) { 430 final boolean primary = 431 getRequestedOverrideWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 432 repositionSplitScreenStackAfterRotation(parentConfig, primary, inOutBounds); 433 final DisplayCutout cutout = mDisplayContent.getDisplayInfo().displayCutout; 434 snapDockedStackAfterRotation(parentConfig, cutout, inOutBounds); 435 if (primary) { 436 final int newDockSide = getDockSide(parentConfig, inOutBounds); 437 // Update the dock create mode and clear the dock create bounds, these 438 // might change after a rotation and the original values will be invalid. 439 mWmService.setDockedStackCreateStateLocked( 440 (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP) 441 ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT 442 : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, 443 null); 444 mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide); 445 } 446 } 447 448 /** 449 * Some primary split screen sides are not allowed by the policy. This method queries the policy 450 * and moves the primary stack around if needed. 451 * 452 * @param parentConfig the configuration of the stack's parent. 453 * @param primary true if adjusting the primary docked stack, false for secondary. 454 * @param inOutBounds the bounds of the stack to adjust. 455 */ repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary, Rect inOutBounds)456 void repositionSplitScreenStackAfterRotation(Configuration parentConfig, boolean primary, 457 Rect inOutBounds) { 458 final int dockSide = getDockSide(mDisplayContent, parentConfig, inOutBounds); 459 final int otherDockSide = DockedDividerUtils.invertDockSide(dockSide); 460 final int primaryDockSide = primary ? dockSide : otherDockSide; 461 if (mDisplayContent.getDockedDividerController() 462 .canPrimaryStackDockTo(primaryDockSide, 463 parentConfig.windowConfiguration.getBounds(), 464 parentConfig.windowConfiguration.getRotation())) { 465 return; 466 } 467 final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); 468 switch (otherDockSide) { 469 case DOCKED_LEFT: 470 int movement = inOutBounds.left; 471 inOutBounds.left -= movement; 472 inOutBounds.right -= movement; 473 break; 474 case DOCKED_RIGHT: 475 movement = parentBounds.right - inOutBounds.right; 476 inOutBounds.left += movement; 477 inOutBounds.right += movement; 478 break; 479 case DOCKED_TOP: 480 movement = inOutBounds.top; 481 inOutBounds.top -= movement; 482 inOutBounds.bottom -= movement; 483 break; 484 case DOCKED_BOTTOM: 485 movement = parentBounds.bottom - inOutBounds.bottom; 486 inOutBounds.top += movement; 487 inOutBounds.bottom += movement; 488 break; 489 } 490 } 491 492 /** 493 * Snaps the bounds after rotation to the closest snap target for the docked stack. 494 */ snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout, Rect outBounds)495 void snapDockedStackAfterRotation(Configuration parentConfig, DisplayCutout displayCutout, 496 Rect outBounds) { 497 498 // Calculate the current position. 499 final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth(); 500 final int dockSide = getDockSide(parentConfig, outBounds); 501 final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds, 502 dockSide, dividerSize); 503 final int displayWidth = parentConfig.windowConfiguration.getBounds().width(); 504 final int displayHeight = parentConfig.windowConfiguration.getBounds().height(); 505 506 // Snap the position to a target. 507 final int rotation = parentConfig.windowConfiguration.getRotation(); 508 final int orientation = parentConfig.orientation; 509 mDisplayContent.getDisplayPolicy().getStableInsetsLw(rotation, displayWidth, displayHeight, 510 displayCutout, outBounds); 511 final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm( 512 mWmService.mContext.getResources(), displayWidth, displayHeight, 513 dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds, 514 getDockSide(), isMinimizedDockAndHomeStackResizable()); 515 final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition); 516 517 // Recalculate the bounds based on the position of the target. 518 DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide, 519 outBounds, displayWidth, displayHeight, 520 dividerSize); 521 } 522 523 // TODO: Checkout the call points of this method and the ones below to see how they can fit in WC. addTask(Task task, int position)524 void addTask(Task task, int position) { 525 addTask(task, position, task.showForAllUsers(), true /* moveParents */); 526 } 527 528 /** 529 * Put a Task in this stack. Used for adding only. 530 * When task is added to top of the stack, the entire branch of the hierarchy (including stack 531 * and display) will be brought to top. 532 * @param task The task to add. 533 * @param position Target position to add the task to. 534 * @param showForAllUsers Whether to show the task regardless of the current user. 535 */ addTask(Task task, int position, boolean showForAllUsers, boolean moveParents)536 void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) { 537 final TaskStack currentStack = task.mStack; 538 // TODO: We pass stack to task's constructor, but we still need to call this method. 539 // This doesn't make sense, mStack will already be set equal to "this" at this point. 540 if (currentStack != null && currentStack.mStackId != mStackId) { 541 throw new IllegalStateException("Trying to add taskId=" + task.mTaskId 542 + " to stackId=" + mStackId 543 + ", but it is already attached to stackId=" + task.mStack.mStackId); 544 } 545 546 // Add child task. 547 task.mStack = this; 548 addChild(task, null); 549 550 // Move child to a proper position, as some restriction for position might apply. 551 positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers); 552 } 553 positionChildAt(Task child, int position)554 void positionChildAt(Task child, int position) { 555 if (DEBUG_STACK) { 556 Slog.i(TAG_WM, "positionChildAt: positioning task=" + child + " at " + position); 557 } 558 if (child == null) { 559 if (DEBUG_STACK) { 560 Slog.i(TAG_WM, "positionChildAt: could not find task=" + this); 561 } 562 return; 563 } 564 child.positionAt(position); 565 getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); 566 } 567 positionChildAtTop(Task child, boolean includingParents)568 void positionChildAtTop(Task child, boolean includingParents) { 569 if (child == null) { 570 // TODO: Fix the call-points that cause this to happen. 571 return; 572 } 573 574 positionChildAt(POSITION_TOP, child, includingParents); 575 576 final DisplayContent displayContent = getDisplayContent(); 577 if (displayContent.mAppTransition.isTransitionSet()) { 578 child.setSendingToBottom(false); 579 } 580 displayContent.layoutAndAssignWindowLayersIfNeeded(); 581 } 582 positionChildAtBottom(Task child, boolean includingParents)583 void positionChildAtBottom(Task child, boolean includingParents) { 584 if (child == null) { 585 // TODO: Fix the call-points that cause this to happen. 586 return; 587 } 588 589 positionChildAt(POSITION_BOTTOM, child, includingParents); 590 591 if (getDisplayContent().mAppTransition.isTransitionSet()) { 592 child.setSendingToBottom(true); 593 } 594 getDisplayContent().layoutAndAssignWindowLayersIfNeeded(); 595 } 596 597 @Override positionChildAt(int position, Task child, boolean includingParents)598 void positionChildAt(int position, Task child, boolean includingParents) { 599 positionChildAt(position, child, includingParents, child.showForAllUsers()); 600 } 601 602 /** 603 * Overridden version of {@link TaskStack#positionChildAt(int, Task, boolean)}. Used in 604 * {@link TaskStack#addTask(Task, int, boolean showForAllUsers, boolean)}, as it can receive 605 * showForAllUsers param from {@link AppWindowToken} instead of {@link Task#showForAllUsers()}. 606 */ positionChildAt(int position, Task child, boolean includingParents, boolean showForAllUsers)607 private void positionChildAt(int position, Task child, boolean includingParents, 608 boolean showForAllUsers) { 609 final int targetPosition = findPositionForTask(child, position, showForAllUsers, 610 false /* addingNew */); 611 super.positionChildAt(targetPosition, child, includingParents); 612 613 // Log positioning. 614 if (DEBUG_TASK_MOVEMENT) 615 Slog.d(TAG_WM, "positionTask: task=" + this + " position=" + position); 616 617 final int toTop = targetPosition == mChildren.size() - 1 ? 1 : 0; 618 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop, targetPosition); 619 } 620 reparent(int displayId, Rect outStackBounds, boolean onTop)621 void reparent(int displayId, Rect outStackBounds, boolean onTop) { 622 final DisplayContent targetDc = mWmService.mRoot.getDisplayContent(displayId); 623 if (targetDc == null) { 624 throw new IllegalArgumentException("Trying to move stackId=" + mStackId 625 + " to unknown displayId=" + displayId); 626 } 627 628 targetDc.moveStackToDisplay(this, onTop); 629 if (matchParentBounds()) { 630 outStackBounds.setEmpty(); 631 } else { 632 getRawBounds(outStackBounds); 633 } 634 } 635 636 // TODO: We should really have users as a window container in the hierarchy so that we don't 637 // have to do complicated things like we are doing in this method. findPositionForTask(Task task, int targetPosition, boolean showForAllUsers, boolean addingNew)638 private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers, 639 boolean addingNew) { 640 final boolean canShowTask = 641 showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId); 642 643 final int stackSize = mChildren.size(); 644 int minPosition = 0; 645 int maxPosition = addingNew ? stackSize : stackSize - 1; 646 647 if (canShowTask) { 648 minPosition = computeMinPosition(minPosition, stackSize); 649 } else { 650 maxPosition = computeMaxPosition(maxPosition); 651 } 652 653 // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid. 654 if (targetPosition == POSITION_BOTTOM && minPosition == 0) { 655 return POSITION_BOTTOM; 656 } else if (targetPosition == POSITION_TOP 657 && maxPosition == (addingNew ? stackSize : stackSize - 1)) { 658 return POSITION_TOP; 659 } 660 // Reset position based on minimum/maximum possible positions. 661 return Math.min(Math.max(targetPosition, minPosition), maxPosition); 662 } 663 664 /** Calculate the minimum possible position for a task that can be shown to the user. 665 * The minimum position will be above all other tasks that can't be shown. 666 * @param minPosition The minimum position the caller is suggesting. 667 * We will start adjusting up from here. 668 * @param size The size of the current task list. 669 */ computeMinPosition(int minPosition, int size)670 private int computeMinPosition(int minPosition, int size) { 671 while (minPosition < size) { 672 final Task tmpTask = mChildren.get(minPosition); 673 final boolean canShowTmpTask = 674 tmpTask.showForAllUsers() 675 || mWmService.isCurrentProfileLocked(tmpTask.mUserId); 676 if (canShowTmpTask) { 677 break; 678 } 679 minPosition++; 680 } 681 return minPosition; 682 } 683 684 /** Calculate the maximum possible position for a task that can't be shown to the user. 685 * The maximum position will be below all other tasks that can be shown. 686 * @param maxPosition The maximum position the caller is suggesting. 687 * We will start adjusting down from here. 688 */ computeMaxPosition(int maxPosition)689 private int computeMaxPosition(int maxPosition) { 690 while (maxPosition > 0) { 691 final Task tmpTask = mChildren.get(maxPosition); 692 final boolean canShowTmpTask = 693 tmpTask.showForAllUsers() 694 || mWmService.isCurrentProfileLocked(tmpTask.mUserId); 695 if (!canShowTmpTask) { 696 break; 697 } 698 maxPosition--; 699 } 700 return maxPosition; 701 } 702 703 /** 704 * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the 705 * back. 706 * @param task The Task to delete. 707 */ 708 @Override removeChild(Task task)709 void removeChild(Task task) { 710 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeChild: task=" + task); 711 712 super.removeChild(task); 713 task.mStack = null; 714 715 if (mDisplayContent != null) { 716 if (mChildren.isEmpty()) { 717 getParent().positionChildAt(POSITION_BOTTOM, this, false /* includingParents */); 718 } 719 mDisplayContent.setLayoutNeeded(); 720 } 721 for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { 722 final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); 723 if (wtoken.getTask() == task) { 724 wtoken.mIsExiting = false; 725 mExitingAppTokens.remove(appNdx); 726 } 727 } 728 } 729 730 @Override onConfigurationChanged(Configuration newParentConfig)731 public void onConfigurationChanged(Configuration newParentConfig) { 732 final int prevWindowingMode = getWindowingMode(); 733 super.onConfigurationChanged(newParentConfig); 734 735 // Only need to update surface size here since the super method will handle updating 736 // surface position. 737 updateSurfaceSize(getPendingTransaction()); 738 final int windowingMode = getWindowingMode(); 739 final boolean isAlwaysOnTop = isAlwaysOnTop(); 740 741 if (mDisplayContent == null) { 742 return; 743 } 744 745 if (prevWindowingMode != windowingMode) { 746 mDisplayContent.onStackWindowingModeChanged(this); 747 748 if (inSplitScreenSecondaryWindowingMode()) { 749 // When the stack is resized due to entering split screen secondary, offset the 750 // windows to compensate for the new stack position. 751 forAllWindows(w -> { 752 w.mWinAnimator.setOffsetPositionForStackResize(true); 753 }, true); 754 } 755 } 756 } 757 updateSurfaceBounds()758 private void updateSurfaceBounds() { 759 updateSurfaceSize(getPendingTransaction()); 760 updateSurfacePosition(); 761 scheduleAnimation(); 762 } 763 764 /** 765 * Calculate an amount by which to expand the stack bounds in each direction. 766 * Used to make room for shadows in the pinned windowing mode. 767 */ getStackOutset()768 int getStackOutset() { 769 DisplayContent displayContent = getDisplayContent(); 770 if (inPinnedWindowingMode() && displayContent != null) { 771 final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics(); 772 773 // We multiply by two to match the client logic for converting view elevation 774 // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets} 775 return (int)Math.ceil(mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP, 776 displayMetrics) * 2); 777 } 778 return 0; 779 } 780 781 @Override getRelativeDisplayedPosition(Point outPos)782 void getRelativeDisplayedPosition(Point outPos) { 783 super.getRelativeDisplayedPosition(outPos); 784 final int outset = getStackOutset(); 785 outPos.x -= outset; 786 outPos.y -= outset; 787 } 788 updateSurfaceSize(SurfaceControl.Transaction transaction)789 private void updateSurfaceSize(SurfaceControl.Transaction transaction) { 790 if (mSurfaceControl == null) { 791 return; 792 } 793 794 final Rect stackBounds = getDisplayedBounds(); 795 int width = stackBounds.width(); 796 int height = stackBounds.height(); 797 798 final int outset = getStackOutset(); 799 width += 2*outset; 800 height += 2*outset; 801 802 if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) { 803 return; 804 } 805 if (getWindowConfiguration().tasksAreFloating()) { 806 // Don't crop freeform windows to the stack. 807 transaction.setWindowCrop(mSurfaceControl, -1, -1); 808 } else { 809 transaction.setWindowCrop(mSurfaceControl, width, height); 810 } 811 mLastSurfaceSize.set(width, height); 812 } 813 814 @VisibleForTesting getLastSurfaceSize()815 Point getLastSurfaceSize() { 816 return mLastSurfaceSize; 817 } 818 819 @Override onDisplayChanged(DisplayContent dc)820 void onDisplayChanged(DisplayContent dc) { 821 if (mDisplayContent != null && mDisplayContent != dc) { 822 throw new IllegalStateException("onDisplayChanged: Already attached"); 823 } 824 825 super.onDisplayChanged(dc); 826 827 updateSurfaceBounds(); 828 if (mAnimationBackgroundSurface == null) { 829 mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer() 830 .setName("animation background stackId=" + mStackId) 831 .build(); 832 } 833 } 834 835 /** 836 * Determines the stack and task bounds of the other stack when in docked mode. The current task 837 * bounds is passed in but depending on the stack, the task and stack must match. Only in 838 * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds 839 * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds 840 * is calculated and is also used for its task bounds. 841 * If any of the out bounds are empty, it represents default bounds 842 * 843 * @param currentTempTaskBounds the current task bounds of the other stack 844 * @param outStackBounds the calculated stack bounds of the other stack 845 * @param outTempTaskBounds the calculated task bounds of the other stack 846 */ getStackDockedModeBoundsLocked(Configuration parentConfig, Rect dockedBounds, Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds)847 void getStackDockedModeBoundsLocked(Configuration parentConfig, Rect dockedBounds, 848 Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds) { 849 outTempTaskBounds.setEmpty(); 850 851 if (dockedBounds == null || dockedBounds.isEmpty()) { 852 // Calculate the primary docked bounds. 853 final boolean dockedOnTopOrLeft = mWmService.mDockedStackCreateMode 854 == SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT; 855 getStackDockedModeBounds(parentConfig, 856 true /* primary */, outStackBounds, dockedBounds, 857 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); 858 return; 859 } 860 final int dockedSide = getDockSide(parentConfig, dockedBounds); 861 862 // When the home stack is resizable, should always have the same stack and task bounds 863 if (isActivityTypeHome()) { 864 final Task homeTask = findHomeTask(); 865 if (homeTask != null && homeTask.isResizeable()) { 866 // Calculate the home stack bounds when in docked mode and the home stack is 867 // resizeable. 868 getDisplayContent().mDividerControllerLocked 869 .getHomeStackBoundsInDockedMode(parentConfig, 870 dockedSide, outStackBounds); 871 } else { 872 // Home stack isn't resizeable, so don't specify stack bounds. 873 outStackBounds.setEmpty(); 874 } 875 876 outTempTaskBounds.set(outStackBounds); 877 return; 878 } 879 880 // When minimized state, the stack bounds for all non-home and docked stack bounds should 881 // match the passed task bounds 882 if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) { 883 outStackBounds.set(currentTempTaskBounds); 884 return; 885 } 886 887 if (dockedSide == DOCKED_INVALID) { 888 // Not sure how you got here...Only thing we can do is return current bounds. 889 Slog.e(TAG_WM, "Failed to get valid docked side for docked stack"); 890 outStackBounds.set(getRawBounds()); 891 return; 892 } 893 894 final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT; 895 getStackDockedModeBounds(parentConfig, 896 false /* primary */, outStackBounds, dockedBounds, 897 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); 898 } 899 900 /** 901 * Outputs the bounds a stack should be given the presence of a docked stack on the display. 902 * @param parentConfig The parent configuration. 903 * @param primary {@code true} if getting the primary stack bounds. 904 * @param outBounds Output bounds that should be used for the stack. 905 * @param dockedBounds Bounds of the docked stack. 906 * @param dockDividerWidth We need to know the width of the divider make to the output bounds 907 * close to the side of the dock. 908 * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen. 909 */ getStackDockedModeBounds(Configuration parentConfig, boolean primary, Rect outBounds, Rect dockedBounds, int dockDividerWidth, boolean dockOnTopOrLeft)910 private void getStackDockedModeBounds(Configuration parentConfig, boolean primary, 911 Rect outBounds, Rect dockedBounds, int dockDividerWidth, 912 boolean dockOnTopOrLeft) { 913 final Rect displayRect = parentConfig.windowConfiguration.getBounds(); 914 final boolean splitHorizontally = displayRect.width() > displayRect.height(); 915 916 outBounds.set(displayRect); 917 if (primary) { 918 if (mWmService.mDockedStackCreateBounds != null) { 919 outBounds.set(mWmService.mDockedStackCreateBounds); 920 return; 921 } 922 923 // The initial bounds of the docked stack when it is created about half the screen space 924 // and its bounds can be adjusted after that. The bounds of all other stacks are 925 // adjusted to occupy whatever screen space the docked stack isn't occupying. 926 final DisplayCutout displayCutout = mDisplayContent.getDisplayInfo().displayCutout; 927 mDisplayContent.getDisplayPolicy().getStableInsetsLw( 928 parentConfig.windowConfiguration.getRotation(), 929 displayRect.width(), displayRect.height(), displayCutout, mTmpRect2); 930 final int position = new DividerSnapAlgorithm(mWmService.mContext.getResources(), 931 displayRect.width(), 932 displayRect.height(), 933 dockDividerWidth, 934 parentConfig.orientation == ORIENTATION_PORTRAIT, 935 mTmpRect2).getMiddleTarget().position; 936 937 if (dockOnTopOrLeft) { 938 if (splitHorizontally) { 939 outBounds.right = position; 940 } else { 941 outBounds.bottom = position; 942 } 943 } else { 944 if (splitHorizontally) { 945 outBounds.left = position + dockDividerWidth; 946 } else { 947 outBounds.top = position + dockDividerWidth; 948 } 949 } 950 return; 951 } 952 953 // Other stacks occupy whatever space is left by the docked stack. 954 if (!dockOnTopOrLeft) { 955 if (splitHorizontally) { 956 outBounds.right = dockedBounds.left - dockDividerWidth; 957 } else { 958 outBounds.bottom = dockedBounds.top - dockDividerWidth; 959 } 960 } else { 961 if (splitHorizontally) { 962 outBounds.left = dockedBounds.right + dockDividerWidth; 963 } else { 964 outBounds.top = dockedBounds.bottom + dockDividerWidth; 965 } 966 } 967 DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft); 968 } 969 resetDockedStackToMiddle()970 void resetDockedStackToMiddle() { 971 if (inSplitScreenPrimaryWindowingMode()) { 972 throw new IllegalStateException("Not a docked stack=" + this); 973 } 974 975 mWmService.mDockedStackCreateBounds = null; 976 977 final Rect bounds = new Rect(); 978 final Rect tempBounds = new Rect(); 979 TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); 980 Rect dockedBounds = 981 (dockedStack == null || dockedStack == this) ? null : dockedStack.getRawBounds(); 982 getStackDockedModeBoundsLocked(mDisplayContent.getConfiguration(), dockedBounds, 983 null /* currentTempTaskBounds */, bounds, tempBounds); 984 mActivityStack.requestResize(bounds); 985 } 986 987 @Override removeIfPossible()988 void removeIfPossible() { 989 if (isSelfOrChildAnimating()) { 990 mDeferRemoval = true; 991 return; 992 } 993 removeImmediately(); 994 } 995 996 @Override removeImmediately()997 void removeImmediately() { 998 if (mActivityStack != null) { 999 mActivityStack.unregisterConfigurationChangeListener(this); 1000 } 1001 super.removeImmediately(); 1002 } 1003 1004 @Override onParentChanged()1005 void onParentChanged() { 1006 super.onParentChanged(); 1007 1008 if (getParent() != null || mDisplayContent == null) { 1009 return; 1010 } 1011 1012 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 1013 1014 if (mAnimationBackgroundSurface != null) { 1015 mAnimationBackgroundSurface.remove(); 1016 mAnimationBackgroundSurface = null; 1017 } 1018 1019 mDisplayContent = null; 1020 mWmService.mWindowPlacerLocked.requestTraversal(); 1021 } 1022 resetAnimationBackgroundAnimator()1023 void resetAnimationBackgroundAnimator() { 1024 mAnimationBackgroundAnimator = null; 1025 hideAnimationSurface(); 1026 } 1027 setAnimationBackground(WindowStateAnimator winAnimator, int color)1028 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 1029 if (mAnimationBackgroundAnimator == null) { 1030 mAnimationBackgroundAnimator = winAnimator; 1031 showAnimationSurface(((color >> 24) & 0xff) / 255f); 1032 } 1033 } 1034 1035 // TODO: Should each user have there own stacks? 1036 @Override switchUser()1037 void switchUser() { 1038 super.switchUser(); 1039 int top = mChildren.size(); 1040 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 1041 Task task = mChildren.get(taskNdx); 1042 if (mWmService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) { 1043 mChildren.remove(taskNdx); 1044 mChildren.add(task); 1045 --top; 1046 } 1047 } 1048 } 1049 1050 /** 1051 * Adjusts the stack bounds if the IME is visible. 1052 * 1053 * @param imeWin The IME window. 1054 * @param keepLastAmount Use {@code true} to keep the last adjusted amount from 1055 * {@link DockedStackDividerController} for adjusting the stack bounds, 1056 * Use {@code false} to reset adjusted amount as 0. 1057 * @see #updateAdjustForIme(float, float, boolean) 1058 */ setAdjustedForIme(WindowState imeWin, boolean keepLastAmount)1059 void setAdjustedForIme(WindowState imeWin, boolean keepLastAmount) { 1060 mImeWin = imeWin; 1061 mImeGoingAway = false; 1062 if (!mAdjustedForIme || keepLastAmount) { 1063 mAdjustedForIme = true; 1064 DockedStackDividerController controller = getDisplayContent().mDividerControllerLocked; 1065 final float adjustImeAmount = keepLastAmount ? controller.mLastAnimationProgress : 0f; 1066 final float adjustDividerAmount = keepLastAmount ? controller.mLastDividerProgress : 0f; 1067 updateAdjustForIme(adjustImeAmount, adjustDividerAmount, true /* force */); 1068 } 1069 } 1070 isAdjustedForIme()1071 boolean isAdjustedForIme() { 1072 return mAdjustedForIme; 1073 } 1074 isAnimatingForIme()1075 boolean isAnimatingForIme() { 1076 return mImeWin != null && mImeWin.isAnimatingLw(); 1077 } 1078 1079 /** 1080 * Update the stack's bounds (crop or position) according to the IME window's 1081 * current position. When IME window is animated, the bottom stack is animated 1082 * together to track the IME window's current position, and the top stack is 1083 * cropped as necessary. 1084 * 1085 * @return true if a traversal should be performed after the adjustment. 1086 */ updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force)1087 boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) { 1088 if (adjustAmount != mAdjustImeAmount 1089 || adjustDividerAmount != mAdjustDividerAmount || force) { 1090 mAdjustImeAmount = adjustAmount; 1091 mAdjustDividerAmount = adjustDividerAmount; 1092 updateAdjustedBounds(); 1093 return isVisible(); 1094 } else { 1095 return false; 1096 } 1097 } 1098 1099 /** 1100 * Resets the adjustment after it got adjusted for the IME. 1101 * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about 1102 * animations; otherwise, set flag and animates the window away together 1103 * with IME window. 1104 */ resetAdjustedForIme(boolean adjustBoundsNow)1105 void resetAdjustedForIme(boolean adjustBoundsNow) { 1106 if (adjustBoundsNow) { 1107 mImeWin = null; 1108 mImeGoingAway = false; 1109 mAdjustImeAmount = 0f; 1110 mAdjustDividerAmount = 0f; 1111 if (!mAdjustedForIme) { 1112 return; 1113 } 1114 mAdjustedForIme = false; 1115 updateAdjustedBounds(); 1116 mWmService.setResizeDimLayer(false, getWindowingMode(), 1.0f); 1117 } else { 1118 mImeGoingAway |= mAdjustedForIme; 1119 } 1120 } 1121 1122 /** 1123 * Sets the amount how much we currently minimize our stack. 1124 * 1125 * @param minimizeAmount The amount, between 0 and 1. 1126 * @return Whether the amount has changed and a layout is needed. 1127 */ setAdjustedForMinimizedDock(float minimizeAmount)1128 boolean setAdjustedForMinimizedDock(float minimizeAmount) { 1129 if (minimizeAmount != mMinimizeAmount) { 1130 mMinimizeAmount = minimizeAmount; 1131 updateAdjustedBounds(); 1132 return isVisible(); 1133 } else { 1134 return false; 1135 } 1136 } 1137 shouldIgnoreInput()1138 boolean shouldIgnoreInput() { 1139 return isAdjustedForMinimizedDockedStack() || 1140 (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable()); 1141 } 1142 1143 /** 1144 * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows 1145 * to the list of to be drawn windows the service is waiting for. 1146 */ beginImeAdjustAnimation()1147 void beginImeAdjustAnimation() { 1148 for (int j = mChildren.size() - 1; j >= 0; j--) { 1149 final Task task = mChildren.get(j); 1150 if (task.hasContentToDisplay()) { 1151 task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER); 1152 task.setWaitingForDrawnIfResizingChanged(); 1153 } 1154 } 1155 } 1156 1157 /** 1158 * Resets the resizing state of all windows. 1159 */ endImeAdjustAnimation()1160 void endImeAdjustAnimation() { 1161 for (int j = mChildren.size() - 1; j >= 0; j--) { 1162 mChildren.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER); 1163 } 1164 } 1165 getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom)1166 int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) { 1167 return displayContentRect.top + (int) 1168 ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN); 1169 } 1170 adjustForIME(final WindowState imeWin)1171 private boolean adjustForIME(final WindowState imeWin) { 1172 // To prevent task stack resize animation may flicking when playing app transition 1173 // animation & IME window enter animation in parallel, we need to make sure app 1174 // transition is done and then adjust task size for IME, skip the new adjusted frame when 1175 // app transition is still running. 1176 if (getDisplayContent().mAppTransition.isRunning()) { 1177 return false; 1178 } 1179 1180 final int dockedSide = getDockSide(); 1181 final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; 1182 if (imeWin == null || !dockedTopOrBottom) { 1183 return false; 1184 } 1185 1186 final Rect displayStableRect = mTmpRect; 1187 final Rect contentBounds = mTmpRect2; 1188 1189 // Calculate the content bounds excluding the area occupied by IME 1190 getDisplayContent().getStableRect(displayStableRect); 1191 contentBounds.set(displayStableRect); 1192 int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top); 1193 1194 imeTop += imeWin.getGivenContentInsetsLw().top; 1195 if (contentBounds.bottom > imeTop) { 1196 contentBounds.bottom = imeTop; 1197 } 1198 1199 final int yOffset = displayStableRect.bottom - contentBounds.bottom; 1200 1201 final int dividerWidth = 1202 getDisplayContent().mDividerControllerLocked.getContentWidth(); 1203 final int dividerWidthInactive = 1204 getDisplayContent().mDividerControllerLocked.getContentWidthInactive(); 1205 1206 if (dockedSide == DOCKED_TOP) { 1207 // If this stack is docked on top, we make it smaller so the bottom stack is not 1208 // occluded by IME. We shift its bottom up by the height of the IME, but 1209 // leaves at least 30% of the top stack visible. 1210 final int minTopStackBottom = 1211 getMinTopStackBottom(displayStableRect, getRawBounds().bottom); 1212 final int bottom = Math.max( 1213 getRawBounds().bottom - yOffset + dividerWidth - dividerWidthInactive, 1214 minTopStackBottom); 1215 mTmpAdjustedBounds.set(getRawBounds()); 1216 mTmpAdjustedBounds.bottom = (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) 1217 * getRawBounds().bottom); 1218 mFullyAdjustedImeBounds.set(getRawBounds()); 1219 } else { 1220 // When the stack is on bottom and has no focus, it's only adjusted for divider width. 1221 final int dividerWidthDelta = dividerWidthInactive - dividerWidth; 1222 1223 // When the stack is on bottom and has focus, it needs to be moved up so as to 1224 // not occluded by IME, and at the same time adjusted for divider width. 1225 // We try to move it up by the height of the IME window, but only to the extent 1226 // that leaves at least 30% of the top stack visible. 1227 // 'top' is where the top of bottom stack will move to in this case. 1228 final int topBeforeImeAdjust = 1229 getRawBounds().top - dividerWidth + dividerWidthInactive; 1230 final int minTopStackBottom = 1231 getMinTopStackBottom(displayStableRect, 1232 getRawBounds().top - dividerWidth); 1233 final int top = Math.max( 1234 getRawBounds().top - yOffset, minTopStackBottom + dividerWidthInactive); 1235 1236 mTmpAdjustedBounds.set(getRawBounds()); 1237 // Account for the adjustment for IME and divider width separately. 1238 // (top - topBeforeImeAdjust) is the amount of movement due to IME only, 1239 // and dividerWidthDelta is due to divider width change only. 1240 mTmpAdjustedBounds.top = getRawBounds().top + 1241 (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) + 1242 mAdjustDividerAmount * dividerWidthDelta); 1243 mFullyAdjustedImeBounds.set(getRawBounds()); 1244 mFullyAdjustedImeBounds.top = top; 1245 mFullyAdjustedImeBounds.bottom = top + getRawBounds().height(); 1246 } 1247 return true; 1248 } 1249 adjustForMinimizedDockedStack(float minimizeAmount)1250 private boolean adjustForMinimizedDockedStack(float minimizeAmount) { 1251 final int dockSide = getDockSide(); 1252 if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) { 1253 return false; 1254 } 1255 1256 if (dockSide == DOCKED_TOP) { 1257 mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); 1258 int topInset = mTmpRect.top; 1259 mTmpAdjustedBounds.set(getRawBounds()); 1260 mTmpAdjustedBounds.bottom = (int) (minimizeAmount * topInset + (1 - minimizeAmount) 1261 * getRawBounds().bottom); 1262 } else if (dockSide == DOCKED_LEFT) { 1263 mTmpAdjustedBounds.set(getRawBounds()); 1264 final int width = getRawBounds().width(); 1265 mTmpAdjustedBounds.right = 1266 (int) (minimizeAmount * mDockedStackMinimizeThickness 1267 + (1 - minimizeAmount) * getRawBounds().right); 1268 mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width; 1269 } else if (dockSide == DOCKED_RIGHT) { 1270 mTmpAdjustedBounds.set(getRawBounds()); 1271 mTmpAdjustedBounds.left = (int) (minimizeAmount * 1272 (getRawBounds().right - mDockedStackMinimizeThickness) 1273 + (1 - minimizeAmount) * getRawBounds().left); 1274 } 1275 return true; 1276 } 1277 isMinimizedDockAndHomeStackResizable()1278 private boolean isMinimizedDockAndHomeStackResizable() { 1279 return mDisplayContent.mDividerControllerLocked.isMinimizedDock() 1280 && mDisplayContent.mDividerControllerLocked.isHomeStackResizable(); 1281 } 1282 1283 /** 1284 * @return the distance in pixels how much the stack gets minimized from it's original size 1285 */ getMinimizeDistance()1286 int getMinimizeDistance() { 1287 final int dockSide = getDockSide(); 1288 if (dockSide == DOCKED_INVALID) { 1289 return 0; 1290 } 1291 1292 if (dockSide == DOCKED_TOP) { 1293 mWmService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); 1294 int topInset = mTmpRect.top; 1295 return getRawBounds().bottom - topInset; 1296 } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) { 1297 return getRawBounds().width() - mDockedStackMinimizeThickness; 1298 } else { 1299 return 0; 1300 } 1301 } 1302 1303 /** 1304 * Updates the adjustment depending on it's current state. 1305 */ updateAdjustedBounds()1306 private void updateAdjustedBounds() { 1307 boolean adjust = false; 1308 if (mMinimizeAmount != 0f) { 1309 adjust = adjustForMinimizedDockedStack(mMinimizeAmount); 1310 } else if (mAdjustedForIme) { 1311 adjust = adjustForIME(mImeWin); 1312 } 1313 if (!adjust) { 1314 mTmpAdjustedBounds.setEmpty(); 1315 } 1316 setAdjustedBounds(mTmpAdjustedBounds); 1317 1318 final boolean isImeTarget = (mWmService.getImeFocusStackLocked() == this); 1319 if (mAdjustedForIme && adjust && !isImeTarget) { 1320 final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount) 1321 * IME_ADJUST_DIM_AMOUNT; 1322 mWmService.setResizeDimLayer(true, getWindowingMode(), alpha); 1323 } 1324 } 1325 applyAdjustForImeIfNeeded(Task task)1326 void applyAdjustForImeIfNeeded(Task task) { 1327 if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) { 1328 return; 1329 } 1330 1331 final Rect insetBounds = mImeGoingAway ? getRawBounds() : mFullyAdjustedImeBounds; 1332 task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP); 1333 mDisplayContent.setLayoutNeeded(); 1334 } 1335 1336 isAdjustedForMinimizedDockedStack()1337 boolean isAdjustedForMinimizedDockedStack() { 1338 return mMinimizeAmount != 0f; 1339 } 1340 1341 /** 1342 * @return {@code true} if we have a {@link Task} that is animating (currently only used for the 1343 * recents animation); {@code false} otherwise. 1344 */ isTaskAnimating()1345 boolean isTaskAnimating() { 1346 for (int j = mChildren.size() - 1; j >= 0; j--) { 1347 final Task task = mChildren.get(j); 1348 if (task.isTaskAnimating()) { 1349 return true; 1350 } 1351 } 1352 return false; 1353 } 1354 1355 @CallSuper 1356 @Override writeToProto(ProtoOutputStream proto, long fieldId, @WindowTraceLogLevel int logLevel)1357 public void writeToProto(ProtoOutputStream proto, long fieldId, 1358 @WindowTraceLogLevel int logLevel) { 1359 if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) { 1360 return; 1361 } 1362 1363 final long token = proto.start(fieldId); 1364 super.writeToProto(proto, WINDOW_CONTAINER, logLevel); 1365 proto.write(ID, mStackId); 1366 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) { 1367 mChildren.get(taskNdx).writeToProto(proto, TASKS, logLevel); 1368 } 1369 proto.write(FILLS_PARENT, matchParentBounds()); 1370 getRawBounds().writeToProto(proto, BOUNDS); 1371 proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown); 1372 proto.write(DEFER_REMOVAL, mDeferRemoval); 1373 proto.write(MINIMIZE_AMOUNT, mMinimizeAmount); 1374 proto.write(ADJUSTED_FOR_IME, mAdjustedForIme); 1375 proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount); 1376 proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount); 1377 mAdjustedBounds.writeToProto(proto, ADJUSTED_BOUNDS); 1378 proto.write(ANIMATING_BOUNDS, mBoundsAnimating); 1379 proto.end(token); 1380 } 1381 1382 @Override dump(PrintWriter pw, String prefix, boolean dumpAll)1383 void dump(PrintWriter pw, String prefix, boolean dumpAll) { 1384 pw.println(prefix + "mStackId=" + mStackId); 1385 pw.println(prefix + "mDeferRemoval=" + mDeferRemoval); 1386 pw.println(prefix + "mBounds=" + getRawBounds().toShortString()); 1387 if (mMinimizeAmount != 0f) { 1388 pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount); 1389 } 1390 if (mAdjustedForIme) { 1391 pw.println(prefix + "mAdjustedForIme=true"); 1392 pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount); 1393 pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount); 1394 } 1395 if (!mAdjustedBounds.isEmpty()) { 1396 pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString()); 1397 } 1398 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) { 1399 mChildren.get(taskNdx).dump(pw, prefix + " ", dumpAll); 1400 } 1401 if (mAnimationBackgroundSurfaceIsShown) { 1402 pw.println(prefix + "mWindowAnimationBackgroundSurface is shown"); 1403 } 1404 if (!mExitingAppTokens.isEmpty()) { 1405 pw.println(); 1406 pw.println(" Exiting application tokens:"); 1407 for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) { 1408 WindowToken token = mExitingAppTokens.get(i); 1409 pw.print(" Exiting App #"); pw.print(i); 1410 pw.print(' '); pw.print(token); 1411 pw.println(':'); 1412 token.dump(pw, " ", dumpAll); 1413 } 1414 } 1415 mAnimatingAppWindowTokenRegistry.dump(pw, "AnimatingApps:", prefix); 1416 } 1417 1418 @Override fillsParent()1419 boolean fillsParent() { 1420 return matchParentBounds(); 1421 } 1422 1423 @Override toString()1424 public String toString() { 1425 return "{stackId=" + mStackId + " tasks=" + mChildren + "}"; 1426 } 1427 getName()1428 String getName() { 1429 return toShortString(); 1430 } 1431 toShortString()1432 public String toShortString() { 1433 return "Stack=" + mStackId; 1434 } 1435 1436 /** 1437 * For docked workspace (or workspace that's side-by-side to the docked), provides 1438 * information which side of the screen was the dock anchored. 1439 */ getDockSide()1440 int getDockSide() { 1441 return getDockSide(mDisplayContent.getConfiguration(), getRawBounds()); 1442 } 1443 getDockSideForDisplay(DisplayContent dc)1444 int getDockSideForDisplay(DisplayContent dc) { 1445 return getDockSide(dc, dc.getConfiguration(), getRawBounds()); 1446 } 1447 getDockSide(Configuration parentConfig, Rect bounds)1448 int getDockSide(Configuration parentConfig, Rect bounds) { 1449 if (mDisplayContent == null) { 1450 return DOCKED_INVALID; 1451 } 1452 return getDockSide(mDisplayContent, parentConfig, bounds); 1453 } 1454 getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds)1455 private int getDockSide(DisplayContent dc, Configuration parentConfig, Rect bounds) { 1456 return dc.getDockedDividerController().getDockSide(bounds, 1457 parentConfig.windowConfiguration.getBounds(), 1458 parentConfig.orientation, parentConfig.windowConfiguration.getRotation()); 1459 } 1460 hasTaskForUser(int userId)1461 boolean hasTaskForUser(int userId) { 1462 for (int i = mChildren.size() - 1; i >= 0; i--) { 1463 final Task task = mChildren.get(i); 1464 if (task.mUserId == userId) { 1465 return true; 1466 } 1467 } 1468 return false; 1469 } 1470 findTaskForResizePoint(int x, int y, int delta, DisplayContent.TaskForResizePointSearchResult results)1471 void findTaskForResizePoint(int x, int y, int delta, 1472 DisplayContent.TaskForResizePointSearchResult results) { 1473 if (!getWindowConfiguration().canResizeTask()) { 1474 results.searchDone = true; 1475 return; 1476 } 1477 1478 for (int i = mChildren.size() - 1; i >= 0; --i) { 1479 final Task task = mChildren.get(i); 1480 if (task.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { 1481 results.searchDone = true; 1482 return; 1483 } 1484 1485 // We need to use the task's dim bounds (which is derived from the visible bounds of 1486 // its apps windows) for any touch-related tests. Can't use the task's original 1487 // bounds because it might be adjusted to fit the content frame. One example is when 1488 // the task is put to top-left quadrant, the actual visible area would not start at 1489 // (0,0) after it's adjusted for the status bar. 1490 task.getDimBounds(mTmpRect); 1491 mTmpRect.inset(-delta, -delta); 1492 if (mTmpRect.contains(x, y)) { 1493 mTmpRect.inset(delta, delta); 1494 1495 results.searchDone = true; 1496 1497 if (!mTmpRect.contains(x, y)) { 1498 results.taskForResize = task; 1499 return; 1500 } 1501 // User touched inside the task. No need to look further, 1502 // focus transfer will be handled in ACTION_UP. 1503 return; 1504 } 1505 } 1506 } 1507 setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion, Rect contentRect, Rect postExclude)1508 void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion, 1509 Rect contentRect, Rect postExclude) { 1510 for (int i = mChildren.size() - 1; i >= 0; --i) { 1511 final Task task = mChildren.get(i); 1512 AppWindowToken token = task.getTopVisibleAppToken(); 1513 if (token == null || !token.hasContentToDisplay()) { 1514 continue; 1515 } 1516 1517 /** 1518 * Exclusion region is the region that TapDetector doesn't care about. 1519 * Here we want to remove all non-focused tasks from the exclusion region. 1520 * We also remove the outside touch area for resizing for all freeform 1521 * tasks (including the focused). 1522 * 1523 * We save the focused task region once we find it, and add it back at the end. 1524 * 1525 * If the task is home stack and it is resizable in the minimized state, we want to 1526 * exclude the docked stack from touch so we need the entire screen area and not just a 1527 * small portion which the home stack currently is resized to. 1528 */ 1529 1530 if (task.isActivityTypeHome() && isMinimizedDockAndHomeStackResizable()) { 1531 mDisplayContent.getBounds(mTmpRect); 1532 } else { 1533 task.getDimBounds(mTmpRect); 1534 } 1535 1536 if (task == focusedTask) { 1537 // Add the focused task rect back into the exclude region once we are done 1538 // processing stacks. 1539 postExclude.set(mTmpRect); 1540 } 1541 1542 final boolean isFreeformed = task.inFreeformWindowingMode(); 1543 if (task != focusedTask || isFreeformed) { 1544 if (isFreeformed) { 1545 // If the task is freeformed, enlarge the area to account for outside 1546 // touch area for resize. 1547 mTmpRect.inset(-delta, -delta); 1548 // Intersect with display content rect. If we have system decor (status bar/ 1549 // navigation bar), we want to exclude that from the tap detection. 1550 // Otherwise, if the app is partially placed under some system button (eg. 1551 // Recents, Home), pressing that button would cause a full series of 1552 // unwanted transfer focus/resume/pause, before we could go home. 1553 mTmpRect.intersect(contentRect); 1554 } 1555 touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE); 1556 } 1557 } 1558 } 1559 setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds)1560 public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) { 1561 // Hold the lock since this is called from the BoundsAnimator running on the UiThread 1562 synchronized (mWmService.mGlobalLock) { 1563 if (mCancelCurrentBoundsAnimation) { 1564 return false; 1565 } 1566 } 1567 1568 try { 1569 mWmService.mActivityTaskManager.resizePinnedStack(stackBounds, tempTaskBounds); 1570 } catch (RemoteException e) { 1571 // I don't believe you. 1572 } 1573 return true; 1574 } 1575 onAllWindowsDrawn()1576 void onAllWindowsDrawn() { 1577 if (!mBoundsAnimating && !mBoundsAnimatingRequested) { 1578 return; 1579 } 1580 1581 getDisplayContent().mBoundsAnimationController.onAllWindowsDrawn(); 1582 } 1583 1584 @Override // AnimatesBounds onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType)1585 public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate, 1586 @BoundsAnimationController.AnimationType int animationType) { 1587 // Hold the lock since this is called from the BoundsAnimator running on the UiThread 1588 synchronized (mWmService.mGlobalLock) { 1589 if (!isAttached()) { 1590 // Don't run the animation if the stack is already detached 1591 return false; 1592 } 1593 1594 mBoundsAnimatingRequested = false; 1595 mBoundsAnimating = true; 1596 mAnimationType = animationType; 1597 1598 // If we are changing UI mode, as in the PiP to fullscreen 1599 // transition, then we need to wait for the window to draw. 1600 if (schedulePipModeChangedCallback) { 1601 forAllWindows((w) -> { w.mWinAnimator.resetDrawState(); }, 1602 false /* traverseTopToBottom */); 1603 } 1604 } 1605 1606 if (inPinnedWindowingMode()) { 1607 try { 1608 mWmService.mActivityTaskManager.notifyPinnedStackAnimationStarted(); 1609 } catch (RemoteException e) { 1610 // I don't believe you... 1611 } 1612 1613 if ((schedulePipModeChangedCallback || animationType == FADE_IN) 1614 && mActivityStack != null) { 1615 // We need to schedule the PiP mode change before the animation up. It is possible 1616 // in this case for the animation down to not have been completed, so always 1617 // force-schedule and update to the client to ensure that it is notified that it 1618 // is no longer in picture-in-picture mode 1619 mActivityStack.updatePictureInPictureModeForPinnedStackAnimation(null, 1620 forceUpdate); 1621 } 1622 } 1623 return true; 1624 } 1625 1626 @Override // AnimatesBounds onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, boolean moveToFullscreen)1627 public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, 1628 boolean moveToFullscreen) { 1629 if (inPinnedWindowingMode()) { 1630 // Update to the final bounds if requested. This is done here instead of in the bounds 1631 // animator to allow us to coordinate this after we notify the PiP mode changed 1632 1633 if (schedulePipModeChangedCallback) { 1634 // We need to schedule the PiP mode change after the animation down, so use the 1635 // final bounds 1636 mActivityStack.updatePictureInPictureModeForPinnedStackAnimation( 1637 mBoundsAnimationTarget, false /* forceUpdate */); 1638 } 1639 1640 if (mAnimationType == BoundsAnimationController.FADE_IN) { 1641 setPinnedStackAlpha(1f); 1642 mActivityStack.mService.notifyPinnedStackAnimationEnded(); 1643 return; 1644 } 1645 1646 if (finalStackSize != null && !mCancelCurrentBoundsAnimation) { 1647 setPinnedStackSize(finalStackSize, null); 1648 } else { 1649 // We have been canceled, so the final stack size is null, still run the 1650 // animation-end logic 1651 onPipAnimationEndResize(); 1652 } 1653 1654 mActivityStack.mService.notifyPinnedStackAnimationEnded(); 1655 if (moveToFullscreen) { 1656 mActivityStack.mService.moveTasksToFullscreenStack(mStackId, true /* onTop */); 1657 } 1658 } else { 1659 // No PiP animation, just run the normal animation-end logic 1660 onPipAnimationEndResize(); 1661 } 1662 } 1663 1664 /** 1665 * @return the current stack bounds transformed to the given {@param aspectRatio}. If 1666 * the default bounds is {@code null}, then the {@param aspectRatio} is applied to the 1667 * default bounds. 1668 */ getPictureInPictureBounds(float aspectRatio, Rect stackBounds)1669 Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) { 1670 if (!mWmService.mSupportsPictureInPicture) { 1671 return null; 1672 } 1673 1674 final DisplayContent displayContent = getDisplayContent(); 1675 if (displayContent == null) { 1676 return null; 1677 } 1678 1679 if (!inPinnedWindowingMode()) { 1680 return null; 1681 } 1682 1683 final PinnedStackController pinnedStackController = 1684 displayContent.getPinnedStackController(); 1685 if (stackBounds == null) { 1686 // Calculate the aspect ratio bounds from the default bounds 1687 stackBounds = pinnedStackController.getDefaultOrLastSavedBounds(); 1688 } 1689 1690 if (pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)) { 1691 return pinnedStackController.transformBoundsToAspectRatio(stackBounds, aspectRatio, 1692 true /* useCurrentMinEdgeSize */); 1693 } else { 1694 return stackBounds; 1695 } 1696 } 1697 1698 /** 1699 * Animates the pinned stack. 1700 */ animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, int animationDuration, boolean fromFullscreen)1701 void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds, 1702 int animationDuration, boolean fromFullscreen) { 1703 if (!inPinnedWindowingMode()) { 1704 return; 1705 } 1706 // Get the from-bounds 1707 final Rect fromBounds = new Rect(); 1708 getBounds(fromBounds); 1709 1710 // Get non-null fullscreen to-bounds for animating if the bounds are null 1711 @SchedulePipModeChangedState int schedulePipModeChangedState = 1712 NO_PIP_MODE_CHANGED_CALLBACKS; 1713 final boolean toFullscreen = toBounds == null; 1714 if (toFullscreen) { 1715 if (fromFullscreen) { 1716 throw new IllegalArgumentException("Should not defer scheduling PiP mode" 1717 + " change on animation to fullscreen."); 1718 } 1719 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START; 1720 1721 mWmService.getStackBounds( 1722 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mTmpToBounds); 1723 if (!mTmpToBounds.isEmpty()) { 1724 // If there is a fullscreen bounds, use that 1725 toBounds = new Rect(mTmpToBounds); 1726 } else { 1727 // Otherwise, use the display bounds 1728 toBounds = new Rect(); 1729 getDisplayContent().getBounds(toBounds); 1730 } 1731 } else if (fromFullscreen) { 1732 schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END; 1733 } 1734 1735 setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen); 1736 1737 final Rect finalToBounds = toBounds; 1738 final @SchedulePipModeChangedState int finalSchedulePipModeChangedState = 1739 schedulePipModeChangedState; 1740 final DisplayContent displayContent = getDisplayContent(); 1741 @BoundsAnimationController.AnimationType int intendedAnimationType = 1742 displayContent.mBoundsAnimationController.getAnimationType(); 1743 if (intendedAnimationType == FADE_IN) { 1744 if (fromFullscreen) { 1745 setPinnedStackAlpha(0f); 1746 } 1747 if (toBounds.width() == fromBounds.width() 1748 && toBounds.height() == fromBounds.height()) { 1749 intendedAnimationType = BoundsAnimationController.BOUNDS; 1750 } 1751 } 1752 1753 final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType; 1754 mCancelCurrentBoundsAnimation = false; 1755 displayContent.mBoundsAnimationController.getHandler().post(() -> { 1756 displayContent.mBoundsAnimationController.animateBounds(this, fromBounds, 1757 finalToBounds, animationDuration, finalSchedulePipModeChangedState, 1758 fromFullscreen, toFullscreen, animationType); 1759 }); 1760 } 1761 1762 /** 1763 * Sets the current picture-in-picture aspect ratio. 1764 */ setPictureInPictureAspectRatio(float aspectRatio)1765 void setPictureInPictureAspectRatio(float aspectRatio) { 1766 if (!mWmService.mSupportsPictureInPicture) { 1767 return; 1768 } 1769 1770 if (!inPinnedWindowingMode()) { 1771 return; 1772 } 1773 1774 final PinnedStackController pinnedStackController = 1775 getDisplayContent().getPinnedStackController(); 1776 1777 if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) == 0) { 1778 return; 1779 } 1780 getAnimationOrCurrentBounds(mTmpFromBounds); 1781 mTmpToBounds.set(mTmpFromBounds); 1782 getPictureInPictureBounds(aspectRatio, mTmpToBounds); 1783 if (!mTmpToBounds.equals(mTmpFromBounds)) { 1784 animateResizePinnedStack(mTmpToBounds, null /* sourceHintBounds */, 1785 -1 /* duration */, false /* fromFullscreen */); 1786 } 1787 pinnedStackController.setAspectRatio( 1788 pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio) 1789 ? aspectRatio : -1f); 1790 } 1791 1792 /** 1793 * Sets the current picture-in-picture actions. 1794 */ setPictureInPictureActions(List<RemoteAction> actions)1795 void setPictureInPictureActions(List<RemoteAction> actions) { 1796 if (!mWmService.mSupportsPictureInPicture) { 1797 return; 1798 } 1799 1800 if (!inPinnedWindowingMode()) { 1801 return; 1802 } 1803 1804 getDisplayContent().getPinnedStackController().setActions(actions); 1805 } 1806 1807 @Override isAttached()1808 public boolean isAttached() { 1809 synchronized (mWmService.mGlobalLock) { 1810 return mDisplayContent != null; 1811 } 1812 } 1813 1814 /** 1815 * Called immediately prior to resizing the tasks at the end of the pinned stack animation. 1816 */ onPipAnimationEndResize()1817 public void onPipAnimationEndResize() { 1818 synchronized (mWmService.mGlobalLock) { 1819 mBoundsAnimating = false; 1820 for (int i = 0; i < mChildren.size(); i++) { 1821 final Task t = mChildren.get(i); 1822 t.clearPreserveNonFloatingState(); 1823 } 1824 mWmService.requestTraversal(); 1825 } 1826 } 1827 1828 @Override shouldDeferStartOnMoveToFullscreen()1829 public boolean shouldDeferStartOnMoveToFullscreen() { 1830 synchronized (mWmService.mGlobalLock) { 1831 if (!isAttached()) { 1832 // Unnecessary to pause the animation because the stack is detached. 1833 return false; 1834 } 1835 1836 // Workaround for the recents animation -- normally we need to wait for the new activity 1837 // to show before starting the PiP animation, but because we start and show the home 1838 // activity early for the recents animation prior to the PiP animation starting, there 1839 // is no subsequent all-drawn signal. In this case, we can skip the pause when the home 1840 // stack is already visible and drawn. 1841 final TaskStack homeStack = mDisplayContent.getHomeStack(); 1842 if (homeStack == null) { 1843 return true; 1844 } 1845 final Task homeTask = homeStack.getTopChild(); 1846 if (homeTask == null) { 1847 return true; 1848 } 1849 final AppWindowToken homeApp = homeTask.getTopVisibleAppToken(); 1850 if (!homeTask.isVisible() || homeApp == null) { 1851 return true; 1852 } 1853 return !homeApp.allDrawn; 1854 } 1855 } 1856 1857 /** 1858 * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen 1859 * bounds and we have a deferred PiP mode changed callback set with the animation. 1860 */ deferScheduleMultiWindowModeChanged()1861 public boolean deferScheduleMultiWindowModeChanged() { 1862 if (inPinnedWindowingMode()) { 1863 return (mBoundsAnimatingRequested || mBoundsAnimating); 1864 } 1865 return false; 1866 } 1867 isForceScaled()1868 public boolean isForceScaled() { 1869 return mBoundsAnimating; 1870 } 1871 isAnimatingBounds()1872 public boolean isAnimatingBounds() { 1873 return mBoundsAnimating; 1874 } 1875 lastAnimatingBoundsWasToFullscreen()1876 public boolean lastAnimatingBoundsWasToFullscreen() { 1877 return mBoundsAnimatingToFullscreen; 1878 } 1879 isAnimatingBoundsToFullscreen()1880 public boolean isAnimatingBoundsToFullscreen() { 1881 return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen(); 1882 } 1883 pinnedStackResizeDisallowed()1884 public boolean pinnedStackResizeDisallowed() { 1885 if (mBoundsAnimating && mCancelCurrentBoundsAnimation) { 1886 return true; 1887 } 1888 return false; 1889 } 1890 1891 /** Returns true if a removal action is still being deferred. */ checkCompleteDeferredRemoval()1892 boolean checkCompleteDeferredRemoval() { 1893 if (isSelfOrChildAnimating()) { 1894 return true; 1895 } 1896 if (mDeferRemoval) { 1897 removeImmediately(); 1898 } 1899 1900 return super.checkCompleteDeferredRemoval(); 1901 } 1902 1903 @Override getOrientation()1904 int getOrientation() { 1905 return (canSpecifyOrientation()) ? super.getOrientation() : SCREEN_ORIENTATION_UNSET; 1906 } 1907 canSpecifyOrientation()1908 private boolean canSpecifyOrientation() { 1909 final int windowingMode = getWindowingMode(); 1910 final int activityType = getActivityType(); 1911 return windowingMode == WINDOWING_MODE_FULLSCREEN 1912 || activityType == ACTIVITY_TYPE_HOME 1913 || activityType == ACTIVITY_TYPE_RECENTS 1914 || activityType == ACTIVITY_TYPE_ASSISTANT; 1915 } 1916 1917 @Override getDimmer()1918 Dimmer getDimmer() { 1919 return mDimmer; 1920 } 1921 1922 @Override prepareSurfaces()1923 void prepareSurfaces() { 1924 mDimmer.resetDimStates(); 1925 super.prepareSurfaces(); 1926 getDimBounds(mTmpDimBoundsRect); 1927 1928 // Bounds need to be relative, as the dim layer is a child. 1929 mTmpDimBoundsRect.offsetTo(0, 0); 1930 if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) { 1931 scheduleAnimation(); 1932 } 1933 } 1934 1935 @Override setPinnedStackAlpha(float alpha)1936 public boolean setPinnedStackAlpha(float alpha) { 1937 // Hold the lock since this is called from the BoundsAnimator running on the UiThread 1938 synchronized (mWmService.mGlobalLock) { 1939 final SurfaceControl sc = getSurfaceControl(); 1940 if (sc == null || !sc.isValid()) { 1941 // If the stack is already removed, don't bother updating any stack animation 1942 return false; 1943 } 1944 getPendingTransaction().setAlpha(sc, mCancelCurrentBoundsAnimation ? 1 : alpha); 1945 scheduleAnimation(); 1946 return !mCancelCurrentBoundsAnimation; 1947 } 1948 } 1949 getDisplayInfo()1950 public DisplayInfo getDisplayInfo() { 1951 return mDisplayContent.getDisplayInfo(); 1952 } 1953 dim(float alpha)1954 void dim(float alpha) { 1955 mDimmer.dimAbove(getPendingTransaction(), alpha); 1956 scheduleAnimation(); 1957 } 1958 stopDimming()1959 void stopDimming() { 1960 mDimmer.stopDim(getPendingTransaction()); 1961 scheduleAnimation(); 1962 } 1963 getAnimatingAppWindowTokenRegistry()1964 AnimatingAppWindowTokenRegistry getAnimatingAppWindowTokenRegistry() { 1965 return mAnimatingAppWindowTokenRegistry; 1966 } 1967 } 1968