TaskStack.java revision ae1ff4f85ffd12ab8a14c610b1474a012536888f
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.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 25import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED; 26import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 27import static android.view.Display.DEFAULT_DISPLAY; 28import static android.view.WindowManager.DOCKED_BOTTOM; 29import static android.view.WindowManager.DOCKED_INVALID; 30import static android.view.WindowManager.DOCKED_LEFT; 31import static android.view.WindowManager.DOCKED_RIGHT; 32import static android.view.WindowManager.DOCKED_TOP; 33import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; 34import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; 35import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; 36import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; 37import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 38import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK; 39import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM; 40 41import android.app.ActivityManager.StackId; 42import android.app.IActivityManager; 43import android.content.res.Configuration; 44import android.graphics.Point; 45import android.graphics.PointF; 46import android.graphics.Rect; 47import android.graphics.Region; 48import android.os.Debug; 49import android.os.RemoteException; 50import android.util.EventLog; 51import android.util.Slog; 52import android.util.SparseArray; 53import android.view.DisplayInfo; 54import android.view.Surface; 55 56import android.view.WindowManagerPolicy; 57import com.android.internal.policy.DividerSnapAlgorithm; 58import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; 59import com.android.internal.policy.DockedDividerUtils; 60import com.android.internal.policy.PipSnapAlgorithm; 61import com.android.server.EventLogTags; 62 63import java.io.PrintWriter; 64 65public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser, 66 BoundsAnimationController.AnimateBoundsUser { 67 /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to 68 * restrict IME adjustment so that a min portion of top stack remains visible.*/ 69 private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f; 70 71 /** Dimming amount for non-focused stack when stacks are IME-adjusted. */ 72 private static final float IME_ADJUST_DIM_AMOUNT = 0.25f; 73 74 /** Unique identifier */ 75 final int mStackId; 76 77 /** The service */ 78 private final WindowManagerService mService; 79 80 /** The display this stack sits under. */ 81 // TODO: Track parent marks like this in WindowContainer. 82 private DisplayContent mDisplayContent; 83 84 /** For comparison with DisplayContent bounds. */ 85 private Rect mTmpRect = new Rect(); 86 private Rect mTmpRect2 = new Rect(); 87 88 /** Content limits relative to the DisplayContent this sits in. */ 89 private Rect mBounds = new Rect(); 90 91 /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */ 92 private final Rect mAdjustedBounds = new Rect(); 93 94 /** 95 * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they 96 * represent the state when the animation has ended. 97 */ 98 private final Rect mFullyAdjustedImeBounds = new Rect(); 99 100 /** Whether mBounds is fullscreen */ 101 private boolean mFillsParent = true; 102 103 // Device rotation as of the last time {@link #mBounds} was set. 104 private int mRotation; 105 106 /** Density as of last time {@link #mBounds} was set. */ 107 private int mDensity; 108 109 /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */ 110 private DimLayer mAnimationBackgroundSurface; 111 112 /** The particular window with an Animation with non-zero background color. */ 113 private WindowStateAnimator mAnimationBackgroundAnimator; 114 115 /** Application tokens that are exiting, but still on screen for animations. */ 116 final AppTokenList mExitingAppTokens = new AppTokenList(); 117 118 /** Detach this stack from its display when animation completes. */ 119 // TODO: maybe tie this to WindowContainer#removeChild some how... 120 boolean mDeferRemoval; 121 122 private final Rect mTmpAdjustedBounds = new Rect(); 123 private boolean mAdjustedForIme; 124 private boolean mImeGoingAway; 125 private WindowState mImeWin; 126 private float mMinimizeAmount; 127 private float mAdjustImeAmount; 128 private float mAdjustDividerAmount; 129 private final int mDockedStackMinimizeThickness; 130 131 // If this is true, we are in the bounds animating mode. The task will be down or upscaled to 132 // perfectly fit the region it would have been cropped to. We may also avoid certain logic we 133 // would otherwise apply while resizing, while resizing in the bounds animating mode. 134 private boolean mBoundsAnimating = false; 135 private Rect mBoundsAnimationTarget = new Rect(); 136 137 // Temporary storage for the new bounds that should be used after the configuration change. 138 // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration(). 139 private final Rect mBoundsAfterRotation = new Rect(); 140 141 TaskStack(WindowManagerService service, int stackId) { 142 mService = service; 143 mStackId = stackId; 144 mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize( 145 com.android.internal.R.dimen.docked_stack_minimize_thickness); 146 EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId); 147 } 148 149 DisplayContent getDisplayContent() { 150 return mDisplayContent; 151 } 152 153 Task findHomeTask() { 154 if (mStackId != HOME_STACK_ID) { 155 return null; 156 } 157 158 for (int i = mChildren.size() - 1; i >= 0; i--) { 159 if (mChildren.get(i).isHomeTask()) { 160 return mChildren.get(i); 161 } 162 } 163 return null; 164 } 165 166 boolean hasMultipleTaskWithHomeTaskNotTop() { 167 return mChildren.size() > 1 && !mChildren.get(mChildren.size() - 1).isHomeTask(); 168 } 169 170 boolean topTaskIsOnTopLauncher() { 171 return mChildren.get(mChildren.size() - 1).isOnTopLauncher(); 172 } 173 174 /** 175 * Set the bounds of the stack and its containing tasks. 176 * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen. 177 * @param configs Configuration for individual tasks, keyed by task id. 178 * @param taskBounds Bounds for individual tasks, keyed by task id. 179 * @return True if the stack bounds was changed. 180 * */ 181 boolean setBounds( 182 Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds, 183 SparseArray<Rect> taskTempInsetBounds) { 184 setBounds(stackBounds); 185 186 // Update bounds of containing tasks. 187 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 188 final Task task = mChildren.get(taskNdx); 189 Configuration config = configs.get(task.mTaskId); 190 if (config != null) { 191 Rect bounds = taskBounds.get(task.mTaskId); 192 task.resizeLocked(bounds, config, false /* forced */); 193 task.setTempInsetBounds(taskTempInsetBounds != null ? 194 taskTempInsetBounds.get(task.mTaskId) : null); 195 } else { 196 Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?"); 197 } 198 } 199 return true; 200 } 201 202 void prepareFreezingTaskBounds() { 203 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 204 final Task task = mChildren.get(taskNdx); 205 task.prepareFreezingBounds(); 206 } 207 } 208 209 boolean isFullscreenBounds(Rect bounds) { 210 if (mDisplayContent == null || bounds == null) { 211 return true; 212 } 213 mDisplayContent.getLogicalDisplayRect(mTmpRect); 214 return mTmpRect.equals(bounds); 215 } 216 217 /** 218 * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from 219 * the normal task bounds. 220 * 221 * @param bounds The adjusted bounds. 222 */ 223 private void setAdjustedBounds(Rect bounds) { 224 if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) { 225 return; 226 } 227 228 mAdjustedBounds.set(bounds); 229 final boolean adjusted = !mAdjustedBounds.isEmpty(); 230 Rect insetBounds = null; 231 if (adjusted && isAdjustedForMinimizedDock()) { 232 insetBounds = mBounds; 233 } else if (adjusted && mAdjustedForIme) { 234 if (mImeGoingAway) { 235 insetBounds = mBounds; 236 } else { 237 insetBounds = mFullyAdjustedImeBounds; 238 } 239 } 240 alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds); 241 mDisplayContent.setLayoutNeeded(); 242 } 243 244 private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) { 245 if (mFillsParent) { 246 return; 247 } 248 249 final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP; 250 251 // Update bounds of containing tasks. 252 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 253 final Task task = mChildren.get(taskNdx); 254 task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom); 255 } 256 } 257 258 private boolean setBounds(Rect bounds) { 259 boolean oldFullscreen = mFillsParent; 260 int rotation = Surface.ROTATION_0; 261 int density = DENSITY_DPI_UNDEFINED; 262 if (mDisplayContent != null) { 263 mDisplayContent.getLogicalDisplayRect(mTmpRect); 264 rotation = mDisplayContent.getDisplayInfo().rotation; 265 density = mDisplayContent.getDisplayInfo().logicalDensityDpi; 266 mFillsParent = bounds == null; 267 if (mFillsParent) { 268 bounds = mTmpRect; 269 } 270 } 271 272 if (bounds == null) { 273 // Can't set to fullscreen if we don't have a display to get bounds from... 274 return false; 275 } 276 if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) { 277 return false; 278 } 279 280 if (mDisplayContent != null) { 281 mDisplayContent.mDimLayerController.updateDimLayer(this); 282 mAnimationBackgroundSurface.setBounds(bounds); 283 } 284 285 mBounds.set(bounds); 286 mRotation = rotation; 287 mDensity = density; 288 289 updateAdjustedBounds(); 290 291 return true; 292 } 293 294 /** Bounds of the stack without adjusting for other factors in the system like visibility 295 * of docked stack. 296 * Most callers should be using {@link #getBounds} as it take into consideration other system 297 * factors. */ 298 void getRawBounds(Rect out) { 299 out.set(mBounds); 300 } 301 302 /** Return true if the current bound can get outputted to the rest of the system as-is. */ 303 private boolean useCurrentBounds() { 304 if (mFillsParent 305 || !StackId.isResizeableByDockedStack(mStackId) 306 || mDisplayContent == null 307 || mDisplayContent.getDockedStackLocked() != null) { 308 return true; 309 } 310 return false; 311 } 312 313 public void getBounds(Rect out) { 314 if (useCurrentBounds()) { 315 // If we're currently adjusting for IME or minimized docked stack, we use the adjusted 316 // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked 317 // stack is visible since it is already what we want to represent to the rest of the 318 // system. 319 if (!mAdjustedBounds.isEmpty()) { 320 out.set(mAdjustedBounds); 321 } else { 322 out.set(mBounds); 323 } 324 return; 325 } 326 327 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 328 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 329 // system. 330 mDisplayContent.getLogicalDisplayRect(out); 331 } 332 333 /** 334 * Sets the bounds animation target bounds. This can't currently be done in onAnimationStart() 335 * since that is started on the UiThread. 336 */ 337 void setAnimatingBounds(Rect bounds) { 338 if (bounds != null) { 339 mBoundsAnimationTarget.set(bounds); 340 } else { 341 mBoundsAnimationTarget.setEmpty(); 342 } 343 } 344 345 /** 346 * @return the bounds that the task stack is currently being animated towards, or the current 347 * stack bounds if there is no animation in progress. 348 */ 349 void getAnimatingBounds(Rect outBounds) { 350 if (!mBoundsAnimationTarget.isEmpty()) { 351 outBounds.set(mBoundsAnimationTarget); 352 return; 353 } 354 getBounds(outBounds); 355 } 356 357 /** Bounds of the stack with other system factors taken into consideration. */ 358 @Override 359 public void getDimBounds(Rect out) { 360 getBounds(out); 361 } 362 363 void updateDisplayInfo(Rect bounds) { 364 if (mDisplayContent == null) { 365 return; 366 } 367 368 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 369 mChildren.get(taskNdx).updateDisplayInfo(mDisplayContent); 370 } 371 if (bounds != null) { 372 setBounds(bounds); 373 return; 374 } else if (mFillsParent) { 375 setBounds(null); 376 return; 377 } 378 379 mTmpRect2.set(mBounds); 380 final int newRotation = mDisplayContent.getDisplayInfo().rotation; 381 final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi; 382 if (mRotation == newRotation && mDensity == newDensity) { 383 setBounds(mTmpRect2); 384 } 385 386 // If the rotation or density didn't match, we'll update it in onConfigurationChanged. 387 } 388 389 /** @return true if bounds were updated to some non-empty value. */ 390 boolean updateBoundsAfterConfigChange() { 391 if (mDisplayContent == null) { 392 // If the stack is already detached we're not updating anything, 393 // as it's going away soon anyway. 394 return false; 395 } 396 final int newRotation = getDisplayInfo().rotation; 397 final int newDensity = getDisplayInfo().logicalDensityDpi; 398 399 if (mRotation == newRotation && mDensity == newDensity) { 400 // Nothing to do here as we already update the state in updateDisplayInfo. 401 return false; 402 } 403 404 if (mFillsParent) { 405 // Update stack bounds again since rotation changed since updateDisplayInfo(). 406 setBounds(null); 407 // Return false since we don't need the client to resize. 408 return false; 409 } 410 411 mTmpRect2.set(mBounds); 412 mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2); 413 switch (mStackId) { 414 case PINNED_STACK_ID: 415 mTmpRect2 = mDisplayContent.getPinnedStackController().onDisplayChanged(mBounds, 416 getDisplayInfo()); 417 break; 418 case DOCKED_STACK_ID: 419 repositionDockedStackAfterRotation(mTmpRect2); 420 snapDockedStackAfterRotation(mTmpRect2); 421 final int newDockSide = getDockSide(mTmpRect2); 422 423 // Update the dock create mode and clear the dock create bounds, these 424 // might change after a rotation and the original values will be invalid. 425 mService.setDockedStackCreateStateLocked( 426 (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP) 427 ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT 428 : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT, 429 null); 430 mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide); 431 break; 432 } 433 434 mBoundsAfterRotation.set(mTmpRect2); 435 return true; 436 } 437 438 void getBoundsForNewConfiguration(Rect outBounds) { 439 outBounds.set(mBoundsAfterRotation); 440 mBoundsAfterRotation.setEmpty(); 441 } 442 443 /** 444 * Some dock sides are not allowed by the policy. This method queries the policy and moves 445 * the docked stack around if needed. 446 * 447 * @param inOutBounds the bounds of the docked stack to adjust 448 */ 449 private void repositionDockedStackAfterRotation(Rect inOutBounds) { 450 int dockSide = getDockSide(inOutBounds); 451 if (mService.mPolicy.isDockSideAllowed(dockSide)) { 452 return; 453 } 454 mDisplayContent.getLogicalDisplayRect(mTmpRect); 455 dockSide = DockedDividerUtils.invertDockSide(dockSide); 456 switch (dockSide) { 457 case DOCKED_LEFT: 458 int movement = inOutBounds.left; 459 inOutBounds.left -= movement; 460 inOutBounds.right -= movement; 461 break; 462 case DOCKED_RIGHT: 463 movement = mTmpRect.right - inOutBounds.right; 464 inOutBounds.left += movement; 465 inOutBounds.right += movement; 466 break; 467 case DOCKED_TOP: 468 movement = inOutBounds.top; 469 inOutBounds.top -= movement; 470 inOutBounds.bottom -= movement; 471 break; 472 case DOCKED_BOTTOM: 473 movement = mTmpRect.bottom - inOutBounds.bottom; 474 inOutBounds.top += movement; 475 inOutBounds.bottom += movement; 476 break; 477 } 478 } 479 480 /** 481 * Snaps the bounds after rotation to the closest snap target for the docked stack. 482 */ 483 private void snapDockedStackAfterRotation(Rect outBounds) { 484 485 // Calculate the current position. 486 final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); 487 final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth(); 488 final int dockSide = getDockSide(outBounds); 489 final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds, 490 dockSide, dividerSize); 491 final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth; 492 final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight; 493 494 // Snap the position to a target. 495 final int rotation = displayInfo.rotation; 496 final int orientation = mDisplayContent.getConfiguration().orientation; 497 mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds); 498 final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm( 499 mService.mContext.getResources(), displayWidth, displayHeight, 500 dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds); 501 final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition); 502 503 // Recalculate the bounds based on the position of the target. 504 DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide, 505 outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight, 506 dividerSize); 507 } 508 509 // TODO: Checkout the call points of this method and the ones below to see how they can fit in WC. 510 void addTask(Task task, boolean toTop) { 511 addTask(task, toTop, task.showForAllUsers()); 512 } 513 514 /** 515 * Put a Task in this stack. Used for adding and moving. 516 * @param task The task to add. 517 * @param toTop Whether to add it to the top or bottom. 518 * @param showForAllUsers Whether to show the task regardless of the current user. 519 */ 520 void addTask(Task task, boolean toTop, boolean showForAllUsers) { 521 positionTask(task, toTop ? mChildren.size() : 0, showForAllUsers); 522 } 523 524 // TODO: We should really have users as a window container in the hierarchy so that we don't 525 // have to do complicated things like we are doing in this method and also also the method to 526 // just be WindowContainer#addChild 527 void positionTask(Task task, int position, boolean showForAllUsers) { 528 final boolean canShowTask = 529 showForAllUsers || mService.isCurrentProfileLocked(task.mUserId); 530 if (mChildren.contains(task)) { 531 super.removeChild(task); 532 } 533 int stackSize = mChildren.size(); 534 int minPosition = 0; 535 int maxPosition = stackSize; 536 537 if (canShowTask) { 538 minPosition = computeMinPosition(minPosition, stackSize); 539 } else { 540 maxPosition = computeMaxPosition(maxPosition); 541 } 542 // Reset position based on minimum/maximum possible positions. 543 position = Math.min(Math.max(position, minPosition), maxPosition); 544 545 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, 546 "positionTask: task=" + task + " position=" + position); 547 addChild(task, position); 548 task.mStack = this; 549 task.updateDisplayInfo(mDisplayContent); 550 boolean toTop = position == mChildren.size() - 1; 551 if (toTop) { 552 // TODO: Have a WidnowContainer method that moves all parents of a container to the 553 // front for cases like this. 554 mDisplayContent.moveStack(this, true); 555 } 556 557 if (StackId.windowsAreScaleable(mStackId)) { 558 // We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them 559 // while a resize is pending. 560 task.forceWindowsScaleable(true); 561 } else { 562 task.forceWindowsScaleable(false); 563 } 564 EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position); 565 } 566 567 /** Calculate the minimum possible position for a task that can be shown to the user. 568 * The minimum position will be above all other tasks that can't be shown. 569 * @param minPosition The minimum position the caller is suggesting. 570 * We will start adjusting up from here. 571 * @param size The size of the current task list. 572 */ 573 private int computeMinPosition(int minPosition, int size) { 574 while (minPosition < size) { 575 final Task tmpTask = mChildren.get(minPosition); 576 final boolean canShowTmpTask = 577 tmpTask.showForAllUsers() 578 || mService.isCurrentProfileLocked(tmpTask.mUserId); 579 if (canShowTmpTask) { 580 break; 581 } 582 minPosition++; 583 } 584 return minPosition; 585 } 586 587 /** Calculate the maximum possible position for a task that can't be shown to the user. 588 * The maximum position will be below all other tasks that can be shown. 589 * @param maxPosition The maximum position the caller is suggesting. 590 * We will start adjusting down from here. 591 */ 592 private int computeMaxPosition(int maxPosition) { 593 while (maxPosition > 0) { 594 final Task tmpTask = mChildren.get(maxPosition - 1); 595 final boolean canShowTmpTask = 596 tmpTask.showForAllUsers() 597 || mService.isCurrentProfileLocked(tmpTask.mUserId); 598 if (!canShowTmpTask) { 599 break; 600 } 601 maxPosition--; 602 } 603 return maxPosition; 604 } 605 606 // TODO: Have functionality in WC to move things to the bottom or top. Also, look at the call 607 // points for this methods to see if we need functionality to move something to the front/bottom 608 // with its parents. 609 void moveTaskToTop(Task task) { 610 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToTop: task=" + task + " Callers=" 611 + Debug.getCallers(6)); 612 addTask(task, true); 613 } 614 615 void moveTaskToBottom(Task task) { 616 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToBottom: task=" + 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 @Override 626 void removeChild(Task task) { 627 if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeChild: task=" + task); 628 629 super.removeChild(task); 630 631 if (mDisplayContent != null) { 632 if (mChildren.isEmpty()) { 633 mDisplayContent.moveStack(this, false); 634 } 635 mDisplayContent.setLayoutNeeded(); 636 } 637 for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) { 638 final AppWindowToken wtoken = mExitingAppTokens.get(appNdx); 639 if (wtoken.mTask == task) { 640 wtoken.mIsExiting = false; 641 mExitingAppTokens.remove(appNdx); 642 } 643 } 644 } 645 646 void onDisplayChanged(DisplayContent dc) { 647 if (mDisplayContent != null) { 648 throw new IllegalStateException("onDisplayChanged: Already attached"); 649 } 650 651 mDisplayContent = dc; 652 mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(), 653 "animation background stackId=" + mStackId); 654 655 final Rect oldBounds = new Rect(mBounds); 656 Rect bounds = null; 657 final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID); 658 if (mStackId == DOCKED_STACK_ID 659 || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId) 660 && !dockedStack.fillsParent())) { 661 // The existence of a docked stack affects the size of other static stack created since 662 // the docked stack occupies a dedicated region on screen, but only if the dock stack is 663 // not fullscreen. If it's fullscreen, it means that we are in the transition of 664 // dismissing it, so we must not resize this stack. 665 bounds = new Rect(); 666 dc.getLogicalDisplayRect(mTmpRect); 667 mTmpRect2.setEmpty(); 668 if (dockedStack != null) { 669 dockedStack.getRawBounds(mTmpRect2); 670 } 671 final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode 672 == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; 673 getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2, 674 mDisplayContent.mDividerControllerLocked.getContentWidth(), 675 dockedOnTopOrLeft); 676 } 677 678 updateDisplayInfo(bounds); 679 680 // Update the pinned stack controller after the display info is updated 681 if (mStackId == PINNED_STACK_ID) { 682 mDisplayContent.getPinnedStackController().onDisplayChanged(oldBounds, 683 getDisplayInfo()); 684 } 685 686 super.onDisplayChanged(dc); 687 } 688 689 void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibility) { 690 if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) 691 || mDisplayContent == null) { 692 outBounds.set(mBounds); 693 return; 694 } 695 696 final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID); 697 if (dockedStack == null) { 698 // Not sure why you are calling this method when there is no docked stack... 699 throw new IllegalStateException( 700 "Calling getStackDockedModeBoundsLocked() when there is no docked stack."); 701 } 702 if (!ignoreVisibility && !dockedStack.isVisible()) { 703 // The docked stack is being dismissed, but we caught before it finished being 704 // dismissed. In that case we want to treat it as if it is not occupying any space and 705 // let others occupy the whole display. 706 mDisplayContent.getLogicalDisplayRect(outBounds); 707 return; 708 } 709 710 final int dockedSide = dockedStack.getDockSide(); 711 if (dockedSide == DOCKED_INVALID) { 712 // Not sure how you got here...Only thing we can do is return current bounds. 713 Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack); 714 outBounds.set(mBounds); 715 return; 716 } 717 718 mDisplayContent.getLogicalDisplayRect(mTmpRect); 719 dockedStack.getRawBounds(mTmpRect2); 720 final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT; 721 getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2, 722 mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft); 723 724 } 725 726 /** 727 * Outputs the bounds a stack should be given the presence of a docked stack on the display. 728 * @param displayRect The bounds of the display the docked stack is on. 729 * @param outBounds Output bounds that should be used for the stack. 730 * @param stackId Id of stack we are calculating the bounds for. 731 * @param dockedBounds Bounds of the docked stack. 732 * @param dockDividerWidth We need to know the width of the divider make to the output bounds 733 * close to the side of the dock. 734 * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen. 735 */ 736 private void getStackDockedModeBounds( 737 Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth, 738 boolean dockOnTopOrLeft) { 739 final boolean dockedStack = stackId == DOCKED_STACK_ID; 740 final boolean splitHorizontally = displayRect.width() > displayRect.height(); 741 742 outBounds.set(displayRect); 743 if (dockedStack) { 744 if (mService.mDockedStackCreateBounds != null) { 745 outBounds.set(mService.mDockedStackCreateBounds); 746 return; 747 } 748 749 // The initial bounds of the docked stack when it is created about half the screen space 750 // and its bounds can be adjusted after that. The bounds of all other stacks are 751 // adjusted to occupy whatever screen space the docked stack isn't occupying. 752 final DisplayInfo di = mDisplayContent.getDisplayInfo(); 753 mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, 754 mTmpRect2); 755 final int position = new DividerSnapAlgorithm(mService.mContext.getResources(), 756 di.logicalWidth, 757 di.logicalHeight, 758 dockDividerWidth, 759 mDisplayContent.getConfiguration().orientation == ORIENTATION_PORTRAIT, 760 mTmpRect2).getMiddleTarget().position; 761 762 if (dockOnTopOrLeft) { 763 if (splitHorizontally) { 764 outBounds.right = position; 765 } else { 766 outBounds.bottom = position; 767 } 768 } else { 769 if (splitHorizontally) { 770 outBounds.left = position + dockDividerWidth; 771 } else { 772 outBounds.top = position + dockDividerWidth; 773 } 774 } 775 return; 776 } 777 778 // Other stacks occupy whatever space is left by the docked stack. 779 if (!dockOnTopOrLeft) { 780 if (splitHorizontally) { 781 outBounds.right = dockedBounds.left - dockDividerWidth; 782 } else { 783 outBounds.bottom = dockedBounds.top - dockDividerWidth; 784 } 785 } else { 786 if (splitHorizontally) { 787 outBounds.left = dockedBounds.right + dockDividerWidth; 788 } else { 789 outBounds.top = dockedBounds.bottom + dockDividerWidth; 790 } 791 } 792 DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft); 793 } 794 795 void resetDockedStackToMiddle() { 796 if (mStackId != DOCKED_STACK_ID) { 797 throw new IllegalStateException("Not a docked stack=" + this); 798 } 799 800 mService.mDockedStackCreateBounds = null; 801 802 final Rect bounds = new Rect(); 803 getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/); 804 mService.mH.obtainMessage(RESIZE_STACK, DOCKED_STACK_ID, 805 1 /*allowResizeInDockedMode*/, bounds).sendToTarget(); 806 } 807 808 @Override 809 void removeIfPossible() { 810 if (isAnimating()) { 811 mDeferRemoval = true; 812 return; 813 } 814 removeImmediately(); 815 } 816 817 @Override 818 void removeImmediately() { 819 super.removeImmediately(); 820 821 onRemovedFromDisplay(); 822 } 823 824 /** 825 * Removes the stack it from its current parent, so it can be either destroyed completely or 826 * re-parented. 827 */ 828 void onRemovedFromDisplay() { 829 mDisplayContent.mDimLayerController.removeDimLayerUser(this); 830 EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId); 831 832 if (mAnimationBackgroundSurface != null) { 833 mAnimationBackgroundSurface.destroySurface(); 834 mAnimationBackgroundSurface = null; 835 } 836 837 if (mStackId == DOCKED_STACK_ID) { 838 mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false); 839 } 840 841 mDisplayContent = null; 842 mService.mWindowPlacerLocked.requestTraversal(); 843 } 844 845 void resetAnimationBackgroundAnimator() { 846 mAnimationBackgroundAnimator = null; 847 mAnimationBackgroundSurface.hide(); 848 } 849 850 void setAnimationBackground(WindowStateAnimator winAnimator, int color) { 851 int animLayer = winAnimator.mAnimLayer; 852 if (mAnimationBackgroundAnimator == null 853 || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { 854 mAnimationBackgroundAnimator = winAnimator; 855 animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator); 856 mAnimationBackgroundSurface.show(animLayer - LAYER_OFFSET_DIM, 857 ((color >> 24) & 0xff) / 255f, 0); 858 } 859 } 860 861 // TODO: Should each user have there own stacks? 862 void switchUser() { 863 int top = mChildren.size(); 864 for (int taskNdx = 0; taskNdx < top; ++taskNdx) { 865 Task task = mChildren.get(taskNdx); 866 if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) { 867 mChildren.remove(taskNdx); 868 mChildren.add(task); 869 --top; 870 } 871 } 872 } 873 874 /** 875 * Adjusts the stack bounds if the IME is visible. 876 * 877 * @param imeWin The IME window. 878 */ 879 void setAdjustedForIme(WindowState imeWin, boolean forceUpdate) { 880 mImeWin = imeWin; 881 mImeGoingAway = false; 882 if (!mAdjustedForIme || forceUpdate) { 883 mAdjustedForIme = true; 884 mAdjustImeAmount = 0f; 885 mAdjustDividerAmount = 0f; 886 updateAdjustForIme(0f, 0f, true /* force */); 887 } 888 } 889 890 boolean isAdjustedForIme() { 891 return mAdjustedForIme; 892 } 893 894 boolean isAnimatingForIme() { 895 return mImeWin != null && mImeWin.isAnimatingLw(); 896 } 897 898 /** 899 * Update the stack's bounds (crop or position) according to the IME window's 900 * current position. When IME window is animated, the bottom stack is animated 901 * together to track the IME window's current position, and the top stack is 902 * cropped as necessary. 903 * 904 * @return true if a traversal should be performed after the adjustment. 905 */ 906 boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) { 907 if (adjustAmount != mAdjustImeAmount 908 || adjustDividerAmount != mAdjustDividerAmount || force) { 909 mAdjustImeAmount = adjustAmount; 910 mAdjustDividerAmount = adjustDividerAmount; 911 updateAdjustedBounds(); 912 return isVisible(); 913 } else { 914 return false; 915 } 916 } 917 918 /** 919 * Resets the adjustment after it got adjusted for the IME. 920 * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about 921 * animations; otherwise, set flag and animates the window away together 922 * with IME window. 923 */ 924 void resetAdjustedForIme(boolean adjustBoundsNow) { 925 if (adjustBoundsNow) { 926 mImeWin = null; 927 mAdjustedForIme = false; 928 mImeGoingAway = false; 929 mAdjustImeAmount = 0f; 930 mAdjustDividerAmount = 0f; 931 updateAdjustedBounds(); 932 mService.setResizeDimLayer(false, mStackId, 1.0f); 933 } else { 934 mImeGoingAway |= mAdjustedForIme; 935 } 936 } 937 938 /** 939 * Sets the amount how much we currently minimize our stack. 940 * 941 * @param minimizeAmount The amount, between 0 and 1. 942 * @return Whether the amount has changed and a layout is needed. 943 */ 944 boolean setAdjustedForMinimizedDock(float minimizeAmount) { 945 if (minimizeAmount != mMinimizeAmount) { 946 mMinimizeAmount = minimizeAmount; 947 updateAdjustedBounds(); 948 return isVisible(); 949 } else { 950 return false; 951 } 952 } 953 954 boolean isAdjustedForMinimizedDock() { 955 return mMinimizeAmount != 0f; 956 } 957 958 /** 959 * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows 960 * to the list of to be drawn windows the service is waiting for. 961 */ 962 void beginImeAdjustAnimation() { 963 for (int j = mChildren.size() - 1; j >= 0; j--) { 964 final Task task = mChildren.get(j); 965 if (task.hasContentToDisplay()) { 966 task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER); 967 task.setWaitingForDrawnIfResizingChanged(); 968 } 969 } 970 } 971 972 /** 973 * Resets the resizing state of all windows. 974 */ 975 void endImeAdjustAnimation() { 976 for (int j = mChildren.size() - 1; j >= 0; j--) { 977 mChildren.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER); 978 } 979 } 980 981 int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) { 982 return displayContentRect.top + (int) 983 ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN); 984 } 985 986 private boolean adjustForIME(final WindowState imeWin) { 987 final int dockedSide = getDockSide(); 988 final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; 989 if (imeWin == null || !dockedTopOrBottom) { 990 return false; 991 } 992 993 final Rect displayContentRect = mTmpRect; 994 final Rect contentBounds = mTmpRect2; 995 996 // Calculate the content bounds excluding the area occupied by IME 997 getDisplayContent().getContentRect(displayContentRect); 998 contentBounds.set(displayContentRect); 999 int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top); 1000 1001 imeTop += imeWin.getGivenContentInsetsLw().top; 1002 if (contentBounds.bottom > imeTop) { 1003 contentBounds.bottom = imeTop; 1004 } 1005 1006 final int yOffset = displayContentRect.bottom - contentBounds.bottom; 1007 1008 final int dividerWidth = 1009 getDisplayContent().mDividerControllerLocked.getContentWidth(); 1010 final int dividerWidthInactive = 1011 getDisplayContent().mDividerControllerLocked.getContentWidthInactive(); 1012 1013 if (dockedSide == DOCKED_TOP) { 1014 // If this stack is docked on top, we make it smaller so the bottom stack is not 1015 // occluded by IME. We shift its bottom up by the height of the IME, but 1016 // leaves at least 30% of the top stack visible. 1017 final int minTopStackBottom = 1018 getMinTopStackBottom(displayContentRect, mBounds.bottom); 1019 final int bottom = Math.max( 1020 mBounds.bottom - yOffset + dividerWidth - dividerWidthInactive, 1021 minTopStackBottom); 1022 mTmpAdjustedBounds.set(mBounds); 1023 mTmpAdjustedBounds.bottom = 1024 (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom); 1025 mFullyAdjustedImeBounds.set(mBounds); 1026 } else { 1027 // When the stack is on bottom and has no focus, it's only adjusted for divider width. 1028 final int dividerWidthDelta = dividerWidthInactive - dividerWidth; 1029 1030 // When the stack is on bottom and has focus, it needs to be moved up so as to 1031 // not occluded by IME, and at the same time adjusted for divider width. 1032 // We try to move it up by the height of the IME window, but only to the extent 1033 // that leaves at least 30% of the top stack visible. 1034 // 'top' is where the top of bottom stack will move to in this case. 1035 final int topBeforeImeAdjust = mBounds.top - dividerWidth + dividerWidthInactive; 1036 final int minTopStackBottom = 1037 getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth); 1038 final int top = Math.max( 1039 mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive); 1040 1041 mTmpAdjustedBounds.set(mBounds); 1042 // Account for the adjustment for IME and divider width separately. 1043 // (top - topBeforeImeAdjust) is the amount of movement due to IME only, 1044 // and dividerWidthDelta is due to divider width change only. 1045 mTmpAdjustedBounds.top = mBounds.top + 1046 (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) + 1047 mAdjustDividerAmount * dividerWidthDelta); 1048 mFullyAdjustedImeBounds.set(mBounds); 1049 mFullyAdjustedImeBounds.top = top; 1050 mFullyAdjustedImeBounds.bottom = top + mBounds.height(); 1051 } 1052 return true; 1053 } 1054 1055 private boolean adjustForMinimizedDockedStack(float minimizeAmount) { 1056 final int dockSide = getDockSide(); 1057 if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) { 1058 return false; 1059 } 1060 1061 if (dockSide == DOCKED_TOP) { 1062 mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); 1063 int topInset = mTmpRect.top; 1064 mTmpAdjustedBounds.set(mBounds); 1065 mTmpAdjustedBounds.bottom = 1066 (int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom); 1067 } else if (dockSide == DOCKED_LEFT) { 1068 mTmpAdjustedBounds.set(mBounds); 1069 final int width = mBounds.width(); 1070 mTmpAdjustedBounds.right = 1071 (int) (minimizeAmount * mDockedStackMinimizeThickness 1072 + (1 - minimizeAmount) * mBounds.right); 1073 mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width; 1074 } else if (dockSide == DOCKED_RIGHT) { 1075 mTmpAdjustedBounds.set(mBounds); 1076 mTmpAdjustedBounds.left = 1077 (int) (minimizeAmount * (mBounds.right - mDockedStackMinimizeThickness) 1078 + (1 - minimizeAmount) * mBounds.left); 1079 } 1080 return true; 1081 } 1082 1083 /** 1084 * @return the distance in pixels how much the stack gets minimized from it's original size 1085 */ 1086 int getMinimizeDistance() { 1087 final int dockSide = getDockSide(); 1088 if (dockSide == DOCKED_INVALID) { 1089 return 0; 1090 } 1091 1092 if (dockSide == DOCKED_TOP) { 1093 mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect); 1094 int topInset = mTmpRect.top; 1095 return mBounds.bottom - topInset; 1096 } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) { 1097 return mBounds.width() - mDockedStackMinimizeThickness; 1098 } else { 1099 return 0; 1100 } 1101 } 1102 1103 /** 1104 * Updates the adjustment depending on it's current state. 1105 */ 1106 private void updateAdjustedBounds() { 1107 boolean adjust = false; 1108 if (mMinimizeAmount != 0f) { 1109 adjust = adjustForMinimizedDockedStack(mMinimizeAmount); 1110 } else if (mAdjustedForIme) { 1111 adjust = adjustForIME(mImeWin); 1112 } 1113 if (!adjust) { 1114 mTmpAdjustedBounds.setEmpty(); 1115 } 1116 setAdjustedBounds(mTmpAdjustedBounds); 1117 1118 final boolean isImeTarget = (mService.getImeFocusStackLocked() == this); 1119 if (mAdjustedForIme && adjust && !isImeTarget) { 1120 final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount) 1121 * IME_ADJUST_DIM_AMOUNT; 1122 mService.setResizeDimLayer(true, mStackId, alpha); 1123 } 1124 } 1125 1126 void applyAdjustForImeIfNeeded(Task task) { 1127 if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) { 1128 return; 1129 } 1130 1131 final Rect insetBounds = mImeGoingAway ? mBounds : mFullyAdjustedImeBounds; 1132 task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP); 1133 mDisplayContent.setLayoutNeeded(); 1134 } 1135 1136 boolean isAdjustedForMinimizedDockedStack() { 1137 return mMinimizeAmount != 0f; 1138 } 1139 1140 public void dump(String prefix, PrintWriter pw) { 1141 pw.println(prefix + "mStackId=" + mStackId); 1142 pw.println(prefix + "mDeferRemoval=" + mDeferRemoval); 1143 pw.println(prefix + "mFillsParent=" + mFillsParent); 1144 pw.println(prefix + "mBounds=" + mBounds.toShortString()); 1145 if (mMinimizeAmount != 0f) { 1146 pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount); 1147 } 1148 if (mAdjustedForIme) { 1149 pw.println(prefix + "mAdjustedForIme=true"); 1150 pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount); 1151 pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount); 1152 } 1153 if (!mAdjustedBounds.isEmpty()) { 1154 pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString()); 1155 } 1156 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) { 1157 mChildren.get(taskNdx).dump(prefix + " ", pw); 1158 } 1159 if (mAnimationBackgroundSurface.isDimming()) { 1160 pw.println(prefix + "mWindowAnimationBackgroundSurface:"); 1161 mAnimationBackgroundSurface.printTo(prefix + " ", pw); 1162 } 1163 if (!mExitingAppTokens.isEmpty()) { 1164 pw.println(); 1165 pw.println(" Exiting application tokens:"); 1166 for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) { 1167 WindowToken token = mExitingAppTokens.get(i); 1168 pw.print(" Exiting App #"); pw.print(i); 1169 pw.print(' '); pw.print(token); 1170 pw.println(':'); 1171 token.dump(pw, " "); 1172 } 1173 } 1174 } 1175 1176 /** Fullscreen status of the stack without adjusting for other factors in the system like 1177 * visibility of docked stack. 1178 * Most callers should be using {@link #fillsParent} as it take into consideration other 1179 * system factors. */ 1180 boolean getRawFullscreen() { 1181 return mFillsParent; 1182 } 1183 1184 @Override 1185 public boolean dimFullscreen() { 1186 return StackId.isHomeOrRecentsStack(mStackId) || fillsParent(); 1187 } 1188 1189 @Override 1190 boolean fillsParent() { 1191 if (useCurrentBounds()) { 1192 return mFillsParent; 1193 } 1194 // The bounds has been adjusted to accommodate for a docked stack, but the docked stack 1195 // is not currently visible. Go ahead a represent it as fullscreen to the rest of the 1196 // system. 1197 return true; 1198 } 1199 1200 @Override 1201 public DisplayInfo getDisplayInfo() { 1202 return mDisplayContent.getDisplayInfo(); 1203 } 1204 1205 @Override 1206 public String toString() { 1207 return "{stackId=" + mStackId + " tasks=" + mChildren + "}"; 1208 } 1209 1210 String getName() { 1211 return toShortString(); 1212 } 1213 1214 @Override 1215 public String toShortString() { 1216 return "Stack=" + mStackId; 1217 } 1218 1219 /** 1220 * For docked workspace (or workspace that's side-by-side to the docked), provides 1221 * information which side of the screen was the dock anchored. 1222 */ 1223 int getDockSide() { 1224 return getDockSide(mBounds); 1225 } 1226 1227 int getDockSide(Rect bounds) { 1228 if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) { 1229 return DOCKED_INVALID; 1230 } 1231 if (mDisplayContent == null) { 1232 return DOCKED_INVALID; 1233 } 1234 mDisplayContent.getLogicalDisplayRect(mTmpRect); 1235 final int orientation = mDisplayContent.getConfiguration().orientation; 1236 return getDockSideUnchecked(bounds, mTmpRect, orientation); 1237 } 1238 1239 static int getDockSideUnchecked(Rect bounds, Rect displayRect, int orientation) { 1240 if (orientation == Configuration.ORIENTATION_PORTRAIT) { 1241 // Portrait mode, docked either at the top or the bottom. 1242 if (bounds.top - displayRect.top <= displayRect.bottom - bounds.bottom) { 1243 return DOCKED_TOP; 1244 } else { 1245 return DOCKED_BOTTOM; 1246 } 1247 } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 1248 // Landscape mode, docked either on the left or on the right. 1249 if (bounds.left - displayRect.left <= displayRect.right - bounds.right) { 1250 return DOCKED_LEFT; 1251 } else { 1252 return DOCKED_RIGHT; 1253 } 1254 } else { 1255 return DOCKED_INVALID; 1256 } 1257 } 1258 1259 boolean hasTaskForUser(int userId) { 1260 for (int i = mChildren.size() - 1; i >= 0; i--) { 1261 final Task task = mChildren.get(i); 1262 if (task.mUserId == userId) { 1263 return true; 1264 } 1265 } 1266 return false; 1267 } 1268 1269 int taskIdFromPoint(int x, int y) { 1270 getBounds(mTmpRect); 1271 if (!mTmpRect.contains(x, y) || isAdjustedForMinimizedDockedStack()) { 1272 return -1; 1273 } 1274 1275 for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) { 1276 final Task task = mChildren.get(taskNdx); 1277 final WindowState win = task.getTopVisibleAppMainWindow(); 1278 if (win == null) { 1279 continue; 1280 } 1281 // We need to use the task's dim bounds (which is derived from the visible bounds of its 1282 // apps windows) for any touch-related tests. Can't use the task's original bounds 1283 // because it might be adjusted to fit the content frame. For example, the presence of 1284 // the IME adjusting the windows frames when the app window is the IME target. 1285 task.getDimBounds(mTmpRect); 1286 if (mTmpRect.contains(x, y)) { 1287 return task.mTaskId; 1288 } 1289 } 1290 1291 return -1; 1292 } 1293 1294 void findTaskForResizePoint(int x, int y, int delta, 1295 DisplayContent.TaskForResizePointSearchResult results) { 1296 if (!StackId.isTaskResizeAllowed(mStackId)) { 1297 results.searchDone = true; 1298 return; 1299 } 1300 1301 for (int i = mChildren.size() - 1; i >= 0; --i) { 1302 final Task task = mChildren.get(i); 1303 if (task.isFullscreen()) { 1304 results.searchDone = true; 1305 return; 1306 } 1307 1308 // We need to use the task's dim bounds (which is derived from the visible bounds of 1309 // its apps windows) for any touch-related tests. Can't use the task's original 1310 // bounds because it might be adjusted to fit the content frame. One example is when 1311 // the task is put to top-left quadrant, the actual visible area would not start at 1312 // (0,0) after it's adjusted for the status bar. 1313 task.getDimBounds(mTmpRect); 1314 mTmpRect.inset(-delta, -delta); 1315 if (mTmpRect.contains(x, y)) { 1316 mTmpRect.inset(delta, delta); 1317 1318 results.searchDone = true; 1319 1320 if (!mTmpRect.contains(x, y)) { 1321 results.taskForResize = task; 1322 return; 1323 } 1324 // User touched inside the task. No need to look further, 1325 // focus transfer will be handled in ACTION_UP. 1326 return; 1327 } 1328 } 1329 } 1330 1331 void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion, 1332 Rect contentRect, Rect postExclude) { 1333 for (int i = mChildren.size() - 1; i >= 0; --i) { 1334 final Task task = mChildren.get(i); 1335 AppWindowToken token = task.getTopVisibleAppToken(); 1336 if (token == null || !token.hasContentToDisplay()) { 1337 continue; 1338 } 1339 1340 /** 1341 * Exclusion region is the region that TapDetector doesn't care about. 1342 * Here we want to remove all non-focused tasks from the exclusion region. 1343 * We also remove the outside touch area for resizing for all freeform 1344 * tasks (including the focused). 1345 * 1346 * We save the focused task region once we find it, and add it back at the end. 1347 */ 1348 1349 task.getDimBounds(mTmpRect); 1350 1351 if (task == focusedTask) { 1352 // Add the focused task rect back into the exclude region once we are done 1353 // processing stacks. 1354 postExclude.set(mTmpRect); 1355 } 1356 1357 final boolean isFreeformed = task.inFreeformWorkspace(); 1358 if (task != focusedTask || isFreeformed) { 1359 if (isFreeformed) { 1360 // If the task is freeformed, enlarge the area to account for outside 1361 // touch area for resize. 1362 mTmpRect.inset(-delta, -delta); 1363 // Intersect with display content rect. If we have system decor (status bar/ 1364 // navigation bar), we want to exclude that from the tap detection. 1365 // Otherwise, if the app is partially placed under some system button (eg. 1366 // Recents, Home), pressing that button would cause a full series of 1367 // unwanted transfer focus/resume/pause, before we could go home. 1368 mTmpRect.intersect(contentRect); 1369 } 1370 touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE); 1371 } 1372 } 1373 } 1374 1375 @Override // AnimatesBounds 1376 public boolean setSize(Rect bounds) { 1377 synchronized (mService.mWindowMap) { 1378 if (mDisplayContent == null) { 1379 return false; 1380 } 1381 } 1382 try { 1383 mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false, -1); 1384 } catch (RemoteException e) { 1385 } 1386 return true; 1387 } 1388 1389 public boolean setPinnedStackSize(Rect bounds, Rect tempTaskBounds) { 1390 synchronized (mService.mWindowMap) { 1391 if (mDisplayContent == null) { 1392 return false; 1393 } 1394 if (mStackId != PINNED_STACK_ID) { 1395 Slog.w(TAG_WM, "Attempt to use pinned stack resize animation helper on" 1396 + "non pinned stack"); 1397 return false; 1398 } 1399 } 1400 try { 1401 mService.mActivityManager.resizePinnedStack(bounds, tempTaskBounds); 1402 } catch (RemoteException e) { 1403 // I don't believe you. 1404 } 1405 return true; 1406 } 1407 1408 @Override // AnimatesBounds 1409 public void onAnimationStart() { 1410 synchronized (mService.mWindowMap) { 1411 mBoundsAnimating = true; 1412 } 1413 } 1414 1415 @Override // AnimatesBounds 1416 public void onAnimationEnd() { 1417 synchronized (mService.mWindowMap) { 1418 mBoundsAnimating = false; 1419 mBoundsAnimationTarget.setEmpty(); 1420 mService.requestTraversal(); 1421 } 1422 if (mStackId == PINNED_STACK_ID) { 1423 try { 1424 mService.mActivityManager.notifyPinnedStackAnimationEnded(); 1425 } catch (RemoteException e) { 1426 // I don't believe you... 1427 } 1428 } 1429 } 1430 1431 @Override 1432 public void moveToFullscreen() { 1433 try { 1434 mService.mActivityManager.moveTasksToFullscreenStack(mStackId, true); 1435 } catch (RemoteException e) { 1436 e.printStackTrace(); 1437 } 1438 } 1439 1440 @Override 1441 public void getFullScreenBounds(Rect bounds) { 1442 getDisplayContent().getContentRect(bounds); 1443 } 1444 1445 public boolean hasMovementAnimations() { 1446 return StackId.hasMovementAnimations(mStackId); 1447 } 1448 1449 public boolean getForceScaleToCrop() { 1450 return mBoundsAnimating; 1451 } 1452 1453 public boolean getBoundsAnimating() { 1454 return mBoundsAnimating; 1455 } 1456 1457 /** Returns true if a removal action is still being deferred. */ 1458 boolean checkCompleteDeferredRemoval() { 1459 if (isAnimating()) { 1460 return true; 1461 } 1462 if (mDeferRemoval) { 1463 removeImmediately(); 1464 } 1465 1466 return super.checkCompleteDeferredRemoval(); 1467 } 1468 1469 void stepAppWindowsAnimation(long currentTime) { 1470 super.stepAppWindowsAnimation(currentTime); 1471 1472 // TODO: Why aren't we just using the loop above for this? mAppAnimator.animating isn't set 1473 // below but is set in the loop above. See if it really matters... 1474 final int exitingCount = mExitingAppTokens.size(); 1475 for (int i = 0; i < exitingCount; i++) { 1476 final AppWindowAnimator appAnimator = mExitingAppTokens.get(i).mAppAnimator; 1477 appAnimator.wasAnimating = appAnimator.animating; 1478 if (appAnimator.stepAnimationLocked(currentTime)) { 1479 mService.mAnimator.setAnimating(true); 1480 mService.mAnimator.mAppWindowAnimating = true; 1481 } else if (appAnimator.wasAnimating) { 1482 // stopped animating, do one more pass through the layout 1483 appAnimator.mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER, 1484 "exiting appToken " + appAnimator.mAppToken + " done"); 1485 if (DEBUG_ANIM) Slog.v(TAG_WM, 1486 "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken); 1487 } 1488 } 1489 } 1490 1491 void getWindowOnDisplayBeforeToken(DisplayContent dc, WindowToken token, 1492 DisplayContent.GetWindowOnDisplaySearchResult result) { 1493 for (int i = mChildren.size() - 1; i >= 0; --i) { 1494 final Task task = mChildren.get(i); 1495 task.getWindowOnDisplayBeforeToken(dc, token, result); 1496 if (result.reachedToken) { 1497 // We have reach the token we are interested in. End search. 1498 return; 1499 } 1500 } 1501 } 1502 1503 void getWindowOnDisplayAfterToken(DisplayContent dc, WindowToken token, 1504 DisplayContent.GetWindowOnDisplaySearchResult result) { 1505 for (int i = mChildren.size() - 1; i >= 0; --i) { 1506 final Task task = mChildren.get(i); 1507 task.getWindowOnDisplayAfterToken(dc, token, result); 1508 if (result.foundWindow != null) { 1509 // We have found a window after the token. End search. 1510 return; 1511 } 1512 } 1513 } 1514 1515 @Override 1516 int getOrientation() { 1517 return (StackId.canSpecifyOrientation(mStackId)) 1518 ? super.getOrientation() : SCREEN_ORIENTATION_UNSET; 1519 } 1520} 1521