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