TaskStack.java revision 42625d1bc7ef99c4d4435e8cdebfe3eee57b8d97
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 android.app.ActivityManager.StackId; 20import android.content.res.Configuration; 21import android.graphics.Rect; 22import android.os.Debug; 23import android.os.RemoteException; 24import android.util.EventLog; 25import android.util.Slog; 26import android.util.SparseArray; 27import android.view.DisplayInfo; 28import android.view.Surface; 29 30import com.android.internal.policy.DividerSnapAlgorithm; 31import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; 32import com.android.internal.policy.DockedDividerUtils; 33import com.android.server.EventLogTags; 34 35import java.io.PrintWriter; 36import java.util.ArrayList; 37 38import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 39import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; 40import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; 41import static android.app.ActivityManager.StackId.PINNED_STACK_ID; 42import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 43import static android.view.WindowManager.DOCKED_BOTTOM; 44import static android.view.WindowManager.DOCKED_INVALID; 45import static android.view.WindowManager.DOCKED_LEFT; 46import static android.view.WindowManager.DOCKED_RIGHT; 47import static android.view.WindowManager.DOCKED_TOP; 48import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; 49import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK; 50import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 51 52public class TaskStack implements DimLayer.DimLayerUser, 53 BoundsAnimationController.AnimateBoundsUser { 54 55 // If the stack should be resized to fullscreen. 56 private static final boolean FULLSCREEN = true; 57 58 /** Unique identifier */ 59 final int mStackId; 60 61 /** The service */ 62 private final WindowManagerService mService; 63 64 /** The display this stack sits under. */ 65 private DisplayContent mDisplayContent; 66 67 /** The Tasks that define this stack. Oldest Tasks are at the bottom. The ordering must match 68 * mTaskHistory in the ActivityStack with the same mStackId */ 69 private final ArrayList<Task> mTasks = new ArrayList<>(); 70 71 /** For comparison with DisplayContent bounds. */ 72 private Rect mTmpRect = new Rect(); 73 private Rect mTmpRect2 = new Rect(); 74 75 /** Content limits relative to the DisplayContent this sits in. */ 76 private Rect mBounds = new Rect(); 77 78 /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */ 79 private final Rect mAdjustedBounds = new Rect(); 80 81 /** Whether mBounds is fullscreen */ 82 private boolean mFullscreen = true; 83 84 // Device rotation as of the last time {@link #mBounds} was set. 85 int mRotation; 86 87 /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */ 88 DimLayer mAnimationBackgroundSurface; 89 90 /** The particular window with an Animation with non-zero background color. */ 91 WindowStateAnimator mAnimationBackgroundAnimator; 92 93 /** Application tokens that are exiting, but still on screen for animations. */ 94 final AppTokenList mExitingAppTokens = new AppTokenList(); 95 96 /** Detach this stack from its display when animation completes. */ 97 boolean mDeferDetach; 98 private boolean mUpdateBoundsAfterRotation = false; 99 100 // Whether the stack and all its tasks is currently being drag-resized 101 private boolean mDragResizing; 102 103 private final Rect mLastContentBounds = new Rect(); 104 private final Rect mTmpAdjustedBounds = new Rect(); 105 private boolean mAdjustedForIme; 106 private WindowState mImeWin; 107 private float mMinimizeAmount; 108 private final int mDockedStackMinimizeThickness; 109 110 TaskStack(WindowManagerService service, int stackId) { 111 mService = service; 112 mStackId = stackId; 113 mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize( 114 com.android.internal.R.dimen.docked_stack_minimize_thickness); 115 EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId); 116 } 117 118 DisplayContent getDisplayContent() { 119 return mDisplayContent; 120 } 121 122 ArrayList<Task> getTasks() { 123 return mTasks; 124 } 125 126 /** 127 * Set the bounds of the stack and its containing tasks. 128 * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen. 129 * @param configs Configuration for individual tasks, keyed by task id. 130 * @param taskBounds Bounds for individual tasks, keyed by task id. 131 * @return True if the stack bounds was changed. 132 * */ 133 boolean setBounds( 134 Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, 135 SparseArray<Rect> taskTempInsetBounds) { 136 if (!setBounds(stackBounds)) { 137 return false; 138 } 139 140 // Update bounds of containing tasks. 141 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 142 final Task task = mTasks.get(taskNdx); 143 Configuration config = configs.get(task.mTaskId); 144 if (config != null) { 145 Rect bounds = taskBounds.get(task.mTaskId); 146 if (task.isTwoFingerScrollMode()) { 147 // This is a non-resizeable task that's docked (or side-by-side to the docked 148 // stack). It might have been scrolled previously, and after the stack resizing, 149 // it might no longer fully cover the stack area. 150 // Save the old bounds and re-apply the scroll. This adjusts the bounds to 151 // fit the new stack bounds. 152 task.resizeLocked(bounds, config, false /* forced */); 153 task.getBounds(mTmpRect); 154 task.scrollLocked(mTmpRect); 155 } else { 156 task.resizeLocked(bounds, config, false /* forced */); 157 task.setTempInsetBounds( 158 taskTempInsetBounds != null ? taskTempInsetBounds.get(task.mTaskId) 159 : null); 160 } 161 } else { 162 Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?"); 163 } 164 } 165 return true; 166 } 167 168 void prepareFreezingTaskBounds() { 169 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 170 final Task task = mTasks.get(taskNdx); 171 task.prepareFreezingBounds(); 172 } 173 } 174 175 boolean isFullscreenBounds(Rect bounds) { 176 if (mDisplayContent == null || bounds == null) { 177 return true; 178 } 179 mDisplayContent.getLogicalDisplayRect(mTmpRect); 180 return mTmpRect.equals(bounds); 181 } 182 183 /** 184 * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from 185 * the normal task bounds. 186 * 187 * @param bounds The adjusted bounds. 188 * @param keepInsets Whether to keep the insets from the original bounds or to calculate new 189 * ones depending on the adjusted bounds. 190 */ 191 private void setAdjustedBounds(Rect bounds, boolean keepInsets) { 192 if (mAdjustedBounds.equals(bounds)) { 193 return; 194 } 195 196 mAdjustedBounds.set(bounds); 197 final boolean adjusted = !mAdjustedBounds.isEmpty(); 198 alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, 199 adjusted && keepInsets ? mBounds : null); 200 mDisplayContent.layoutNeeded = true; 201 } 202 203 private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) { 204 if (mFullscreen) { 205 return; 206 } 207 // Update bounds of containing tasks. 208 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 209 final Task task = mTasks.get(taskNdx); 210 if (task.isTwoFingerScrollMode()) { 211 // If we're scrolling we don't care about your bounds or configs, 212 // they should be null as if we were in fullscreen. 213 task.resizeLocked(null, null, false /* forced */); 214 task.getBounds(mTmpRect2); 215 task.scrollLocked(mTmpRect2); 216 } else if (task.isResizeable() && task.mOverrideConfig != Configuration.EMPTY) { 217 task.getBounds(mTmpRect2); 218 mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top); 219 task.setTempInsetBounds(tempInsetBounds); 220 task.resizeLocked(mTmpRect2, task.mOverrideConfig, false /* forced */); 221 } 222 } 223 } 224 225 private boolean setBounds(Rect bounds) { 226 boolean oldFullscreen = mFullscreen; 227 int rotation = Surface.ROTATION_0; 228 if (mDisplayContent != null) { 229 mDisplayContent.getLogicalDisplayRect(mTmpRect); 230 rotation = mDisplayContent.getDisplayInfo().rotation; 231 mFullscreen = bounds == null; 232 if (mFullscreen) { 233 bounds = mTmpRect; 234 } 235 } 236 237 if (bounds == null) { 238 // Can't set to fullscreen if we don't have a display to get bounds from... 239 return false; 240 } 241 if (mBounds.equals(bounds) && oldFullscreen == mFullscreen && mRotation == rotation) { 242 return false; 243 } 244 245 if (mDisplayContent != null) { 246 mDisplayContent.mDimLayerController.updateDimLayer(this); 247 mAnimationBackgroundSurface.setBounds(bounds); 248 } 249 250 mBounds.set(bounds); 251 mRotation = rotation; 252 253 updateAdjustedBounds(); 254 255 return true; 256 } 257 258 /** Bounds of the stack without adjusting for other factors in the system like visibility 259 * of docked stack. 260 * Most callers should be using {@link #getBounds} as it take into consideration other system 261 * factors. */ 262 void getRawBounds(Rect out) { 263 out.set(mBounds); 264 } 265 266 /** Return true if the current bound can get outputted to the rest of the system as-is. */ 267 private boolean useCurrentBounds() { 268 if (mFullscreen 269 || !StackId.isResizeableByDockedStack(mStackId) 270 || mDisplayContent == null 271 || mDisplayContent.getDockedStackLocked() != null) { 272 return true; 273 } 274 return false; 275 } 276 277 public void getBounds(Rect out) { 278 if (useCurrentBounds()) { 279 // If we're currently adjusting for IME or minimized docked stack, we use the adjusted 280 // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked 281 // stack is visible since it is already what we want to represent to the rest of the 282 // system. 283 if (!mAdjustedBounds.isEmpty()) { 284 out.set(mAdjustedBounds); 285 } else { 286 out.set(mBounds); 287 } 288 return; 289 } 290 291 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 292 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 293 // system. 294 mDisplayContent.getLogicalDisplayRect(out); 295 } 296 297 /** Bounds of the stack with other system factors taken into consideration. */ 298 @Override 299 public void getDimBounds(Rect out) { 300 getBounds(out); 301 } 302 303 void updateDisplayInfo(Rect bounds) { 304 mUpdateBoundsAfterRotation = false; 305 if (mDisplayContent != null) { 306 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 307 mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent); 308 } 309 if (bounds != null) { 310 setBounds(bounds); 311 } else if (mFullscreen) { 312 setBounds(null); 313 } else { 314 mUpdateBoundsAfterRotation = true; 315 mTmpRect2.set(mBounds); 316 final int newRotation = mDisplayContent.getDisplayInfo().rotation; 317 if (mRotation == newRotation) { 318 setBounds(mTmpRect2); 319 } 320 321 // If the rotation changes, we'll handle it in updateBoundsAfterRotation 322 } 323 } 324 } 325 326 /** 327 * Updates the bounds after rotating the screen. We can't handle it in 328 * {@link #updateDisplayInfo} because at that point the configuration might not be fully updated 329 * yet. 330 */ 331 void updateBoundsAfterRotation() { 332 if (!mUpdateBoundsAfterRotation) { 333 return; 334 } 335 mUpdateBoundsAfterRotation = false; 336 final int newRotation = getDisplayInfo().rotation; 337 mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2); 338 if (mStackId == DOCKED_STACK_ID) { 339 snapDockedStackAfterRotation(mTmpRect2); 340 } 341 342 // Post message to inform activity manager of the bounds change simulating 343 // a one-way call. We do this to prevent a deadlock between window manager 344 // lock and activity manager lock been held. 345 mService.mH.sendMessage(mService.mH.obtainMessage( 346 RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2)); 347 } 348 349 /** 350 * Snaps the bounds after rotation to the closest snap target for the docked stack. 351 */ 352 private void snapDockedStackAfterRotation(Rect outBounds) { 353 354 // Calculate the current position. 355 final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); 356 final int dividerSize = mService.getDefaultDisplayContentLocked() 357 .getDockedDividerController().getContentWidth(); 358 final int dockSide = getDockSide(outBounds); 359 final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds, 360 dockSide, dividerSize); 361 final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth; 362 final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight; 363 364 // Snap the position to a target. 365 final int rotation = displayInfo.rotation; 366 final int orientation = mService.mCurConfiguration.orientation; 367 mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds); 368 final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm( 369 mService.mContext.getResources(), displayWidth, displayHeight, 370 dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds); 371 final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition); 372 373 // Recalculate the bounds based on the position of the target. 374 DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide, 375 outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight, 376 dividerSize); 377 } 378 379 boolean isAnimating() { 380 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 381 final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens; 382 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { 383 final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows; 384 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { 385 final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator; 386 if (winAnimator.isAnimating() || winAnimator.mWin.mExiting) { 387 return true; 388 } 389 } 390 } 391 } 392 return false; 393 } 394 395 void addTask(Task task, boolean toTop) { 396 addTask(task, toTop, task.showForAllUsers()); 397 } 398 399 /** 400 * Put a Task in this stack. Used for adding and moving. 401 * @param task The task to add. 402 * @param toTop Whether to add it to the top or bottom. 403 * @param showForAllUsers Whether to show the task regardless of the current user. 404 */ 405 void addTask(Task task, boolean toTop, boolean showForAllUsers) { 406 positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers); 407 } 408 409 void positionTask(Task task, int position, boolean showForAllUsers) { 410 final boolean canShowTask = 411 showForAllUsers || mService.isCurrentProfileLocked(task.mUserId); 412 mTasks.remove(task); 413 int stackSize = mTasks.size(); 414 int minPosition = 0; 415 int maxPosition = stackSize; 416 417 if (canShowTask) { 418 minPosition = computeMinPosition(minPosition, stackSize); 419 } else { 420 maxPosition = computeMaxPosition(maxPosition); 421 } 422 // Reset position based on minimum/maximum possible positions. 423 position = Math.min(Math.max(position, minPosition), maxPosition); 424 425 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, 426 "positionTask: task=" + task + " position=" + position); 427 mTasks.add(position, task); 428 429 // If we are moving the task across stacks, the scroll is no longer valid. 430 if (task.mStack != this) { 431 task.resetScrollLocked(); 432 } 433 task.mStack = this; 434 task.updateDisplayInfo(mDisplayContent); 435 boolean toTop = position == mTasks.size() - 1; 436 if (toTop) { 437 mDisplayContent.moveStack(this, true); 438 } 439 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position); 440 } 441 442 /** Calculate the minimum possible position for a task that can be shown to the user. 443 * The minimum position will be above all other tasks that can't be shown. 444 * @param minPosition The minimum position the caller is suggesting. 445 * We will start adjusting up from here. 446 * @param size The size of the current task list. 447 */ 448 private int computeMinPosition(int minPosition, int size) { 449 while (minPosition < size) { 450 final Task tmpTask = mTasks.get(minPosition); 451 final boolean canShowTmpTask = 452 tmpTask.showForAllUsers() 453 || mService.isCurrentProfileLocked(tmpTask.mUserId); 454 if (canShowTmpTask) { 455 break; 456 } 457 minPosition++; 458 } 459 return minPosition; 460 } 461 462 /** Calculate the maximum possible position for a task that can't be shown to the user. 463 * The maximum position will be below all other tasks that can be shown. 464 * @param maxPosition The maximum position the caller is suggesting. 465 * We will start adjusting down from here. 466 */ 467 private int computeMaxPosition(int maxPosition) { 468 while (maxPosition > 0) { 469 final Task tmpTask = mTasks.get(maxPosition - 1); 470 final boolean canShowTmpTask = 471 tmpTask.showForAllUsers() 472 || mService.isCurrentProfileLocked(tmpTask.mUserId); 473 if (!canShowTmpTask) { 474 break; 475 } 476 maxPosition--; 477 } 478 return maxPosition; 479 } 480 481 void moveTaskToTop(Task task) { 482 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToTop: task=" + task + " Callers=" 483 + Debug.getCallers(6)); 484 mTasks.remove(task); 485 addTask(task, true); 486 } 487 488 void moveTaskToBottom(Task task) { 489 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToBottom: task=" + task); 490 mTasks.remove(task); 491 addTask(task, false); 492 } 493 494 /** 495 * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the 496 * back. 497 * @param task The Task to delete. 498 */ 499 void removeTask(Task task) { 500 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeTask: task=" + task); 501 mTasks.remove(task); 502 if (mDisplayContent != null) { 503 if (mTasks.isEmpty()) { 504 mDisplayContent.moveStack(this, false); 505 } 506 mDisplayContent.layoutNeeded = true; 507 } 508 for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { 509 final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); 510 if (wtoken.mTask == task) { 511 wtoken.mIsExiting = false; 512 mExitingAppTokens.remove(appNdx); 513 } 514 } 515 } 516 517 void attachDisplayContent(DisplayContent displayContent) { 518 if (mDisplayContent != null) { 519 throw new IllegalStateException("attachDisplayContent: Already attached"); 520 } 521 522 mDisplayContent = displayContent; 523 mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId()); 524 525 Rect bounds = null; 526 final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID); 527 if (mStackId == DOCKED_STACK_ID 528 || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId))) { 529 // The existence of a docked stack affects the size of other static stack created since 530 // the docked stack occupies a dedicated region on screen. 531 bounds = new Rect(); 532 displayContent.getLogicalDisplayRect(mTmpRect); 533 mTmpRect2.setEmpty(); 534 if (dockedStack != null) { 535 dockedStack.getRawBounds(mTmpRect2); 536 } 537 final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode 538 == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 539 getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2, 540 mDisplayContent.mDividerControllerLocked.getContentWidth(), 541 dockedOnTopOrLeft); 542 } 543 544 updateDisplayInfo(bounds); 545 546 if (mStackId == DOCKED_STACK_ID) { 547 // Attaching a docked stack to the display affects the size of all other static 548 // stacks since the docked stack occupies a dedicated region on screen. 549 // Resize existing static stacks so they are pushed to the side of the docked stack. 550 resizeNonDockedStacks(!FULLSCREEN, mBounds); 551 } 552 } 553 554 void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibility) { 555 if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) 556 || mDisplayContent == null) { 557 outBounds.set(mBounds); 558 return; 559 } 560 561 final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID); 562 if (dockedStack == null) { 563 // Not sure why you are calling this method when there is no docked stack... 564 throw new IllegalStateException( 565 "Calling getStackDockedModeBoundsLocked() when there is no docked stack."); 566 } 567 if (!ignoreVisibility && !dockedStack.isVisibleLocked()) { 568 // The docked stack is being dismissed, but we caught before it finished being 569 // dismissed. In that case we want to treat it as if it is not occupying any space and 570 // let others occupy the whole display. 571 mDisplayContent.getLogicalDisplayRect(outBounds); 572 return; 573 } 574 575 final int dockedSide = dockedStack.getDockSide(); 576 if (dockedSide == DOCKED_INVALID) { 577 // Not sure how you got here...Only thing we can do is return current bounds. 578 Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack); 579 outBounds.set(mBounds); 580 return; 581 } 582 583 mDisplayContent.getLogicalDisplayRect(mTmpRect); 584 dockedStack.getRawBounds(mTmpRect2); 585 final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT; 586 getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2, 587 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); 588 589 } 590 591 /** 592 * Outputs the bounds a stack should be given the presence of a docked stack on the display. 593 * @param displayRect The bounds of the display the docked stack is on. 594 * @param outBounds Output bounds that should be used for the stack. 595 * @param stackId Id of stack we are calculating the bounds for. 596 * @param dockedBounds Bounds of the docked stack. 597 * @param dockDividerWidth We need to know the width of the divider make to the output bounds 598 * close to the side of the dock. 599 * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen. 600 */ 601 private void getStackDockedModeBounds( 602 Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth, 603 boolean dockOnTopOrLeft) { 604 final boolean dockedStack = stackId == DOCKED_STACK_ID; 605 final boolean splitHorizontally = displayRect.width() > displayRect.height(); 606 607 outBounds.set(displayRect); 608 if (dockedStack) { 609 if (mService.mDockedStackCreateBounds != null) { 610 outBounds.set(mService.mDockedStackCreateBounds); 611 return; 612 } 613 614 // The initial bounds of the docked stack when it is created about half the screen space 615 // and its bounds can be adjusted after that. The bounds of all other stacks are 616 // adjusted to occupy whatever screen space the docked stack isn't occupying. 617 final DisplayInfo di = mDisplayContent.getDisplayInfo(); 618 mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, 619 mTmpRect2); 620 final int position = new DividerSnapAlgorithm(mService.mContext.getResources(), 621 di.logicalWidth, 622 di.logicalHeight, 623 dockDividerWidth, 624 mService.mCurConfiguration.orientation == ORIENTATION_PORTRAIT, 625 mTmpRect2).getMiddleTarget().position; 626 627 if (dockOnTopOrLeft) { 628 if (splitHorizontally) { 629 outBounds.right = position; 630 } else { 631 outBounds.bottom = position; 632 } 633 } else { 634 if (splitHorizontally) { 635 outBounds.left = position - dockDividerWidth; 636 } else { 637 outBounds.top = position - dockDividerWidth; 638 } 639 } 640 return; 641 } 642 643 // Other stacks occupy whatever space is left by the docked stack. 644 if (!dockOnTopOrLeft) { 645 if (splitHorizontally) { 646 outBounds.right = dockedBounds.left - dockDividerWidth; 647 } else { 648 outBounds.bottom = dockedBounds.top - dockDividerWidth; 649 } 650 } else { 651 if (splitHorizontally) { 652 outBounds.left = dockedBounds.right + dockDividerWidth; 653 } else { 654 outBounds.top = dockedBounds.bottom + dockDividerWidth; 655 } 656 } 657 DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft); 658 } 659 660 /** Resizes all non-docked stacks in the system to either fullscreen or the appropriate size 661 * based on the presence of a docked stack. 662 * @param fullscreen If true the stacks will be resized to fullscreen, else they will be 663 * resized to the appropriate size based on the presence of a docked stack. 664 * @param dockedBounds Bounds of the docked stack. 665 */ 666 private void resizeNonDockedStacks(boolean fullscreen, Rect dockedBounds) { 667 // Not using mTmpRect because we are posting the object in a message. 668 final Rect bounds = new Rect(); 669 mDisplayContent.getLogicalDisplayRect(bounds); 670 if (!fullscreen) { 671 final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode 672 == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 673 getStackDockedModeBounds(bounds, bounds, FULLSCREEN_WORKSPACE_STACK_ID, dockedBounds, 674 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); 675 } 676 677 final int count = mService.mStackIdToStack.size(); 678 for (int i = 0; i < count; i++) { 679 final TaskStack otherStack = mService.mStackIdToStack.valueAt(i); 680 final int otherStackId = otherStack.mStackId; 681 if (StackId.isResizeableByDockedStack(otherStackId) 682 && !otherStack.mBounds.equals(bounds)) { 683 mService.mH.sendMessage( 684 mService.mH.obtainMessage(RESIZE_STACK, otherStackId, 685 1 /*allowResizeInDockedMode*/, fullscreen ? null : bounds)); 686 } 687 } 688 } 689 690 void resetDockedStackToMiddle() { 691 if (mStackId != DOCKED_STACK_ID) { 692 throw new IllegalStateException("Not a docked stack=" + this); 693 } 694 695 mService.mDockedStackCreateBounds = null; 696 697 final Rect bounds = new Rect(); 698 getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/); 699 mService.mH.obtainMessage(RESIZE_STACK, DOCKED_STACK_ID, 700 1 /*allowResizeInDockedMode*/, bounds).sendToTarget(); 701 } 702 703 void detachDisplay() { 704 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 705 706 boolean doAnotherLayoutPass = false; 707 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { 708 final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens; 709 for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) { 710 final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows; 711 for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) { 712 // We are in the middle of changing the state of displays/stacks/tasks. We need 713 // to finish that, before we let layout interfere with it. 714 mService.removeWindowLocked(appWindows.get(winNdx)); 715 doAnotherLayoutPass = true; 716 } 717 } 718 } 719 if (doAnotherLayoutPass) { 720 mService.mWindowPlacerLocked.requestTraversal(); 721 } 722 723 if (mStackId == DOCKED_STACK_ID) { 724 // Docked stack was detached from the display, so we no longer need to restrict the 725 // region of the screen other static stacks occupy. Go ahead and make them fullscreen. 726 resizeNonDockedStacks(FULLSCREEN, null); 727 } 728 729 close(); 730 } 731 732 void resetAnimationBackgroundAnimator() { 733 mAnimationBackgroundAnimator = null; 734 mAnimationBackgroundSurface.hide(); 735 } 736 737 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 738 int animLayer = winAnimator.mAnimLayer; 739 if (mAnimationBackgroundAnimator == null 740 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { 741 mAnimationBackgroundAnimator = winAnimator; 742 animLayer = mService.adjustAnimationBackground(winAnimator); 743 mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM, 744 ((color >> 24) & 0xff) / 255f, 0); 745 } 746 } 747 748 void switchUser() { 749 int top = mTasks.size(); 750 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 751 Task task = mTasks.get(taskNdx); 752 if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) { 753 mTasks.remove(taskNdx); 754 mTasks.add(task); 755 --top; 756 } 757 } 758 } 759 760 void close() { 761 if (mAnimationBackgroundSurface != null) { 762 mAnimationBackgroundSurface.destroySurface(); 763 mAnimationBackgroundSurface = null; 764 } 765 mDisplayContent = null; 766 } 767 768 /** 769 * Adjusts the stack bounds if the IME is visible. 770 * 771 * @param imeWin The IME window. 772 */ 773 void setAdjustedForIme(WindowState imeWin) { 774 mAdjustedForIme = true; 775 mImeWin = imeWin; 776 updateAdjustedBounds(); 777 } 778 779 /** 780 * Resets the adjustment after it got adjusted for the IME. 781 */ 782 void resetAdjustedForIme() { 783 mAdjustedForIme = false; 784 mImeWin = null; 785 updateAdjustedBounds(); 786 } 787 788 /** 789 * Sets the amount how much we currently minimize our stack. 790 * 791 * @param minimizeAmount The amount, between 0 and 1. 792 * @return Whether the amount has changed and a layout is needed. 793 */ 794 boolean setAdjustedForMinimizedDock(float minimizeAmount) { 795 if (minimizeAmount != mMinimizeAmount) { 796 mMinimizeAmount = minimizeAmount; 797 updateAdjustedBounds(); 798 return isVisibleForUserLocked(); 799 } else { 800 return false; 801 } 802 } 803 804 private boolean adjustForIME(final WindowState imeWin) { 805 final int dockedSide = getDockSide(); 806 final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; 807 final Rect adjustedBounds = mTmpAdjustedBounds; 808 if (imeWin == null || !dockedTopOrBottom) { 809 return false; 810 } 811 812 final Rect displayContentRect = mTmpRect; 813 final Rect contentBounds = mTmpRect2; 814 815 // Calculate the content bounds excluding the area occupied by IME 816 getDisplayContent().getContentRect(displayContentRect); 817 contentBounds.set(displayContentRect); 818 int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top); 819 imeTop += imeWin.getGivenContentInsetsLw().top; 820 if (contentBounds.bottom > imeTop) { 821 contentBounds.bottom = imeTop; 822 } 823 824 // If content bounds not changing, nothing to do. 825 if (mLastContentBounds.equals(contentBounds)) { 826 return true; 827 } 828 829 // Content bounds changed, need to apply adjustments depending on dock sides. 830 mLastContentBounds.set(contentBounds); 831 adjustedBounds.set(mBounds); 832 final int yOffset = displayContentRect.bottom - contentBounds.bottom; 833 834 if (dockedSide == DOCKED_TOP) { 835 // If this stack is docked on top, we make it smaller so the bottom stack is not 836 // occluded by IME. We shift its bottom up by the height of the IME (capped by 837 // the display content rect). Note that we don't change the task bounds. 838 adjustedBounds.bottom = Math.max( 839 adjustedBounds.bottom - yOffset, displayContentRect.top); 840 } else { 841 // If this stack is docked on bottom, we shift it up so that it's not occluded by 842 // IME. We try to move it up by the height of the IME window (although the best 843 // we could do is to make the top stack fully collapsed). 844 final int dividerWidth = getDisplayContent().mDividerControllerLocked 845 .getContentWidth(); 846 adjustedBounds.top = Math.max( 847 adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth); 848 adjustedBounds.bottom = adjustedBounds.top + mBounds.height(); 849 } 850 return true; 851 } 852 853 private boolean adjustForMinimizedDockedStack(float minimizeAmount) { 854 final int dockSide = getDockSide(); 855 if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) { 856 return false; 857 } 858 859 if (dockSide == DOCKED_TOP) { 860 mService.getStableInsetsLocked(mTmpRect); 861 int topInset = mTmpRect.top; 862 mTmpAdjustedBounds.set(mBounds); 863 mTmpAdjustedBounds.bottom = 864 (int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom); 865 } else if (dockSide == DOCKED_LEFT) { 866 mTmpAdjustedBounds.set(mBounds); 867 mTmpAdjustedBounds.right = 868 (int) (minimizeAmount * mDockedStackMinimizeThickness 869 + (1 - minimizeAmount) * mBounds.right); 870 } else if (dockSide == DOCKED_RIGHT) { 871 mTmpAdjustedBounds.set(mBounds); 872 mTmpAdjustedBounds.left = 873 (int) (minimizeAmount * (mBounds.right - mDockedStackMinimizeThickness) 874 + (1 - minimizeAmount) * mBounds.left); 875 } 876 return true; 877 } 878 879 /** 880 * Updates the adjustment depending on it's current state. 881 */ 882 void updateAdjustedBounds() { 883 boolean adjust = false; 884 if (mMinimizeAmount != 0f) { 885 adjust = adjustForMinimizedDockedStack(mMinimizeAmount); 886 } else if (mAdjustedForIme) { 887 adjust = adjustForIME(mImeWin); 888 } 889 if (!adjust) { 890 mTmpAdjustedBounds.setEmpty(); 891 mLastContentBounds.setEmpty(); 892 } 893 setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack()); 894 } 895 896 boolean isAdjustedForMinimizedDockedStack() { 897 return mMinimizeAmount != 0f; 898 } 899 900 public void dump(String prefix, PrintWriter pw) { 901 pw.println(prefix + "mStackId=" + mStackId); 902 pw.println(prefix + "mDeferDetach=" + mDeferDetach); 903 pw.println(prefix + "mFullscreen=" + mFullscreen); 904 pw.println(prefix + "mBounds=" + mBounds.toShortString()); 905 for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) { 906 mTasks.get(taskNdx).dump(prefix + " ", pw); 907 } 908 if (mAnimationBackgroundSurface.isDimming()) { 909 pw.println(prefix + "mWindowAnimationBackgroundSurface:"); 910 mAnimationBackgroundSurface.printTo(prefix + " ", pw); 911 } 912 if (!mExitingAppTokens.isEmpty()) { 913 pw.println(); 914 pw.println(" Exiting application tokens:"); 915 for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) { 916 WindowToken token = mExitingAppTokens.get(i); 917 pw.print(" Exiting App #"); pw.print(i); 918 pw.print(' '); pw.print(token); 919 pw.println(':'); 920 token.dump(pw, " "); 921 } 922 } 923 } 924 925 /** Fullscreen status of the stack without adjusting for other factors in the system like 926 * visibility of docked stack. 927 * Most callers should be using {@link #isFullscreen} as it take into consideration other 928 * system factors. */ 929 boolean getRawFullscreen() { 930 return mFullscreen; 931 } 932 933 @Override 934 public boolean isFullscreen() { 935 if (useCurrentBounds()) { 936 return mFullscreen; 937 } 938 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 939 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 940 // system. 941 return true; 942 } 943 944 @Override 945 public DisplayInfo getDisplayInfo() { 946 return mDisplayContent.getDisplayInfo(); 947 } 948 949 @Override 950 public String toString() { 951 return "{stackId=" + mStackId + " tasks=" + mTasks + "}"; 952 } 953 954 @Override 955 public String toShortString() { 956 return "Stack=" + mStackId; 957 } 958 959 /** 960 * For docked workspace (or workspace that's side-by-side to the docked), provides 961 * information which side of the screen was the dock anchored. 962 */ 963 int getDockSide() { 964 return getDockSide(mBounds); 965 } 966 967 int getDockSide(Rect bounds) { 968 if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) { 969 return DOCKED_INVALID; 970 } 971 if (mDisplayContent == null) { 972 return DOCKED_INVALID; 973 } 974 mDisplayContent.getLogicalDisplayRect(mTmpRect); 975 final int orientation = mService.mCurConfiguration.orientation; 976 if (orientation == Configuration.ORIENTATION_PORTRAIT) { 977 // Portrait mode, docked either at the top or the bottom. 978 if (bounds.top - mTmpRect.top <= mTmpRect.bottom - bounds.bottom) { 979 return DOCKED_TOP; 980 } else { 981 return DOCKED_BOTTOM; 982 } 983 } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 984 // Landscape mode, docked either on the left or on the right. 985 if (bounds.left - mTmpRect.left <= mTmpRect.right - bounds.right) { 986 return DOCKED_LEFT; 987 } else { 988 return DOCKED_RIGHT; 989 } 990 } else { 991 return DOCKED_INVALID; 992 } 993 } 994 995 boolean isVisibleLocked() { 996 final boolean keyguardOn = mService.mPolicy.isKeyguardShowingOrOccluded() 997 && !mService.mAnimator.mKeyguardGoingAway; 998 if (keyguardOn && !StackId.isAllowedOverLockscreen(mStackId)) { 999 // The keyguard is showing and the stack shouldn't show on top of the keyguard. 1000 return false; 1001 } 1002 1003 for (int i = mTasks.size() - 1; i >= 0; i--) { 1004 final Task task = mTasks.get(i); 1005 for (int j = task.mAppTokens.size() - 1; j >= 0; j--) { 1006 if (!task.mAppTokens.get(j).hidden) { 1007 return true; 1008 } 1009 } 1010 } 1011 1012 return false; 1013 } 1014 1015 /** 1016 * @return true if a the stack is visible for the current in user, ignoring any other visibility 1017 * aspects, and false otherwise 1018 */ 1019 boolean isVisibleForUserLocked() { 1020 for (int i = mTasks.size() - 1; i >= 0; i--) { 1021 final Task task = mTasks.get(i); 1022 if (task.isVisibleForUser()) { 1023 return true; 1024 } 1025 } 1026 return false; 1027 } 1028 1029 boolean isDragResizing() { 1030 return mDragResizing; 1031 } 1032 1033 private void setDragResizingLocked(boolean resizing) { 1034 if (mDragResizing == resizing) { 1035 return; 1036 } 1037 mDragResizing = resizing; 1038 for (int i = mTasks.size() - 1; i >= 0 ; i--) { 1039 mTasks.get(i).resetDragResizingChangeReported(); 1040 } 1041 } 1042 1043 @Override // AnimatesBounds 1044 public boolean setSize(Rect bounds) { 1045 synchronized (mService.mWindowMap) { 1046 if (mDisplayContent == null) { 1047 return false; 1048 } 1049 } 1050 try { 1051 mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false); 1052 } catch (RemoteException e) { 1053 } 1054 return true; 1055 } 1056 1057 @Override // AnimatesBounds 1058 public void onAnimationStart() { 1059 synchronized (mService.mWindowMap) { 1060 setDragResizingLocked(true); 1061 } 1062 } 1063 1064 @Override // AnimatesBounds 1065 public void onAnimationEnd() { 1066 synchronized (mService.mWindowMap) { 1067 setDragResizingLocked(false); 1068 mService.requestTraversal(); 1069 } 1070 if (mStackId == PINNED_STACK_ID) { 1071 try { 1072 mService.mActivityManager.notifyPinnedStackAnimationEnded(); 1073 } catch (RemoteException e) { 1074 // I don't believe you... 1075 } 1076 } 1077 } 1078 1079 @Override 1080 public void moveToFullscreen() { 1081 try { 1082 mService.mActivityManager.moveTasksToFullscreenStack(mStackId, true); 1083 } catch (RemoteException e) { 1084 e.printStackTrace(); 1085 } 1086 } 1087 1088 @Override 1089 public void getFullScreenBounds(Rect bounds) { 1090 getDisplayContent().getContentRect(bounds); 1091 } 1092}