• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.StackId.DOCKED_STACK_ID;
20 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
21 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
22 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
23 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
24 import static android.view.Surface.ROTATION_270;
25 import static android.view.Surface.ROTATION_90;
26 import static android.view.WindowManager.DOCKED_BOTTOM;
27 import static android.view.WindowManager.DOCKED_LEFT;
28 import static android.view.WindowManager.DOCKED_RIGHT;
29 import static android.view.WindowManager.DOCKED_TOP;
30 import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
31 import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
32 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
33 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
34 import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
35 
36 import android.content.Context;
37 import android.content.res.Configuration;
38 import android.graphics.Rect;
39 import android.os.RemoteCallbackList;
40 import android.os.RemoteException;
41 import android.util.Slog;
42 import android.view.DisplayInfo;
43 import android.view.IDockedStackListener;
44 import android.view.SurfaceControl;
45 import android.view.animation.AnimationUtils;
46 import android.view.animation.Interpolator;
47 import android.view.animation.PathInterpolator;
48 import android.view.inputmethod.InputMethodManagerInternal;
49 
50 import com.android.internal.policy.DividerSnapAlgorithm;
51 import com.android.internal.policy.DockedDividerUtils;
52 import com.android.server.LocalServices;
53 import com.android.server.wm.DimLayer.DimLayerUser;
54 import com.android.server.wm.WindowManagerService.H;
55 
56 import java.io.PrintWriter;
57 import java.util.ArrayList;
58 
59 /**
60  * Keeps information about the docked stack divider.
61  */
62 public class DockedStackDividerController implements DimLayerUser {
63 
64     private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
65 
66     /**
67      * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
68      * revealing surface at the earliest.
69      */
70     private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f;
71 
72     /**
73      * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
74      * revealing surface at the latest.
75      */
76     private static final float CLIP_REVEAL_MEET_LAST = 1f;
77 
78     /**
79      * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start
80      * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}.
81      */
82     private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f;
83 
84     /**
85      * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance,
86      * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}.
87      */
88     private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
89 
90     private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR =
91             new PathInterpolator(0.2f, 0f, 0.1f, 1f);
92 
93     private static final long IME_ADJUST_ANIM_DURATION = 280;
94 
95     private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
96 
97     private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
98 
99     private final WindowManagerService mService;
100     private final DisplayContent mDisplayContent;
101     private int mDividerWindowWidth;
102     private int mDividerWindowWidthInactive;
103     private int mDividerInsets;
104     private boolean mResizing;
105     private WindowState mWindow;
106     private final Rect mTmpRect = new Rect();
107     private final Rect mTmpRect2 = new Rect();
108     private final Rect mTmpRect3 = new Rect();
109     private final Rect mLastRect = new Rect();
110     private boolean mLastVisibility = false;
111     private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
112             = new RemoteCallbackList<>();
113     private final DimLayer mDimLayer;
114 
115     private boolean mMinimizedDock;
116     private boolean mAnimatingForMinimizedDockedStack;
117     private boolean mAnimationStarted;
118     private long mAnimationStartTime;
119     private float mAnimationStart;
120     private float mAnimationTarget;
121     private long mAnimationDuration;
122     private boolean mAnimationStartDelayed;
123     private final Interpolator mMinimizedDockInterpolator;
124     private float mMaximizeMeetFraction;
125     private final Rect mTouchRegion = new Rect();
126     private boolean mAnimatingForIme;
127     private boolean mAdjustedForIme;
128     private int mImeHeight;
129     private WindowState mDelayedImeWin;
130     private boolean mAdjustedForDivider;
131     private float mDividerAnimationStart;
132     private float mDividerAnimationTarget;
133     private float mLastAnimationProgress;
134     private float mLastDividerProgress;
135     private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
136     private boolean mImeHideRequested;
137 
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent)138     DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
139         mService = service;
140         mDisplayContent = displayContent;
141         final Context context = service.mContext;
142         mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
143                 "DockedStackDim");
144         mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
145                 context, android.R.interpolator.fast_out_slow_in);
146         loadDimens();
147     }
148 
getSmallestWidthDpForBounds(Rect bounds)149     int getSmallestWidthDpForBounds(Rect bounds) {
150         final DisplayInfo di = mDisplayContent.getDisplayInfo();
151 
152         // If the bounds are fullscreen, return the value of the fullscreen configuration
153         if (bounds == null || (bounds.left == 0 && bounds.top == 0
154                 && bounds.right == di.logicalWidth && bounds.bottom == di.logicalHeight)) {
155             return mService.mCurConfiguration.smallestScreenWidthDp;
156         }
157         final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth;
158         final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight;
159         int minWidth = Integer.MAX_VALUE;
160 
161         // Go through all screen orientations and find the orientation in which the task has the
162         // smallest width.
163         for (int rotation = 0; rotation < 4; rotation++) {
164             mTmpRect.set(bounds);
165             mDisplayContent.rotateBounds(di.rotation, rotation, mTmpRect);
166             final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
167             mTmpRect2.set(0, 0,
168                     rotated ? baseDisplayHeight : baseDisplayWidth,
169                     rotated ? baseDisplayWidth : baseDisplayHeight);
170             final int orientation = mTmpRect2.width() <= mTmpRect2.height()
171                     ? ORIENTATION_PORTRAIT
172                     : ORIENTATION_LANDSCAPE;
173             final int dockSide = TaskStack.getDockSideUnchecked(mTmpRect, mTmpRect2, orientation);
174             final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
175                     getContentWidth());
176 
177             // Since we only care about feasible states, snap to the closest snap target, like it
178             // would happen when actually rotating the screen.
179             final int snappedPosition = mSnapAlgorithmForRotation[rotation]
180                     .calculateNonDismissingSnapTarget(position).position;
181             DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect,
182                     mTmpRect2.width(), mTmpRect2.height(), getContentWidth());
183             mService.mPolicy.getStableInsetsLw(rotation, mTmpRect2.width(), mTmpRect2.height(),
184                     mTmpRect3);
185             mService.subtractInsets(mTmpRect2, mTmpRect3, mTmpRect);
186             minWidth = Math.min(mTmpRect.width(), minWidth);
187         }
188         return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
189     }
190 
initSnapAlgorithmForRotations()191     private void initSnapAlgorithmForRotations() {
192         final Configuration baseConfig = mService.mCurConfiguration;
193 
194         // Initialize the snap algorithms for all 4 screen orientations.
195         final Configuration config = new Configuration();
196         for (int rotation = 0; rotation < 4; rotation++) {
197             final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
198             final int dw = rotated
199                     ? mDisplayContent.mBaseDisplayHeight
200                     : mDisplayContent.mBaseDisplayWidth;
201             final int dh = rotated
202                     ? mDisplayContent.mBaseDisplayWidth
203                     : mDisplayContent.mBaseDisplayHeight;
204             mService.mPolicy.getStableInsetsLw(rotation, dw, dh, mTmpRect);
205             config.setToDefaults();
206             config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
207             config.screenWidthDp = (int)
208                     (mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, baseConfig.uiMode) /
209                             mDisplayContent.getDisplayMetrics().density);
210             config.screenHeightDp = (int)
211                     (mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, baseConfig.uiMode) /
212                             mDisplayContent.getDisplayMetrics().density);
213             final Context rotationContext = mService.mContext.createConfigurationContext(config);
214             mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm(
215                     rotationContext.getResources(), dw, dh, getContentWidth(),
216                     config.orientation == ORIENTATION_PORTRAIT, mTmpRect);
217         }
218     }
219 
loadDimens()220     private void loadDimens() {
221         final Context context = mService.mContext;
222         mDividerWindowWidth = context.getResources().getDimensionPixelSize(
223                 com.android.internal.R.dimen.docked_stack_divider_thickness);
224         mDividerInsets = context.getResources().getDimensionPixelSize(
225                 com.android.internal.R.dimen.docked_stack_divider_insets);
226         mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
227                 DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());
228         initSnapAlgorithmForRotations();
229     }
230 
onConfigurationChanged()231     void onConfigurationChanged() {
232         loadDimens();
233     }
234 
isResizing()235     boolean isResizing() {
236         return mResizing;
237     }
238 
getContentWidth()239     int getContentWidth() {
240         return mDividerWindowWidth - 2 * mDividerInsets;
241     }
242 
getContentInsets()243     int getContentInsets() {
244         return mDividerInsets;
245     }
246 
getContentWidthInactive()247     int getContentWidthInactive() {
248         return mDividerWindowWidthInactive;
249     }
250 
setResizing(boolean resizing)251     void setResizing(boolean resizing) {
252         if (mResizing != resizing) {
253             mResizing = resizing;
254             resetDragResizingChangeReported();
255         }
256     }
257 
setTouchRegion(Rect touchRegion)258     void setTouchRegion(Rect touchRegion) {
259         mTouchRegion.set(touchRegion);
260     }
261 
getTouchRegion(Rect outRegion)262     void getTouchRegion(Rect outRegion) {
263         outRegion.set(mTouchRegion);
264         outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
265     }
266 
resetDragResizingChangeReported()267     private void resetDragResizingChangeReported() {
268         final WindowList windowList = mDisplayContent.getWindowList();
269         for (int i = windowList.size() - 1; i >= 0; i--) {
270             windowList.get(i).resetDragResizingChangeReported();
271         }
272     }
273 
setWindow(WindowState window)274     void setWindow(WindowState window) {
275         mWindow = window;
276         reevaluateVisibility(false);
277     }
278 
reevaluateVisibility(boolean force)279     void reevaluateVisibility(boolean force) {
280         if (mWindow == null) {
281             return;
282         }
283         TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID);
284 
285         // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
286         final boolean visible = stack != null;
287         if (mLastVisibility == visible && !force) {
288             return;
289         }
290         mLastVisibility = visible;
291         notifyDockedDividerVisibilityChanged(visible);
292         if (!visible) {
293             setResizeDimLayer(false, INVALID_STACK_ID, 0f);
294         }
295     }
296 
wasVisible()297     boolean wasVisible() {
298         return mLastVisibility;
299     }
300 
setAdjustedForIme( boolean adjustedForIme, boolean adjustedForDivider, boolean animate, WindowState imeWin, int imeHeight)301     void setAdjustedForIme(
302             boolean adjustedForIme, boolean adjustedForDivider,
303             boolean animate, WindowState imeWin, int imeHeight) {
304         if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight)
305                 || mAdjustedForDivider != adjustedForDivider) {
306             if (animate && !mAnimatingForMinimizedDockedStack) {
307                 startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin);
308             } else {
309                 // Animation might be delayed, so only notify if we don't run an animation.
310                 notifyAdjustedForImeChanged(adjustedForIme || adjustedForDivider, 0 /* duration */);
311             }
312             mAdjustedForIme = adjustedForIme;
313             mImeHeight = imeHeight;
314             mAdjustedForDivider = adjustedForDivider;
315         }
316     }
317 
getImeHeightAdjustedFor()318     int getImeHeightAdjustedFor() {
319         return mImeHeight;
320     }
321 
positionDockedStackedDivider(Rect frame)322     void positionDockedStackedDivider(Rect frame) {
323         TaskStack stack = mDisplayContent.getDockedStackLocked();
324         if (stack == null) {
325             // Unfortunately we might end up with still having a divider, even though the underlying
326             // stack was already removed. This is because we are on AM thread and the removal of the
327             // divider was deferred to WM thread and hasn't happened yet. In that case let's just
328             // keep putting it in the same place it was before the stack was removed to have
329             // continuity and prevent it from jumping to the center. It will get hidden soon.
330             frame.set(mLastRect);
331             return;
332         } else {
333             stack.getDimBounds(mTmpRect);
334         }
335         int side = stack.getDockSide();
336         switch (side) {
337             case DOCKED_LEFT:
338                 frame.set(mTmpRect.right - mDividerInsets, frame.top,
339                         mTmpRect.right + frame.width() - mDividerInsets, frame.bottom);
340                 break;
341             case DOCKED_TOP:
342                 frame.set(frame.left, mTmpRect.bottom - mDividerInsets,
343                         mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets);
344                 break;
345             case DOCKED_RIGHT:
346                 frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top,
347                         mTmpRect.left + mDividerInsets, frame.bottom);
348                 break;
349             case DOCKED_BOTTOM:
350                 frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets,
351                         frame.right, mTmpRect.top + mDividerInsets);
352                 break;
353         }
354         mLastRect.set(frame);
355     }
356 
notifyDockedDividerVisibilityChanged(boolean visible)357     void notifyDockedDividerVisibilityChanged(boolean visible) {
358         final int size = mDockedStackListeners.beginBroadcast();
359         for (int i = 0; i < size; ++i) {
360             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
361             try {
362                 listener.onDividerVisibilityChanged(visible);
363             } catch (RemoteException e) {
364                 Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e);
365             }
366         }
367         mDockedStackListeners.finishBroadcast();
368     }
369 
notifyDockedStackExistsChanged(boolean exists)370     void notifyDockedStackExistsChanged(boolean exists) {
371         final int size = mDockedStackListeners.beginBroadcast();
372         for (int i = 0; i < size; ++i) {
373             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
374             try {
375                 listener.onDockedStackExistsChanged(exists);
376             } catch (RemoteException e) {
377                 Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e);
378             }
379         }
380         mDockedStackListeners.finishBroadcast();
381         if (exists) {
382             InputMethodManagerInternal inputMethodManagerInternal =
383                     LocalServices.getService(InputMethodManagerInternal.class);
384             if (inputMethodManagerInternal != null) {
385 
386                 // Hide the current IME to avoid problems with animations from IME adjustment when
387                 // attaching the docked stack.
388                 inputMethodManagerInternal.hideCurrentInputMethod();
389                 mImeHideRequested = true;
390             }
391         } else if (setMinimizedDockedStack(false)) {
392             mService.mWindowPlacerLocked.performSurfacePlacement();
393         }
394     }
395 
396     /**
397      * Resets the state that IME hide has been requested. See {@link #isImeHideRequested}.
398      */
resetImeHideRequested()399     void resetImeHideRequested() {
400         mImeHideRequested = false;
401     }
402 
403     /**
404      * The docked stack divider controller makes sure the IME gets hidden when attaching the docked
405      * stack, to avoid animation problems. This flag indicates whether the request to hide the IME
406      * has been sent in an asynchronous manner, and the IME should be treated as hidden already.
407      *
408      * @return whether IME hide request has been sent
409      */
isImeHideRequested()410     boolean isImeHideRequested() {
411         return mImeHideRequested;
412     }
413 
notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration)414     void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
415         mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
416         mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
417                 minimizedDock ? 1 : 0, 0).sendToTarget();
418         final int size = mDockedStackListeners.beginBroadcast();
419         for (int i = 0; i < size; ++i) {
420             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
421             try {
422                 listener.onDockedStackMinimizedChanged(minimizedDock, animDuration);
423             } catch (RemoteException e) {
424                 Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e);
425             }
426         }
427         mDockedStackListeners.finishBroadcast();
428     }
429 
notifyDockSideChanged(int newDockSide)430     void notifyDockSideChanged(int newDockSide) {
431         final int size = mDockedStackListeners.beginBroadcast();
432         for (int i = 0; i < size; ++i) {
433             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
434             try {
435                 listener.onDockSideChanged(newDockSide);
436             } catch (RemoteException e) {
437                 Slog.e(TAG_WM, "Error delivering dock side changed event.", e);
438             }
439         }
440         mDockedStackListeners.finishBroadcast();
441     }
442 
notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration)443     void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
444         final int size = mDockedStackListeners.beginBroadcast();
445         for (int i = 0; i < size; ++i) {
446             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
447             try {
448                 listener.onAdjustedForImeChanged(adjustedForIme, animDuration);
449             } catch (RemoteException e) {
450                 Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e);
451             }
452         }
453         mDockedStackListeners.finishBroadcast();
454     }
455 
registerDockedStackListener(IDockedStackListener listener)456     void registerDockedStackListener(IDockedStackListener listener) {
457         mDockedStackListeners.register(listener);
458         notifyDockedDividerVisibilityChanged(wasVisible());
459         notifyDockedStackExistsChanged(
460                 mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
461         notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
462         notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
463 
464     }
465 
setResizeDimLayer(boolean visible, int targetStackId, float alpha)466     void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
467         SurfaceControl.openTransaction();
468         final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
469         final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
470         boolean visibleAndValid = visible && stack != null && dockedStack != null;
471         if (visibleAndValid) {
472             stack.getDimBounds(mTmpRect);
473             if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
474                 mDimLayer.setBounds(mTmpRect);
475                 mDimLayer.show(mService.mLayersController.getResizeDimLayer(),
476                         alpha, 0 /* duration */);
477             } else {
478                 visibleAndValid = false;
479             }
480         }
481         if (!visibleAndValid) {
482             mDimLayer.hide();
483         }
484         SurfaceControl.closeTransaction();
485     }
486 
487     /**
488      * Notifies the docked stack divider controller of a visibility change that happens without
489      * an animation.
490      */
notifyAppVisibilityChanged()491     void notifyAppVisibilityChanged() {
492         checkMinimizeChanged(false /* animate */);
493     }
494 
notifyAppTransitionStarting()495     void notifyAppTransitionStarting() {
496         checkMinimizeChanged(true /* animate */);
497     }
498 
isMinimizedDock()499     boolean isMinimizedDock() {
500         return mMinimizedDock;
501     }
502 
checkMinimizeChanged(boolean animate)503     private void checkMinimizeChanged(boolean animate) {
504         if (mDisplayContent.getDockedStackVisibleForUserLocked() == null) {
505             return;
506         }
507         final TaskStack homeStack = mDisplayContent.getHomeStack();
508         if (homeStack == null) {
509             return;
510         }
511         final Task homeTask = homeStack.findHomeTask();
512         if (homeTask == null || !isWithinDisplay(homeTask)) {
513             return;
514         }
515         final TaskStack fullscreenStack
516                 = mService.mStackIdToStack.get(FULLSCREEN_WORKSPACE_STACK_ID);
517         final ArrayList<Task> homeStackTasks = homeStack.getTasks();
518         final Task topHomeStackTask = homeStackTasks.get(homeStackTasks.size() - 1);
519         final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
520         final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisibleLocked())
521                 || (homeStackTasks.size() > 1 && topHomeStackTask != homeTask);
522         setMinimizedDockedStack(homeVisible && !homeBehind, animate);
523     }
524 
isWithinDisplay(Task task)525     private boolean isWithinDisplay(Task task) {
526         task.mStack.getBounds(mTmpRect);
527         mDisplayContent.getLogicalDisplayRect(mTmpRect2);
528         return mTmpRect.intersect(mTmpRect2);
529     }
530 
531     /**
532      * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the
533      * docked stack are heavily clipped so you can only see a minimal peek state.
534      *
535      * @param minimizedDock Whether the docked stack is currently minimized.
536      * @param animate Whether to animate the change.
537      */
setMinimizedDockedStack(boolean minimizedDock, boolean animate)538     private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
539         final boolean wasMinimized = mMinimizedDock;
540         mMinimizedDock = minimizedDock;
541         if (minimizedDock == wasMinimized) {
542             return;
543         }
544 
545         final boolean imeChanged = clearImeAdjustAnimation();
546         boolean minimizedChange = false;
547         if (minimizedDock) {
548             if (animate) {
549                 startAdjustAnimation(0f, 1f);
550             } else {
551                 minimizedChange |= setMinimizedDockedStack(true);
552             }
553         } else {
554             if (animate) {
555                 startAdjustAnimation(1f, 0f);
556             } else {
557                 minimizedChange |= setMinimizedDockedStack(false);
558             }
559         }
560         if (imeChanged || minimizedChange) {
561             if (imeChanged && !minimizedChange) {
562                 Slog.d(TAG, "setMinimizedDockedStack: IME adjust changed due to minimizing,"
563                         + " minimizedDock=" + minimizedDock
564                         + " minimizedChange=" + minimizedChange);
565             }
566             mService.mWindowPlacerLocked.performSurfacePlacement();
567         }
568     }
569 
clearImeAdjustAnimation()570     private boolean clearImeAdjustAnimation() {
571         boolean changed = false;
572         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
573         for (int i = stacks.size() - 1; i >= 0; --i) {
574             final TaskStack stack = stacks.get(i);
575             if (stack != null && stack.isAdjustedForIme()) {
576                 stack.resetAdjustedForIme(true /* adjustBoundsNow */);
577                 changed  = true;
578             }
579         }
580         mAnimatingForIme = false;
581         return changed;
582     }
583 
startAdjustAnimation(float from, float to)584     private void startAdjustAnimation(float from, float to) {
585         mAnimatingForMinimizedDockedStack = true;
586         mAnimationStarted = false;
587         mAnimationStart = from;
588         mAnimationTarget = to;
589     }
590 
startImeAdjustAnimation( boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin)591     private void startImeAdjustAnimation(
592             boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) {
593 
594         // If we're not in an animation, the starting point depends on whether we're adjusted
595         // or not. If we're already in an animation, we start from where the current animation
596         // left off, so that the motion doesn't look discontinuous.
597         if (!mAnimatingForIme) {
598             mAnimationStart = mAdjustedForIme ? 1 : 0;
599             mDividerAnimationStart = mAdjustedForDivider ? 1 : 0;
600             mLastAnimationProgress = mAnimationStart;
601             mLastDividerProgress = mDividerAnimationStart;
602         } else {
603             mAnimationStart = mLastAnimationProgress;
604             mDividerAnimationStart = mLastDividerProgress;
605         }
606         mAnimatingForIme = true;
607         mAnimationStarted = false;
608         mAnimationTarget = adjustedForIme ? 1 : 0;
609         mDividerAnimationTarget = adjustedForDivider ? 1 : 0;
610 
611         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
612         for (int i = stacks.size() - 1; i >= 0; --i) {
613             final TaskStack stack = stacks.get(i);
614             if (stack.isVisibleLocked() && stack.isAdjustedForIme()) {
615                 stack.beginImeAdjustAnimation();
616             }
617         }
618 
619         // We put all tasks into drag resizing mode - wait until all of them have completed the
620         // drag resizing switch.
621         if (!mService.mWaitingForDrawn.isEmpty()) {
622             mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
623             mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
624                     IME_ADJUST_DRAWN_TIMEOUT);
625             mAnimationStartDelayed = true;
626             if (imeWin != null) {
627 
628                 // There might be an old window delaying the animation start - clear it.
629                 if (mDelayedImeWin != null) {
630                     mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
631                 }
632                 mDelayedImeWin = imeWin;
633                 imeWin.mWinAnimator.startDelayingAnimationStart();
634             }
635             mService.mWaitingForDrawnCallback = () -> {
636                 mAnimationStartDelayed = false;
637                 if (mDelayedImeWin != null) {
638                     mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
639                 }
640                 // If the adjust status changed since this was posted, only notify
641                 // the new states and don't animate.
642                 long duration = 0;
643                 if (mAdjustedForIme == adjustedForIme
644                         && mAdjustedForDivider == adjustedForDivider) {
645                     duration = IME_ADJUST_ANIM_DURATION;
646                 } else {
647                     Slog.w(TAG, "IME adjust changed while waiting for drawn:"
648                             + " adjustedForIme=" + adjustedForIme
649                             + " adjustedForDivider=" + adjustedForDivider
650                             + " mAdjustedForIme=" + mAdjustedForIme
651                             + " mAdjustedForDivider=" + mAdjustedForDivider);
652                 }
653                 notifyAdjustedForImeChanged(
654                         mAdjustedForIme || mAdjustedForDivider, duration);
655             };
656         } else {
657             notifyAdjustedForImeChanged(
658                     adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
659         }
660     }
661 
setMinimizedDockedStack(boolean minimized)662     private boolean setMinimizedDockedStack(boolean minimized) {
663         final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
664         notifyDockedStackMinimizedChanged(minimized, 0);
665         return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
666     }
667 
isAnimationMaximizing()668     private boolean isAnimationMaximizing() {
669         return mAnimationTarget == 0f;
670     }
671 
animate(long now)672     public boolean animate(long now) {
673         if (mWindow == null) {
674             return false;
675         }
676         if (mAnimatingForMinimizedDockedStack) {
677             return animateForMinimizedDockedStack(now);
678         } else if (mAnimatingForIme) {
679             return animateForIme(now);
680         } else {
681             if (mDimLayer != null && mDimLayer.isDimming()) {
682                 mDimLayer.setLayer(mService.mLayersController.getResizeDimLayer());
683             }
684             return false;
685         }
686     }
687 
animateForIme(long now)688     private boolean animateForIme(long now) {
689         if (!mAnimationStarted || mAnimationStartDelayed) {
690             mAnimationStarted = true;
691             mAnimationStartTime = now;
692             mAnimationDuration = (long)
693                     (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
694         }
695         float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
696         t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
697                 .getInterpolation(t);
698         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
699         boolean updated = false;
700         for (int i = stacks.size() - 1; i >= 0; --i) {
701             final TaskStack stack = stacks.get(i);
702             if (stack != null && stack.isAdjustedForIme()) {
703                 if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) {
704                     stack.resetAdjustedForIme(true /* adjustBoundsNow */);
705                     updated = true;
706                 } else {
707                     mLastAnimationProgress = getInterpolatedAnimationValue(t);
708                     mLastDividerProgress = getInterpolatedDividerValue(t);
709                     updated |= stack.updateAdjustForIme(
710                             mLastAnimationProgress,
711                             mLastDividerProgress,
712                             false /* force */);
713                 }
714                 if (t >= 1f) {
715                     stack.endImeAdjustAnimation();
716                 }
717             }
718         }
719         if (updated) {
720             mService.mWindowPlacerLocked.performSurfacePlacement();
721         }
722         if (t >= 1.0f) {
723             mLastAnimationProgress = mAnimationTarget;
724             mLastDividerProgress = mDividerAnimationTarget;
725             mAnimatingForIme = false;
726             return false;
727         } else {
728             return true;
729         }
730     }
731 
animateForMinimizedDockedStack(long now)732     private boolean animateForMinimizedDockedStack(long now) {
733         final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
734         if (!mAnimationStarted) {
735             mAnimationStarted = true;
736             mAnimationStartTime = now;
737             final long transitionDuration = isAnimationMaximizing()
738                     ? mService.mAppTransition.getLastClipRevealTransitionDuration()
739                     : DEFAULT_APP_TRANSITION_DURATION;
740             mAnimationDuration = (long)
741                     (transitionDuration * mService.getTransitionAnimationScaleLocked());
742             mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
743             notifyDockedStackMinimizedChanged(mMinimizedDock,
744                     (long) (mAnimationDuration * mMaximizeMeetFraction));
745         }
746         float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
747         t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
748                 .getInterpolation(t);
749         if (stack != null) {
750             if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
751                 mService.mWindowPlacerLocked.performSurfacePlacement();
752             }
753         }
754         if (t >= 1.0f) {
755             mAnimatingForMinimizedDockedStack = false;
756             return false;
757         } else {
758             return true;
759         }
760     }
761 
getInterpolatedAnimationValue(float t)762     private float getInterpolatedAnimationValue(float t) {
763         return t * mAnimationTarget + (1 - t) * mAnimationStart;
764     }
765 
getInterpolatedDividerValue(float t)766     private float getInterpolatedDividerValue(float t) {
767         return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
768     }
769 
770     /**
771      * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
772      */
getMinimizeAmount(TaskStack stack, float t)773     private float getMinimizeAmount(TaskStack stack, float t) {
774         final float naturalAmount = getInterpolatedAnimationValue(t);
775         if (isAnimationMaximizing()) {
776             return adjustMaximizeAmount(stack, t, naturalAmount);
777         } else {
778             return naturalAmount;
779         }
780     }
781 
782     /**
783      * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
784      * during the transition such that the edge of the clip reveal rect is met earlier in the
785      * transition so we don't create a visible "hole", but only if both the clip reveal and the
786      * docked stack divider start from about the same portion on the screen.
787      */
adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount)788     private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
789         if (mMaximizeMeetFraction == 1f) {
790             return naturalAmount;
791         }
792         final int minimizeDistance = stack.getMinimizeDistance();
793         float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
794                 / (float) minimizeDistance;
795         final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
796         final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
797         return amountPrime * t2 + naturalAmount * (1 - t2);
798     }
799 
800     /**
801      * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
802      * edge. See {@link #adjustMaximizeAmount}.
803      */
getClipRevealMeetFraction(TaskStack stack)804     private float getClipRevealMeetFraction(TaskStack stack) {
805         if (!isAnimationMaximizing() || stack == null ||
806                 !mService.mAppTransition.hadClipRevealAnimation()) {
807             return 1f;
808         }
809         final int minimizeDistance = stack.getMinimizeDistance();
810         final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
811                 / (float) minimizeDistance;
812         final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
813                 / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
814         return CLIP_REVEAL_MEET_EARLIEST
815                 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
816     }
817 
818     @Override
dimFullscreen()819     public boolean dimFullscreen() {
820         return false;
821     }
822 
823     @Override
getDisplayInfo()824     public DisplayInfo getDisplayInfo() {
825         return mDisplayContent.getDisplayInfo();
826     }
827 
828     @Override
getDimBounds(Rect outBounds)829     public void getDimBounds(Rect outBounds) {
830         // This dim layer user doesn't need this.
831     }
832 
833     @Override
toShortString()834     public String toShortString() {
835         return TAG;
836     }
837 
getWindow()838     WindowState getWindow() {
839         return mWindow;
840     }
841 
dump(String prefix, PrintWriter pw)842     void dump(String prefix, PrintWriter pw) {
843         pw.println(prefix + "DockedStackDividerController");
844         pw.println(prefix + "  mLastVisibility=" + mLastVisibility);
845         pw.println(prefix + "  mMinimizedDock=" + mMinimizedDock);
846         pw.println(prefix + "  mAdjustedForIme=" + mAdjustedForIme);
847         pw.println(prefix + "  mAdjustedForDivider=" + mAdjustedForDivider);
848         if (mDimLayer.isDimming()) {
849             pw.println(prefix + "  Dim layer is dimming: ");
850             mDimLayer.printTo(prefix + "    ", pw);
851         }
852     }
853 }
854