Home | History | Annotate | Download | only in wm
      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 
    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 
    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 
    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 
    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 
    233     void onConfigurationChanged() {
    234         loadDimens();
    235     }
    236 
    237     boolean isResizing() {
    238         return mResizing;
    239     }
    240 
    241     int getContentWidth() {
    242         return mDividerWindowWidth - 2 * mDividerInsets;
    243     }
    244 
    245     int getContentInsets() {
    246         return mDividerInsets;
    247     }
    248 
    249     int getContentWidthInactive() {
    250         return mDividerWindowWidthInactive;
    251     }
    252 
    253     void setResizing(boolean resizing) {
    254         if (mResizing != resizing) {
    255             mResizing = resizing;
    256             resetDragResizingChangeReported();
    257         }
    258     }
    259 
    260     void setTouchRegion(Rect touchRegion) {
    261         mTouchRegion.set(touchRegion);
    262     }
    263 
    264     void getTouchRegion(Rect outRegion) {
    265         outRegion.set(mTouchRegion);
    266         outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
    267     }
    268 
    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 
    276     void setWindow(WindowState window) {
    277         mWindow = window;
    278         reevaluateVisibility(false);
    279     }
    280 
    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 
    299     boolean wasVisible() {
    300         return mLastVisibility;
    301     }
    302 
    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 
    320     int getImeHeightAdjustedFor() {
    321         return mImeHeight;
    322     }
    323 
    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 
    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 
    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      */
    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      */
    412     boolean isImeHideRequested() {
    413         return mImeHideRequested;
    414     }
    415 
    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 
    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 
    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 
    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 
    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      */
    493     void notifyAppVisibilityChanged() {
    494         checkMinimizeChanged(false /* animate */);
    495     }
    496 
    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      */
    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 
    525     boolean isMinimizedDock() {
    526         return mMinimizedDock;
    527     }
    528 
    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 
    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      */
    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 
    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 
    610     private void startAdjustAnimation(float from, float to) {
    611         mAnimatingForMinimizedDockedStack = true;
    612         mAnimationStarted = false;
    613         mAnimationStart = from;
    614         mAnimationTarget = to;
    615     }
    616 
    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             mService.mWaitingForDrawnCallback = () -> {
    662                 mAnimationStartDelayed = false;
    663                 if (mDelayedImeWin != null) {
    664                     mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
    665                 }
    666                 // If the adjust status changed since this was posted, only notify
    667                 // the new states and don't animate.
    668                 long duration = 0;
    669                 if (mAdjustedForIme == adjustedForIme
    670                         && mAdjustedForDivider == adjustedForDivider) {
    671                     duration = IME_ADJUST_ANIM_DURATION;
    672                 } else {
    673                     Slog.w(TAG, "IME adjust changed while waiting for drawn:"
    674                             + " adjustedForIme=" + adjustedForIme
    675                             + " adjustedForDivider=" + adjustedForDivider
    676                             + " mAdjustedForIme=" + mAdjustedForIme
    677                             + " mAdjustedForDivider=" + mAdjustedForDivider);
    678                 }
    679                 notifyAdjustedForImeChanged(
    680                         mAdjustedForIme || mAdjustedForDivider, duration);
    681             };
    682         } else {
    683             notifyAdjustedForImeChanged(
    684                     adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
    685         }
    686     }
    687 
    688     private boolean setMinimizedDockedStack(boolean minimized) {
    689         final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
    690         notifyDockedStackMinimizedChanged(minimized, 0);
    691         return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
    692     }
    693 
    694     private boolean isAnimationMaximizing() {
    695         return mAnimationTarget == 0f;
    696     }
    697 
    698     public boolean animate(long now) {
    699         if (mWindow == null) {
    700             return false;
    701         }
    702         if (mAnimatingForMinimizedDockedStack) {
    703             return animateForMinimizedDockedStack(now);
    704         } else if (mAnimatingForIme) {
    705             return animateForIme(now);
    706         } else {
    707             if (mDimLayer != null && mDimLayer.isDimming()) {
    708                 mDimLayer.setLayer(mService.mLayersController.getResizeDimLayer());
    709             }
    710             return false;
    711         }
    712     }
    713 
    714     private boolean animateForIme(long now) {
    715         if (!mAnimationStarted || mAnimationStartDelayed) {
    716             mAnimationStarted = true;
    717             mAnimationStartTime = now;
    718             mAnimationDuration = (long)
    719                     (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
    720         }
    721         float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
    722         t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
    723                 .getInterpolation(t);
    724         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
    725         boolean updated = false;
    726         for (int i = stacks.size() - 1; i >= 0; --i) {
    727             final TaskStack stack = stacks.get(i);
    728             if (stack != null && stack.isAdjustedForIme()) {
    729                 if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) {
    730                     stack.resetAdjustedForIme(true /* adjustBoundsNow */);
    731                     updated = true;
    732                 } else {
    733                     mLastAnimationProgress = getInterpolatedAnimationValue(t);
    734                     mLastDividerProgress = getInterpolatedDividerValue(t);
    735                     updated |= stack.updateAdjustForIme(
    736                             mLastAnimationProgress,
    737                             mLastDividerProgress,
    738                             false /* force */);
    739                 }
    740                 if (t >= 1f) {
    741                     stack.endImeAdjustAnimation();
    742                 }
    743             }
    744         }
    745         if (updated) {
    746             mService.mWindowPlacerLocked.performSurfacePlacement();
    747         }
    748         if (t >= 1.0f) {
    749             mLastAnimationProgress = mAnimationTarget;
    750             mLastDividerProgress = mDividerAnimationTarget;
    751             mAnimatingForIme = false;
    752             return false;
    753         } else {
    754             return true;
    755         }
    756     }
    757 
    758     private boolean animateForMinimizedDockedStack(long now) {
    759         final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
    760         if (!mAnimationStarted) {
    761             mAnimationStarted = true;
    762             mAnimationStartTime = now;
    763             final long transitionDuration = isAnimationMaximizing()
    764                     ? mService.mAppTransition.getLastClipRevealTransitionDuration()
    765                     : DEFAULT_APP_TRANSITION_DURATION;
    766             mAnimationDuration = (long)
    767                     (transitionDuration * mService.getTransitionAnimationScaleLocked());
    768             mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
    769             notifyDockedStackMinimizedChanged(mMinimizedDock,
    770                     (long) (mAnimationDuration * mMaximizeMeetFraction));
    771         }
    772         float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
    773         t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
    774                 .getInterpolation(t);
    775         if (stack != null) {
    776             if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
    777                 mService.mWindowPlacerLocked.performSurfacePlacement();
    778             }
    779         }
    780         if (t >= 1.0f) {
    781             mAnimatingForMinimizedDockedStack = false;
    782             return false;
    783         } else {
    784             return true;
    785         }
    786     }
    787 
    788     private float getInterpolatedAnimationValue(float t) {
    789         return t * mAnimationTarget + (1 - t) * mAnimationStart;
    790     }
    791 
    792     private float getInterpolatedDividerValue(float t) {
    793         return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
    794     }
    795 
    796     /**
    797      * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
    798      */
    799     private float getMinimizeAmount(TaskStack stack, float t) {
    800         final float naturalAmount = getInterpolatedAnimationValue(t);
    801         if (isAnimationMaximizing()) {
    802             return adjustMaximizeAmount(stack, t, naturalAmount);
    803         } else {
    804             return naturalAmount;
    805         }
    806     }
    807 
    808     /**
    809      * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
    810      * during the transition such that the edge of the clip reveal rect is met earlier in the
    811      * transition so we don't create a visible "hole", but only if both the clip reveal and the
    812      * docked stack divider start from about the same portion on the screen.
    813      */
    814     private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
    815         if (mMaximizeMeetFraction == 1f) {
    816             return naturalAmount;
    817         }
    818         final int minimizeDistance = stack.getMinimizeDistance();
    819         float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
    820                 / (float) minimizeDistance;
    821         final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
    822         final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
    823         return amountPrime * t2 + naturalAmount * (1 - t2);
    824     }
    825 
    826     /**
    827      * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
    828      * edge. See {@link #adjustMaximizeAmount}.
    829      */
    830     private float getClipRevealMeetFraction(TaskStack stack) {
    831         if (!isAnimationMaximizing() || stack == null ||
    832                 !mService.mAppTransition.hadClipRevealAnimation()) {
    833             return 1f;
    834         }
    835         final int minimizeDistance = stack.getMinimizeDistance();
    836         final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
    837                 / (float) minimizeDistance;
    838         final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
    839                 / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
    840         return CLIP_REVEAL_MEET_EARLIEST
    841                 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
    842     }
    843 
    844     @Override
    845     public boolean dimFullscreen() {
    846         return false;
    847     }
    848 
    849     @Override
    850     public DisplayInfo getDisplayInfo() {
    851         return mDisplayContent.getDisplayInfo();
    852     }
    853 
    854     @Override
    855     public void getDimBounds(Rect outBounds) {
    856         // This dim layer user doesn't need this.
    857     }
    858 
    859     @Override
    860     public String toShortString() {
    861         return TAG;
    862     }
    863 
    864     WindowState getWindow() {
    865         return mWindow;
    866     }
    867 
    868     void dump(String prefix, PrintWriter pw) {
    869         pw.println(prefix + "DockedStackDividerController");
    870         pw.println(prefix + "  mLastVisibility=" + mLastVisibility);
    871         pw.println(prefix + "  mMinimizedDock=" + mMinimizedDock);
    872         pw.println(prefix + "  mAdjustedForIme=" + mAdjustedForIme);
    873         pw.println(prefix + "  mAdjustedForDivider=" + mAdjustedForDivider);
    874         if (mDimLayer.isDimming()) {
    875             pw.println(prefix + "  Dim layer is dimming: ");
    876             mDimLayer.printTo(prefix + "    ", pw);
    877         }
    878     }
    879 }
    880