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