TaskStack.java revision 19d9a8f47625319406f593d4ec71de0c8df1fcfe
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        if (mFullscreen) {
387            // Bounds will already be set correctly when display info is updated in the case of
388            // fullscreen.
389            return false;
390        }
391
392        final int newRotation = getDisplayInfo().rotation;
393        final int newDensity = getDisplayInfo().logicalDensityDpi;
394
395        if (mRotation == newRotation && mDensity == newDensity) {
396            // Nothing to do here as we already update the state in updateDisplayInfo.
397            return false;
398        }
399
400        final int oldDockSide = mStackId == DOCKED_STACK_ID ? getDockSide() : DOCKED_INVALID;
401        mTmpRect2.set(mBounds);
402        mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
403        if (mStackId == DOCKED_STACK_ID) {
404            repositionDockedStackAfterRotation(mTmpRect2);
405            snapDockedStackAfterRotation(mTmpRect2);
406            final int newDockSide = getDockSide(mTmpRect2);
407            if (oldDockSide != newDockSide) {
408                mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
409            }
410        }
411
412        mBoundsAfterRotation.set(mTmpRect2);
413        return true;
414    }
415
416    void getBoundsForNewConfiguration(Rect outBounds) {
417        outBounds.set(mBoundsAfterRotation);
418        mBoundsAfterRotation.setEmpty();
419    }
420
421    /**
422     * Some dock sides are not allowed by the policy. This method queries the policy and moves
423     * the docked stack around if needed.
424     *
425     * @param inOutBounds the bounds of the docked stack to adjust
426     */
427    private void repositionDockedStackAfterRotation(Rect inOutBounds) {
428        int dockSide = getDockSide(inOutBounds);
429        if (mService.mPolicy.isDockSideAllowed(dockSide)) {
430            return;
431        }
432        mDisplayContent.getLogicalDisplayRect(mTmpRect);
433        dockSide = DockedDividerUtils.invertDockSide(dockSide);
434        switch (dockSide) {
435            case DOCKED_LEFT:
436                int movement = inOutBounds.left;
437                inOutBounds.left -= movement;
438                inOutBounds.right -= movement;
439                break;
440            case DOCKED_RIGHT:
441                movement = mTmpRect.right - inOutBounds.right;
442                inOutBounds.left += movement;
443                inOutBounds.right += movement;
444                break;
445            case DOCKED_TOP:
446                movement = inOutBounds.top;
447                inOutBounds.top -= movement;
448                inOutBounds.bottom -= movement;
449                break;
450            case DOCKED_BOTTOM:
451                movement = mTmpRect.bottom - inOutBounds.bottom;
452                inOutBounds.top += movement;
453                inOutBounds.bottom += movement;
454                break;
455        }
456    }
457
458    /**
459     * Snaps the bounds after rotation to the closest snap target for the docked stack.
460     */
461    private void snapDockedStackAfterRotation(Rect outBounds) {
462
463        // Calculate the current position.
464        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
465        final int dividerSize = mService.getDefaultDisplayContentLocked()
466                .getDockedDividerController().getContentWidth();
467        final int dockSide = getDockSide(outBounds);
468        final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
469                dockSide, dividerSize);
470        final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth;
471        final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight;
472
473        // Snap the position to a target.
474        final int rotation = displayInfo.rotation;
475        final int orientation = mService.mCurConfiguration.orientation;
476        mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
477        final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
478                mService.mContext.getResources(), displayWidth, displayHeight,
479                dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds);
480        final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
481
482        // Recalculate the bounds based on the position of the target.
483        DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
484                outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight,
485                dividerSize);
486    }
487
488    boolean isAnimating() {
489        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
490            final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
491            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
492                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
493                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
494                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
495                    if (winAnimator.isAnimationSet() || winAnimator.mWin.mAnimatingExit) {
496                        return true;
497                    }
498                }
499            }
500        }
501        return false;
502    }
503
504    void addTask(Task task, boolean toTop) {
505        addTask(task, toTop, task.showForAllUsers());
506    }
507
508    /**
509     * Put a Task in this stack. Used for adding and moving.
510     * @param task The task to add.
511     * @param toTop Whether to add it to the top or bottom.
512     * @param showForAllUsers Whether to show the task regardless of the current user.
513     */
514    void addTask(Task task, boolean toTop, boolean showForAllUsers) {
515        positionTask(task, toTop ? mTasks.size() : 0, showForAllUsers);
516    }
517
518    void positionTask(Task task, int position, boolean showForAllUsers) {
519        final boolean canShowTask =
520                showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
521        mTasks.remove(task);
522        int stackSize = mTasks.size();
523        int minPosition = 0;
524        int maxPosition = stackSize;
525
526        if (canShowTask) {
527            minPosition = computeMinPosition(minPosition, stackSize);
528        } else {
529            maxPosition = computeMaxPosition(maxPosition);
530        }
531        // Reset position based on minimum/maximum possible positions.
532        position = Math.min(Math.max(position, minPosition), maxPosition);
533
534        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM,
535                "positionTask: task=" + task + " position=" + position);
536        mTasks.add(position, task);
537
538        // If we are moving the task across stacks, the scroll is no longer valid.
539        if (task.mStack != this) {
540            task.resetScrollLocked();
541        }
542        task.mStack = this;
543        task.updateDisplayInfo(mDisplayContent);
544        boolean toTop = position == mTasks.size() - 1;
545        if (toTop) {
546            mDisplayContent.moveStack(this, true);
547        }
548
549        if (StackId.windowsAreScaleable(mStackId)) {
550            // We force windows out of SCALING_MODE_FREEZE
551            // so that we can continue to animate them
552            // while a resize is pending.
553            forceWindowsScaleable(task, true);
554        } else {
555            forceWindowsScaleable(task, false);
556        }
557        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, task.mTaskId, toTop ? 1 : 0, position);
558    }
559
560    /** Calculate the minimum possible position for a task that can be shown to the user.
561     *  The minimum position will be above all other tasks that can't be shown.
562     *  @param minPosition The minimum position the caller is suggesting.
563     *                  We will start adjusting up from here.
564     *  @param size The size of the current task list.
565     */
566    private int computeMinPosition(int minPosition, int size) {
567        while (minPosition < size) {
568            final Task tmpTask = mTasks.get(minPosition);
569            final boolean canShowTmpTask =
570                    tmpTask.showForAllUsers()
571                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
572            if (canShowTmpTask) {
573                break;
574            }
575            minPosition++;
576        }
577        return minPosition;
578    }
579
580    /** Calculate the maximum possible position for a task that can't be shown to the user.
581     *  The maximum position will be below all other tasks that can be shown.
582     *  @param maxPosition The maximum position the caller is suggesting.
583     *                  We will start adjusting down from here.
584     */
585    private int computeMaxPosition(int maxPosition) {
586        while (maxPosition > 0) {
587            final Task tmpTask = mTasks.get(maxPosition - 1);
588            final boolean canShowTmpTask =
589                    tmpTask.showForAllUsers()
590                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
591            if (!canShowTmpTask) {
592                break;
593            }
594            maxPosition--;
595        }
596        return maxPosition;
597    }
598
599    void moveTaskToTop(Task task) {
600        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToTop: task=" + task + " Callers="
601                + Debug.getCallers(6));
602        mTasks.remove(task);
603        addTask(task, true);
604    }
605
606    void moveTaskToBottom(Task task) {
607        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "moveTaskToBottom: task=" + task);
608        mTasks.remove(task);
609        addTask(task, false);
610    }
611
612    /**
613     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
614     * back.
615     * @param task The Task to delete.
616     */
617    void removeTask(Task task) {
618        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeTask: task=" + task);
619        mTasks.remove(task);
620        if (mDisplayContent != null) {
621            if (mTasks.isEmpty()) {
622                mDisplayContent.moveStack(this, false);
623            }
624            mDisplayContent.layoutNeeded = true;
625        }
626        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
627            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
628            if (wtoken.mTask == task) {
629                wtoken.mIsExiting = false;
630                mExitingAppTokens.remove(appNdx);
631            }
632        }
633    }
634
635    void attachDisplayContent(DisplayContent displayContent) {
636        if (mDisplayContent != null) {
637            throw new IllegalStateException("attachDisplayContent: Already attached");
638        }
639
640        mDisplayContent = displayContent;
641        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
642                "animation background stackId=" + mStackId);
643
644        Rect bounds = null;
645        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
646        if (mStackId == DOCKED_STACK_ID
647                || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId)
648                        && !dockedStack.isFullscreen())) {
649            // The existence of a docked stack affects the size of other static stack created since
650            // the docked stack occupies a dedicated region on screen, but only if the dock stack is
651            // not fullscreen. If it's fullscreen, it means that we are in the transition of
652            // dismissing it, so we must not resize this stack.
653            bounds = new Rect();
654            displayContent.getLogicalDisplayRect(mTmpRect);
655            mTmpRect2.setEmpty();
656            if (dockedStack != null) {
657                dockedStack.getRawBounds(mTmpRect2);
658            }
659            final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
660                    == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
661            getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
662                    mDisplayContent.mDividerControllerLocked.getContentWidth(),
663                    dockedOnTopOrLeft);
664        }
665
666        updateDisplayInfo(bounds);
667    }
668
669    void getStackDockedModeBoundsLocked(Rect outBounds, boolean ignoreVisibility) {
670        if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId))
671                || mDisplayContent == null) {
672            outBounds.set(mBounds);
673            return;
674        }
675
676        final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
677        if (dockedStack == null) {
678            // Not sure why you are calling this method when there is no docked stack...
679            throw new IllegalStateException(
680                    "Calling getStackDockedModeBoundsLocked() when there is no docked stack.");
681        }
682        if (!ignoreVisibility && !dockedStack.isVisibleLocked()) {
683            // The docked stack is being dismissed, but we caught before it finished being
684            // dismissed. In that case we want to treat it as if it is not occupying any space and
685            // let others occupy the whole display.
686            mDisplayContent.getLogicalDisplayRect(outBounds);
687            return;
688        }
689
690        final int dockedSide = dockedStack.getDockSide();
691        if (dockedSide == DOCKED_INVALID) {
692            // Not sure how you got here...Only thing we can do is return current bounds.
693            Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack);
694            outBounds.set(mBounds);
695            return;
696        }
697
698        mDisplayContent.getLogicalDisplayRect(mTmpRect);
699        dockedStack.getRawBounds(mTmpRect2);
700        final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
701        getStackDockedModeBounds(mTmpRect, outBounds, mStackId, mTmpRect2,
702                mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
703
704    }
705
706    /**
707     * Outputs the bounds a stack should be given the presence of a docked stack on the display.
708     * @param displayRect The bounds of the display the docked stack is on.
709     * @param outBounds Output bounds that should be used for the stack.
710     * @param stackId Id of stack we are calculating the bounds for.
711     * @param dockedBounds Bounds of the docked stack.
712     * @param dockDividerWidth We need to know the width of the divider make to the output bounds
713     *                         close to the side of the dock.
714     * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
715     */
716    private void getStackDockedModeBounds(
717            Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
718            boolean dockOnTopOrLeft) {
719        final boolean dockedStack = stackId == DOCKED_STACK_ID;
720        final boolean splitHorizontally = displayRect.width() > displayRect.height();
721
722        outBounds.set(displayRect);
723        if (dockedStack) {
724            if (mService.mDockedStackCreateBounds != null) {
725                outBounds.set(mService.mDockedStackCreateBounds);
726                return;
727            }
728
729            // The initial bounds of the docked stack when it is created about half the screen space
730            // and its bounds can be adjusted after that. The bounds of all other stacks are
731            // adjusted to occupy whatever screen space the docked stack isn't occupying.
732            final DisplayInfo di = mDisplayContent.getDisplayInfo();
733            mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
734                    mTmpRect2);
735            final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
736                    di.logicalWidth,
737                    di.logicalHeight,
738                    dockDividerWidth,
739                    mService.mCurConfiguration.orientation == ORIENTATION_PORTRAIT,
740                    mTmpRect2).getMiddleTarget().position;
741
742            if (dockOnTopOrLeft) {
743                if (splitHorizontally) {
744                    outBounds.right = position;
745                } else {
746                    outBounds.bottom = position;
747                }
748            } else {
749                if (splitHorizontally) {
750                    outBounds.left = position + dockDividerWidth;
751                } else {
752                    outBounds.top = position + dockDividerWidth;
753                }
754            }
755            return;
756        }
757
758        // Other stacks occupy whatever space is left by the docked stack.
759        if (!dockOnTopOrLeft) {
760            if (splitHorizontally) {
761                outBounds.right = dockedBounds.left - dockDividerWidth;
762            } else {
763                outBounds.bottom = dockedBounds.top - dockDividerWidth;
764            }
765        } else {
766            if (splitHorizontally) {
767                outBounds.left = dockedBounds.right + dockDividerWidth;
768            } else {
769                outBounds.top = dockedBounds.bottom + dockDividerWidth;
770            }
771        }
772        DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
773    }
774
775    void resetDockedStackToMiddle() {
776        if (mStackId != DOCKED_STACK_ID) {
777            throw new IllegalStateException("Not a docked stack=" + this);
778        }
779
780        mService.mDockedStackCreateBounds = null;
781
782        final Rect bounds = new Rect();
783        getStackDockedModeBoundsLocked(bounds, true /*ignoreVisibility*/);
784        mService.mH.obtainMessage(RESIZE_STACK, DOCKED_STACK_ID,
785                1 /*allowResizeInDockedMode*/, bounds).sendToTarget();
786    }
787
788    void detachDisplay() {
789        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
790
791        boolean doAnotherLayoutPass = false;
792        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
793            final AppTokenList appWindowTokens = mTasks.get(taskNdx).mAppTokens;
794            for (int appNdx = appWindowTokens.size() - 1; appNdx >= 0; --appNdx) {
795                final WindowList appWindows = appWindowTokens.get(appNdx).allAppWindows;
796                for (int winNdx = appWindows.size() - 1; winNdx >= 0; --winNdx) {
797                    // We are in the middle of changing the state of displays/stacks/tasks. We need
798                    // to finish that, before we let layout interfere with it.
799                    mService.removeWindowLocked(appWindows.get(winNdx));
800                    doAnotherLayoutPass = true;
801                }
802            }
803        }
804        if (doAnotherLayoutPass) {
805            mService.mWindowPlacerLocked.requestTraversal();
806        }
807
808        close();
809    }
810
811    void resetAnimationBackgroundAnimator() {
812        mAnimationBackgroundAnimator = null;
813        mAnimationBackgroundSurface.hide();
814    }
815
816    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
817        int animLayer = winAnimator.mAnimLayer;
818        if (mAnimationBackgroundAnimator == null
819                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
820            mAnimationBackgroundAnimator = winAnimator;
821            animLayer = mService.adjustAnimationBackground(winAnimator);
822            mAnimationBackgroundSurface.show(animLayer - WindowManagerService.LAYER_OFFSET_DIM,
823                    ((color >> 24) & 0xff) / 255f, 0);
824        }
825    }
826
827    void switchUser() {
828        int top = mTasks.size();
829        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
830            Task task = mTasks.get(taskNdx);
831            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
832                mTasks.remove(taskNdx);
833                mTasks.add(task);
834                --top;
835            }
836        }
837    }
838
839    void close() {
840        if (mAnimationBackgroundSurface != null) {
841            mAnimationBackgroundSurface.destroySurface();
842            mAnimationBackgroundSurface = null;
843        }
844        mDisplayContent = null;
845    }
846
847    /**
848     * Adjusts the stack bounds if the IME is visible.
849     *
850     * @param imeWin The IME window.
851     */
852    void setAdjustedForIme(WindowState imeWin, boolean forceUpdate) {
853        mImeWin = imeWin;
854        mImeGoingAway = false;
855        if (!mAdjustedForIme || forceUpdate) {
856            mAdjustedForIme = true;
857            mAdjustImeAmount = 0f;
858            mAdjustDividerAmount = 0f;
859            updateAdjustForIme(0f, 0f, true /* force */);
860        }
861    }
862
863    boolean isAdjustedForIme() {
864        return mAdjustedForIme;
865    }
866
867    boolean isAnimatingForIme() {
868        return mImeWin != null && mImeWin.isAnimatingLw();
869    }
870
871    /**
872     * Update the stack's bounds (crop or position) according to the IME window's
873     * current position. When IME window is animated, the bottom stack is animated
874     * together to track the IME window's current position, and the top stack is
875     * cropped as necessary.
876     *
877     * @return true if a traversal should be performed after the adjustment.
878     */
879    boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) {
880        if (adjustAmount != mAdjustImeAmount
881                || adjustDividerAmount != mAdjustDividerAmount || force) {
882            mAdjustImeAmount = adjustAmount;
883            mAdjustDividerAmount = adjustDividerAmount;
884            updateAdjustedBounds();
885            return isVisibleForUserLocked();
886        } else {
887            return false;
888        }
889    }
890
891    /**
892     * Resets the adjustment after it got adjusted for the IME.
893     * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
894     *                        animations; otherwise, set flag and animates the window away together
895     *                        with IME window.
896     */
897    void resetAdjustedForIme(boolean adjustBoundsNow) {
898        if (adjustBoundsNow) {
899            mImeWin = null;
900            mAdjustedForIme = false;
901            mImeGoingAway = false;
902            mAdjustImeAmount = 0f;
903            mAdjustDividerAmount = 0f;
904            updateAdjustedBounds();
905            mService.setResizeDimLayer(false, mStackId, 1.0f);
906        } else {
907            mImeGoingAway |= mAdjustedForIme;
908        }
909    }
910
911    /**
912     * Sets the amount how much we currently minimize our stack.
913     *
914     * @param minimizeAmount The amount, between 0 and 1.
915     * @return Whether the amount has changed and a layout is needed.
916     */
917    boolean setAdjustedForMinimizedDock(float minimizeAmount) {
918        if (minimizeAmount != mMinimizeAmount) {
919            mMinimizeAmount = minimizeAmount;
920            updateAdjustedBounds();
921            return isVisibleForUserLocked();
922        } else {
923            return false;
924        }
925    }
926
927    boolean isAdjustedForMinimizedDock() {
928        return mMinimizeAmount != 0f;
929    }
930
931    /**
932     * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
933     * to the list of to be drawn windows the service is waiting for.
934     */
935    void beginImeAdjustAnimation() {
936        for (int j = mTasks.size() - 1; j >= 0; j--) {
937            final Task task = mTasks.get(j);
938            if (task.isVisibleForUser()) {
939                task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
940                task.addWindowsWaitingForDrawnIfResizingChanged();
941            }
942        }
943    }
944
945    /**
946     * Resets the resizing state of all windows.
947     */
948    void endImeAdjustAnimation() {
949        for (int j = mTasks.size() - 1; j >= 0; j--) {
950            mTasks.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
951        }
952    }
953
954    int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
955        return displayContentRect.top + (int)
956                ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
957    }
958
959    private boolean adjustForIME(final WindowState imeWin) {
960        final int dockedSide = getDockSide();
961        final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
962        if (imeWin == null || !dockedTopOrBottom) {
963            return false;
964        }
965
966        final Rect displayContentRect = mTmpRect;
967        final Rect contentBounds = mTmpRect2;
968
969        // Calculate the content bounds excluding the area occupied by IME
970        getDisplayContent().getContentRect(displayContentRect);
971        contentBounds.set(displayContentRect);
972        int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
973
974        imeTop += imeWin.getGivenContentInsetsLw().top;
975        if (contentBounds.bottom > imeTop) {
976            contentBounds.bottom = imeTop;
977        }
978
979        final int yOffset = displayContentRect.bottom - contentBounds.bottom;
980
981        final int dividerWidth =
982                getDisplayContent().mDividerControllerLocked.getContentWidth();
983        final int dividerWidthInactive =
984                getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
985
986        if (dockedSide == DOCKED_TOP) {
987            // If this stack is docked on top, we make it smaller so the bottom stack is not
988            // occluded by IME. We shift its bottom up by the height of the IME, but
989            // leaves at least 30% of the top stack visible.
990            final int minTopStackBottom =
991                    getMinTopStackBottom(displayContentRect, mBounds.bottom);
992            final int bottom = Math.max(
993                    mBounds.bottom - yOffset + dividerWidth - dividerWidthInactive,
994                    minTopStackBottom);
995            mTmpAdjustedBounds.set(mBounds);
996            mTmpAdjustedBounds.bottom =
997                    (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
998            mFullyAdjustedImeBounds.set(mBounds);
999        } else {
1000            // When the stack is on bottom and has no focus, it's only adjusted for divider width.
1001            final int dividerWidthDelta = dividerWidthInactive - dividerWidth;
1002
1003            // When the stack is on bottom and has focus, it needs to be moved up so as to
1004            // not occluded by IME, and at the same time adjusted for divider width.
1005            // We try to move it up by the height of the IME window, but only to the extent
1006            // that leaves at least 30% of the top stack visible.
1007            // 'top' is where the top of bottom stack will move to in this case.
1008            final int topBeforeImeAdjust = mBounds.top - dividerWidth + dividerWidthInactive;
1009            final int minTopStackBottom =
1010                    getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth);
1011            final int top = Math.max(
1012                    mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive);
1013
1014            mTmpAdjustedBounds.set(mBounds);
1015            // Account for the adjustment for IME and divider width separately.
1016            // (top - topBeforeImeAdjust) is the amount of movement due to IME only,
1017            // and dividerWidthDelta is due to divider width change only.
1018            mTmpAdjustedBounds.top = mBounds.top +
1019                    (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) +
1020                            mAdjustDividerAmount * dividerWidthDelta);
1021            mFullyAdjustedImeBounds.set(mBounds);
1022            mFullyAdjustedImeBounds.top = top;
1023            mFullyAdjustedImeBounds.bottom = top + mBounds.height();
1024        }
1025        return true;
1026    }
1027
1028    private boolean adjustForMinimizedDockedStack(float minimizeAmount) {
1029        final int dockSide = getDockSide();
1030        if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) {
1031            return false;
1032        }
1033
1034        if (dockSide == DOCKED_TOP) {
1035            mService.getStableInsetsLocked(mTmpRect);
1036            int topInset = mTmpRect.top;
1037            mTmpAdjustedBounds.set(mBounds);
1038            mTmpAdjustedBounds.bottom =
1039                    (int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom);
1040        } else if (dockSide == DOCKED_LEFT) {
1041            mTmpAdjustedBounds.set(mBounds);
1042            final int width = mBounds.width();
1043            mTmpAdjustedBounds.right =
1044                    (int) (minimizeAmount * mDockedStackMinimizeThickness
1045                            + (1 - minimizeAmount) * mBounds.right);
1046            mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
1047        } else if (dockSide == DOCKED_RIGHT) {
1048            mTmpAdjustedBounds.set(mBounds);
1049            mTmpAdjustedBounds.left =
1050                    (int) (minimizeAmount * (mBounds.right - mDockedStackMinimizeThickness)
1051                            + (1 - minimizeAmount) * mBounds.left);
1052        }
1053        return true;
1054    }
1055
1056    /**
1057     * @return the distance in pixels how much the stack gets minimized from it's original size
1058     */
1059    int getMinimizeDistance() {
1060        final int dockSide = getDockSide();
1061        if (dockSide == DOCKED_INVALID) {
1062            return 0;
1063        }
1064
1065        if (dockSide == DOCKED_TOP) {
1066            mService.getStableInsetsLocked(mTmpRect);
1067            int topInset = mTmpRect.top;
1068            return mBounds.bottom - topInset;
1069        } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
1070            return mBounds.width() - mDockedStackMinimizeThickness;
1071        } else {
1072            return 0;
1073        }
1074    }
1075
1076    /**
1077     * Updates the adjustment depending on it's current state.
1078     */
1079    private void updateAdjustedBounds() {
1080        boolean adjust = false;
1081        if (mMinimizeAmount != 0f) {
1082            adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
1083        } else if (mAdjustedForIme) {
1084            adjust = adjustForIME(mImeWin);
1085        }
1086        if (!adjust) {
1087            mTmpAdjustedBounds.setEmpty();
1088        }
1089        setAdjustedBounds(mTmpAdjustedBounds);
1090
1091        final boolean isImeTarget = (mService.getImeTargetStackLocked() == this);
1092        if (mAdjustedForIme && adjust && !isImeTarget) {
1093            final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
1094                    * IME_ADJUST_DIM_AMOUNT;
1095            mService.setResizeDimLayer(true, mStackId, alpha);
1096        }
1097    }
1098
1099    void applyAdjustForImeIfNeeded(Task task) {
1100        if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) {
1101            return;
1102        }
1103
1104        final Rect insetBounds = mImeGoingAway ? mBounds : mFullyAdjustedImeBounds;
1105        task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP);
1106        mDisplayContent.layoutNeeded = true;
1107    }
1108
1109    boolean isAdjustedForMinimizedDockedStack() {
1110        return mMinimizeAmount != 0f;
1111    }
1112
1113    public void dump(String prefix, PrintWriter pw) {
1114        pw.println(prefix + "mStackId=" + mStackId);
1115        pw.println(prefix + "mDeferDetach=" + mDeferDetach);
1116        pw.println(prefix + "mFullscreen=" + mFullscreen);
1117        pw.println(prefix + "mBounds=" + mBounds.toShortString());
1118        if (mMinimizeAmount != 0f) {
1119            pw.println(prefix + "mMinimizeAmout=" + mMinimizeAmount);
1120        }
1121        if (mAdjustedForIme) {
1122            pw.println(prefix + "mAdjustedForIme=true");
1123            pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount);
1124            pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount);
1125        }
1126        if (!mAdjustedBounds.isEmpty()) {
1127            pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
1128        }
1129        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) {
1130            mTasks.get(taskNdx).dump(prefix + "  ", pw);
1131        }
1132        if (mAnimationBackgroundSurface.isDimming()) {
1133            pw.println(prefix + "mWindowAnimationBackgroundSurface:");
1134            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
1135        }
1136        if (!mExitingAppTokens.isEmpty()) {
1137            pw.println();
1138            pw.println("  Exiting application tokens:");
1139            for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
1140                WindowToken token = mExitingAppTokens.get(i);
1141                pw.print("  Exiting App #"); pw.print(i);
1142                pw.print(' '); pw.print(token);
1143                pw.println(':');
1144                token.dump(pw, "    ");
1145            }
1146        }
1147    }
1148
1149    /** Fullscreen status of the stack without adjusting for other factors in the system like
1150     * visibility of docked stack.
1151     * Most callers should be using {@link #isFullscreen} as it take into consideration other
1152     * system factors. */
1153    boolean getRawFullscreen() {
1154        return mFullscreen;
1155    }
1156
1157    @Override
1158    public boolean isFullscreen() {
1159        if (useCurrentBounds()) {
1160            return mFullscreen;
1161        }
1162        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
1163        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
1164        // system.
1165        return true;
1166    }
1167
1168    @Override
1169    public DisplayInfo getDisplayInfo() {
1170        return mDisplayContent.getDisplayInfo();
1171    }
1172
1173    @Override
1174    public String toString() {
1175        return "{stackId=" + mStackId + " tasks=" + mTasks + "}";
1176    }
1177
1178    @Override
1179    public String toShortString() {
1180        return "Stack=" + mStackId;
1181    }
1182
1183    /**
1184     * For docked workspace (or workspace that's side-by-side to the docked), provides
1185     * information which side of the screen was the dock anchored.
1186     */
1187    int getDockSide() {
1188        return getDockSide(mBounds);
1189    }
1190
1191    int getDockSide(Rect bounds) {
1192        if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) {
1193            return DOCKED_INVALID;
1194        }
1195        if (mDisplayContent == null) {
1196            return DOCKED_INVALID;
1197        }
1198        mDisplayContent.getLogicalDisplayRect(mTmpRect);
1199        final int orientation = mService.mCurConfiguration.orientation;
1200        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
1201            // Portrait mode, docked either at the top or the bottom.
1202            if (bounds.top - mTmpRect.top <= mTmpRect.bottom - bounds.bottom) {
1203                return DOCKED_TOP;
1204            } else {
1205                return DOCKED_BOTTOM;
1206            }
1207        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
1208            // Landscape mode, docked either on the left or on the right.
1209            if (bounds.left - mTmpRect.left <= mTmpRect.right - bounds.right) {
1210                return DOCKED_LEFT;
1211            } else {
1212                return DOCKED_RIGHT;
1213            }
1214        } else {
1215            return DOCKED_INVALID;
1216        }
1217    }
1218
1219    boolean isVisibleLocked() {
1220        final boolean keyguardOn = mService.mPolicy.isKeyguardShowingOrOccluded()
1221                && !mService.mAnimator.mKeyguardGoingAway;
1222        if (keyguardOn && !StackId.isAllowedOverLockscreen(mStackId)) {
1223            // The keyguard is showing and the stack shouldn't show on top of the keyguard.
1224            return false;
1225        }
1226
1227        for (int i = mTasks.size() - 1; i >= 0; i--) {
1228            final Task task = mTasks.get(i);
1229            for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
1230                if (!task.mAppTokens.get(j).hidden) {
1231                    return true;
1232                }
1233            }
1234        }
1235
1236        return false;
1237    }
1238
1239    /**
1240     * @return true if a the stack is visible for the current in user, ignoring any other visibility
1241     *         aspects, and false otherwise
1242     */
1243    boolean isVisibleForUserLocked() {
1244        for (int i = mTasks.size() - 1; i >= 0; i--) {
1245            final Task task = mTasks.get(i);
1246            if (task.isVisibleForUser()) {
1247                return true;
1248            }
1249        }
1250        return false;
1251    }
1252
1253    boolean isDragResizing() {
1254        return mDragResizing;
1255    }
1256
1257    void setDragResizingLocked(boolean resizing) {
1258        if (mDragResizing == resizing) {
1259            return;
1260        }
1261        mDragResizing = resizing;
1262        for (int i = mTasks.size() - 1; i >= 0 ; i--) {
1263            mTasks.get(i).resetDragResizingChangeReported();
1264        }
1265    }
1266
1267    @Override  // AnimatesBounds
1268    public boolean setSize(Rect bounds) {
1269        synchronized (mService.mWindowMap) {
1270            if (mDisplayContent == null) {
1271                return false;
1272            }
1273        }
1274        try {
1275            mService.mActivityManager.resizeStack(mStackId, bounds, false, true, false, -1);
1276        } catch (RemoteException e) {
1277        }
1278        return true;
1279    }
1280
1281    public boolean setPinnedStackSize(Rect bounds, Rect tempTaskBounds) {
1282        synchronized (mService.mWindowMap) {
1283            if (mDisplayContent == null) {
1284                return false;
1285            }
1286            if (mStackId != PINNED_STACK_ID) {
1287                Slog.w(TAG_WM, "Attempt to use pinned stack resize animation helper on"
1288                        + "non pinned stack");
1289                return false;
1290            }
1291        }
1292        try {
1293            mService.mActivityManager.resizePinnedStack(bounds, tempTaskBounds);
1294        } catch (RemoteException e) {
1295            // I don't believe you.
1296        }
1297        return true;
1298    }
1299
1300    void forceWindowsScaleable(Task task, boolean force) {
1301        SurfaceControl.openTransaction();
1302        try {
1303            final ArrayList<AppWindowToken> activities = task.mAppTokens;
1304            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
1305                final ArrayList<WindowState> windows = activities.get(activityNdx).allAppWindows;
1306                for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
1307                    final WindowStateAnimator winAnimator = windows.get(winNdx).mWinAnimator;
1308                    if (winAnimator == null || !winAnimator.hasSurface()) {
1309                        continue;
1310                    }
1311                    winAnimator.mSurfaceController.forceScaleableInTransaction(force);
1312                }
1313            }
1314        } finally {
1315            SurfaceControl.closeTransaction();
1316        }
1317    }
1318
1319    @Override  // AnimatesBounds
1320    public void onAnimationStart() {
1321        synchronized (mService.mWindowMap) {
1322            mFreezeMovementAnimations = true;
1323            mBoundsAnimating = true;
1324        }
1325    }
1326
1327    @Override  // AnimatesBounds
1328    public void onAnimationEnd() {
1329        synchronized (mService.mWindowMap) {
1330            mFreezeMovementAnimations = false;
1331            mBoundsAnimating = false;
1332            mService.requestTraversal();
1333        }
1334        if (mStackId == PINNED_STACK_ID) {
1335            try {
1336                mService.mActivityManager.notifyPinnedStackAnimationEnded();
1337            } catch (RemoteException e) {
1338                // I don't believe you...
1339            }
1340        }
1341    }
1342
1343    @Override
1344    public void moveToFullscreen() {
1345        try {
1346            mService.mActivityManager.moveTasksToFullscreenStack(mStackId, true);
1347        } catch (RemoteException e) {
1348            e.printStackTrace();
1349        }
1350    }
1351
1352    @Override
1353    public void getFullScreenBounds(Rect bounds) {
1354        getDisplayContent().getContentRect(bounds);
1355    }
1356
1357    public boolean getFreezeMovementAnimations() {
1358        return mFreezeMovementAnimations;
1359    }
1360
1361    public boolean getForceScaleToCrop() {
1362        return mBoundsAnimating;
1363    }
1364
1365    public boolean getBoundsAnimating() {
1366        return mBoundsAnimating;
1367    }
1368}
1369