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.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 
    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 
    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 
    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 
    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 
    231     void onConfigurationChanged() {
    232         loadDimens();
    233     }
    234 
    235     boolean isResizing() {
    236         return mResizing;
    237     }
    238 
    239     int getContentWidth() {
    240         return mDividerWindowWidth - 2 * mDividerInsets;
    241     }
    242 
    243     int getContentInsets() {
    244         return mDividerInsets;
    245     }
    246 
    247     int getContentWidthInactive() {
    248         return mDividerWindowWidthInactive;
    249     }
    250 
    251     void setResizing(boolean resizing) {
    252         if (mResizing != resizing) {
    253             mResizing = resizing;
    254             resetDragResizingChangeReported();
    255         }
    256     }
    257 
    258     void setTouchRegion(Rect touchRegion) {
    259         mTouchRegion.set(touchRegion);
    260     }
    261 
    262     void getTouchRegion(Rect outRegion) {
    263         outRegion.set(mTouchRegion);
    264         outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
    265     }
    266 
    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 
    274     void setWindow(WindowState window) {
    275         mWindow = window;
    276         reevaluateVisibility(false);
    277     }
    278 
    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 
    297     boolean wasVisible() {
    298         return mLastVisibility;
    299     }
    300 
    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 
    318     int getImeHeightAdjustedFor() {
    319         return mImeHeight;
    320     }
    321 
    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 
    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 
    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      */
    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      */
    410     boolean isImeHideRequested() {
    411         return mImeHideRequested;
    412     }
    413 
    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 
    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 
    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 
    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 
    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      */
    491     void notifyAppVisibilityChanged() {
    492         checkMinimizeChanged(false /* animate */);
    493     }
    494 
    495     void notifyAppTransitionStarting() {
    496         checkMinimizeChanged(true /* animate */);
    497     }
    498 
    499     boolean isMinimizedDock() {
    500         return mMinimizedDock;
    501     }
    502 
    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 
    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      */
    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 
    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 
    584     private void startAdjustAnimation(float from, float to) {
    585         mAnimatingForMinimizedDockedStack = true;
    586         mAnimationStarted = false;
    587         mAnimationStart = from;
    588         mAnimationTarget = to;
    589     }
    590 
    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 
    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 
    668     private boolean isAnimationMaximizing() {
    669         return mAnimationTarget == 0f;
    670     }
    671 
    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 
    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 
    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 
    762     private float getInterpolatedAnimationValue(float t) {
    763         return t * mAnimationTarget + (1 - t) * mAnimationStart;
    764     }
    765 
    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      */
    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      */
    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      */
    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
    819     public boolean dimFullscreen() {
    820         return false;
    821     }
    822 
    823     @Override
    824     public DisplayInfo getDisplayInfo() {
    825         return mDisplayContent.getDisplayInfo();
    826     }
    827 
    828     @Override
    829     public void getDimBounds(Rect outBounds) {
    830         // This dim layer user doesn't need this.
    831     }
    832 
    833     @Override
    834     public String toShortString() {
    835         return TAG;
    836     }
    837 
    838     WindowState getWindow() {
    839         return mWindow;
    840     }
    841 
    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