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