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.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 20 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT; 21 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; 22 import static android.app.ActivityManager.StackId.HOME_STACK_ID; 23 import static android.app.ActivityManager.StackId.PINNED_STACK_ID; 24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 25 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED; 26 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 27 import static android.view.Display.DEFAULT_DISPLAY; 28 import static android.view.WindowManager.DOCKED_BOTTOM; 29 import static android.view.WindowManager.DOCKED_INVALID; 30 import static android.view.WindowManager.DOCKED_LEFT; 31 import static android.view.WindowManager.DOCKED_RIGHT; 32 import static android.view.WindowManager.DOCKED_TOP; 33 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; 34 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; 35 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; 36 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; 37 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 38 import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM; 39 40 import android.app.ActivityManager.StackId; 41 import android.content.res.Configuration; 42 import android.graphics.Rect; 43 import android.graphics.Region; 44 import android.os.RemoteException; 45 import android.util.EventLog; 46 import android.util.Slog; 47 import android.util.SparseArray; 48 import android.view.DisplayInfo; 49 import android.view.Surface; 50 51 import com.android.internal.policy.DividerSnapAlgorithm; 52 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; 53 import com.android.internal.policy.DockedDividerUtils; 54 import com.android.server.EventLogTags; 55 import com.android.server.UiThread; 56 57 import java.io.PrintWriter; 58 59 public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser, 60 BoundsAnimationTarget { 61 /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to 62 * restrict IME adjustment so that a min portion of top stack remains visible.*/ 63 private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f; 64 65 /** Dimming amount for non-focused stack when stacks are IME-adjusted. */ 66 private static final float IME_ADJUST_DIM_AMOUNT = 0.25f; 67 68 /** Unique identifier */ 69 final int mStackId; 70 71 /** The service */ 72 private final WindowManagerService mService; 73 74 /** The display this stack sits under. */ 75 // TODO: Track parent marks like this in WindowContainer. 76 private DisplayContent mDisplayContent; 77 78 /** For comparison with DisplayContent bounds. */ 79 private Rect mTmpRect = new Rect(); 80 private Rect mTmpRect2 = new Rect(); 81 private Rect mTmpRect3 = new Rect(); 82 83 /** Content limits relative to the DisplayContent this sits in. */ 84 private Rect mBounds = new Rect(); 85 86 /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */ 87 private final Rect mAdjustedBounds = new Rect(); 88 89 /** 90 * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they 91 * represent the state when the animation has ended. 92 */ 93 private final Rect mFullyAdjustedImeBounds = new Rect(); 94 95 /** Whether mBounds is fullscreen */ 96 private boolean mFillsParent = true; 97 98 // Device rotation as of the last time {@link #mBounds} was set. 99 private int mRotation; 100 101 /** Density as of last time {@link #mBounds} was set. */ 102 private int mDensity; 103 104 /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */ 105 private DimLayer mAnimationBackgroundSurface; 106 107 /** The particular window with an Animation with non-zero background color. */ 108 private WindowStateAnimator mAnimationBackgroundAnimator; 109 110 /** Application tokens that are exiting, but still on screen for animations. */ 111 final AppTokenList mExitingAppTokens = new AppTokenList(); 112 final AppTokenList mTmpAppTokens = new AppTokenList(); 113 114 /** Detach this stack from its display when animation completes. */ 115 // TODO: maybe tie this to WindowContainer#removeChild some how... 116 boolean mDeferRemoval; 117 118 private final Rect mTmpAdjustedBounds = new Rect(); 119 private boolean mAdjustedForIme; 120 private boolean mImeGoingAway; 121 private WindowState mImeWin; 122 private float mMinimizeAmount; 123 private float mAdjustImeAmount; 124 private float mAdjustDividerAmount; 125 private final int mDockedStackMinimizeThickness; 126 127 // If this is true, we are in the bounds animating mode. The task will be down or upscaled to 128 // perfectly fit the region it would have been cropped to. We may also avoid certain logic we 129 // would otherwise apply while resizing, while resizing in the bounds animating mode. 130 private boolean mBoundsAnimating = false; 131 // Set when an animation has been requested but has not yet started from the UI thread. This is 132 // cleared when the animation actually starts. 133 private boolean mBoundsAnimatingRequested = false; 134 private boolean mBoundsAnimatingToFullscreen = false; 135 private boolean mCancelCurrentBoundsAnimation = false; 136 private Rect mBoundsAnimationTarget = new Rect(); 137 private Rect mBoundsAnimationSourceHintBounds = new Rect(); 138 139 // Temporary storage for the new bounds that should be used after the configuration change. 140 // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration(). 141 private final Rect mBoundsAfterRotation = new Rect(); 142 143 Rect mPreAnimationBounds = new Rect(); 144 TaskStack(WindowManagerService service, int stackId)145 TaskStack(WindowManagerService service, int stackId) { 146 mService = service; 147 mStackId = stackId; 148 mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize( 149 com.android.internal.R.dimen.docked_stack_minimize_thickness); 150 EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId); 151 } 152 getDisplayContent()153 DisplayContent getDisplayContent() { 154 return mDisplayContent; 155 } 156 findHomeTask()157 Task findHomeTask() { 158 if (mStackId != HOME_STACK_ID) { 159 return null; 160 } 161 162 for (int i = mChildren.size() - 1; i >= 0; i--) { 163 if (mChildren.get(i).isHomeTask()) { 164 return mChildren.get(i); 165 } 166 } 167 return null; 168 } 169 hasMultipleTaskWithHomeTaskNotTop()170 boolean hasMultipleTaskWithHomeTaskNotTop() { 171 return mChildren.size() > 1 && !mChildren.get(mChildren.size() - 1).isHomeTask(); 172 } 173 174 /** 175 * Set the bounds of the stack and its containing tasks. 176 * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen. 177 * @param configs Configuration for individual tasks, keyed by task id. 178 * @param taskBounds Bounds for individual tasks, keyed by task id. 179 * @return True if the stack bounds was changed. 180 * */ setBounds( Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, SparseArray<Rect> taskTempInsetBounds)181 boolean setBounds( 182 Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, 183 SparseArray<Rect> taskTempInsetBounds) { 184 setBounds(stackBounds); 185 186 // Update bounds of containing tasks. 187 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 188 final Task task = mChildren.get(taskNdx); 189 Configuration config = configs.get(task.mTaskId); 190 if (config != null) { 191 Rect bounds = taskBounds.get(task.mTaskId); 192 task.resizeLocked(bounds, config, false /* forced */); 193 task.setTempInsetBounds(taskTempInsetBounds != null ? 194 taskTempInsetBounds.get(task.mTaskId) : null); 195 } else { 196 Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?"); 197 } 198 } 199 return true; 200 } 201 prepareFreezingTaskBounds()202 void prepareFreezingTaskBounds() { 203 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 204 final Task task = mChildren.get(taskNdx); 205 task.prepareFreezingBounds(); 206 } 207 } 208 209 /** 210 * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from 211 * the normal task bounds. 212 * 213 * @param bounds The adjusted bounds. 214 */ setAdjustedBounds(Rect bounds)215 private void setAdjustedBounds(Rect bounds) { 216 if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) { 217 return; 218 } 219 220 mAdjustedBounds.set(bounds); 221 final boolean adjusted = !mAdjustedBounds.isEmpty(); 222 Rect insetBounds = null; 223 if (adjusted && isAdjustedForMinimizedDockedStack()) { 224 insetBounds = mBounds; 225 } else if (adjusted && mAdjustedForIme) { 226 if (mImeGoingAway) { 227 insetBounds = mBounds; 228 } else { 229 insetBounds = mFullyAdjustedImeBounds; 230 } 231 } 232 alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds); 233 mDisplayContent.setLayoutNeeded(); 234 } 235 alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds)236 private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) { 237 if (mFillsParent) { 238 return; 239 } 240 241 final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP; 242 243 // Update bounds of containing tasks. 244 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 245 final Task task = mChildren.get(taskNdx); 246 task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom); 247 } 248 } 249 setBounds(Rect bounds)250 private boolean setBounds(Rect bounds) { 251 boolean oldFullscreen = mFillsParent; 252 int rotation = Surface.ROTATION_0; 253 int density = DENSITY_DPI_UNDEFINED; 254 if (mDisplayContent != null) { 255 mDisplayContent.getLogicalDisplayRect(mTmpRect); 256 rotation = mDisplayContent.getDisplayInfo().rotation; 257 density = mDisplayContent.getDisplayInfo().logicalDensityDpi; 258 mFillsParent = bounds == null; 259 if (mFillsParent) { 260 bounds = mTmpRect; 261 } 262 } 263 264 if (bounds == null) { 265 // Can't set to fullscreen if we don't have a display to get bounds from... 266 return false; 267 } 268 if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) { 269 return false; 270 } 271 272 if (mDisplayContent != null) { 273 mDisplayContent.mDimLayerController.updateDimLayer(this); 274 mAnimationBackgroundSurface.setBounds(bounds); 275 } 276 277 mBounds.set(bounds); 278 mRotation = rotation; 279 mDensity = density; 280 281 updateAdjustedBounds(); 282 283 return true; 284 } 285 286 /** Bounds of the stack without adjusting for other factors in the system like visibility 287 * of docked stack. 288 * Most callers should be using {@link #getBounds} as it take into consideration other system 289 * factors. */ getRawBounds(Rect out)290 void getRawBounds(Rect out) { 291 out.set(mBounds); 292 } 293 294 /** Return true if the current bound can get outputted to the rest of the system as-is. */ useCurrentBounds()295 private boolean useCurrentBounds() { 296 if (mFillsParent 297 || !StackId.isResizeableByDockedStack(mStackId) 298 || mDisplayContent == null 299 || mDisplayContent.getDockedStackLocked() != null) { 300 return true; 301 } 302 return false; 303 } 304 getBounds(Rect out)305 public void getBounds(Rect out) { 306 if (useCurrentBounds()) { 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 out.set(mAdjustedBounds); 313 } else { 314 out.set(mBounds); 315 } 316 return; 317 } 318 319 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 320 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 321 // system. 322 mDisplayContent.getLogicalDisplayRect(out); 323 } 324 325 /** 326 * Sets the bounds animation target bounds ahead of an animation. This can't currently be done 327 * in onAnimationStart() since that is started on the UiThread. 328 */ setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen)329 void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen) { 330 mBoundsAnimatingRequested = true; 331 mBoundsAnimatingToFullscreen = toFullscreen; 332 if (destBounds != null) { 333 mBoundsAnimationTarget.set(destBounds); 334 } else { 335 mBoundsAnimationTarget.setEmpty(); 336 } 337 if (sourceHintBounds != null) { 338 mBoundsAnimationSourceHintBounds.set(sourceHintBounds); 339 } else { 340 mBoundsAnimationSourceHintBounds.setEmpty(); 341 } 342 343 mPreAnimationBounds.set(mBounds); 344 } 345 346 /** 347 * @return the final bounds for the bounds animation. 348 */ getFinalAnimationBounds(Rect outBounds)349 void getFinalAnimationBounds(Rect outBounds) { 350 outBounds.set(mBoundsAnimationTarget); 351 } 352 353 /** 354 * @return the final source bounds for the bounds animation. 355 */ getFinalAnimationSourceHintBounds(Rect outBounds)356 void getFinalAnimationSourceHintBounds(Rect outBounds) { 357 outBounds.set(mBoundsAnimationSourceHintBounds); 358 } 359 360 /** 361 * @return the final animation bounds if the task stack is currently being animated, or the 362 * current stack bounds otherwise. 363 */ getAnimationOrCurrentBounds(Rect outBounds)364 void getAnimationOrCurrentBounds(Rect outBounds) { 365 if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) { 366 getFinalAnimationBounds(outBounds); 367 return; 368 } 369 getBounds(outBounds); 370 } 371 372 /** Bounds of the stack with other system factors taken into consideration. */ 373 @Override getDimBounds(Rect out)374 public void getDimBounds(Rect out) { 375 getBounds(out); 376 } 377 updateDisplayInfo(Rect bounds)378 void updateDisplayInfo(Rect bounds) { 379 if (mDisplayContent == null) { 380 return; 381 } 382 383 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 384 mChildren.get(taskNdx).updateDisplayInfo(mDisplayContent); 385 } 386 if (bounds != null) { 387 setBounds(bounds); 388 return; 389 } else if (mFillsParent) { 390 setBounds(null); 391 return; 392 } 393 394 mTmpRect2.set(mBounds); 395 final int newRotation = mDisplayContent.getDisplayInfo().rotation; 396 final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi; 397 if (mRotation == newRotation && mDensity == newDensity) { 398 setBounds(mTmpRect2); 399 } 400 401 // If the rotation or density didn't match, we'll update it in onConfigurationChanged. 402 } 403 404 /** @return true if bounds were updated to some non-empty value. */ updateBoundsAfterConfigChange()405 boolean updateBoundsAfterConfigChange() { 406 if (mDisplayContent == null) { 407 // If the stack is already detached we're not updating anything, 408 // as it's going away soon anyway. 409 return false; 410 } 411 412 if (mStackId == PINNED_STACK_ID) { 413 getAnimationOrCurrentBounds(mTmpRect2); 414 boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged( 415 mTmpRect2, mTmpRect3); 416 if (updated) { 417 mBoundsAfterRotation.set(mTmpRect3); 418 419 // Once we've set the bounds based on the rotation of the old bounds in the new 420 // orientation, clear the animation target bounds since they are obsolete, and 421 // cancel any currently running animations 422 mBoundsAnimationTarget.setEmpty(); 423 mBoundsAnimationSourceHintBounds.setEmpty(); 424 mCancelCurrentBoundsAnimation = true; 425 return true; 426 } 427 } 428 429 final int newRotation = getDisplayInfo().rotation; 430 final int newDensity = getDisplayInfo().logicalDensityDpi; 431 432 if (mRotation == newRotation && mDensity == newDensity) { 433 // Nothing to do here as we already update the state in updateDisplayInfo. 434 return false; 435 } 436 437 if (mFillsParent) { 438 // Update stack bounds again since rotation changed since updateDisplayInfo(). 439 setBounds(null); 440 // Return false since we don't need the client to resize. 441 return false; 442 } 443 444 mTmpRect2.set(mBounds); 445 mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2); 446 switch (mStackId) { 447 case DOCKED_STACK_ID: 448 repositionDockedStackAfterRotation(mTmpRect2); 449 snapDockedStackAfterRotation(mTmpRect2); 450 final int newDockSide = getDockSide(mTmpRect2); 451 452 // Update the dock create mode and clear the dock create bounds, these 453 // might change after a rotation and the original values will be invalid. 454 mService.setDockedStackCreateStateLocked( 455 (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP) 456 ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT 457 : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 458 null); 459 mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide); 460 break; 461 } 462 463 mBoundsAfterRotation.set(mTmpRect2); 464 return true; 465 } 466 getBoundsForNewConfiguration(Rect outBounds)467 void getBoundsForNewConfiguration(Rect outBounds) { 468 outBounds.set(mBoundsAfterRotation); 469 mBoundsAfterRotation.setEmpty(); 470 } 471 472 /** 473 * Some dock sides are not allowed by the policy. This method queries the policy and moves 474 * the docked stack around if needed. 475 * 476 * @param inOutBounds the bounds of the docked stack to adjust 477 */ repositionDockedStackAfterRotation(Rect inOutBounds)478 private void repositionDockedStackAfterRotation(Rect inOutBounds) { 479 int dockSide = getDockSide(inOutBounds); 480 if (mService.mPolicy.isDockSideAllowed(dockSide)) { 481 return; 482 } 483 mDisplayContent.getLogicalDisplayRect(mTmpRect); 484 dockSide = DockedDividerUtils.invertDockSide(dockSide); 485 switch (dockSide) { 486 case DOCKED_LEFT: 487 int movement = inOutBounds.left; 488 inOutBounds.left -= movement; 489 inOutBounds.right -= movement; 490 break; 491 case DOCKED_RIGHT: 492 movement = mTmpRect.right - inOutBounds.right; 493 inOutBounds.left += movement; 494 inOutBounds.right += movement; 495 break; 496 case DOCKED_TOP: 497 movement = inOutBounds.top; 498 inOutBounds.top -= movement; 499 inOutBounds.bottom -= movement; 500 break; 501 case DOCKED_BOTTOM: 502 movement = mTmpRect.bottom - inOutBounds.bottom; 503 inOutBounds.top += movement; 504 inOutBounds.bottom += movement; 505 break; 506 } 507 } 508 509 /** 510 * Snaps the bounds after rotation to the closest snap target for the docked stack. 511 */ snapDockedStackAfterRotation(Rect outBounds)512 private void snapDockedStackAfterRotation(Rect outBounds) { 513 514 // Calculate the current position. 515 final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); 516 final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth(); 517 final int dockSide = getDockSide(outBounds); 518 final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds, 519 dockSide, dividerSize); 520 final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth; 521 final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight; 522 523 // Snap the position to a target. 524 final int rotation = displayInfo.rotation; 525 final int orientation = mDisplayContent.getConfiguration().orientation; 526 mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds); 527 final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm( 528 mService.mContext.getResources(), displayWidth, displayHeight, 529 dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds, 530 isMinimizedDockAndHomeStackResizable()); 531 final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition); 532 533 // Recalculate the bounds based on the position of the target. 534 DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide, 535 outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight, 536 dividerSize); 537 } 538 539 // 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)540 void addTask(Task task, int position) { 541 addTask(task, position, task.showForAllUsers(), true /* moveParents */); 542 } 543 544 /** 545 * Put a Task in this stack. Used for adding only. 546 * When task is added to top of the stack, the entire branch of the hierarchy (including stack 547 * and display) will be brought to top. 548 * @param task The task to add. 549 * @param position Target position to add the task to. 550 * @param showForAllUsers Whether to show the task regardless of the current user. 551 */ addTask(Task task, int position, boolean showForAllUsers, boolean moveParents)552 void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) { 553 final TaskStack currentStack = task.mStack; 554 // TODO: We pass stack to task's constructor, but we still need to call this method. 555 // This doesn't make sense, mStack will already be set equal to "this" at this point. 556 if (currentStack != null && currentStack.mStackId != mStackId) { 557 throw new IllegalStateException("Trying to add taskId=" + task.mTaskId 558 + " to stackId=" + mStackId 559 + ", but it is already attached to stackId=" + task.mStack.mStackId); 560 } 561 562 // Add child task. 563 task.mStack = this; 564 addChild(task, null); 565 566 // Move child to a proper position, as some restriction for position might apply. 567 positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers); 568 } 569 570 @Override positionChildAt(int position, Task child, boolean includingParents)571 void positionChildAt(int position, Task child, boolean includingParents) { 572 positionChildAt(position, child, includingParents, child.showForAllUsers()); 573 } 574 575 /** 576 * Overridden version of {@link TaskStack#positionChildAt(int, Task, boolean)}. Used in 577 * {@link TaskStack#addTask(Task, int, boolean showForAllUsers, boolean)}, as it can receive 578 * showForAllUsers param from {@link AppWindowToken} instead of {@link Task#showForAllUsers()}. 579 */ positionChildAt(int position, Task child, boolean includingParents, boolean showForAllUsers)580 private void positionChildAt(int position, Task child, boolean includingParents, 581 boolean showForAllUsers) { 582 final int targetPosition = findPositionForTask(child, position, showForAllUsers, 583 false /* addingNew */); 584 super.positionChildAt(targetPosition, child, includingParents); 585 586 // Log positioning. 587 if (DEBUG_TASK_MOVEMENT) 588 Slog.d(TAG_WM, "positionTask: task=" + this + " position=" + position); 589 590 final int toTop = targetPosition == mChildren.size() - 1 ? 1 : 0; 591 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop, targetPosition); 592 } 593 594 // TODO: We should really have users as a window container in the hierarchy so that we don't 595 // have to do complicated things like we are doing in this method. findPositionForTask(Task task, int targetPosition, boolean showForAllUsers, boolean addingNew)596 private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers, 597 boolean addingNew) { 598 final boolean canShowTask = 599 showForAllUsers || mService.isCurrentProfileLocked(task.mUserId); 600 601 final int stackSize = mChildren.size(); 602 int minPosition = 0; 603 int maxPosition = addingNew ? stackSize : stackSize - 1; 604 605 if (canShowTask) { 606 minPosition = computeMinPosition(minPosition, stackSize); 607 } else { 608 maxPosition = computeMaxPosition(maxPosition); 609 } 610 // Reset position based on minimum/maximum possible positions. 611 return Math.min(Math.max(targetPosition, minPosition), maxPosition); 612 } 613 614 /** Calculate the minimum possible position for a task that can be shown to the user. 615 * The minimum position will be above all other tasks that can't be shown. 616 * @param minPosition The minimum position the caller is suggesting. 617 * We will start adjusting up from here. 618 * @param size The size of the current task list. 619 */ computeMinPosition(int minPosition, int size)620 private int computeMinPosition(int minPosition, int size) { 621 while (minPosition < size) { 622 final Task tmpTask = mChildren.get(minPosition); 623 final boolean canShowTmpTask = 624 tmpTask.showForAllUsers() 625 || mService.isCurrentProfileLocked(tmpTask.mUserId); 626 if (canShowTmpTask) { 627 break; 628 } 629 minPosition++; 630 } 631 return minPosition; 632 } 633 634 /** Calculate the maximum possible position for a task that can't be shown to the user. 635 * The maximum position will be below all other tasks that can be shown. 636 * @param maxPosition The maximum position the caller is suggesting. 637 * We will start adjusting down from here. 638 */ computeMaxPosition(int maxPosition)639 private int computeMaxPosition(int maxPosition) { 640 while (maxPosition > 0) { 641 final Task tmpTask = mChildren.get(maxPosition); 642 final boolean canShowTmpTask = 643 tmpTask.showForAllUsers() 644 || mService.isCurrentProfileLocked(tmpTask.mUserId); 645 if (!canShowTmpTask) { 646 break; 647 } 648 maxPosition--; 649 } 650 return maxPosition; 651 } 652 653 /** 654 * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the 655 * back. 656 * @param task The Task to delete. 657 */ 658 @Override removeChild(Task task)659 void removeChild(Task task) { 660 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeChild: task=" + task); 661 662 super.removeChild(task); 663 task.mStack = null; 664 665 if (mDisplayContent != null) { 666 if (mChildren.isEmpty()) { 667 getParent().positionChildAt(POSITION_BOTTOM, this, false /* includingParents */); 668 } 669 mDisplayContent.setLayoutNeeded(); 670 } 671 for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { 672 final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); 673 if (wtoken.getTask() == task) { 674 wtoken.mIsExiting = false; 675 mExitingAppTokens.remove(appNdx); 676 } 677 } 678 } 679 onDisplayChanged(DisplayContent dc)680 void onDisplayChanged(DisplayContent dc) { 681 if (mDisplayContent != null) { 682 throw new IllegalStateException("onDisplayChanged: Already attached"); 683 } 684 685 mDisplayContent = dc; 686 mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(), 687 "animation background stackId=" + mStackId); 688 689 Rect bounds = null; 690 final TaskStack dockedStack = dc.getDockedStackIgnoringVisibility(); 691 if (mStackId == DOCKED_STACK_ID 692 || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId) 693 && !dockedStack.fillsParent())) { 694 // The existence of a docked stack affects the size of other static stack created since 695 // the docked stack occupies a dedicated region on screen, but only if the dock stack is 696 // not fullscreen. If it's fullscreen, it means that we are in the transition of 697 // dismissing it, so we must not resize this stack. 698 bounds = new Rect(); 699 dc.getLogicalDisplayRect(mTmpRect); 700 mTmpRect2.setEmpty(); 701 if (dockedStack != null) { 702 dockedStack.getRawBounds(mTmpRect2); 703 } 704 final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode 705 == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 706 getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2, 707 mDisplayContent.mDividerControllerLocked.getContentWidth(), 708 dockedOnTopOrLeft); 709 } else if (mStackId == PINNED_STACK_ID) { 710 // Update the bounds based on any changes to the display info 711 getAnimationOrCurrentBounds(mTmpRect2); 712 boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged( 713 mTmpRect2, mTmpRect3); 714 if (updated) { 715 bounds = new Rect(mTmpRect3); 716 } 717 } 718 719 updateDisplayInfo(bounds); 720 super.onDisplayChanged(dc); 721 } 722 723 /** 724 * Determines the stack and task bounds of the other stack when in docked mode. The current task 725 * bounds is passed in but depending on the stack, the task and stack must match. Only in 726 * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds 727 * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds 728 * is calculated and is also used for its task bounds. 729 * If any of the out bounds are empty, it represents default bounds 730 * 731 * @param currentTempTaskBounds the current task bounds of the other stack 732 * @param outStackBounds the calculated stack bounds of the other stack 733 * @param outTempTaskBounds the calculated task bounds of the other stack 734 * @param ignoreVisibility ignore visibility in getting the stack bounds 735 */ getStackDockedModeBoundsLocked(Rect currentTempTaskBounds, Rect outStackBounds, Rect outTempTaskBounds, boolean ignoreVisibility)736 void getStackDockedModeBoundsLocked(Rect currentTempTaskBounds, Rect outStackBounds, 737 Rect outTempTaskBounds, boolean ignoreVisibility) { 738 outTempTaskBounds.setEmpty(); 739 740 // When the home stack is resizable, should always have the same stack and task bounds 741 if (mStackId == HOME_STACK_ID) { 742 final Task homeTask = findHomeTask(); 743 if (homeTask != null && homeTask.isResizeable()) { 744 // Calculate the home stack bounds when in docked mode and the home stack is 745 // resizeable. 746 getDisplayContent().mDividerControllerLocked 747 .getHomeStackBoundsInDockedMode(outStackBounds); 748 } else { 749 // Home stack isn't resizeable, so don't specify stack bounds. 750 outStackBounds.setEmpty(); 751 } 752 753 outTempTaskBounds.set(outStackBounds); 754 return; 755 } 756 757 // When minimized state, the stack bounds for all non-home and docked stack bounds should 758 // match the passed task bounds 759 if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) { 760 outStackBounds.set(currentTempTaskBounds); 761 return; 762 } 763 764 if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) 765 || mDisplayContent == null) { 766 outStackBounds.set(mBounds); 767 return; 768 } 769 770 final TaskStack dockedStack = mDisplayContent.getDockedStackIgnoringVisibility(); 771 if (dockedStack == null) { 772 // Not sure why you are calling this method when there is no docked stack... 773 throw new IllegalStateException( 774 "Calling getStackDockedModeBoundsLocked() when there is no docked stack."); 775 } 776 if (!ignoreVisibility && !dockedStack.isVisible()) { 777 // The docked stack is being dismissed, but we caught before it finished being 778 // dismissed. In that case we want to treat it as if it is not occupying any space and 779 // let others occupy the whole display. 780 mDisplayContent.getLogicalDisplayRect(outStackBounds); 781 return; 782 } 783 784 final int dockedSide = dockedStack.getDockSide(); 785 if (dockedSide == DOCKED_INVALID) { 786 // Not sure how you got here...Only thing we can do is return current bounds. 787 Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack); 788 outStackBounds.set(mBounds); 789 return; 790 } 791 792 mDisplayContent.getLogicalDisplayRect(mTmpRect); 793 dockedStack.getRawBounds(mTmpRect2); 794 final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT; 795 getStackDockedModeBounds(mTmpRect, outStackBounds, mStackId, mTmpRect2, 796 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); 797 798 } 799 800 /** 801 * Outputs the bounds a stack should be given the presence of a docked stack on the display. 802 * @param displayRect The bounds of the display the docked stack is on. 803 * @param outBounds Output bounds that should be used for the stack. 804 * @param stackId Id of stack we are calculating the bounds for. 805 * @param dockedBounds Bounds of the docked stack. 806 * @param dockDividerWidth We need to know the width of the divider make to the output bounds 807 * close to the side of the dock. 808 * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen. 809 */ getStackDockedModeBounds( Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth, boolean dockOnTopOrLeft)810 private void getStackDockedModeBounds( 811 Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth, 812 boolean dockOnTopOrLeft) { 813 final boolean dockedStack = stackId == DOCKED_STACK_ID; 814 final boolean splitHorizontally = displayRect.width() > displayRect.height(); 815 816 outBounds.set(displayRect); 817 if (dockedStack) { 818 if (mService.mDockedStackCreateBounds != null) { 819 outBounds.set(mService.mDockedStackCreateBounds); 820 return; 821 } 822 823 // The initial bounds of the docked stack when it is created about half the screen space 824 // and its bounds can be adjusted after that. The bounds of all other stacks are 825 // adjusted to occupy whatever screen space the docked stack isn't occupying. 826 final DisplayInfo di = mDisplayContent.getDisplayInfo(); 827 mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, 828 mTmpRect2); 829 final int position = new DividerSnapAlgorithm(mService.mContext.getResources(), 830 di.logicalWidth, 831 di.logicalHeight, 832 dockDividerWidth, 833 mDisplayContent.getConfiguration().orientation == ORIENTATION_PORTRAIT, 834 mTmpRect2).getMiddleTarget().position; 835 836 if (dockOnTopOrLeft) { 837 if (splitHorizontally) { 838 outBounds.right = position; 839 } else { 840 outBounds.bottom = position; 841 } 842 } else { 843 if (splitHorizontally) { 844 outBounds.left = position + dockDividerWidth; 845 } else { 846 outBounds.top = position + dockDividerWidth; 847 } 848 } 849 return; 850 } 851 852 // Other stacks occupy whatever space is left by the docked stack. 853 if (!dockOnTopOrLeft) { 854 if (splitHorizontally) { 855 outBounds.right = dockedBounds.left - dockDividerWidth; 856 } else { 857 outBounds.bottom = dockedBounds.top - dockDividerWidth; 858 } 859 } else { 860 if (splitHorizontally) { 861 outBounds.left = dockedBounds.right + dockDividerWidth; 862 } else { 863 outBounds.top = dockedBounds.bottom + dockDividerWidth; 864 } 865 } 866 DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft); 867 } 868 resetDockedStackToMiddle()869 void resetDockedStackToMiddle() { 870 if (mStackId != DOCKED_STACK_ID) { 871 throw new IllegalStateException("Not a docked stack=" + this); 872 } 873 874 mService.mDockedStackCreateBounds = null; 875 876 final Rect bounds = new Rect(); 877 final Rect tempBounds = new Rect(); 878 getStackDockedModeBoundsLocked(null /* currentTempTaskBounds */, bounds, tempBounds, 879 true /*ignoreVisibility*/); 880 getController().requestResize(bounds); 881 } 882 883 @Override getController()884 StackWindowController getController() { 885 return (StackWindowController) super.getController(); 886 } 887 888 @Override removeIfPossible()889 void removeIfPossible() { 890 if (isAnimating()) { 891 mDeferRemoval = true; 892 return; 893 } 894 removeImmediately(); 895 } 896 897 @Override removeImmediately()898 void removeImmediately() { 899 super.removeImmediately(); 900 901 onRemovedFromDisplay(); 902 } 903 904 /** 905 * Removes the stack it from its current parent, so it can be either destroyed completely or 906 * re-parented. 907 */ onRemovedFromDisplay()908 void onRemovedFromDisplay() { 909 mDisplayContent.mDimLayerController.removeDimLayerUser(this); 910 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 911 912 if (mAnimationBackgroundSurface != null) { 913 mAnimationBackgroundSurface.destroySurface(); 914 mAnimationBackgroundSurface = null; 915 } 916 917 if (mStackId == DOCKED_STACK_ID) { 918 mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false); 919 } 920 921 mDisplayContent = null; 922 mService.mWindowPlacerLocked.requestTraversal(); 923 } 924 resetAnimationBackgroundAnimator()925 void resetAnimationBackgroundAnimator() { 926 mAnimationBackgroundAnimator = null; 927 mAnimationBackgroundSurface.hide(); 928 } 929 setAnimationBackground(WindowStateAnimator winAnimator, int color)930 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 931 int animLayer = winAnimator.mAnimLayer; 932 if (mAnimationBackgroundAnimator == null 933 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { 934 mAnimationBackgroundAnimator = winAnimator; 935 animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator); 936 mAnimationBackgroundSurface.show(animLayer - LAYER_OFFSET_DIM, 937 ((color >> 24) & 0xff) / 255f, 0); 938 } 939 } 940 941 // TODO: Should each user have there own stacks? 942 @Override switchUser()943 void switchUser() { 944 super.switchUser(); 945 int top = mChildren.size(); 946 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 947 Task task = mChildren.get(taskNdx); 948 if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) { 949 mChildren.remove(taskNdx); 950 mChildren.add(task); 951 --top; 952 } 953 } 954 } 955 956 /** 957 * Adjusts the stack bounds if the IME is visible. 958 * 959 * @param imeWin The IME window. 960 */ setAdjustedForIme(WindowState imeWin, boolean forceUpdate)961 void setAdjustedForIme(WindowState imeWin, boolean forceUpdate) { 962 mImeWin = imeWin; 963 mImeGoingAway = false; 964 if (!mAdjustedForIme || forceUpdate) { 965 mAdjustedForIme = true; 966 mAdjustImeAmount = 0f; 967 mAdjustDividerAmount = 0f; 968 updateAdjustForIme(0f, 0f, true /* force */); 969 } 970 } 971 isAdjustedForIme()972 boolean isAdjustedForIme() { 973 return mAdjustedForIme; 974 } 975 isAnimatingForIme()976 boolean isAnimatingForIme() { 977 return mImeWin != null && mImeWin.isAnimatingLw(); 978 } 979 980 /** 981 * Update the stack's bounds (crop or position) according to the IME window's 982 * current position. When IME window is animated, the bottom stack is animated 983 * together to track the IME window's current position, and the top stack is 984 * cropped as necessary. 985 * 986 * @return true if a traversal should be performed after the adjustment. 987 */ updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force)988 boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) { 989 if (adjustAmount != mAdjustImeAmount 990 || adjustDividerAmount != mAdjustDividerAmount || force) { 991 mAdjustImeAmount = adjustAmount; 992 mAdjustDividerAmount = adjustDividerAmount; 993 updateAdjustedBounds(); 994 return isVisible(); 995 } else { 996 return false; 997 } 998 } 999 1000 /** 1001 * Resets the adjustment after it got adjusted for the IME. 1002 * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about 1003 * animations; otherwise, set flag and animates the window away together 1004 * with IME window. 1005 */ resetAdjustedForIme(boolean adjustBoundsNow)1006 void resetAdjustedForIme(boolean adjustBoundsNow) { 1007 if (adjustBoundsNow) { 1008 mImeWin = null; 1009 mAdjustedForIme = false; 1010 mImeGoingAway = false; 1011 mAdjustImeAmount = 0f; 1012 mAdjustDividerAmount = 0f; 1013 updateAdjustedBounds(); 1014 mService.setResizeDimLayer(false, mStackId, 1.0f); 1015 } else { 1016 mImeGoingAway |= mAdjustedForIme; 1017 } 1018 } 1019 1020 /** 1021 * Sets the amount how much we currently minimize our stack. 1022 * 1023 * @param minimizeAmount The amount, between 0 and 1. 1024 * @return Whether the amount has changed and a layout is needed. 1025 */ setAdjustedForMinimizedDock(float minimizeAmount)1026 boolean setAdjustedForMinimizedDock(float minimizeAmount) { 1027 if (minimizeAmount != mMinimizeAmount) { 1028 mMinimizeAmount = minimizeAmount; 1029 updateAdjustedBounds(); 1030 return isVisible(); 1031 } else { 1032 return false; 1033 } 1034 } 1035 shouldIgnoreInput()1036 boolean shouldIgnoreInput() { 1037 return isAdjustedForMinimizedDockedStack() || mStackId == DOCKED_STACK_ID && 1038 isMinimizedDockAndHomeStackResizable(); 1039 } 1040 1041 /** 1042 * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows 1043 * to the list of to be drawn windows the service is waiting for. 1044 */ beginImeAdjustAnimation()1045 void beginImeAdjustAnimation() { 1046 for (int j = mChildren.size() - 1; j >= 0; j--) { 1047 final Task task = mChildren.get(j); 1048 if (task.hasContentToDisplay()) { 1049 task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER); 1050 task.setWaitingForDrawnIfResizingChanged(); 1051 } 1052 } 1053 } 1054 1055 /** 1056 * Resets the resizing state of all windows. 1057 */ endImeAdjustAnimation()1058 void endImeAdjustAnimation() { 1059 for (int j = mChildren.size() - 1; j >= 0; j--) { 1060 mChildren.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER); 1061 } 1062 } 1063 getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom)1064 int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) { 1065 return displayContentRect.top + (int) 1066 ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN); 1067 } 1068 adjustForIME(final WindowState imeWin)1069 private boolean adjustForIME(final WindowState imeWin) { 1070 final int dockedSide = getDockSide(); 1071 final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; 1072 if (imeWin == null || !dockedTopOrBottom) { 1073 return false; 1074 } 1075 1076 final Rect displayContentRect = mTmpRect; 1077 final Rect contentBounds = mTmpRect2; 1078 1079 // Calculate the content bounds excluding the area occupied by IME 1080 getDisplayContent().getContentRect(displayContentRect); 1081 contentBounds.set(displayContentRect); 1082 int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top); 1083 1084 imeTop += imeWin.getGivenContentInsetsLw().top; 1085 if (contentBounds.bottom > imeTop) { 1086 contentBounds.bottom = imeTop; 1087 } 1088 1089 final int yOffset = displayContentRect.bottom - contentBounds.bottom; 1090 1091 final int dividerWidth = 1092 getDisplayContent().mDividerControllerLocked.getContentWidth(); 1093 final int dividerWidthInactive = 1094 getDisplayContent().mDividerControllerLocked.getContentWidthInactive(); 1095 1096 if (dockedSide == DOCKED_TOP) { 1097 // If this stack is docked on top, we make it smaller so the bottom stack is not 1098 // occluded by IME. We shift its bottom up by the height of the IME, but 1099 // leaves at least 30% of the top stack visible. 1100 final int minTopStackBottom = 1101 getMinTopStackBottom(displayContentRect, mBounds.bottom); 1102 final int bottom = Math.max( 1103 mBounds.bottom - yOffset + dividerWidth - dividerWidthInactive, 1104 minTopStackBottom); 1105 mTmpAdjustedBounds.set(mBounds); 1106 mTmpAdjustedBounds.bottom = 1107 (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom); 1108 mFullyAdjustedImeBounds.set(mBounds); 1109 } else { 1110 // When the stack is on bottom and has no focus, it's only adjusted for divider width. 1111 final int dividerWidthDelta = dividerWidthInactive - dividerWidth; 1112 1113 // When the stack is on bottom and has focus, it needs to be moved up so as to 1114 // not occluded by IME, and at the same time adjusted for divider width. 1115 // We try to move it up by the height of the IME window, but only to the extent 1116 // that leaves at least 30% of the top stack visible. 1117 // 'top' is where the top of bottom stack will move to in this case. 1118 final int topBeforeImeAdjust = mBounds.top - dividerWidth + dividerWidthInactive; 1119 final int minTopStackBottom = 1120 getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth); 1121 final int top = Math.max( 1122 mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive); 1123 1124 mTmpAdjustedBounds.set(mBounds); 1125 // Account for the adjustment for IME and divider width separately. 1126 // (top - topBeforeImeAdjust) is the amount of movement due to IME only, 1127 // and dividerWidthDelta is due to divider width change only. 1128 mTmpAdjustedBounds.top = mBounds.top + 1129 (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) + 1130 mAdjustDividerAmount * dividerWidthDelta); 1131 mFullyAdjustedImeBounds.set(mBounds); 1132 mFullyAdjustedImeBounds.top = top; 1133 mFullyAdjustedImeBounds.bottom = top + mBounds.height(); 1134 } 1135 return true; 1136 } 1137 adjustForMinimizedDockedStack(float minimizeAmount)1138 private boolean adjustForMinimizedDockedStack(float minimizeAmount) { 1139 final int dockSide = getDockSide(); 1140 if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) { 1141 return false; 1142 } 1143 1144 if (dockSide == DOCKED_TOP) { 1145 mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); 1146 int topInset = mTmpRect.top; 1147 mTmpAdjustedBounds.set(mBounds); 1148 mTmpAdjustedBounds.bottom = 1149 (int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom); 1150 } else if (dockSide == DOCKED_LEFT) { 1151 mTmpAdjustedBounds.set(mBounds); 1152 final int width = mBounds.width(); 1153 mTmpAdjustedBounds.right = 1154 (int) (minimizeAmount * mDockedStackMinimizeThickness 1155 + (1 - minimizeAmount) * mBounds.right); 1156 mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width; 1157 } else if (dockSide == DOCKED_RIGHT) { 1158 mTmpAdjustedBounds.set(mBounds); 1159 mTmpAdjustedBounds.left = 1160 (int) (minimizeAmount * (mBounds.right - mDockedStackMinimizeThickness) 1161 + (1 - minimizeAmount) * mBounds.left); 1162 } 1163 return true; 1164 } 1165 isMinimizedDockAndHomeStackResizable()1166 private boolean isMinimizedDockAndHomeStackResizable() { 1167 return mDisplayContent.mDividerControllerLocked.isMinimizedDock() 1168 && mDisplayContent.mDividerControllerLocked.isHomeStackResizable(); 1169 } 1170 1171 /** 1172 * @return the distance in pixels how much the stack gets minimized from it's original size 1173 */ getMinimizeDistance()1174 int getMinimizeDistance() { 1175 final int dockSide = getDockSide(); 1176 if (dockSide == DOCKED_INVALID) { 1177 return 0; 1178 } 1179 1180 if (dockSide == DOCKED_TOP) { 1181 mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); 1182 int topInset = mTmpRect.top; 1183 return mBounds.bottom - topInset; 1184 } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) { 1185 return mBounds.width() - mDockedStackMinimizeThickness; 1186 } else { 1187 return 0; 1188 } 1189 } 1190 1191 /** 1192 * Updates the adjustment depending on it's current state. 1193 */ updateAdjustedBounds()1194 private void updateAdjustedBounds() { 1195 boolean adjust = false; 1196 if (mMinimizeAmount != 0f) { 1197 adjust = adjustForMinimizedDockedStack(mMinimizeAmount); 1198 } else if (mAdjustedForIme) { 1199 adjust = adjustForIME(mImeWin); 1200 } 1201 if (!adjust) { 1202 mTmpAdjustedBounds.setEmpty(); 1203 } 1204 setAdjustedBounds(mTmpAdjustedBounds); 1205 1206 final boolean isImeTarget = (mService.getImeFocusStackLocked() == this); 1207 if (mAdjustedForIme && adjust && !isImeTarget) { 1208 final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount) 1209 * IME_ADJUST_DIM_AMOUNT; 1210 mService.setResizeDimLayer(true, mStackId, alpha); 1211 } 1212 } 1213 applyAdjustForImeIfNeeded(Task task)1214 void applyAdjustForImeIfNeeded(Task task) { 1215 if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) { 1216 return; 1217 } 1218 1219 final Rect insetBounds = mImeGoingAway ? mBounds : mFullyAdjustedImeBounds; 1220 task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP); 1221 mDisplayContent.setLayoutNeeded(); 1222 } 1223 isAdjustedForMinimizedDockedStack()1224 boolean isAdjustedForMinimizedDockedStack() { 1225 return mMinimizeAmount != 0f; 1226 } 1227 dump(String prefix, PrintWriter pw)1228 public void dump(String prefix, PrintWriter pw) { 1229 pw.println(prefix + "mStackId=" + mStackId); 1230 pw.println(prefix + "mDeferRemoval=" + mDeferRemoval); 1231 pw.println(prefix + "mFillsParent=" + mFillsParent); 1232 pw.println(prefix + "mBounds=" + mBounds.toShortString()); 1233 if (mMinimizeAmount != 0f) { 1234 pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount); 1235 } 1236 if (mAdjustedForIme) { 1237 pw.println(prefix + "mAdjustedForIme=true"); 1238 pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount); 1239 pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount); 1240 } 1241 if (!mAdjustedBounds.isEmpty()) { 1242 pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString()); 1243 } 1244 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) { 1245 mChildren.get(taskNdx).dump(prefix + " ", pw); 1246 } 1247 if (mAnimationBackgroundSurface.isDimming()) { 1248 pw.println(prefix + "mWindowAnimationBackgroundSurface:"); 1249 mAnimationBackgroundSurface.printTo(prefix + " ", pw); 1250 } 1251 if (!mExitingAppTokens.isEmpty()) { 1252 pw.println(); 1253 pw.println(" Exiting application tokens:"); 1254 for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) { 1255 WindowToken token = mExitingAppTokens.get(i); 1256 pw.print(" Exiting App #"); pw.print(i); 1257 pw.print(' '); pw.print(token); 1258 pw.println(':'); 1259 token.dump(pw, " "); 1260 } 1261 } 1262 } 1263 1264 /** Fullscreen status of the stack without adjusting for other factors in the system like 1265 * visibility of docked stack. 1266 * Most callers should be using {@link #fillsParent} as it take into consideration other 1267 * system factors. */ getRawFullscreen()1268 boolean getRawFullscreen() { 1269 return mFillsParent; 1270 } 1271 1272 @Override dimFullscreen()1273 public boolean dimFullscreen() { 1274 return StackId.isHomeOrRecentsStack(mStackId) || fillsParent(); 1275 } 1276 1277 @Override fillsParent()1278 boolean fillsParent() { 1279 if (useCurrentBounds()) { 1280 return mFillsParent; 1281 } 1282 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 1283 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 1284 // system. 1285 return true; 1286 } 1287 1288 @Override getDisplayInfo()1289 public DisplayInfo getDisplayInfo() { 1290 return mDisplayContent.getDisplayInfo(); 1291 } 1292 1293 @Override isAttachedToDisplay()1294 public boolean isAttachedToDisplay() { 1295 return mDisplayContent != null; 1296 } 1297 1298 @Override toString()1299 public String toString() { 1300 return "{stackId=" + mStackId + " tasks=" + mChildren + "}"; 1301 } 1302 getName()1303 String getName() { 1304 return toShortString(); 1305 } 1306 1307 @Override toShortString()1308 public String toShortString() { 1309 return "Stack=" + mStackId; 1310 } 1311 1312 /** 1313 * For docked workspace (or workspace that's side-by-side to the docked), provides 1314 * information which side of the screen was the dock anchored. 1315 */ getDockSide()1316 int getDockSide() { 1317 return getDockSide(mBounds); 1318 } 1319 getDockSide(Rect bounds)1320 int getDockSide(Rect bounds) { 1321 if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) { 1322 return DOCKED_INVALID; 1323 } 1324 if (mDisplayContent == null) { 1325 return DOCKED_INVALID; 1326 } 1327 mDisplayContent.getLogicalDisplayRect(mTmpRect); 1328 final int orientation = mDisplayContent.getConfiguration().orientation; 1329 return getDockSideUnchecked(bounds, mTmpRect, orientation); 1330 } 1331 getDockSideUnchecked(Rect bounds, Rect displayRect, int orientation)1332 static int getDockSideUnchecked(Rect bounds, Rect displayRect, int orientation) { 1333 if (orientation == Configuration.ORIENTATION_PORTRAIT) { 1334 // Portrait mode, docked either at the top or the bottom. 1335 if (bounds.top - displayRect.top <= displayRect.bottom - bounds.bottom) { 1336 return DOCKED_TOP; 1337 } else { 1338 return DOCKED_BOTTOM; 1339 } 1340 } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 1341 // Landscape mode, docked either on the left or on the right. 1342 if (bounds.left - displayRect.left <= displayRect.right - bounds.right) { 1343 return DOCKED_LEFT; 1344 } else { 1345 return DOCKED_RIGHT; 1346 } 1347 } else { 1348 return DOCKED_INVALID; 1349 } 1350 } 1351 hasTaskForUser(int userId)1352 boolean hasTaskForUser(int userId) { 1353 for (int i = mChildren.size() - 1; i >= 0; i--) { 1354 final Task task = mChildren.get(i); 1355 if (task.mUserId == userId) { 1356 return true; 1357 } 1358 } 1359 return false; 1360 } 1361 taskIdFromPoint(int x, int y)1362 int taskIdFromPoint(int x, int y) { 1363 getBounds(mTmpRect); 1364 if (!mTmpRect.contains(x, y) || isAdjustedForMinimizedDockedStack()) { 1365 return -1; 1366 } 1367 1368 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 1369 final Task task = mChildren.get(taskNdx); 1370 final WindowState win = task.getTopVisibleAppMainWindow(); 1371 if (win == null) { 1372 continue; 1373 } 1374 // We need to use the task's dim bounds (which is derived from the visible bounds of its 1375 // apps windows) for any touch-related tests. Can't use the task's original bounds 1376 // because it might be adjusted to fit the content frame. For example, the presence of 1377 // the IME adjusting the windows frames when the app window is the IME target. 1378 task.getDimBounds(mTmpRect); 1379 if (mTmpRect.contains(x, y)) { 1380 return task.mTaskId; 1381 } 1382 } 1383 1384 return -1; 1385 } 1386 findTaskForResizePoint(int x, int y, int delta, DisplayContent.TaskForResizePointSearchResult results)1387 void findTaskForResizePoint(int x, int y, int delta, 1388 DisplayContent.TaskForResizePointSearchResult results) { 1389 if (!StackId.isTaskResizeAllowed(mStackId)) { 1390 results.searchDone = true; 1391 return; 1392 } 1393 1394 for (int i = mChildren.size() - 1; i >= 0; --i) { 1395 final Task task = mChildren.get(i); 1396 if (task.isFullscreen()) { 1397 results.searchDone = true; 1398 return; 1399 } 1400 1401 // We need to use the task's dim bounds (which is derived from the visible bounds of 1402 // its apps windows) for any touch-related tests. Can't use the task's original 1403 // bounds because it might be adjusted to fit the content frame. One example is when 1404 // the task is put to top-left quadrant, the actual visible area would not start at 1405 // (0,0) after it's adjusted for the status bar. 1406 task.getDimBounds(mTmpRect); 1407 mTmpRect.inset(-delta, -delta); 1408 if (mTmpRect.contains(x, y)) { 1409 mTmpRect.inset(delta, delta); 1410 1411 results.searchDone = true; 1412 1413 if (!mTmpRect.contains(x, y)) { 1414 results.taskForResize = task; 1415 return; 1416 } 1417 // User touched inside the task. No need to look further, 1418 // focus transfer will be handled in ACTION_UP. 1419 return; 1420 } 1421 } 1422 } 1423 setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion, Rect contentRect, Rect postExclude)1424 void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion, 1425 Rect contentRect, Rect postExclude) { 1426 for (int i = mChildren.size() - 1; i >= 0; --i) { 1427 final Task task = mChildren.get(i); 1428 AppWindowToken token = task.getTopVisibleAppToken(); 1429 if (token == null || !token.hasContentToDisplay()) { 1430 continue; 1431 } 1432 1433 /** 1434 * Exclusion region is the region that TapDetector doesn't care about. 1435 * Here we want to remove all non-focused tasks from the exclusion region. 1436 * We also remove the outside touch area for resizing for all freeform 1437 * tasks (including the focused). 1438 * 1439 * We save the focused task region once we find it, and add it back at the end. 1440 * 1441 * If the task is home stack and it is resizable in the minimized state, we want to 1442 * exclude the docked stack from touch so we need the entire screen area and not just a 1443 * small portion which the home stack currently is resized to. 1444 */ 1445 1446 if (task.isHomeTask() && isMinimizedDockAndHomeStackResizable()) { 1447 mDisplayContent.getLogicalDisplayRect(mTmpRect); 1448 } else { 1449 task.getDimBounds(mTmpRect); 1450 } 1451 1452 if (task == focusedTask) { 1453 // Add the focused task rect back into the exclude region once we are done 1454 // processing stacks. 1455 postExclude.set(mTmpRect); 1456 } 1457 1458 final boolean isFreeformed = task.inFreeformWorkspace(); 1459 if (task != focusedTask || isFreeformed) { 1460 if (isFreeformed) { 1461 // If the task is freeformed, enlarge the area to account for outside 1462 // touch area for resize. 1463 mTmpRect.inset(-delta, -delta); 1464 // Intersect with display content rect. If we have system decor (status bar/ 1465 // navigation bar), we want to exclude that from the tap detection. 1466 // Otherwise, if the app is partially placed under some system button (eg. 1467 // Recents, Home), pressing that button would cause a full series of 1468 // unwanted transfer focus/resume/pause, before we could go home. 1469 mTmpRect.intersect(contentRect); 1470 } 1471 touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE); 1472 } 1473 } 1474 } 1475 setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds)1476 public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) { 1477 // Hold the lock since this is called from the BoundsAnimator running on the UiThread 1478 synchronized (mService.mWindowMap) { 1479 if (mCancelCurrentBoundsAnimation) { 1480 return false; 1481 } 1482 } 1483 1484 try { 1485 mService.mActivityManager.resizePinnedStack(stackBounds, tempTaskBounds); 1486 } catch (RemoteException e) { 1487 // I don't believe you. 1488 } 1489 return true; 1490 } 1491 onAllWindowsDrawn()1492 void onAllWindowsDrawn() { 1493 if (!mBoundsAnimating && !mBoundsAnimatingRequested) { 1494 return; 1495 } 1496 1497 mService.mBoundsAnimationController.onAllWindowsDrawn(); 1498 } 1499 1500 @Override // AnimatesBounds onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate)1501 public void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) { 1502 // Hold the lock since this is called from the BoundsAnimator running on the UiThread 1503 synchronized (mService.mWindowMap) { 1504 mBoundsAnimatingRequested = false; 1505 mBoundsAnimating = true; 1506 mCancelCurrentBoundsAnimation = false; 1507 1508 // If we are changing UI mode, as in the PiP to fullscreen 1509 // transition, then we need to wait for the window to draw. 1510 if (schedulePipModeChangedCallback) { 1511 forAllWindows((w) -> { w.mWinAnimator.resetDrawState(); }, 1512 false /* traverseTopToBottom */); 1513 } 1514 } 1515 1516 if (mStackId == PINNED_STACK_ID) { 1517 try { 1518 mService.mActivityManager.notifyPinnedStackAnimationStarted(); 1519 } catch (RemoteException e) { 1520 // I don't believe you... 1521 } 1522 1523 final PinnedStackWindowController controller = 1524 (PinnedStackWindowController) getController(); 1525 if (schedulePipModeChangedCallback && controller != null) { 1526 // We need to schedule the PiP mode change before the animation up. It is possible 1527 // in this case for the animation down to not have been completed, so always 1528 // force-schedule and update to the client to ensure that it is notified that it 1529 // is no longer in picture-in-picture mode 1530 controller.updatePictureInPictureModeForPinnedStackAnimation(null, forceUpdate); 1531 } 1532 } 1533 } 1534 1535 @Override // AnimatesBounds onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, boolean moveToFullscreen)1536 public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, 1537 boolean moveToFullscreen) { 1538 // Hold the lock since this is called from the BoundsAnimator running on the UiThread 1539 synchronized (mService.mWindowMap) { 1540 mBoundsAnimating = false; 1541 for (int i = 0; i < mChildren.size(); i++) { 1542 final Task t = mChildren.get(i); 1543 t.clearPreserveNonFloatingState(); 1544 } 1545 mService.requestTraversal(); 1546 } 1547 1548 if (mStackId == PINNED_STACK_ID) { 1549 // Update to the final bounds if requested. This is done here instead of in the bounds 1550 // animator to allow us to coordinate this after we notify the PiP mode changed 1551 1552 final PinnedStackWindowController controller = 1553 (PinnedStackWindowController) getController(); 1554 if (schedulePipModeChangedCallback && controller != null) { 1555 // We need to schedule the PiP mode change after the animation down, so use the 1556 // final bounds 1557 controller.updatePictureInPictureModeForPinnedStackAnimation( 1558 mBoundsAnimationTarget, false /* forceUpdate */); 1559 } 1560 1561 if (finalStackSize != null) { 1562 setPinnedStackSize(finalStackSize, null); 1563 } 1564 1565 try { 1566 mService.mActivityManager.notifyPinnedStackAnimationEnded(); 1567 if (moveToFullscreen) { 1568 mService.mActivityManager.moveTasksToFullscreenStack(mStackId, 1569 true /* onTop */); 1570 } 1571 } catch (RemoteException e) { 1572 // I don't believe you... 1573 } 1574 } 1575 } 1576 1577 /** 1578 * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen 1579 * bounds and we have a deferred PiP mode changed callback set with the animation. 1580 */ deferScheduleMultiWindowModeChanged()1581 public boolean deferScheduleMultiWindowModeChanged() { 1582 if (mStackId == PINNED_STACK_ID) { 1583 return (mBoundsAnimatingRequested || mBoundsAnimating); 1584 } 1585 return false; 1586 } 1587 hasMovementAnimations()1588 public boolean hasMovementAnimations() { 1589 return StackId.hasMovementAnimations(mStackId); 1590 } 1591 isForceScaled()1592 public boolean isForceScaled() { 1593 return mBoundsAnimating; 1594 } 1595 isAnimatingBounds()1596 public boolean isAnimatingBounds() { 1597 return mBoundsAnimating; 1598 } 1599 lastAnimatingBoundsWasToFullscreen()1600 public boolean lastAnimatingBoundsWasToFullscreen() { 1601 return mBoundsAnimatingToFullscreen; 1602 } 1603 isAnimatingBoundsToFullscreen()1604 public boolean isAnimatingBoundsToFullscreen() { 1605 return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen(); 1606 } 1607 pinnedStackResizeDisallowed()1608 public boolean pinnedStackResizeDisallowed() { 1609 if (mBoundsAnimating && mCancelCurrentBoundsAnimation) { 1610 return true; 1611 } 1612 return false; 1613 } 1614 1615 /** Returns true if a removal action is still being deferred. */ checkCompleteDeferredRemoval()1616 boolean checkCompleteDeferredRemoval() { 1617 if (isAnimating()) { 1618 return true; 1619 } 1620 if (mDeferRemoval) { 1621 removeImmediately(); 1622 } 1623 1624 return super.checkCompleteDeferredRemoval(); 1625 } 1626 stepAppWindowsAnimation(long currentTime)1627 void stepAppWindowsAnimation(long currentTime) { 1628 super.stepAppWindowsAnimation(currentTime); 1629 1630 // TODO: Why aren't we just using the loop above for this? mAppAnimator.animating isn't set 1631 // below but is set in the loop above. See if it really matters... 1632 1633 // Clear before using. 1634 mTmpAppTokens.clear(); 1635 // We copy the list as things can be removed from the exiting token list while we are 1636 // processing. 1637 mTmpAppTokens.addAll(mExitingAppTokens); 1638 for (int i = 0; i < mTmpAppTokens.size(); i++) { 1639 final AppWindowAnimator appAnimator = mTmpAppTokens.get(i).mAppAnimator; 1640 appAnimator.wasAnimating = appAnimator.animating; 1641 if (appAnimator.stepAnimationLocked(currentTime)) { 1642 mService.mAnimator.setAnimating(true); 1643 mService.mAnimator.mAppWindowAnimating = true; 1644 } else if (appAnimator.wasAnimating) { 1645 // stopped animating, do one more pass through the layout 1646 appAnimator.mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER, 1647 "exiting appToken " + appAnimator.mAppToken + " done"); 1648 if (DEBUG_ANIM) Slog.v(TAG_WM, 1649 "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken); 1650 } 1651 } 1652 // Clear to avoid holding reference to tokens. 1653 mTmpAppTokens.clear(); 1654 } 1655 1656 @Override getOrientation()1657 int getOrientation() { 1658 return (StackId.canSpecifyOrientation(mStackId)) 1659 ? super.getOrientation() : SCREEN_ORIENTATION_UNSET; 1660 } 1661 } 1662