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