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