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