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 {
    392             setMinimizedDockedStack(false);
    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         clearImeAdjustAnimation();
    546         if (minimizedDock) {
    547             if (animate) {
    548                 startAdjustAnimation(0f, 1f);
    549             } else {
    550                 setMinimizedDockedStack(true);
    551             }
    552         } else {
    553             if (animate) {
    554                 startAdjustAnimation(1f, 0f);
    555             } else {
    556                 setMinimizedDockedStack(false);
    557             }
    558         }
    559     }
    560 
    561     private void clearImeAdjustAnimation() {
    562         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
    563         for (int i = stacks.size() - 1; i >= 0; --i) {
    564             final TaskStack stack = stacks.get(i);
    565             if (stack != null && stack.isAdjustedForIme()) {
    566                 stack.resetAdjustedForIme(true /* adjustBoundsNow */);
    567             }
    568         }
    569         mAnimatingForIme = false;
    570     }
    571 
    572     private void startAdjustAnimation(float from, float to) {
    573         mAnimatingForMinimizedDockedStack = true;
    574         mAnimationStarted = false;
    575         mAnimationStart = from;
    576         mAnimationTarget = to;
    577     }
    578 
    579     private void startImeAdjustAnimation(
    580             boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) {
    581 
    582         // If we're not in an animation, the starting point depends on whether we're adjusted
    583         // or not. If we're already in an animation, we start from where the current animation
    584         // left off, so that the motion doesn't look discontinuous.
    585         if (!mAnimatingForIme) {
    586             mAnimationStart = mAdjustedForIme ? 1 : 0;
    587             mDividerAnimationStart = mAdjustedForDivider ? 1 : 0;
    588             mLastAnimationProgress = mAnimationStart;
    589             mLastDividerProgress = mDividerAnimationStart;
    590         } else {
    591             mAnimationStart = mLastAnimationProgress;
    592             mDividerAnimationStart = mLastDividerProgress;
    593         }
    594         mAnimatingForIme = true;
    595         mAnimationStarted = false;
    596         mAnimationTarget = adjustedForIme ? 1 : 0;
    597         mDividerAnimationTarget = adjustedForDivider ? 1 : 0;
    598 
    599         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
    600         for (int i = stacks.size() - 1; i >= 0; --i) {
    601             final TaskStack stack = stacks.get(i);
    602             if (stack.isVisibleLocked() && stack.isAdjustedForIme()) {
    603                 stack.beginImeAdjustAnimation();
    604             }
    605         }
    606 
    607         // We put all tasks into drag resizing mode - wait until all of them have completed the
    608         // drag resizing switch.
    609         if (!mService.mWaitingForDrawn.isEmpty()) {
    610             mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
    611             mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
    612                     IME_ADJUST_DRAWN_TIMEOUT);
    613             mAnimationStartDelayed = true;
    614             if (imeWin != null) {
    615 
    616                 // There might be an old window delaying the animation start - clear it.
    617                 if (mDelayedImeWin != null) {
    618                     mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
    619                 }
    620                 mDelayedImeWin = imeWin;
    621                 imeWin.mWinAnimator.startDelayingAnimationStart();
    622             }
    623             mService.mWaitingForDrawnCallback = () -> {
    624                 mAnimationStartDelayed = false;
    625                 if (mDelayedImeWin != null) {
    626                     mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
    627                 }
    628                 notifyAdjustedForImeChanged(
    629                         adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
    630             };
    631         } else {
    632             notifyAdjustedForImeChanged(
    633                     adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
    634         }
    635     }
    636 
    637     private void setMinimizedDockedStack(boolean minimized) {
    638         final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
    639         notifyDockedStackMinimizedChanged(minimized, 0);
    640         if (stack == null) {
    641             return;
    642         }
    643         if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) {
    644             mService.mWindowPlacerLocked.performSurfacePlacement();
    645         }
    646     }
    647 
    648     private boolean isAnimationMaximizing() {
    649         return mAnimationTarget == 0f;
    650     }
    651 
    652     public boolean animate(long now) {
    653         if (mWindow == null) {
    654             return false;
    655         }
    656         if (mAnimatingForMinimizedDockedStack) {
    657             return animateForMinimizedDockedStack(now);
    658         } else if (mAnimatingForIme) {
    659             return animateForIme(now);
    660         } else {
    661             if (mDimLayer != null && mDimLayer.isDimming()) {
    662                 mDimLayer.setLayer(mService.mLayersController.getResizeDimLayer());
    663             }
    664             return false;
    665         }
    666     }
    667 
    668     private boolean animateForIme(long now) {
    669         if (!mAnimationStarted || mAnimationStartDelayed) {
    670             mAnimationStarted = true;
    671             mAnimationStartTime = now;
    672             mAnimationDuration = (long)
    673                     (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
    674         }
    675         float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
    676         t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
    677                 .getInterpolation(t);
    678         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
    679         boolean updated = false;
    680         for (int i = stacks.size() - 1; i >= 0; --i) {
    681             final TaskStack stack = stacks.get(i);
    682             if (stack != null && stack.isAdjustedForIme()) {
    683                 if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) {
    684                     stack.resetAdjustedForIme(true /* adjustBoundsNow */);
    685                     updated = true;
    686                 } else {
    687                     mLastAnimationProgress = getInterpolatedAnimationValue(t);
    688                     mLastDividerProgress = getInterpolatedDividerValue(t);
    689                     updated |= stack.updateAdjustForIme(
    690                             mLastAnimationProgress,
    691                             mLastDividerProgress,
    692                             false /* force */);
    693                 }
    694                 if (t >= 1f) {
    695                     stack.endImeAdjustAnimation();
    696                 }
    697             }
    698         }
    699         if (updated) {
    700             mService.mWindowPlacerLocked.performSurfacePlacement();
    701         }
    702         if (t >= 1.0f) {
    703             mLastAnimationProgress = mAnimationTarget;
    704             mLastDividerProgress = mDividerAnimationTarget;
    705             mAnimatingForIme = false;
    706             return false;
    707         } else {
    708             return true;
    709         }
    710     }
    711 
    712     private boolean animateForMinimizedDockedStack(long now) {
    713         final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
    714         if (!mAnimationStarted) {
    715             mAnimationStarted = true;
    716             mAnimationStartTime = now;
    717             final long transitionDuration = isAnimationMaximizing()
    718                     ? mService.mAppTransition.getLastClipRevealTransitionDuration()
    719                     : DEFAULT_APP_TRANSITION_DURATION;
    720             mAnimationDuration = (long)
    721                     (transitionDuration * mService.getTransitionAnimationScaleLocked());
    722             mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
    723             notifyDockedStackMinimizedChanged(mMinimizedDock,
    724                     (long) (mAnimationDuration * mMaximizeMeetFraction));
    725         }
    726         float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
    727         t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
    728                 .getInterpolation(t);
    729         if (stack != null) {
    730             if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
    731                 mService.mWindowPlacerLocked.performSurfacePlacement();
    732             }
    733         }
    734         if (t >= 1.0f) {
    735             mAnimatingForMinimizedDockedStack = false;
    736             return false;
    737         } else {
    738             return true;
    739         }
    740     }
    741 
    742     private float getInterpolatedAnimationValue(float t) {
    743         return t * mAnimationTarget + (1 - t) * mAnimationStart;
    744     }
    745 
    746     private float getInterpolatedDividerValue(float t) {
    747         return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
    748     }
    749 
    750     /**
    751      * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
    752      */
    753     private float getMinimizeAmount(TaskStack stack, float t) {
    754         final float naturalAmount = getInterpolatedAnimationValue(t);
    755         if (isAnimationMaximizing()) {
    756             return adjustMaximizeAmount(stack, t, naturalAmount);
    757         } else {
    758             return naturalAmount;
    759         }
    760     }
    761 
    762     /**
    763      * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
    764      * during the transition such that the edge of the clip reveal rect is met earlier in the
    765      * transition so we don't create a visible "hole", but only if both the clip reveal and the
    766      * docked stack divider start from about the same portion on the screen.
    767      */
    768     private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
    769         if (mMaximizeMeetFraction == 1f) {
    770             return naturalAmount;
    771         }
    772         final int minimizeDistance = stack.getMinimizeDistance();
    773         float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
    774                 / (float) minimizeDistance;
    775         final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
    776         final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
    777         return amountPrime * t2 + naturalAmount * (1 - t2);
    778     }
    779 
    780     /**
    781      * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
    782      * edge. See {@link #adjustMaximizeAmount}.
    783      */
    784     private float getClipRevealMeetFraction(TaskStack stack) {
    785         if (!isAnimationMaximizing() || stack == null ||
    786                 !mService.mAppTransition.hadClipRevealAnimation()) {
    787             return 1f;
    788         }
    789         final int minimizeDistance = stack.getMinimizeDistance();
    790         final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
    791                 / (float) minimizeDistance;
    792         final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
    793                 / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
    794         return CLIP_REVEAL_MEET_EARLIEST
    795                 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
    796     }
    797 
    798     @Override
    799     public boolean dimFullscreen() {
    800         return false;
    801     }
    802 
    803     @Override
    804     public DisplayInfo getDisplayInfo() {
    805         return mDisplayContent.getDisplayInfo();
    806     }
    807 
    808     @Override
    809     public void getDimBounds(Rect outBounds) {
    810         // This dim layer user doesn't need this.
    811     }
    812 
    813     @Override
    814     public String toShortString() {
    815         return TAG;
    816     }
    817 
    818     WindowState getWindow() {
    819         return mWindow;
    820     }
    821 
    822     void dump(String prefix, PrintWriter pw) {
    823         pw.println(prefix + "DockedStackDividerController");
    824         pw.println(prefix + "  mLastVisibility=" + mLastVisibility);
    825         pw.println(prefix + "  mMinimizedDock=" + mMinimizedDock);
    826         pw.println(prefix + "  mAdjustedForIme=" + mAdjustedForIme);
    827         pw.println(prefix + "  mAdjustedForDivider=" + mAdjustedForDivider);
    828         if (mDimLayer.isDimming()) {
    829             pw.println(prefix + "  Dim layer is dimming: ");
    830             mDimLayer.printTo(prefix + "    ", pw);
    831         }
    832     }
    833 }
    834