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