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