Animate IME adjustment for docked stack through the divider

This makes sure the top stack crop, divider position and bottom stack
surface position moves together with the IME window. Currently the
bottom stack and divider are moving together but top stack crop is
changed immediately.

bug: 27779495
Change-Id: I653ad9093621b218d9c11b0bb2efdddb1d33763e
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6741aba..68ea4df 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -41,6 +41,8 @@
 
 import com.android.server.wm.DimLayer.DimLayerUser;
 
+import java.util.ArrayList;
+
 /**
  * Keeps information about the docked stack divider.
  */
@@ -87,7 +89,7 @@
     private final DimLayer mDimLayer;
 
     private boolean mMinimizedDock;
-    private boolean mAnimating;
+    private boolean mAnimatingForMinimizedDockedStack;
     private boolean mAnimationStarted;
     private long mAnimationStartTime;
     private float mAnimationStart;
@@ -96,7 +98,8 @@
     private final Interpolator mMinimizedDockInterpolator;
     private float mMaximizeMeetFraction;
     private final Rect mTouchRegion = new Rect();
-    private boolean mAdjustingForIme;
+    private boolean mAnimatingForIme;
+    private boolean mAdjustedForIme;
 
     DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
@@ -174,12 +177,11 @@
         return mLastVisibility;
     }
 
