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