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