-    void setAdjustingForIme(boolean adjusting) {
-        mAdjustingForIme = adjusting;
-    }
-
-    boolean isAdjustingForIme() {
-        return mAdjustingForIme;
+    void setAdjustedForIme(boolean adjusted, boolean animate) {
+        if (mAdjustedForIme != adjusted) {
+            mAnimatingForIme = animate;
+            mAdjustedForIme = adjusted;
+        }
     }
 
     void positionDockedStackedDivider(Rect frame) {
@@ -342,6 +344,7 @@
         }
 
         mMinimizedDock = minimizedDock;
+        mAnimatingForIme = false;
         if (minimizedDock) {
             if (animate) {
                 startAdjustAnimation(0f, 1f);
@@ -358,7 +361,7 @@
     }
 
     private void startAdjustAnimation(float from, float to) {
-        mAnimating = true;
+        mAnimatingForMinimizedDockedStack = true;
         mAnimationStarted = false;
         mAnimationStart = from;
         mAnimationTarget = to;
@@ -380,10 +383,45 @@
     }
 
     public boolean animate(long now) {
-        if (!mAnimating) {
+        if (mAnimatingForMinimizedDockedStack) {
+            return animateForMinimizedDockedStack(now);
+        } else if (mAnimatingForIme) {
+            return animateForIme();
+        } else {
             return false;
         }
+    }
 
+    private boolean animateForIme() {
+        boolean updated = false;
+        boolean animating = false;
+
+        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+        for (int i = stacks.size() - 1; i >= 0; --i) {
+            final TaskStack stack = stacks.get(i);
+            if (stack != null && stack.isAdjustedForIme()) {
+                updated |= stack.updateAdjustForIme();
+                animating |= stack.isAnimatingForIme();
+            }
+        }
+
+        if (updated) {
+            mService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+
+        if (!animating) {
+            mAnimatingForIme = false;
+            for (int i = stacks.size() - 1; i >= 0; --i) {
+                final TaskStack stack = stacks.get(i);
+                if (stack != null) {
+                    stack.clearImeGoingAway();
+                }
+            }
+        }
+        return animating;
+    }
+
+    private boolean animateForMinimizedDockedStack(long now) {
         final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
         if (!mAnimationStarted) {
             mAnimationStarted = true;
@@ -406,7 +444,7 @@
             }
         }
         if (t >= 1.0f) {
-            mAnimating = false;
+            mAnimatingForMinimizedDockedStack = false;
             return false;
         } else {
             return true;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 8d67771..0bf7102 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -55,6 +55,11 @@
     // If the stack should be resized to fullscreen.
     private static final boolean FULLSCREEN = true;
 
+    // When we have a top-bottom split screen, we shift the bottom stack up to accommodate
+    // the IME window. The static flag below controls whether to run animation when the
+    // IME window goes away.
+    private static final boolean ANIMATE_IME_GOING_AWAY = false;
+
     /** Unique identifier */
     final int mStackId;
 
@@ -107,6 +112,7 @@
     private final Rect mLastContentBounds = new Rect();
     private final Rect mTmpAdjustedBounds = new Rect();
     private boolean mAdjustedForIme;
+    private boolean mImeGoingAway;
     private WindowState mImeWin;
     private float mMinimizeAmount;
     private final int mDockedStackMinimizeThickness;
@@ -796,19 +802,54 @@
     void setAdjustedForIme(WindowState imeWin) {
         mAdjustedForIme = true;
         mImeWin = imeWin;
-        if (updateAdjustedBounds()) {
-            getDisplayContent().mDividerControllerLocked.setAdjustingForIme(true);
+        mImeGoingAway = false;
+    }
+
+    boolean isAdjustedForIme() {
+        return mAdjustedForIme || mImeGoingAway;
+    }
+    void clearImeGoingAway() {
+        mImeGoingAway = false;
+    }
+
+    boolean isAnimatingForIme() {
+        return mImeWin != null && mImeWin.isAnimatingLw();
+    }
+
+    /**
+     * Update the stack's bounds (crop or position) according to the IME window's
+     * current position. When IME window is animated, the bottom stack is animated
+     * together to track the IME window's current position, and the top stack is
+     * cropped as necessary.
+     *
+     * @return true if a traversal should be performed after the adjustment.
+     */
+    boolean updateAdjustForIme() {
+        boolean stopped = false;
+        if (mImeGoingAway && (!ANIMATE_IME_GOING_AWAY || !isAnimatingForIme())) {
+            mImeWin = null;
+            mAdjustedForIme = false;
+            stopped = true;
         }
+        // Make sure to run a traversal when the animation stops so that the stack
+        // is moved to its final position.
+        return updateAdjustedBounds() || stopped;
     }
 
     /**
      * Resets the adjustment after it got adjusted for the IME.
+     * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
+     *                        animations; otherwise, set flag and animates the window away together
+     *                        with IME window.
      */
-    void resetAdjustedForIme() {
-        mAdjustedForIme = false;
-        mImeWin = null;
-        if (updateAdjustedBounds()) {
-            getDisplayContent().mDividerControllerLocked.setAdjustingForIme(true);
+    void resetAdjustedForIme(boolean adjustBoundsNow) {
+        if (adjustBoundsNow) {
+            mImeWin = null;
+            mAdjustedForIme = false;
+            mImeGoingAway = false;
+            updateAdjustedBounds();
+        } else {
+            mImeGoingAway |= mAdjustedForIme;
         }
     }
 
@@ -843,6 +884,12 @@
         getDisplayContent().getContentRect(displayContentRect);
         contentBounds.set(displayContentRect);
         int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
+
+        // if IME window is animating, get its actual vertical shown position (but no smaller than
+        // the final target vertical position)
+        if (imeWin.isAnimatingLw()) {
+            imeTop = Math.max(imeTop, imeWin.getShownPositionLw().y);
+        }
         imeTop += imeWin.getGivenContentInsetsLw().top;
         if (contentBounds.bottom > imeTop) {
             contentBounds.bottom = imeTop;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5771d69..d55d3b8 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7380,8 +7380,9 @@
         final WindowState imeWin = mInputMethodWindow;
         final TaskStack focusedStack =
                 mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+        final boolean dockVisible = isStackVisibleLocked(DOCKED_STACK_ID);
         if (imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
-                && isStackVisibleLocked(DOCKED_STACK_ID)
+                && dockVisible
                 && focusedStack != null
                 && focusedStack.getDockSide() == DOCKED_BOTTOM){
             final ArrayList<TaskStack> stacks = displayContent.getStacks();
@@ -7391,12 +7392,14 @@
                     stack.setAdjustedForIme(imeWin);
                 }
             }
+            displayContent.mDividerControllerLocked.setAdjustedForIme(true, true);
         } else {
             final ArrayList<TaskStack> stacks = displayContent.getStacks();
             for (int i = stacks.size() - 1; i >= 0; --i) {
                 final TaskStack stack = stacks.get(i);
-                stack.resetAdjustedForIme();
+                stack.resetAdjustedForIme(!dockVisible);
             }
+            displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 3b0081d..3e5ddbc 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -693,16 +693,14 @@
                     // currently animating... let's do something.
                     final int left = w.mFrame.left;
                     final int top = w.mFrame.top;
-                    final boolean adjustedForMinimizedDockedStack = w.getTask() != null &&
-                            w.getTask().mStack.isAdjustedForMinimizedDockedStack();
+                    final boolean adjustedForMinimizedDockOrIme = task != null
+                                && (task.mStack.isAdjustedForMinimizedDockedStack()
+                                    || task.mStack.isAdjustedForIme());
                     if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
-                            && !w.isDragResizing() && !adjustedForMinimizedDockedStack
+                            && !w.isDragResizing() && !adjustedForMinimizedDockOrIme
                             && (task == null || !w.getTask().mStack.getFreezeMovementAnimations())
                             && !w.mWinAnimator.mLastHidden) {
                         winAnimator.setMoveAnimation(left, top);
-                    } else if (w.mAttrs.type  == TYPE_DOCK_DIVIDER &&
-                            displayContent.getDockedDividerController().isAdjustingForIme()) {
-                        winAnimator.setMoveAnimation(left, top);
                     }
 
                     //TODO (multidisplay): Accessibility supported only for the default display.
@@ -819,8 +817,6 @@
                 mService.updateResizingWindows(w);
             }
 
-            displayContent.getDockedDividerController().setAdjustingForIme(false);
-
             mService.mDisplayManagerInternal.setDisplayProperties(displayId,
                     mDisplayHasContent,
                     mPreferredRefreshRate,