TaskStack.java revision e7ba686f63682246d2e2d856208d23e23c801079
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
19import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
20import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
21import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
22import static android.app.ActivityManager.StackId.HOME_STACK_ID;
23import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
24import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
25import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
26import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
27import static android.view.Display.DEFAULT_DISPLAY;
28import static android.view.WindowManager.DOCKED_BOTTOM;
29import static android.view.WindowManager.DOCKED_INVALID;
30import static android.view.WindowManager.DOCKED_LEFT;
31import static android.view.WindowManager.DOCKED_RIGHT;
32import static android.view.WindowManager.DOCKED_TOP;
33import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
34import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
35import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
36import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
37import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
38import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
39
40import android.app.ActivityManager.StackId;
41import android.content.res.Configuration;
42import android.graphics.Rect;
43import android.graphics.Region;
44import android.os.RemoteException;
45import android.util.EventLog;
46import android.util.Slog;
47import android.util.SparseArray;
48import android.view.DisplayInfo;
49import android.view.Surface;
50
51import com.android.internal.policy.DividerSnapAlgorithm;
52import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
53import com.android.internal.policy.DockedDividerUtils;
54import com.android.server.EventLogTags;
55import com.android.server.UiThread;
56
57import java.io.PrintWriter;
58
59public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser,
60        BoundsAnimationTarget {
61    /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
62     * restrict IME adjustment so that a min portion of top stack remains visible.*/
63    private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
64
65    /** Dimming amount for non-focused stack when stacks are IME-adjusted. */
66    private static final float IME_ADJUST_DIM_AMOUNT = 0.25f;
67
68    /** Unique identifier */
69    final int mStackId;
70
71    /** The service */
72    private final WindowManagerService mService;
73
74    /** The display this stack sits under. */
75    // TODO: Track parent marks like this in WindowContainer.
76    private DisplayContent mDisplayContent;
77
78    /** For comparison with DisplayContent bounds. */
79    private Rect mTmpRect = new Rect();
80    private Rect mTmpRect2 = new Rect();
81    private Rect mTmpRect3 = new Rect();
82
83    /** Content limits relative to the DisplayContent this sits in. */
84    private Rect mBounds = new Rect();
85
86    /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
87    private final Rect mAdjustedBounds = new Rect();
88
89    /**
90     * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
91     * represent the state when the animation has ended.
92     */
93    private final Rect mFullyAdjustedImeBounds = new Rect();
94
95    /** Whether mBounds is fullscreen */
96    private boolean mFillsParent = true;
97
98    // Device rotation as of the last time {@link #mBounds} was set.
99    private int mRotation;
100
101    /** Density as of last time {@link #mBounds} was set. */
102    private int mDensity;
103
104    /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
105    private DimLayer mAnimationBackgroundSurface;
106
107    /** The particular window with an Animation with non-zero background color. */
108    private WindowStateAnimator mAnimationBackgroundAnimator;
109
110    /** Application tokens that are exiting, but still on screen for animations. */
111    final AppTokenList mExitingAppTokens = new AppTokenList();
112
113    /** Detach this stack from its display when animation completes. */
114    // TODO: maybe tie this to WindowContainer#removeChild some how...
115    boolean mDeferRemoval;
116
117    private final Rect mTmpAdjustedBounds = new Rect();
118    private boolean mAdjustedForIme;
119    private boolean mImeGoingAway;
120    private WindowState mImeWin;
121    private float mMinimizeAmount;
122    private float mAdjustImeAmount;
123    private float mAdjustDividerAmount;
124    private final int mDockedStackMinimizeThickness;
125
126    // If this is true, we are in the bounds animating mode. The task will be down or upscaled to
127    // perfectly fit the region it would have been cropped to. We may also avoid certain logic we
128    // would otherwise apply while resizing, while resizing in the bounds animating mode.
129    private boolean mBoundsAnimating = false;
130    // Set when an animation has been requested but has not yet started from the UI thread. This is
131    // cleared when the animation actually starts.
132    private boolean mBoundsAnimatingRequested = false;
133    private boolean mBoundsAnimatingToFullscreen = false;
134    private boolean mCancelCurrentBoundsAnimation = false;
135    private Rect mBoundsAnimationTarget = new Rect();
136    private Rect mBoundsAnimationSourceHintBounds = new Rect();
137
138    // Temporary storage for the new bounds that should be used after the configuration change.
139    // Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration().
140    private final Rect mBoundsAfterRotation = new Rect();
141
142    Rect mPreAnimationBounds = new Rect();
143
144    TaskStack(WindowManagerService service, int stackId) {
145        mService = service;
146        mStackId = stackId;
147        mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
148                com.android.internal.R.dimen.docked_stack_minimize_thickness);
149        EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
150    }
151
152    DisplayContent getDisplayContent() {
153        return mDisplayContent;
154    }
155
156    Task findHomeTask() {
157        if (mStackId != HOME_STACK_ID) {
158            return null;
159        }
160
161        for (int i = mChildren.size() - 1; i >= 0; i--) {
162            if (mChildren.get(i).isHomeTask()) {
163                return mChildren.get(i);
164            }
165        }
166        return null;
167    }
168
169    boolean hasMultipleTaskWithHomeTaskNotTop() {
170        return mChildren.size() > 1 && !mChildren.get(mChildren.size() - 1).isHomeTask();
171    }
172
173    /**
174     * Set the bounds of the stack and its containing tasks.
175     * @param stackBounds New stack bounds. Passing in null sets the bounds to fullscreen.
176     * @param configs Configuration for individual tasks, keyed by task id.
177     * @param taskBounds Bounds for individual tasks, keyed by task id.
178     * @return True if the stack bounds was changed.
179     * */
180    boolean setBounds(
181            Rect stackBounds, SparseArray<Configuration> configs, SparseArray<Rect> taskBounds,
182            SparseArray<Rect> taskTempInsetBounds) {
183        setBounds(stackBounds);
184
185        // Update bounds of containing tasks.
186        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
187            final Task task = mChildren.get(taskNdx);
188            Configuration config = configs.get(task.mTaskId);
189            if (config != null) {
190                Rect bounds = taskBounds.get(task.mTaskId);
191                task.resizeLocked(bounds, config, false /* forced */);
192                task.setTempInsetBounds(taskTempInsetBounds != null ?
193                        taskTempInsetBounds.get(task.mTaskId) : null);
194            } else {
195                Slog.wtf(TAG_WM, "No config for task: " + task + ", is there a mismatch with AM?");
196            }
197        }
198        return true;
199    }
200
201    void prepareFreezingTaskBounds() {
202        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
203            final Task task = mChildren.get(taskNdx);
204            task.prepareFreezingBounds();
205        }
206    }
207
208    /**
209     * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
210     * the normal task bounds.
211     *
212     * @param bounds The adjusted bounds.
213     */
214    private void setAdjustedBounds(Rect bounds) {
215        if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
216            return;
217        }
218
219        mAdjustedBounds.set(bounds);
220        final boolean adjusted = !mAdjustedBounds.isEmpty();
221        Rect insetBounds = null;
222        if (adjusted && isAdjustedForMinimizedDockedStack()) {
223            insetBounds = mBounds;
224        } else if (adjusted && mAdjustedForIme) {
225            if (mImeGoingAway) {
226                insetBounds = mBounds;
227            } else {
228                insetBounds = mFullyAdjustedImeBounds;
229            }
230        }
231        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
232        mDisplayContent.setLayoutNeeded();
233    }
234
235    private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
236        if (mFillsParent) {
237            return;
238        }
239
240        final boolean alignBottom = mAdjustedForIme && getDockSide() == DOCKED_TOP;
241
242        // Update bounds of containing tasks.
243        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
244            final Task task = mChildren.get(taskNdx);
245            task.alignToAdjustedBounds(adjustedBounds, tempInsetBounds, alignBottom);
246        }
247    }
248
249    private boolean setBounds(Rect bounds) {
250        boolean oldFullscreen = mFillsParent;
251        int rotation = Surface.ROTATION_0;
252        int density = DENSITY_DPI_UNDEFINED;
253        if (mDisplayContent != null) {
254            mDisplayContent.getLogicalDisplayRect(mTmpRect);
255            rotation = mDisplayContent.getDisplayInfo().rotation;
256            density = mDisplayContent.getDisplayInfo().logicalDensityDpi;
257            mFillsParent = bounds == null;
258            if (mFillsParent) {
259                bounds = mTmpRect;
260            }
261        }
262
263        if (bounds == null) {
264            // Can't set to fullscreen if we don't have a display to get bounds from...
265            return false;
266        }
267        if (mBounds.equals(bounds) && oldFullscreen == mFillsParent && mRotation == rotation) {
268            return false;
269        }
270
271        if (mDisplayContent != null) {
272            mDisplayContent.mDimLayerController.updateDimLayer(this);
273            mAnimationBackgroundSurface.setBounds(bounds);
274        }
275
276        mBounds.set(bounds);
277        mRotation = rotation;
278        mDensity = density;
279
280        updateAdjustedBounds();
281
282        return true;
283    }
284
285    /** Bounds of the stack without adjusting for other factors in the system like visibility
286     * of docked stack.
287     * Most callers should be using {@link #getBounds} as it take into consideration other system
288     * factors. */
289    void getRawBounds(Rect out) {
290        out.set(mBounds);
291    }
292
293    /** Return true if the current bound can get outputted to the rest of the system as-is. */
294    private boolean useCurrentBounds() {
295        if (mFillsParent
296                || !StackId.isResizeableByDockedStack(mStackId)
297                || mDisplayContent == null
298                || mDisplayContent.getDockedStackLocked() != null) {
299            return true;
300        }
301        return false;
302    }
303
304    public void getBounds(Rect out) {
305        if (useCurrentBounds()) {
306            // If we're currently adjusting for IME or minimized docked stack, we use the adjusted
307            // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked
308            // stack is visible since it is already what we want to represent to the rest of the
309            // system.
310            if (!mAdjustedBounds.isEmpty()) {
311                out.set(mAdjustedBounds);
312            } else {
313                out.set(mBounds);
314            }
315            return;
316        }
317
318        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
319        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
320        // system.
321        mDisplayContent.getLogicalDisplayRect(out);
322    }
323
324    /**
325     * Sets the bounds animation target bounds ahead of an animation.  This can't currently be done
326     * in onAnimationStart() since that is started on the UiThread.
327     */
328    void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen) {
329        mBoundsAnimatingRequested = true;
330        mBoundsAnimatingToFullscreen = toFullscreen;
331        if (destBounds != null) {
332            mBoundsAnimationTarget.set(destBounds);
333        } else {
334            mBoundsAnimationTarget.setEmpty();
335        }
336        if (sourceHintBounds != null) {
337            mBoundsAnimationSourceHintBounds.set(sourceHintBounds);
338        } else {
339            mBoundsAnimationSourceHintBounds.setEmpty();
340        }
341
342        mPreAnimationBounds.set(mBounds);
343    }
344
345    /**
346     * @return the final bounds for the bounds animation.
347     */
348    void getFinalAnimationBounds(Rect outBounds) {
349        outBounds.set(mBoundsAnimationTarget);
350    }
351
352    /**
353     * @return the final source bounds for the bounds animation.
354     */
355    void getFinalAnimationSourceHintBounds(Rect outBounds) {
356        outBounds.set(mBoundsAnimationSourceHintBounds);
357    }
358
359    /**
360     * @return the final animation bounds if the task stack is currently being animated, or the
361     *         current stack bounds otherwise.
362     */
363    void getAnimationOrCurrentBounds(Rect outBounds) {
364        if ((mBoundsAnimatingRequested || mBoundsAnimating) && !mBoundsAnimationTarget.isEmpty()) {
365            getFinalAnimationBounds(outBounds);
366            return;
367        }
368        getBounds(outBounds);
369    }
370
371    /** Bounds of the stack with other system factors taken into consideration. */
372    @Override
373    public void getDimBounds(Rect out) {
374        getBounds(out);
375    }
376
377    void updateDisplayInfo(Rect bounds) {
378        if (mDisplayContent == null) {
379            return;
380        }
381
382        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
383            mChildren.get(taskNdx).updateDisplayInfo(mDisplayContent);
384        }
385        if (bounds != null) {
386            setBounds(bounds);
387            return;
388        } else if (mFillsParent) {
389            setBounds(null);
390            return;
391        }
392
393        mTmpRect2.set(mBounds);
394        final int newRotation = mDisplayContent.getDisplayInfo().rotation;
395        final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi;
396        if (mRotation == newRotation && mDensity == newDensity) {
397            setBounds(mTmpRect2);
398        }
399
400        // If the rotation or density didn't match, we'll update it in onConfigurationChanged.
401    }
402
403    /** @return true if bounds were updated to some non-empty value. */
404    boolean updateBoundsAfterConfigChange() {
405        if (mDisplayContent == null) {
406            // If the stack is already detached we're not updating anything,
407            // as it's going away soon anyway.
408            return false;
409        }
410
411        if (mStackId == PINNED_STACK_ID) {
412            getAnimationOrCurrentBounds(mTmpRect2);
413            boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
414                    mTmpRect2, mTmpRect3);
415            if (updated) {
416                mBoundsAfterRotation.set(mTmpRect3);
417
418                // Once we've set the bounds based on the rotation of the old bounds in the new
419                // orientation, clear the animation target bounds since they are obsolete, and
420                // cancel any currently running animations
421                mBoundsAnimationTarget.setEmpty();
422                mBoundsAnimationSourceHintBounds.setEmpty();
423                mCancelCurrentBoundsAnimation = true;
424                return true;
425            }
426        }
427
428        final int newRotation = getDisplayInfo().rotation;
429        final int newDensity = getDisplayInfo().logicalDensityDpi;
430
431        if (mRotation == newRotation && mDensity == newDensity) {
432            // Nothing to do here as we already update the state in updateDisplayInfo.
433            return false;
434        }
435
436        if (mFillsParent) {
437            // Update stack bounds again since rotation changed since updateDisplayInfo().
438            setBounds(null);
439            // Return false since we don't need the client to resize.
440            return false;
441        }
442
443        mTmpRect2.set(mBounds);
444        mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
445        switch (mStackId) {
446            case DOCKED_STACK_ID:
447                repositionDockedStackAfterRotation(mTmpRect2);
448                snapDockedStackAfterRotation(mTmpRect2);
449                final int newDockSide = getDockSide(mTmpRect2);
450
451                // Update the dock create mode and clear the dock create bounds, these
452                // might change after a rotation and the original values will be invalid.
453                mService.setDockedStackCreateStateLocked(
454                        (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
455                                ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
456                                : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
457                        null);
458                mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
459                break;
460        }
461
462        mBoundsAfterRotation.set(mTmpRect2);
463        return true;
464    }
465
466    void getBoundsForNewConfiguration(Rect outBounds) {
467        outBounds.set(mBoundsAfterRotation);
468        mBoundsAfterRotation.setEmpty();
469    }
470
471    /**
472     * Some dock sides are not allowed by the policy. This method queries the policy and moves
473     * the docked stack around if needed.
474     *
475     * @param inOutBounds the bounds of the docked stack to adjust
476     */
477    private void repositionDockedStackAfterRotation(Rect inOutBounds) {
478        int dockSide = getDockSide(inOutBounds);
479        if (mService.mPolicy.isDockSideAllowed(dockSide)) {
480            return;
481        }
482        mDisplayContent.getLogicalDisplayRect(mTmpRect);
483        dockSide = DockedDividerUtils.invertDockSide(dockSide);
484        switch (dockSide) {
485            case DOCKED_LEFT:
486                int movement = inOutBounds.left;
487                inOutBounds.left -= movement;
488                inOutBounds.right -= movement;
489                break;
490            case DOCKED_RIGHT:
491                movement = mTmpRect.right - inOutBounds.right;
492                inOutBounds.left += movement;
493                inOutBounds.right += movement;
494                break;
495            case DOCKED_TOP:
496                movement = inOutBounds.top;
497                inOutBounds.top -= movement;
498                inOutBounds.bottom -= movement;
499                break;
500            case DOCKED_BOTTOM:
501                movement = mTmpRect.bottom - inOutBounds.bottom;
502                inOutBounds.top += movement;
503                inOutBounds.bottom += movement;
504                break;
505        }
506    }
507
508    /**
509     * Snaps the bounds after rotation to the closest snap target for the docked stack.
510     */
511    private void snapDockedStackAfterRotation(Rect outBounds) {
512
513        // Calculate the current position.
514        final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
515        final int dividerSize = mDisplayContent.getDockedDividerController().getContentWidth();
516        final int dockSide = getDockSide(outBounds);
517        final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
518                dockSide, dividerSize);
519        final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth;
520        final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight;
521
522        // Snap the position to a target.
523        final int rotation = displayInfo.rotation;
524        final int orientation = mDisplayContent.getConfiguration().orientation;
525        mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
526        final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
527                mService.mContext.getResources(), displayWidth, displayHeight,
528                dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds,
529                isMinimizedDockAndHomeStackResizable());
530        final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
531
532        // Recalculate the bounds based on the position of the target.
533        DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
534                outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight,
535                dividerSize);
536    }
537
538    // TODO: Checkout the call points of this method and the ones below to see how they can fit in WC.
539    void addTask(Task task, int position) {
540        addTask(task, position, task.showForAllUsers(), true /* moveParents */);
541    }
542
543    /**
544     * Put a Task in this stack. Used for adding only.
545     * When task is added to top of the stack, the entire branch of the hierarchy (including stack
546     * and display) will be brought to top.
547     * @param task The task to add.
548     * @param position Target position to add the task to.
549     * @param showForAllUsers Whether to show the task regardless of the current user.
550     */
551    void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) {
552        final TaskStack currentStack = task.mStack;
553        // TODO: We pass stack to task's constructor, but we still need to call this method.
554        // This doesn't make sense, mStack will already be set equal to "this" at this point.
555        if (currentStack != null && currentStack.mStackId != mStackId) {
556            throw new IllegalStateException("Trying to add taskId=" + task.mTaskId
557                    + " to stackId=" + mStackId
558                    + ", but it is already attached to stackId=" + task.mStack.mStackId);
559        }
560
561        // Add child task.
562        task.mStack = this;
563        addChild(task, null);
564
565        // Move child to a proper position, as some restriction for position might apply.
566        positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers);
567    }
568
569    @Override
570    void positionChildAt(int position, Task child, boolean includingParents) {
571        positionChildAt(position, child, includingParents, child.showForAllUsers());
572    }
573
574    /**
575     * Overridden version of {@link TaskStack#positionChildAt(int, Task, boolean)}. Used in
576     * {@link TaskStack#addTask(Task, int, boolean showForAllUsers, boolean)}, as it can receive
577     * showForAllUsers param from {@link AppWindowToken} instead of {@link Task#showForAllUsers()}.
578     */
579    private void positionChildAt(int position, Task child, boolean includingParents,
580            boolean showForAllUsers) {
581        final int targetPosition = findPositionForTask(child, position, showForAllUsers,
582                false /* addingNew */);
583        super.positionChildAt(targetPosition, child, includingParents);
584
585        // Log positioning.
586        if (DEBUG_TASK_MOVEMENT)
587            Slog.d(TAG_WM, "positionTask: task=" + this + " position=" + position);
588
589        final int toTop = targetPosition == mChildren.size() - 1 ? 1 : 0;
590        EventLog.writeEvent(EventLogTags.WM_TASK_MOVED, child.mTaskId, toTop, targetPosition);
591    }
592
593    // TODO: We should really have users as a window container in the hierarchy so that we don't
594    // have to do complicated things like we are doing in this method.
595    private int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers,
596            boolean addingNew) {
597        final boolean canShowTask =
598                showForAllUsers || mService.isCurrentProfileLocked(task.mUserId);
599
600        final int stackSize = mChildren.size();
601        int minPosition = 0;
602        int maxPosition = addingNew ? stackSize : stackSize - 1;
603
604        if (canShowTask) {
605            minPosition = computeMinPosition(minPosition, stackSize);
606        } else {
607            maxPosition = computeMaxPosition(maxPosition);
608        }
609        // Reset position based on minimum/maximum possible positions.
610        return Math.min(Math.max(targetPosition, minPosition), maxPosition);
611    }
612
613    /** Calculate the minimum possible position for a task that can be shown to the user.
614     *  The minimum position will be above all other tasks that can't be shown.
615     *  @param minPosition The minimum position the caller is suggesting.
616     *                  We will start adjusting up from here.
617     *  @param size The size of the current task list.
618     */
619    private int computeMinPosition(int minPosition, int size) {
620        while (minPosition < size) {
621            final Task tmpTask = mChildren.get(minPosition);
622            final boolean canShowTmpTask =
623                    tmpTask.showForAllUsers()
624                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
625            if (canShowTmpTask) {
626                break;
627            }
628            minPosition++;
629        }
630        return minPosition;
631    }
632
633    /** Calculate the maximum possible position for a task that can't be shown to the user.
634     *  The maximum position will be below all other tasks that can be shown.
635     *  @param maxPosition The maximum position the caller is suggesting.
636     *                  We will start adjusting down from here.
637     */
638    private int computeMaxPosition(int maxPosition) {
639        while (maxPosition > 0) {
640            final Task tmpTask = mChildren.get(maxPosition);
641            final boolean canShowTmpTask =
642                    tmpTask.showForAllUsers()
643                            || mService.isCurrentProfileLocked(tmpTask.mUserId);
644            if (!canShowTmpTask) {
645                break;
646            }
647            maxPosition--;
648        }
649        return maxPosition;
650    }
651
652    /**
653     * Delete a Task from this stack. If it is the last Task in the stack, move this stack to the
654     * back.
655     * @param task The Task to delete.
656     */
657    @Override
658    void removeChild(Task task) {
659        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG_WM, "removeChild: task=" + task);
660
661        super.removeChild(task);
662        task.mStack = null;
663
664        if (mDisplayContent != null) {
665            if (mChildren.isEmpty()) {
666                getParent().positionChildAt(POSITION_BOTTOM, this, false /* includingParents */);
667            }
668            mDisplayContent.setLayoutNeeded();
669        }
670        for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
671            final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
672            if (wtoken.getTask() == task) {
673                wtoken.mIsExiting = false;
674                mExitingAppTokens.remove(appNdx);
675            }
676        }
677    }
678
679    void onDisplayChanged(DisplayContent dc) {
680        if (mDisplayContent != null) {
681            throw new IllegalStateException("onDisplayChanged: Already attached");
682        }
683
684        mDisplayContent = dc;
685        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
686                "animation background stackId=" + mStackId);
687
688        Rect bounds = null;
689        final TaskStack dockedStack = dc.getDockedStackIgnoringVisibility();
690        if (mStackId == DOCKED_STACK_ID
691                || (dockedStack != null && StackId.isResizeableByDockedStack(mStackId)
692                        && !dockedStack.fillsParent())) {
693            // The existence of a docked stack affects the size of other static stack created since
694            // the docked stack occupies a dedicated region on screen, but only if the dock stack is
695            // not fullscreen. If it's fullscreen, it means that we are in the transition of
696            // dismissing it, so we must not resize this stack.
697            bounds = new Rect();
698            dc.getLogicalDisplayRect(mTmpRect);
699            mTmpRect2.setEmpty();
700            if (dockedStack != null) {
701                dockedStack.getRawBounds(mTmpRect2);
702            }
703            final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
704                    == DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
705            getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
706                    mDisplayContent.mDividerControllerLocked.getContentWidth(),
707                    dockedOnTopOrLeft);
708        } else if (mStackId == PINNED_STACK_ID) {
709            // Update the bounds based on any changes to the display info
710            getAnimationOrCurrentBounds(mTmpRect2);
711            boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
712                    mTmpRect2, mTmpRect3);
713            if (updated) {
714                bounds = new Rect(mTmpRect3);
715            }
716        }
717
718        updateDisplayInfo(bounds);
719        super.onDisplayChanged(dc);
720    }
721
722    /**
723     * Determines the stack and task bounds of the other stack when in docked mode. The current task
724     * bounds is passed in but depending on the stack, the task and stack must match. Only in
725     * minimized mode with resizable launcher, the other stack ignores calculating the stack bounds
726     * and uses the task bounds passed in as the stack and task bounds, otherwise the stack bounds
727     * is calculated and is also used for its task bounds.
728     * If any of the out bounds are empty, it represents default bounds
729     *
730     * @param currentTempTaskBounds the current task bounds of the other stack
731     * @param outStackBounds the calculated stack bounds of the other stack
732     * @param outTempTaskBounds the calculated task bounds of the other stack
733     * @param ignoreVisibility ignore visibility in getting the stack bounds
734     */
735    void getStackDockedModeBoundsLocked(Rect currentTempTaskBounds, Rect outStackBounds,
736            Rect outTempTaskBounds, boolean ignoreVisibility) {
737        outTempTaskBounds.setEmpty();
738
739        // When the home stack is resizable, should always have the same stack and task bounds
740        if (mStackId == HOME_STACK_ID) {
741            if (findHomeTask().isResizeable()) {
742                // Calculate the home stack bounds when in docked mode and the home stack is
743                // resizeable.
744                getDisplayContent().mDividerControllerLocked
745                        .getHomeStackBoundsInDockedMode(outStackBounds);
746            } else {
747                // Home stack isn't resizeable, so don't specify stack bounds.
748                outStackBounds.setEmpty();
749            }
750
751            outTempTaskBounds.set(outStackBounds);
752            return;
753        }
754
755        // When minimized state, the stack bounds for all non-home and docked stack bounds should
756        // match the passed task bounds
757        if (isMinimizedDockAndHomeStackResizable() && currentTempTaskBounds != null) {
758            outStackBounds.set(currentTempTaskBounds);
759            return;
760        }
761
762        if ((mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId))
763                || mDisplayContent == null) {
764            outStackBounds.set(mBounds);
765            return;
766        }
767
768        final TaskStack dockedStack = mDisplayContent.getDockedStackIgnoringVisibility();
769        if (dockedStack == null) {
770            // Not sure why you are calling this method when there is no docked stack...
771            throw new IllegalStateException(
772                    "Calling getStackDockedModeBoundsLocked() when there is no docked stack.");
773        }
774        if (!ignoreVisibility && !dockedStack.isVisible()) {
775            // The docked stack is being dismissed, but we caught before it finished being
776            // dismissed. In that case we want to treat it as if it is not occupying any space and
777            // let others occupy the whole display.
778            mDisplayContent.getLogicalDisplayRect(outStackBounds);
779            return;
780        }
781
782        final int dockedSide = dockedStack.getDockSide();
783        if (dockedSide == DOCKED_INVALID) {
784            // Not sure how you got here...Only thing we can do is return current bounds.
785            Slog.e(TAG_WM, "Failed to get valid docked side for docked stack=" + dockedStack);
786            outStackBounds.set(mBounds);
787            return;
788        }
789
790        mDisplayContent.getLogicalDisplayRect(mTmpRect);
791        dockedStack.getRawBounds(mTmpRect2);
792        final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
793        getStackDockedModeBounds(mTmpRect, outStackBounds, mStackId, mTmpRect2,
794                mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
795
796    }
797
798    /**
799     * Outputs the bounds a stack should be given the presence of a docked stack on the display.
800     * @param displayRect The bounds of the display the docked stack is on.
801     * @param outBounds Output bounds that should be used for the stack.
802     * @param stackId Id of stack we are calculating the bounds for.
803     * @param dockedBounds Bounds of the docked stack.
804     * @param dockDividerWidth We need to know the width of the divider make to the output bounds
805     *                         close to the side of the dock.
806     * @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
807     */
808    private void getStackDockedModeBounds(
809            Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
810            boolean dockOnTopOrLeft) {
811        final boolean dockedStack = stackId == DOCKED_STACK_ID;
812        final boolean splitHorizontally = displayRect.width() > displayRect.height();
813
814        outBounds.set(displayRect);
815        if (dockedStack) {
816            if (mService.mDockedStackCreateBounds != null) {
817                outBounds.set(mService.mDockedStackCreateBounds);
818                return;
819            }
820
821            // The initial bounds of the docked stack when it is created about half the screen space
822            // and its bounds can be adjusted after that. The bounds of all other stacks are
823            // adjusted to occupy whatever screen space the docked stack isn't occupying.
824            final DisplayInfo di = mDisplayContent.getDisplayInfo();
825            mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
826                    mTmpRect2);
827            final int position = new DividerSnapAlgorithm(mService.mContext.getResources(),
828                    di.logicalWidth,
829                    di.logicalHeight,
830                    dockDividerWidth,
831                    mDisplayContent.getConfiguration().orientation == ORIENTATION_PORTRAIT,
832                    mTmpRect2).getMiddleTarget().position;
833
834            if (dockOnTopOrLeft) {
835                if (splitHorizontally) {
836                    outBounds.right = position;
837                } else {
838                    outBounds.bottom = position;
839                }
840            } else {
841                if (splitHorizontally) {
842                    outBounds.left = position + dockDividerWidth;
843                } else {
844                    outBounds.top = position + dockDividerWidth;
845                }
846            }
847            return;
848        }
849
850        // Other stacks occupy whatever space is left by the docked stack.
851        if (!dockOnTopOrLeft) {
852            if (splitHorizontally) {
853                outBounds.right = dockedBounds.left - dockDividerWidth;
854            } else {
855                outBounds.bottom = dockedBounds.top - dockDividerWidth;
856            }
857        } else {
858            if (splitHorizontally) {
859                outBounds.left = dockedBounds.right + dockDividerWidth;
860            } else {
861                outBounds.top = dockedBounds.bottom + dockDividerWidth;
862            }
863        }
864        DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
865    }
866
867    void resetDockedStackToMiddle() {
868        if (mStackId != DOCKED_STACK_ID) {
869            throw new IllegalStateException("Not a docked stack=" + this);
870        }
871
872        mService.mDockedStackCreateBounds = null;
873
874        final Rect bounds = new Rect();
875        final Rect tempBounds = new Rect();
876        getStackDockedModeBoundsLocked(null /* currentTempTaskBounds */, bounds, tempBounds,
877                true /*ignoreVisibility*/);
878        getController().requestResize(bounds);
879    }
880
881    @Override
882    StackWindowController getController() {
883        return (StackWindowController) super.getController();
884    }
885
886    @Override
887    void removeIfPossible() {
888        if (isAnimating()) {
889            mDeferRemoval = true;
890            return;
891        }
892        removeImmediately();
893    }
894
895    @Override
896    void removeImmediately() {
897        super.removeImmediately();
898
899        onRemovedFromDisplay();
900    }
901
902    /**
903     * Removes the stack it from its current parent, so it can be either destroyed completely or
904     * re-parented.
905     */
906    void onRemovedFromDisplay() {
907        mDisplayContent.mDimLayerController.removeDimLayerUser(this);
908        EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
909
910        if (mAnimationBackgroundSurface != null) {
911            mAnimationBackgroundSurface.destroySurface();
912            mAnimationBackgroundSurface = null;
913        }
914
915        if (mStackId == DOCKED_STACK_ID) {
916            mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false);
917        }
918
919        mDisplayContent = null;
920        mService.mWindowPlacerLocked.requestTraversal();
921    }
922
923    void resetAnimationBackgroundAnimator() {
924        mAnimationBackgroundAnimator = null;
925        mAnimationBackgroundSurface.hide();
926    }
927
928    void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
929        int animLayer = winAnimator.mAnimLayer;
930        if (mAnimationBackgroundAnimator == null
931                || animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
932            mAnimationBackgroundAnimator = winAnimator;
933            animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator);
934            mAnimationBackgroundSurface.show(animLayer - LAYER_OFFSET_DIM,
935                    ((color >> 24) & 0xff) / 255f, 0);
936        }
937    }
938
939    // TODO: Should each user have there own stacks?
940    @Override
941    void switchUser() {
942        super.switchUser();
943        int top = mChildren.size();
944        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
945            Task task = mChildren.get(taskNdx);
946            if (mService.isCurrentProfileLocked(task.mUserId) || task.showForAllUsers()) {
947                mChildren.remove(taskNdx);
948                mChildren.add(task);
949                --top;
950            }
951        }
952    }
953
954    /**
955     * Adjusts the stack bounds if the IME is visible.
956     *
957     * @param imeWin The IME window.
958     */
959    void setAdjustedForIme(WindowState imeWin, boolean forceUpdate) {
960        mImeWin = imeWin;
961        mImeGoingAway = false;
962        if (!mAdjustedForIme || forceUpdate) {
963            mAdjustedForIme = true;
964            mAdjustImeAmount = 0f;
965            mAdjustDividerAmount = 0f;
966            updateAdjustForIme(0f, 0f, true /* force */);
967        }
968    }
969
970    boolean isAdjustedForIme() {
971        return mAdjustedForIme;
972    }
973
974    boolean isAnimatingForIme() {
975        return mImeWin != null && mImeWin.isAnimatingLw();
976    }
977
978    /**
979     * Update the stack's bounds (crop or position) according to the IME window's
980     * current position. When IME window is animated, the bottom stack is animated
981     * together to track the IME window's current position, and the top stack is
982     * cropped as necessary.
983     *
984     * @return true if a traversal should be performed after the adjustment.
985     */
986    boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) {
987        if (adjustAmount != mAdjustImeAmount
988                || adjustDividerAmount != mAdjustDividerAmount || force) {
989            mAdjustImeAmount = adjustAmount;
990            mAdjustDividerAmount = adjustDividerAmount;
991            updateAdjustedBounds();
992            return isVisible();
993        } else {
994            return false;
995        }
996    }
997
998    /**
999     * Resets the adjustment after it got adjusted for the IME.
1000     * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
1001     *                        animations; otherwise, set flag and animates the window away together
1002     *                        with IME window.
1003     */
1004    void resetAdjustedForIme(boolean adjustBoundsNow) {
1005        if (adjustBoundsNow) {
1006            mImeWin = null;
1007            mAdjustedForIme = false;
1008            mImeGoingAway = false;
1009            mAdjustImeAmount = 0f;
1010            mAdjustDividerAmount = 0f;
1011            updateAdjustedBounds();
1012            mService.setResizeDimLayer(false, mStackId, 1.0f);
1013        } else {
1014            mImeGoingAway |= mAdjustedForIme;
1015        }
1016    }
1017
1018    /**
1019     * Sets the amount how much we currently minimize our stack.
1020     *
1021     * @param minimizeAmount The amount, between 0 and 1.
1022     * @return Whether the amount has changed and a layout is needed.
1023     */
1024    boolean setAdjustedForMinimizedDock(float minimizeAmount) {
1025        if (minimizeAmount != mMinimizeAmount) {
1026            mMinimizeAmount = minimizeAmount;
1027            updateAdjustedBounds();
1028            return isVisible();
1029        } else {
1030            return false;
1031        }
1032    }
1033
1034    boolean shouldIgnoreInput() {
1035        return isAdjustedForMinimizedDockedStack() || mStackId == DOCKED_STACK_ID &&
1036                isMinimizedDockAndHomeStackResizable();
1037    }
1038
1039    /**
1040     * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows
1041     * to the list of to be drawn windows the service is waiting for.
1042     */
1043    void beginImeAdjustAnimation() {
1044        for (int j = mChildren.size() - 1; j >= 0; j--) {
1045            final Task task = mChildren.get(j);
1046            if (task.hasContentToDisplay()) {
1047                task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
1048                task.setWaitingForDrawnIfResizingChanged();
1049            }
1050        }
1051    }
1052
1053    /**
1054     * Resets the resizing state of all windows.
1055     */
1056    void endImeAdjustAnimation() {
1057        for (int j = mChildren.size() - 1; j >= 0; j--) {
1058            mChildren.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
1059        }
1060    }
1061
1062    int getMinTopStackBottom(final Rect displayContentRect, int originalStackBottom) {
1063        return displayContentRect.top + (int)
1064                ((originalStackBottom - displayContentRect.top) * ADJUSTED_STACK_FRACTION_MIN);
1065    }
1066
1067    private boolean adjustForIME(final WindowState imeWin) {
1068        final int dockedSide = getDockSide();
1069        final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
1070        if (imeWin == null || !dockedTopOrBottom) {
1071            return false;
1072        }
1073
1074        final Rect displayContentRect = mTmpRect;
1075        final Rect contentBounds = mTmpRect2;
1076
1077        // Calculate the content bounds excluding the area occupied by IME
1078        getDisplayContent().getContentRect(displayContentRect);
1079        contentBounds.set(displayContentRect);
1080        int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);
1081
1082        imeTop += imeWin.getGivenContentInsetsLw().top;
1083        if (contentBounds.bottom > imeTop) {
1084            contentBounds.bottom = imeTop;
1085        }
1086
1087        final int yOffset = displayContentRect.bottom - contentBounds.bottom;
1088
1089        final int dividerWidth =
1090                getDisplayContent().mDividerControllerLocked.getContentWidth();
1091        final int dividerWidthInactive =
1092                getDisplayContent().mDividerControllerLocked.getContentWidthInactive();
1093
1094        if (dockedSide == DOCKED_TOP) {
1095            // If this stack is docked on top, we make it smaller so the bottom stack is not
1096            // occluded by IME. We shift its bottom up by the height of the IME, but
1097            // leaves at least 30% of the top stack visible.
1098            final int minTopStackBottom =
1099                    getMinTopStackBottom(displayContentRect, mBounds.bottom);
1100            final int bottom = Math.max(
1101                    mBounds.bottom - yOffset + dividerWidth - dividerWidthInactive,
1102                    minTopStackBottom);
1103            mTmpAdjustedBounds.set(mBounds);
1104            mTmpAdjustedBounds.bottom =
1105                    (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
1106            mFullyAdjustedImeBounds.set(mBounds);
1107        } else {
1108            // When the stack is on bottom and has no focus, it's only adjusted for divider width.
1109            final int dividerWidthDelta = dividerWidthInactive - dividerWidth;
1110
1111            // When the stack is on bottom and has focus, it needs to be moved up so as to
1112            // not occluded by IME, and at the same time adjusted for divider width.
1113            // We try to move it up by the height of the IME window, but only to the extent
1114            // that leaves at least 30% of the top stack visible.
1115            // 'top' is where the top of bottom stack will move to in this case.
1116            final int topBeforeImeAdjust = mBounds.top - dividerWidth + dividerWidthInactive;
1117            final int minTopStackBottom =
1118                    getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth);
1119            final int top = Math.max(
1120                    mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive);
1121
1122            mTmpAdjustedBounds.set(mBounds);
1123            // Account for the adjustment for IME and divider width separately.
1124            // (top - topBeforeImeAdjust) is the amount of movement due to IME only,
1125            // and dividerWidthDelta is due to divider width change only.
1126            mTmpAdjustedBounds.top = mBounds.top +
1127                    (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) +
1128                            mAdjustDividerAmount * dividerWidthDelta);
1129            mFullyAdjustedImeBounds.set(mBounds);
1130            mFullyAdjustedImeBounds.top = top;
1131            mFullyAdjustedImeBounds.bottom = top + mBounds.height();
1132        }
1133        return true;
1134    }
1135
1136    private boolean adjustForMinimizedDockedStack(float minimizeAmount) {
1137        final int dockSide = getDockSide();
1138        if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) {
1139            return false;
1140        }
1141
1142        if (dockSide == DOCKED_TOP) {
1143            mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
1144            int topInset = mTmpRect.top;
1145            mTmpAdjustedBounds.set(mBounds);
1146            mTmpAdjustedBounds.bottom =
1147                    (int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom);
1148        } else if (dockSide == DOCKED_LEFT) {
1149            mTmpAdjustedBounds.set(mBounds);
1150            final int width = mBounds.width();
1151            mTmpAdjustedBounds.right =
1152                    (int) (minimizeAmount * mDockedStackMinimizeThickness
1153                            + (1 - minimizeAmount) * mBounds.right);
1154            mTmpAdjustedBounds.left = mTmpAdjustedBounds.right - width;
1155        } else if (dockSide == DOCKED_RIGHT) {
1156            mTmpAdjustedBounds.set(mBounds);
1157            mTmpAdjustedBounds.left =
1158                    (int) (minimizeAmount * (mBounds.right - mDockedStackMinimizeThickness)
1159                            + (1 - minimizeAmount) * mBounds.left);
1160        }
1161        return true;
1162    }
1163
1164    private boolean isMinimizedDockAndHomeStackResizable() {
1165        return mDisplayContent.mDividerControllerLocked.isMinimizedDock()
1166                && mDisplayContent.mDividerControllerLocked.isHomeStackResizable();
1167    }
1168
1169    /**
1170     * @return the distance in pixels how much the stack gets minimized from it's original size
1171     */
1172    int getMinimizeDistance() {
1173        final int dockSide = getDockSide();
1174        if (dockSide == DOCKED_INVALID) {
1175            return 0;
1176        }
1177
1178        if (dockSide == DOCKED_TOP) {
1179            mService.getStableInsetsLocked(DEFAULT_DISPLAY, mTmpRect);
1180            int topInset = mTmpRect.top;
1181            return mBounds.bottom - topInset;
1182        } else if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) {
1183            return mBounds.width() - mDockedStackMinimizeThickness;
1184        } else {
1185            return 0;
1186        }
1187    }
1188
1189    /**
1190     * Updates the adjustment depending on it's current state.
1191     */
1192    private void updateAdjustedBounds() {
1193        boolean adjust = false;
1194        if (mMinimizeAmount != 0f) {
1195            adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
1196        } else if (mAdjustedForIme) {
1197            adjust = adjustForIME(mImeWin);
1198        }
1199        if (!adjust) {
1200            mTmpAdjustedBounds.setEmpty();
1201        }
1202        setAdjustedBounds(mTmpAdjustedBounds);
1203
1204        final boolean isImeTarget = (mService.getImeFocusStackLocked() == this);
1205        if (mAdjustedForIme && adjust && !isImeTarget) {
1206            final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount)
1207                    * IME_ADJUST_DIM_AMOUNT;
1208            mService.setResizeDimLayer(true, mStackId, alpha);
1209        }
1210    }
1211
1212    void applyAdjustForImeIfNeeded(Task task) {
1213        if (mMinimizeAmount != 0f || !mAdjustedForIme || mAdjustedBounds.isEmpty()) {
1214            return;
1215        }
1216
1217        final Rect insetBounds = mImeGoingAway ? mBounds : mFullyAdjustedImeBounds;
1218        task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP);
1219        mDisplayContent.setLayoutNeeded();
1220    }
1221
1222    boolean isAdjustedForMinimizedDockedStack() {
1223        return mMinimizeAmount != 0f;
1224    }
1225
1226    public void dump(String prefix, PrintWriter pw) {
1227        pw.println(prefix + "mStackId=" + mStackId);
1228        pw.println(prefix + "mDeferRemoval=" + mDeferRemoval);
1229        pw.println(prefix + "mFillsParent=" + mFillsParent);
1230        pw.println(prefix + "mBounds=" + mBounds.toShortString());
1231        if (mMinimizeAmount != 0f) {
1232            pw.println(prefix + "mMinimizeAmount=" + mMinimizeAmount);
1233        }
1234        if (mAdjustedForIme) {
1235            pw.println(prefix + "mAdjustedForIme=true");
1236            pw.println(prefix + "mAdjustImeAmount=" + mAdjustImeAmount);
1237            pw.println(prefix + "mAdjustDividerAmount=" + mAdjustDividerAmount);
1238        }
1239        if (!mAdjustedBounds.isEmpty()) {
1240            pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
1241        }
1242        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
1243            mChildren.get(taskNdx).dump(prefix + "  ", pw);
1244        }
1245        if (mAnimationBackgroundSurface.isDimming()) {
1246            pw.println(prefix + "mWindowAnimationBackgroundSurface:");
1247            mAnimationBackgroundSurface.printTo(prefix + "  ", pw);
1248        }
1249        if (!mExitingAppTokens.isEmpty()) {
1250            pw.println();
1251            pw.println("  Exiting application tokens:");
1252            for (int i = mExitingAppTokens.size() - 1; i >= 0; i--) {
1253                WindowToken token = mExitingAppTokens.get(i);
1254                pw.print("  Exiting App #"); pw.print(i);
1255                pw.print(' '); pw.print(token);
1256                pw.println(':');
1257                token.dump(pw, "    ");
1258            }
1259        }
1260    }
1261
1262    /** Fullscreen status of the stack without adjusting for other factors in the system like
1263     * visibility of docked stack.
1264     * Most callers should be using {@link #fillsParent} as it take into consideration other
1265     * system factors. */
1266    boolean getRawFullscreen() {
1267        return mFillsParent;
1268    }
1269
1270    @Override
1271    public boolean dimFullscreen() {
1272        return StackId.isHomeOrRecentsStack(mStackId) || fillsParent();
1273    }
1274
1275    @Override
1276    boolean fillsParent() {
1277        if (useCurrentBounds()) {
1278            return mFillsParent;
1279        }
1280        // The bounds has been adjusted to accommodate for a docked stack, but the docked stack
1281        // is not currently visible. Go ahead a represent it as fullscreen to the rest of the
1282        // system.
1283        return true;
1284    }
1285
1286    @Override
1287    public DisplayInfo getDisplayInfo() {
1288        return mDisplayContent.getDisplayInfo();
1289    }
1290
1291    @Override
1292    public boolean isAttachedToDisplay() {
1293        return mDisplayContent != null;
1294    }
1295
1296    @Override
1297    public String toString() {
1298        return "{stackId=" + mStackId + " tasks=" + mChildren + "}";
1299    }
1300
1301    String getName() {
1302        return toShortString();
1303    }
1304
1305    @Override
1306    public String toShortString() {
1307        return "Stack=" + mStackId;
1308    }
1309
1310    /**
1311     * For docked workspace (or workspace that's side-by-side to the docked), provides
1312     * information which side of the screen was the dock anchored.
1313     */
1314    int getDockSide() {
1315        return getDockSide(mBounds);
1316    }
1317
1318    int getDockSide(Rect bounds) {
1319        if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) {
1320            return DOCKED_INVALID;
1321        }
1322        if (mDisplayContent == null) {
1323            return DOCKED_INVALID;
1324        }
1325        mDisplayContent.getLogicalDisplayRect(mTmpRect);
1326        final int orientation = mDisplayContent.getConfiguration().orientation;
1327        return getDockSideUnchecked(bounds, mTmpRect, orientation);
1328    }
1329
1330    static int getDockSideUnchecked(Rect bounds, Rect displayRect, int orientation) {
1331        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
1332            // Portrait mode, docked either at the top or the bottom.
1333            if (bounds.top - displayRect.top <= displayRect.bottom - bounds.bottom) {
1334                return DOCKED_TOP;
1335            } else {
1336                return DOCKED_BOTTOM;
1337            }
1338        } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
1339            // Landscape mode, docked either on the left or on the right.
1340            if (bounds.left - displayRect.left <= displayRect.right - bounds.right) {
1341                return DOCKED_LEFT;
1342            } else {
1343                return DOCKED_RIGHT;
1344            }
1345        } else {
1346            return DOCKED_INVALID;
1347        }
1348    }
1349
1350    boolean hasTaskForUser(int userId) {
1351        for (int i = mChildren.size() - 1; i >= 0; i--) {
1352            final Task task = mChildren.get(i);
1353            if (task.mUserId == userId) {
1354                return true;
1355            }
1356        }
1357        return false;
1358    }
1359
1360    int taskIdFromPoint(int x, int y) {
1361        getBounds(mTmpRect);
1362        if (!mTmpRect.contains(x, y) || isAdjustedForMinimizedDockedStack()) {
1363            return -1;
1364        }
1365
1366        for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; --taskNdx) {
1367            final Task task = mChildren.get(taskNdx);
1368            final WindowState win = task.getTopVisibleAppMainWindow();
1369            if (win == null) {
1370                continue;
1371            }
1372            // We need to use the task's dim bounds (which is derived from the visible bounds of its
1373            // apps windows) for any touch-related tests. Can't use the task's original bounds
1374            // because it might be adjusted to fit the content frame. For example, the presence of
1375            // the IME adjusting the windows frames when the app window is the IME target.
1376            task.getDimBounds(mTmpRect);
1377            if (mTmpRect.contains(x, y)) {
1378                return task.mTaskId;
1379            }
1380        }
1381
1382        return -1;
1383    }
1384
1385    void findTaskForResizePoint(int x, int y, int delta,
1386            DisplayContent.TaskForResizePointSearchResult results) {
1387        if (!StackId.isTaskResizeAllowed(mStackId)) {
1388            results.searchDone = true;
1389            return;
1390        }
1391
1392        for (int i = mChildren.size() - 1; i >= 0; --i) {
1393            final Task task = mChildren.get(i);
1394            if (task.isFullscreen()) {
1395                results.searchDone = true;
1396                return;
1397            }
1398
1399            // We need to use the task's dim bounds (which is derived from the visible bounds of
1400            // its apps windows) for any touch-related tests. Can't use the task's original
1401            // bounds because it might be adjusted to fit the content frame. One example is when
1402            // the task is put to top-left quadrant, the actual visible area would not start at
1403            // (0,0) after it's adjusted for the status bar.
1404            task.getDimBounds(mTmpRect);
1405            mTmpRect.inset(-delta, -delta);
1406            if (mTmpRect.contains(x, y)) {
1407                mTmpRect.inset(delta, delta);
1408
1409                results.searchDone = true;
1410
1411                if (!mTmpRect.contains(x, y)) {
1412                    results.taskForResize = task;
1413                    return;
1414                }
1415                // User touched inside the task. No need to look further,
1416                // focus transfer will be handled in ACTION_UP.
1417                return;
1418            }
1419        }
1420    }
1421
1422    void setTouchExcludeRegion(Task focusedTask, int delta, Region touchExcludeRegion,
1423            Rect contentRect, Rect postExclude) {
1424        for (int i = mChildren.size() - 1; i >= 0; --i) {
1425            final Task task = mChildren.get(i);
1426            AppWindowToken token = task.getTopVisibleAppToken();
1427            if (token == null || !token.hasContentToDisplay()) {
1428                continue;
1429            }
1430
1431            /**
1432             * Exclusion region is the region that TapDetector doesn't care about.
1433             * Here we want to remove all non-focused tasks from the exclusion region.
1434             * We also remove the outside touch area for resizing for all freeform
1435             * tasks (including the focused).
1436             *
1437             * We save the focused task region once we find it, and add it back at the end.
1438             *
1439             * If the task is home stack and it is resizable in the minimized state, we want to
1440             * exclude the docked stack from touch so we need the entire screen area and not just a
1441             * small portion which the home stack currently is resized to.
1442             */
1443
1444            if (task.isHomeTask() && isMinimizedDockAndHomeStackResizable()) {
1445                mDisplayContent.getLogicalDisplayRect(mTmpRect);
1446            } else {
1447                task.getDimBounds(mTmpRect);
1448            }
1449
1450            if (task == focusedTask) {
1451                // Add the focused task rect back into the exclude region once we are done
1452                // processing stacks.
1453                postExclude.set(mTmpRect);
1454            }
1455
1456            final boolean isFreeformed = task.inFreeformWorkspace();
1457            if (task != focusedTask || isFreeformed) {
1458                if (isFreeformed) {
1459                    // If the task is freeformed, enlarge the area to account for outside
1460                    // touch area for resize.
1461                    mTmpRect.inset(-delta, -delta);
1462                    // Intersect with display content rect. If we have system decor (status bar/
1463                    // navigation bar), we want to exclude that from the tap detection.
1464                    // Otherwise, if the app is partially placed under some system button (eg.
1465                    // Recents, Home), pressing that button would cause a full series of
1466                    // unwanted transfer focus/resume/pause, before we could go home.
1467                    mTmpRect.intersect(contentRect);
1468                }
1469                touchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
1470            }
1471        }
1472    }
1473
1474    public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
1475        // Hold the lock since this is called from the BoundsAnimator running on the UiThread
1476        synchronized (mService.mWindowMap) {
1477            if (mCancelCurrentBoundsAnimation) {
1478                return false;
1479            }
1480        }
1481
1482        try {
1483            mService.mActivityManager.resizePinnedStack(stackBounds, tempTaskBounds);
1484        } catch (RemoteException e) {
1485            // I don't believe you.
1486        }
1487        return true;
1488    }
1489
1490    void onAllWindowsDrawn() {
1491        if (!mBoundsAnimating && !mBoundsAnimatingRequested) {
1492            return;
1493        }
1494
1495        mService.mBoundsAnimationController.onAllWindowsDrawn();
1496    }
1497
1498    @Override  // AnimatesBounds
1499    public void onAnimationStart(boolean schedulePipModeChangedCallback) {
1500        // Hold the lock since this is called from the BoundsAnimator running on the UiThread
1501        synchronized (mService.mWindowMap) {
1502            mBoundsAnimatingRequested = false;
1503            mBoundsAnimating = true;
1504            mCancelCurrentBoundsAnimation = false;
1505
1506            // If we are changing UI mode, as in the PiP to fullscreen
1507            // transition, then we need to wait for the window to draw.
1508            if (schedulePipModeChangedCallback) {
1509                forAllWindows((w) -> { w.mWinAnimator.resetDrawState(); },
1510                        false /* traverseTopToBottom */);
1511            }
1512        }
1513
1514        if (mStackId == PINNED_STACK_ID) {
1515            try {
1516                mService.mActivityManager.notifyPinnedStackAnimationStarted();
1517            } catch (RemoteException e) {
1518                // I don't believe you...
1519            }
1520
1521            final PinnedStackWindowController controller =
1522                    (PinnedStackWindowController) getController();
1523            if (schedulePipModeChangedCallback && controller != null) {
1524                // We need to schedule the PiP mode change after the animation down, so use the
1525                // final bounds
1526                controller.updatePictureInPictureModeForPinnedStackAnimation(null);
1527            }
1528        }
1529    }
1530
1531    @Override  // AnimatesBounds
1532    public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
1533            boolean moveToFullscreen) {
1534        // Hold the lock since this is called from the BoundsAnimator running on the UiThread
1535        synchronized (mService.mWindowMap) {
1536            mBoundsAnimating = false;
1537            for (int i = 0; i < mChildren.size(); i++) {
1538                final Task t = mChildren.get(i);
1539                t.clearPreserveNonFloatingState();
1540            }
1541            mService.requestTraversal();
1542        }
1543
1544        if (mStackId == PINNED_STACK_ID) {
1545            // Update to the final bounds if requested. This is done here instead of in the bounds
1546            // animator to allow us to coordinate this after we notify the PiP mode changed
1547
1548            final PinnedStackWindowController controller =
1549                    (PinnedStackWindowController) getController();
1550            if (schedulePipModeChangedCallback && controller != null) {
1551                // We need to schedule the PiP mode change after the animation down, so use the
1552                // final bounds
1553                controller.updatePictureInPictureModeForPinnedStackAnimation(
1554                        mBoundsAnimationTarget);
1555            }
1556
1557            if (finalStackSize != null) {
1558                setPinnedStackSize(finalStackSize, null);
1559            }
1560
1561            try {
1562                mService.mActivityManager.notifyPinnedStackAnimationEnded();
1563                if (moveToFullscreen) {
1564                    mService.mActivityManager.moveTasksToFullscreenStack(mStackId,
1565                            true /* onTop */);
1566                }
1567            } catch (RemoteException e) {
1568                // I don't believe you...
1569            }
1570        }
1571    }
1572
1573    /**
1574     * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen
1575     *         bounds and we have a deferred PiP mode changed callback set with the animation.
1576     */
1577    public boolean deferScheduleMultiWindowModeChanged() {
1578        if (mStackId == PINNED_STACK_ID) {
1579            return (mBoundsAnimatingRequested || mBoundsAnimating);
1580        }
1581        return false;
1582    }
1583
1584    public boolean hasMovementAnimations() {
1585        return StackId.hasMovementAnimations(mStackId);
1586    }
1587
1588    public boolean isForceScaled() {
1589        return mBoundsAnimating;
1590    }
1591
1592    public boolean isAnimatingBounds() {
1593        return mBoundsAnimating;
1594    }
1595
1596    public boolean lastAnimatingBoundsWasToFullscreen() {
1597        return mBoundsAnimatingToFullscreen;
1598    }
1599
1600    public boolean isAnimatingBoundsToFullscreen() {
1601        return isAnimatingBounds() && lastAnimatingBoundsWasToFullscreen();
1602    }
1603
1604    public boolean pinnedStackResizeDisallowed() {
1605        if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
1606            return true;
1607        }
1608        return false;
1609    }
1610
1611    /** Returns true if a removal action is still being deferred. */
1612    boolean checkCompleteDeferredRemoval() {
1613        if (isAnimating()) {
1614            return true;
1615        }
1616        if (mDeferRemoval) {
1617            removeImmediately();
1618        }
1619
1620        return super.checkCompleteDeferredRemoval();
1621    }
1622
1623    void stepAppWindowsAnimation(long currentTime) {
1624        super.stepAppWindowsAnimation(currentTime);
1625
1626        // TODO: Why aren't we just using the loop above for this? mAppAnimator.animating isn't set
1627        // below but is set in the loop above. See if it really matters...
1628        final int exitingCount = mExitingAppTokens.size();
1629        for (int i = 0; i < exitingCount; i++) {
1630            final AppWindowAnimator appAnimator = mExitingAppTokens.get(i).mAppAnimator;
1631            appAnimator.wasAnimating = appAnimator.animating;
1632            if (appAnimator.stepAnimationLocked(currentTime)) {
1633                mService.mAnimator.setAnimating(true);
1634                mService.mAnimator.mAppWindowAnimating = true;
1635            } else if (appAnimator.wasAnimating) {
1636                // stopped animating, do one more pass through the layout
1637                appAnimator.mAppToken.setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER,
1638                        "exiting appToken " + appAnimator.mAppToken + " done");
1639                if (DEBUG_ANIM) Slog.v(TAG_WM,
1640                        "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken);
1641            }
1642        }
1643    }
1644
1645    @Override
1646    int getOrientation() {
1647        return (StackId.canSpecifyOrientation(mStackId))
1648                ? super.getOrientation() : SCREEN_ORIENTATION_UNSET;
1649    }
1650}
1651