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