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