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