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