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         }
    431         setMinimizedDockedStack(false, false /* animate */);
    432     }
    433 
    434     /**
    435      * Resets the state that IME hide has been requested. See {@link #isImeHideRequested}.
    436      */
    437     void resetImeHideRequested() {
    438         mImeHideRequested = false;
    439     }
    440 
    441     /**
    442      * The docked stack divider controller makes sure the IME gets hidden when attaching the docked
    443      * stack, to avoid animation problems. This flag indicates whether the request to hide the IME
    444      * has been sent in an asynchronous manner, and the IME should be treated as hidden already.
    445      *
    446      * @return whether IME hide request has been sent
    447      */
    448     boolean isImeHideRequested() {
    449         return mImeHideRequested;
    450     }
    451 
    452     private void notifyDockedStackMinimizedChanged(boolean minimizedDock, boolean animate,
    453             boolean isHomeStackResizable) {
    454         long animDuration = 0;
    455         if (animate) {
    456             final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID);
    457             final long transitionDuration = isAnimationMaximizing()
    458                     ? mService.mAppTransition.getLastClipRevealTransitionDuration()
    459                     : DEFAULT_APP_TRANSITION_DURATION;
    460             mAnimationDuration = (long)
    461                     (transitionDuration * mService.getTransitionAnimationScaleLocked());
    462             mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
    463             animDuration = (long) (mAnimationDuration * mMaximizeMeetFraction);
    464         }
    465         mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
    466         mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
    467                 minimizedDock ? 1 : 0, 0).sendToTarget();
    468         final int size = mDockedStackListeners.beginBroadcast();
    469         for (int i = 0; i < size; ++i) {
    470             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
    471             try {
    472                 listener.onDockedStackMinimizedChanged(minimizedDock, animDuration,
    473                         isHomeStackResizable);
    474             } catch (RemoteException e) {
    475                 Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e);
    476             }
    477         }
    478         mDockedStackListeners.finishBroadcast();
    479     }
    480 
    481     void notifyDockSideChanged(int newDockSide) {
    482         final int size = mDockedStackListeners.beginBroadcast();
    483         for (int i = 0; i < size; ++i) {
    484             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
    485             try {
    486                 listener.onDockSideChanged(newDockSide);
    487             } catch (RemoteException e) {
    488                 Slog.e(TAG_WM, "Error delivering dock side changed event.", e);
    489             }
    490         }
    491         mDockedStackListeners.finishBroadcast();
    492     }
    493 
    494     private void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
    495         final int size = mDockedStackListeners.beginBroadcast();
    496         for (int i = 0; i < size; ++i) {
    497             final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
    498             try {
    499                 listener.onAdjustedForImeChanged(adjustedForIme, animDuration);
    500             } catch (RemoteException e) {
    501                 Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e);
    502             }
    503         }
    504         mDockedStackListeners.finishBroadcast();
    505     }
    506 
    507     void registerDockedStackListener(IDockedStackListener listener) {
    508         mDockedStackListeners.register(listener);
    509         notifyDockedDividerVisibilityChanged(wasVisible());
    510         notifyDockedStackExistsChanged(mDisplayContent.getDockedStackIgnoringVisibility() != null);
    511         notifyDockedStackMinimizedChanged(mMinimizedDock, false /* animate */,
    512                 isHomeStackResizable());
    513         notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
    514 
    515     }
    516 
    517     void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
    518         mService.openSurfaceTransaction();
    519         final TaskStack stack = mDisplayContent.getStackById(targetStackId);
    520         final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
    521         boolean visibleAndValid = visible && stack != null && dockedStack != null;
    522         if (visibleAndValid) {
    523             stack.getDimBounds(mTmpRect);
    524             if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
    525                 mDimLayer.setBounds(mTmpRect);
    526                 mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
    527             } else {
    528                 visibleAndValid = false;
    529             }
    530         }
    531         if (!visibleAndValid) {
    532             mDimLayer.hide();
    533         }
    534         mService.closeSurfaceTransaction();
    535     }
    536 
    537     /**
    538      * @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just
    539      *         above all application surfaces.
    540      */
    541     private int getResizeDimLayer() {
    542         return (mWindow != null) ? mWindow.mLayer - 1 : LAYER_OFFSET_DIM;
    543     }
    544 
    545     /**
    546      * Notifies the docked stack divider controller of a visibility change that happens without
    547      * an animation.
    548      */
    549     void notifyAppVisibilityChanged() {
    550         checkMinimizeChanged(false /* animate */);
    551     }
    552 
    553     void notifyAppTransitionStarting(ArraySet<AppWindowToken> openingApps, int appTransition) {
    554         final boolean wasMinimized = mMinimizedDock;
    555         checkMinimizeChanged(true /* animate */);
    556 
    557         // We were minimized, and now we are still minimized, but somebody is trying to launch an
    558         // app in docked stack, better show recent apps so we actually get unminimized! However do
    559         // not do this if keyguard is dismissed such as when the device is unlocking. This catches
    560         // any case that was missed in ActivityStarter.postStartActivityUncheckedProcessing because
    561         // we couldn't retrace the launch of the app in the docked stack to the launch from
    562         // homescreen.
    563         if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)
    564                 && appTransition != TRANSIT_NONE &&
    565                 !AppTransition.isKeyguardGoingAwayTransit(appTransition)) {
    566             mService.showRecentApps(true /* fromHome */);
    567         }
    568     }
    569 
    570     /**
    571      * @return true if {@param apps} contains an activity in the docked stack, false otherwise.
    572      */
    573     private boolean containsAppInDockedStack(ArraySet<AppWindowToken> apps) {
    574         for (int i = apps.size() - 1; i >= 0; i--) {
    575             final AppWindowToken token = apps.valueAt(i);
    576             if (token.getTask() != null && token.getTask().mStack.mStackId == DOCKED_STACK_ID) {
    577                 return true;
    578             }
    579         }
    580         return false;
    581     }
    582 
    583     boolean isMinimizedDock() {
    584         return mMinimizedDock;
    585     }
    586 
    587     private void checkMinimizeChanged(boolean animate) {
    588         if (mDisplayContent.getDockedStackIgnoringVisibility() == null) {
    589             return;
    590         }
    591         final TaskStack homeStack = mDisplayContent.getHomeStack();
    592         if (homeStack == null) {
    593             return;
    594         }
    595         final Task homeTask = homeStack.findHomeTask();
    596         if (homeTask == null || !isWithinDisplay(homeTask)) {
    597             return;
    598         }
    599 
    600         // Do not minimize when dock is already minimized while keyguard is showing and not
    601         // occluded such as unlocking the screen
    602         if (mMinimizedDock && mService.mPolicy.isKeyguardShowingAndNotOccluded()) {
    603             return;
    604         }
    605         final TaskStack fullscreenStack =
    606                 mDisplayContent.getStackById(FULLSCREEN_WORKSPACE_STACK_ID);
    607         final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
    608         final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisible())
    609                 || (homeStack.hasMultipleTaskWithHomeTaskNotTop());
    610         setMinimizedDockedStack(homeVisible && !homeBehind, animate);
    611     }
    612 
    613     private boolean isWithinDisplay(Task task) {
    614         task.mStack.getBounds(mTmpRect);
    615         mDisplayContent.getLogicalDisplayRect(mTmpRect2);
    616         return mTmpRect.intersect(mTmpRect2);
    617     }
    618 
    619     /**
    620      * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the
    621      * docked stack are heavily clipped so you can only see a minimal peek state.
    622      *
    623      * @param minimizedDock Whether the docked stack is currently minimized.
    624      * @param animate Whether to animate the change.
    625      */
    626     private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
    627         final boolean wasMinimized = mMinimizedDock;
    628         mMinimizedDock = minimizedDock;
    629         if (minimizedDock == wasMinimized) {
    630             return;
    631         }
    632 
    633         final boolean imeChanged = clearImeAdjustAnimation();
    634         boolean minimizedChange = false;
    635         if (isHomeStackResizable()) {
    636             notifyDockedStackMinimizedChanged(minimizedDock, true /* animate */,
    637                     true /* isHomeStackResizable */);
    638             minimizedChange = true;
    639         } else {
    640             if (minimizedDock) {
    641                 if (animate) {
    642                     startAdjustAnimation(0f, 1f);
    643                 } else {
    644                     minimizedChange |= setMinimizedDockedStack(true);
    645                 }
    646             } else {
    647                 if (animate) {
    648                     startAdjustAnimation(1f, 0f);
    649                 } else {
    650                     minimizedChange |= setMinimizedDockedStack(false);
    651                 }
    652             }
    653         }
    654         if (imeChanged || minimizedChange) {
    655             if (imeChanged && !minimizedChange) {
    656                 Slog.d(TAG, "setMinimizedDockedStack: IME adjust changed due to minimizing,"
    657                         + " minimizedDock=" + minimizedDock
    658                         + " minimizedChange=" + minimizedChange);
    659             }
    660             mService.mWindowPlacerLocked.performSurfacePlacement();
    661         }
    662     }
    663 
    664     private boolean clearImeAdjustAnimation() {
    665         final boolean changed = mDisplayContent.clearImeAdjustAnimation();
    666         mAnimatingForIme = false;
    667         return changed;
    668     }
    669 
    670     private void startAdjustAnimation(float from, float to) {
    671         mAnimatingForMinimizedDockedStack = true;
    672         mAnimationStarted = false;
    673         mAnimationStart = from;
    674         mAnimationTarget = to;
    675     }
    676 
    677     private void startImeAdjustAnimation(
    678             boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) {
    679 
    680         // If we're not in an animation, the starting point depends on whether we're adjusted
    681         // or not. If we're already in an animation, we start from where the current animation
    682         // left off, so that the motion doesn't look discontinuous.
    683         if (!mAnimatingForIme) {
    684             mAnimationStart = mAdjustedForIme ? 1 : 0;
    685             mDividerAnimationStart = mAdjustedForDivider ? 1 : 0;
    686             mLastAnimationProgress = mAnimationStart;
    687             mLastDividerProgress = mDividerAnimationStart;
    688         } else {
    689             mAnimationStart = mLastAnimationProgress;
    690             mDividerAnimationStart = mLastDividerProgress;
    691         }
    692         mAnimatingForIme = true;
    693         mAnimationStarted = false;
    694         mAnimationTarget = adjustedForIme ? 1 : 0;
    695         mDividerAnimationTarget = adjustedForDivider ? 1 : 0;
    696 
    697         mDisplayContent.beginImeAdjustAnimation();
    698 
    699         // We put all tasks into drag resizing mode - wait until all of them have completed the
    700         // drag resizing switch.
    701         if (!mService.mWaitingForDrawn.isEmpty()) {
    702             mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
    703             mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
    704                     IME_ADJUST_DRAWN_TIMEOUT);
    705             mAnimationStartDelayed = true;
    706             if (imeWin != null) {
    707 
    708                 // There might be an old window delaying the animation start - clear it.
    709                 if (mDelayedImeWin != null) {
    710                     mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
    711                 }
    712                 mDelayedImeWin = imeWin;
    713                 imeWin.mWinAnimator.startDelayingAnimationStart();
    714             }
    715 
    716             // If we are already waiting for something to be drawn, clear out the old one so it
    717             // still gets executed.
    718             // TODO: Have a real system where we can wait on different windows to be drawn with
    719             // different callbacks.
    720             if (mService.mWaitingForDrawnCallback != null) {
    721                 mService.mWaitingForDrawnCallback.run();
    722             }
    723             mService.mWaitingForDrawnCallback = () -> {
    724                 mAnimationStartDelayed = false;
    725                 if (mDelayedImeWin != null) {
    726                     mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
    727                 }
    728                 // If the adjust status changed since this was posted, only notify
    729                 // the new states and don't animate.
    730                 long duration = 0;
    731                 if (mAdjustedForIme == adjustedForIme
    732                         && mAdjustedForDivider == adjustedForDivider) {
    733                     duration = IME_ADJUST_ANIM_DURATION;
    734                 } else {
    735                     Slog.w(TAG, "IME adjust changed while waiting for drawn:"
    736                             + " adjustedForIme=" + adjustedForIme
    737                             + " adjustedForDivider=" + adjustedForDivider
    738                             + " mAdjustedForIme=" + mAdjustedForIme
    739                             + " mAdjustedForDivider=" + mAdjustedForDivider);
    740                 }
    741                 notifyAdjustedForImeChanged(
    742                         mAdjustedForIme || mAdjustedForDivider, duration);
    743             };
    744         } else {
    745             notifyAdjustedForImeChanged(
    746                     adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
    747         }
    748     }
    749 
    750     private boolean setMinimizedDockedStack(boolean minimized) {
    751         final TaskStack stack = mDisplayContent.getDockedStackIgnoringVisibility();
    752         notifyDockedStackMinimizedChanged(minimized, false /* animate */, isHomeStackResizable());
    753         return stack != null && stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f);
    754     }
    755 
    756     private boolean isAnimationMaximizing() {
    757         return mAnimationTarget == 0f;
    758     }
    759 
    760     public boolean animate(long now) {
    761         if (mWindow == null) {
    762             return false;
    763         }
    764         if (mAnimatingForMinimizedDockedStack) {
    765             return animateForMinimizedDockedStack(now);
    766         } else if (mAnimatingForIme) {
    767             return animateForIme(now);
    768         } else {
    769             if (mDimLayer != null && mDimLayer.isDimming()) {
    770                 mDimLayer.setLayer(getResizeDimLayer());
    771             }
    772             return false;
    773         }
    774     }
    775 
    776     private boolean animateForIme(long now) {
    777         if (!mAnimationStarted || mAnimationStartDelayed) {
    778             mAnimationStarted = true;
    779             mAnimationStartTime = now;
    780             mAnimationDuration = (long)
    781                     (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
    782         }
    783         float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
    784         t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
    785                 .getInterpolation(t);
    786         final boolean updated =
    787                 mDisplayContent.animateForIme(t, mAnimationTarget, mDividerAnimationTarget);
    788         if (updated) {
    789             mService.mWindowPlacerLocked.performSurfacePlacement();
    790         }
    791         if (t >= 1.0f) {
    792             mLastAnimationProgress = mAnimationTarget;
    793             mLastDividerProgress = mDividerAnimationTarget;
    794             mAnimatingForIme = false;
    795             return false;
    796         } else {
    797             return true;
    798         }
    799     }
    800 
    801     private boolean animateForMinimizedDockedStack(long now) {
    802         final TaskStack stack = mDisplayContent.getStackById(DOCKED_STACK_ID);
    803         if (!mAnimationStarted) {
    804             mAnimationStarted = true;
    805             mAnimationStartTime = now;
    806             notifyDockedStackMinimizedChanged(mMinimizedDock, true /* animate */,
    807                     isHomeStackResizable() /* isHomeStackResizable */);
    808         }
    809         float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
    810         t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
    811                 .getInterpolation(t);
    812         if (stack != null) {
    813             if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
    814                 mService.mWindowPlacerLocked.performSurfacePlacement();
    815             }
    816         }
    817         if (t >= 1.0f) {
    818             mAnimatingForMinimizedDockedStack = false;
    819             return false;
    820         } else {
    821             return true;
    822         }
    823     }
    824 
    825     float getInterpolatedAnimationValue(float t) {
    826         return t * mAnimationTarget + (1 - t) * mAnimationStart;
    827     }
    828 
    829     float getInterpolatedDividerValue(float t) {
    830         return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
    831     }
    832 
    833     /**
    834      * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
    835      */
    836     private float getMinimizeAmount(TaskStack stack, float t) {
    837         final float naturalAmount = getInterpolatedAnimationValue(t);
    838         if (isAnimationMaximizing()) {
    839             return adjustMaximizeAmount(stack, t, naturalAmount);
    840         } else {
    841             return naturalAmount;
    842         }
    843     }
    844 
    845     /**
    846      * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
    847      * during the transition such that the edge of the clip reveal rect is met earlier in the
    848      * transition so we don't create a visible "hole", but only if both the clip reveal and the
    849      * docked stack divider start from about the same portion on the screen.
    850      */
    851     private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
    852         if (mMaximizeMeetFraction == 1f) {
    853             return naturalAmount;
    854         }
    855         final int minimizeDistance = stack.getMinimizeDistance();
    856         float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
    857                 / (float) minimizeDistance;
    858         final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
    859         final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
    860         return amountPrime * t2 + naturalAmount * (1 - t2);
    861     }
    862 
    863     /**
    864      * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
    865      * edge. See {@link #adjustMaximizeAmount}.
    866      */
    867     private float getClipRevealMeetFraction(TaskStack stack) {
    868         if (!isAnimationMaximizing() || stack == null ||
    869                 !mService.mAppTransition.hadClipRevealAnimation()) {
    870             return 1f;
    871         }
    872         final int minimizeDistance = stack.getMinimizeDistance();
    873         final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
    874                 / (float) minimizeDistance;
    875         final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
    876                 / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
    877         return CLIP_REVEAL_MEET_EARLIEST
    878                 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
    879     }
    880 
    881     @Override
    882     public boolean dimFullscreen() {
    883         return false;
    884     }
    885 
    886     @Override
    887     public DisplayInfo getDisplayInfo() {
    888         return mDisplayContent.getDisplayInfo();
    889     }
    890 
    891     @Override
    892     public boolean isAttachedToDisplay() {
    893         return mDisplayContent != null;
    894     }
    895 
    896     @Override
    897     public void getDimBounds(Rect outBounds) {
    898         // This dim layer user doesn't need this.
    899     }
    900 
    901     @Override
    902     public String toShortString() {
    903         return TAG;
    904     }
    905 
    906     WindowState getWindow() {
    907         return mWindow;
    908     }
    909 
    910     void dump(String prefix, PrintWriter pw) {
    911         pw.println(prefix + "DockedStackDividerController");
    912         pw.println(prefix + "  mLastVisibility=" + mLastVisibility);
    913         pw.println(prefix + "  mMinimizedDock=" + mMinimizedDock);
    914         pw.println(prefix + "  mAdjustedForIme=" + mAdjustedForIme);
    915         pw.println(prefix + "  mAdjustedForDivider=" + mAdjustedForDivider);
    916         if (mDimLayer.isDimming()) {
    917             pw.println(prefix + "  Dim layer is dimming: ");
    918             mDimLayer.printTo(prefix + "    ", pw);
    919         }
    920     }
    921 }
    922