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