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 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 153 DisplayContent getDisplayContent() { 154 return mDisplayContent; 155 } 156 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 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 * */ 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 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 */ 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 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 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. */ 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. */ 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 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 */ 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 */ 349 void getFinalAnimationBounds(Rect outBounds) { 350 outBounds.set(mBoundsAnimationTarget); 351 } 352 353 /** 354 * @return the final source bounds for the bounds animation. 355 */ 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 */ 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 374 public void getDimBounds(Rect out) { 375 getBounds(out); 376 } 377 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. */ 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 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 */ 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 */ 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. 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 */ 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 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 */ 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. 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 */ 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 */ 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 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 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 */ 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 */ 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 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 884 StackWindowController getController() { 885 return (StackWindowController) super.getController(); 886 } 887 888 @Override 889 void removeIfPossible() { 890 if (isAnimating()) { 891 mDeferRemoval = true; 892 return; 893 } 894 removeImmediately(); 895 } 896 897 @Override 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 */ 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 925 void resetAnimationBackgroundAnimator() { 926 mAnimationBackgroundAnimator = null; 927 mAnimationBackgroundSurface.hide(); 928 } 929 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 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 */ 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 972 boolean isAdjustedForIme() { 973 return mAdjustedForIme; 974 } 975 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 1064 int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) { 1065 return displayContentRect.top + (int) 1066 ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN); 1067 } 1068 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 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 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 */ 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 */ 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 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 1224 boolean isAdjustedForMinimizedDockedStack() { 1225 return mMinimizeAmount != 0f; 1226 } 1227 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. */ 1268 boolean getRawFullscreen() { 1269 return mFillsParent; 1270 } 1271 1272 @Override 1273 public boolean dimFullscreen() { 1274 return StackId.isHomeOrRecentsStack(mStackId) || fillsParent(); 1275 } 1276 1277 @Override 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 1289 public DisplayInfo getDisplayInfo() { 1290 return mDisplayContent.getDisplayInfo(); 1291 } 1292 1293 @Override 1294 public boolean isAttachedToDisplay() { 1295 return mDisplayContent != null; 1296 } 1297 1298 @Override 1299 public String toString() { 1300 return "{stackId=" + mStackId + " tasks=" + mChildren + "}"; 1301 } 1302 1303 String getName() { 1304 return toShortString(); 1305 } 1306 1307 @Override 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 */ 1316 int getDockSide() { 1317 return getDockSide(mBounds); 1318 } 1319 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 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 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 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 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 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 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 1492 void onAllWindowsDrawn() { 1493 if (!mBoundsAnimating && !mBoundsAnimatingRequested) { 1494 return; 1495 } 1496 1497 mService.mBoundsAnimationController.onAllWindowsDrawn(); 1498 } 1499 1500 @Override // AnimatesBounds 1501 public void onAnimationStart(boolean schedulePipModeChangedCallback) { 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 after the animation down, so use the 1527 // final bounds 1528 controller.updatePictureInPictureModeForPinnedStackAnimation(null); 1529 } 1530 } 1531 } 1532 1533 @Override // AnimatesBounds 1534 public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize, 1535 boolean moveToFullscreen) { 1536 // Hold the lock since this is called from the BoundsAnimator running on the UiThread 1537 synchronized (mService.mWindowMap) { 1538 mBoundsAnimating = false; 1539 for (int i = 0; i < mChildren.size(); i++) { 1540 final Task t = mChildren.get(i); 1541 t.clearPreserveNonFloatingState(); 1542 } 1543 mService.requestTraversal(); 1544 } 1545 1546 if (mStackId == PINNED_STACK_ID) { 1547 // Update to the final bounds if requested. This is done here instead of in the bounds 1548 // animator to allow us to coordinate this after we notify the PiP mode changed 1549 1550 final PinnedStackWindowController controller = 1551 (PinnedStackWindowController) getController(); 1552 if (schedulePipModeChangedCallback && controller != null) { 1553 // We need to schedule the PiP mode change after the animation down, so use the 1554 // final bounds 1555 controller.updatePictureInPictureModeForPinnedStackAnimation( 1556 mBoundsAnimationTarget); 1557 } 1558 1559 if (finalStackSize != null) { 1560 setPinnedStackSize(finalStackSize, null); 1561 } 1562 1563 try { 1564 mService.mActivityManager.notifyPinnedStackAnimationEnded(); 1565 if (moveToFullscreen) { 1566 mService.mActivityManager.moveTasksToFullscreenStack(mStackId, 1567 true /* onTop */); 1568 } 1569 } catch (RemoteException e) { 1570 // I don't believe you... 1571 } 1572 } 1573 } 1574 1575 /** 1576 * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen 1577 * bounds and we have a deferred PiP mode changed callback set with the animation. 1578 */ 1579 public boolean deferScheduleMultiWindowModeChanged() { 1580 if (mStackId == PINNED_STACK_ID) { 1581 return (mBoundsAnimatingRequested || mBoundsAnimating); 1582 } 1583 return false; 1584 } 1585 1586 public boolean hasMovementAnimations() { 1587 return StackId.hasMovementAnimations(mStackId); 1588 } 1589 1590 public boolean isForceScaled() { 1591 return mBoundsAnimating; 1592 } 1593 1594 public boolean isAnimatingBounds() { 1595 return mBoundsAnimating; 1596 } 1597 1598 public boolean lastAnimatingBoundsWasToFullscreen() { 1599 return mBoundsAnimatingToFullscreen; 1600 } 1601 1602 public boolean isAnimatingBoundsToFullscreen() { 1603 return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen(); 1604 } 1605 1606 public boolean pinnedStackResizeDisallowed() { 1607 if (mBoundsAnimating && mCancelCurrentBoundsAnimation) { 1608 return true; 1609 } 1610 return false; 1611 } 1612 1613 /** Returns true if a removal action is still being deferred. */ 1614 boolean checkCompleteDeferredRemoval() { 1615 if (isAnimating()) { 1616 return true; 1617 } 1618 if (mDeferRemoval) { 1619 removeImmediately(); 1620 } 1621 1622 return super.checkCompleteDeferredRemoval(); 1623 } 1624 1625 void stepAppWindowsAnimation(long currentTime) { 1626 super.stepAppWindowsAnimation(currentTime); 1627 1628 // TODO: Why aren't we just using the loop above for this? mAppAnimator.animating isn't set 1629 // below but is set in the loop above. See if it really matters... 1630 1631 // Clear before using. 1632 mTmpAppTokens.clear(); 1633 // We copy the list as things can be removed from the exiting token list while we are 1634 // processing. 1635 mTmpAppTokens.addAll(mExitingAppTokens); 1636 for (int i = 0; i < mTmpAppTokens.size(); i++) { 1637 final AppWindowAnimator appAnimator = mTmpAppTokens.get(i).mAppAnimator; 1638 appAnimator.wasAnimating = appAnimator.animating; 1639 if (appAnimator.stepAnimationLocked(currentTime)) { 1640 mService.mAnimator.setAnimating(true); 1641 mService.mAnimator.mAppWindowAnimating = true; 1642 } else if (appAnimator.wasAnimating) { 1643 // stopped animating, do one more pass through the layout 1644 appAnimator.mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER, 1645 "exiting appToken " + appAnimator.mAppToken + " done"); 1646 if (DEBUG_ANIM) Slog.v(TAG_WM, 1647 "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken); 1648 } 1649 } 1650 // Clear to avoid holding reference to tokens. 1651 mTmpAppTokens.clear(); 1652 } 1653 1654 @Override 1655 int getOrientation() { 1656 return (StackId.canSpecifyOrientation(mStackId)) 1657 ? super.getOrientation() : SCREEN_ORIENTATION_UNSET; 1658 } 1659 } 1660