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