Add nice animation when adjusting for IME in multi-window

- Run a separate animation when we need to adjust for the IME. We
can't use the attached animation because the adjustment animation
needs to be longer than the IME animation.
- Also run an animation when IME is disappearing.
- Adjust IME exit animation to better match with adjustment exit
animation.
- Make sure to update adjust for IME when entry/exit animation of
IME is starting, to avoid flickers.
- Don't update the IME window in PhoneWindowManager for layout
until the animation has started. This lead to an issue where the
content inset was set too large too early.

Bug: 27154882
Bug: 28175599
Change-Id: I09a33413e307f84d6c3cf4ae928c280f7ad48348
diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml
index 117774a..575404d 100644
--- a/core/res/res/anim/input_method_exit.xml
+++ b/core/res/res/anim/input_method_exit.xml
@@ -17,10 +17,10 @@
 -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
 	android:shareInterpolator="false">
-    <translate android:fromYDelta="0" android:toYDelta="10%"
-			android:interpolator="@interpolator/accelerate_quint"
-            android:duration="@android:integer/config_shortAnimTime"/>
+    <translate android:fromYDelta="0" android:toYDelta="8%"
+			android:interpolator="@interpolator/fast_out_linear_in"
+            android:duration="150"/>
     <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-			android:interpolator="@interpolator/accelerate_cubic"
-            android:duration="@android:integer/config_shortAnimTime"/>
+			android:interpolator="@interpolator/fast_out_linear_in"
+            android:duration="150"/>
 </set>
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2e81132..8f259db 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4754,7 +4754,7 @@
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
         if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleOrBehindKeyguardLw()
-                && !win.getGivenInsetsPendingLw()) {
+                && win.isDisplayedLw() && !win.getGivenInsetsPendingLw()) {
             setLastInputMethodWindowLw(null, null);
             offsetInputMethodWindowLw(win);
         }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ff537be..7d76759 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -38,6 +38,7 @@
 import android.view.SurfaceControl;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
 
 import com.android.server.wm.DimLayer.DimLayerUser;
 
@@ -74,6 +75,11 @@
      */
     private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
 
+    private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR =
+            new PathInterpolator(0.1f, 0f, 0.1f, 1f);
+
+    private static final long IME_ADJUST_DURATION = 280;
+
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
     private int mDividerWindowWidth;
@@ -188,8 +194,10 @@
 
     void setAdjustedForIme(boolean adjusted, boolean animate) {
         if (mAdjustedForIme != adjusted) {
-            mAnimatingForIme = animate;
             mAdjustedForIme = adjusted;
+            if (animate) {
+                startImeAdjustAnimation(adjusted ? 0 : 1, adjusted ? 1 : 0);
+            }
         }
     }
 
@@ -371,7 +379,7 @@
             return;
         }
 
-        mAnimatingForIme = false;
+        clearImeAdjustAnimation();
         if (minimizedDock) {
             if (animate) {
                 startAdjustAnimation(0f, 1f);
@@ -387,6 +395,17 @@
         }
     }
 
+    private void clearImeAdjustAnimation() {
+        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()) {
+                stack.resetAdjustedForIme(true /* adjustBoundsNow */);
+            }
+        }
+        mAnimatingForIme = false;
+    }
+
     private void startAdjustAnimation(float from, float to) {
         mAnimatingForMinimizedDockedStack = true;
         mAnimationStarted = false;
@@ -394,6 +413,13 @@
         mAnimationTarget = to;
     }
 
+    private void startImeAdjustAnimation(float from, float to) {
+        mAnimatingForIme = true;
+        mAnimationStarted = false;
+        mAnimationStart = from;
+        mAnimationTarget = to;
+    }
+
     private void setMinimizedDockedStack(boolean minimized) {
         final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
         notifyDockedStackMinimizedChanged(minimized, 0);
@@ -413,39 +439,44 @@
         if (mAnimatingForMinimizedDockedStack) {
             return animateForMinimizedDockedStack(now);
         } else if (mAnimatingForIme) {
-            return animateForIme();
+            return animateForIme(now);
         } else {
             return false;
         }
     }
 
-    private boolean animateForIme() {
-        boolean updated = false;
-        boolean animating = false;
-
+    private boolean animateForIme(long now) {
+        if (!mAnimationStarted) {
+            mAnimationStarted = true;
+            mAnimationStartTime = now;
+            mAnimationDuration = (long)
+                    (IME_ADJUST_DURATION * mService.getWindowAnimationScaleLocked());
+        }
+        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
+        t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
+                .getInterpolation(t);
         final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+        boolean updated = false;
         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();
+                if (t >= 1f && mAnimationTarget == 0f) {
+                    stack.resetAdjustedForIme(true /* adjustBoundsNow */);
+                    updated = true;
+                } else {
+                    updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t));
                 }
             }
         }
-        return animating;
+        if (updated) {
+            mService.mWindowPlacerLocked.performSurfacePlacement();
+        }
+        if (t >= 1.0f) {
+            mAnimatingForIme = false;
+            return false;
+        } else {
+            return true;
+        }
     }
 
     private boolean animateForMinimizedDockedStack(long now) {
@@ -478,11 +509,15 @@
         }
     }
 
+    private float getInterpolatedAnimationValue(float t) {
+        return t * mAnimationTarget + (1 - t) * mAnimationStart;
+    }
+
     /**
      * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
      */
     private float getMinimizeAmount(TaskStack stack, float t) {
-        final float naturalAmount = t * mAnimationTarget + (1 - t) * mAnimationStart;
+        final float naturalAmount = getInterpolatedAnimationValue(t);
         if (isAnimationMaximizing()) {
             return adjustMaximizeAmount(stack, t, naturalAmount);
         } else {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 7074a83..0a08a54 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -40,6 +40,7 @@
 import android.util.SparseArray;
 import android.view.DisplayInfo;
 import android.view.Surface;
+import android.view.animation.PathInterpolator;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -52,14 +53,6 @@
 public class TaskStack implements DimLayer.DimLayerUser,
         BoundsAnimationController.AnimateBoundsUser {
 
-    // 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;
 
@@ -83,6 +76,12 @@
     /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
     private final Rect mAdjustedBounds = new Rect();
 
+    /**
+     * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
+     * represent the state when the animation has ended.
+     */
+    private final Rect mFullyAdjustedImeBounds = new Rect();
+
     /** Whether mBounds is fullscreen */
     private boolean mFullscreen = true;
 
@@ -118,6 +117,7 @@
     private boolean mImeGoingAway;
     private WindowState mImeWin;
     private float mMinimizeAmount;
+    private float mAdjustImeAmount;
     private final int mDockedStackMinimizeThickness;
 
     // If this is true, the task will be down or upscaled
@@ -211,21 +211,26 @@
      * the normal task bounds.
      *
      * @param bounds The adjusted bounds.
-     * @param keepInsets Whether to keep the insets from the original bounds or to calculate new
-     *                   ones depending on the adjusted bounds.
-     * @return true if the adjusted bounds has changed.
      */
-    private boolean setAdjustedBounds(Rect bounds, boolean keepInsets) {
-        if (mAdjustedBounds.equals(bounds)) {
-            return false;
+    private void setAdjustedBounds(Rect bounds) {
+        if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
+            return;
         }
 
         mAdjustedBounds.set(bounds);
         final boolean adjusted = !mAdjustedBounds.isEmpty();
-        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds,
-                adjusted && keepInsets ? mBounds : null);
+        Rect insetBounds = null;
+        if (adjusted && isAdjustedForMinimizedDock()) {
+            insetBounds = mBounds;
+        } else if (adjusted && isAdjustedForIme()) {
+            if (mImeGoingAway) {
+                insetBounds = mBounds;
+            } else {
+                insetBounds = mFullyAdjustedImeBounds;
+            }
+        }
+        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
         mDisplayContent.layoutNeeded = true;
-        return true;
     }
 
     private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
@@ -829,6 +834,7 @@
      */
     void setAdjustedForIme(WindowState imeWin) {
         mAdjustedForIme = true;
+        mAdjustImeAmount = 0f;
         mImeWin = imeWin;
         mImeGoingAway = false;
     }
@@ -836,9 +842,6 @@
     boolean isAdjustedForIme() {
         return mAdjustedForIme || mImeGoingAway;
     }
-    void clearImeGoingAway() {
-        mImeGoingAway = false;
-    }
 
     boolean isAnimatingForIme() {
         return mImeWin != null && mImeWin.isAnimatingLw();
@@ -852,16 +855,14 @@
      *
      * @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;
+    boolean updateAdjustForIme(float adjustAmount) {
+        if (adjustAmount != mAdjustImeAmount) {
+            mAdjustImeAmount = adjustAmount;
+            updateAdjustedBounds();
+            return isVisibleForUserLocked();
+        } else {
+            return false;
         }
-        // Make sure to run a traversal when the animation stops so that the stack
-        // is moved to its final position.
-        return updateAdjustedBounds() || stopped;
     }
 
     /**
@@ -875,6 +876,7 @@
             mImeWin = null;
             mAdjustedForIme = false;
             mImeGoingAway = false;
+            mAdjustImeAmount = 0f;
             updateAdjustedBounds();
         } else {
             mImeGoingAway |= mAdjustedForIme;
@@ -904,7 +906,6 @@
     private boolean adjustForIME(final WindowState imeWin) {
         final int dockedSide = getDockSide();
         final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
-        final Rect adjustedBounds = mTmpAdjustedBounds;
         if (imeWin == null || !dockedTopOrBottom) {
             return false;
         }
@@ -917,41 +918,38 @@
         contentBounds.set(displayContentRect);
         int imeTop = Math.max(imeWin.getFrameLw().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;
         }
 
-        // If content bounds not changing, nothing to do.
-        if (mLastContentBounds.equals(contentBounds)) {
-            return true;
-        }
-
-        // Content bounds changed, need to apply adjustments depending on dock sides.
         mLastContentBounds.set(contentBounds);
-        adjustedBounds.set(mBounds);
         final int yOffset = displayContentRect.bottom - contentBounds.bottom;
 
         if (dockedSide == DOCKED_TOP) {
             // If this stack is docked on top, we make it smaller so the bottom stack is not
             // occluded by IME. We shift its bottom up by the height of the IME (capped by
             // the display content rect). Note that we don't change the task bounds.
-            adjustedBounds.bottom = Math.max(
-                    adjustedBounds.bottom - yOffset, displayContentRect.top);
+            int bottom = Math.max(
+                    mBounds.bottom - yOffset, displayContentRect.top);
+            mTmpAdjustedBounds.set(mBounds);
+            mTmpAdjustedBounds.bottom =
+                    (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
+            mFullyAdjustedImeBounds.set(mBounds);
         } else {
             // If this stack is docked on bottom, we shift it up so that it's not occluded by
             // IME. We try to move it up by the height of the IME window (although the best
             // we could do is to make the top stack fully collapsed).
             final int dividerWidth = getDisplayContent().mDividerControllerLocked
                     .getContentWidth();
-            adjustedBounds.top = Math.max(
-                    adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth);
-            adjustedBounds.bottom = adjustedBounds.top + mBounds.height();
+            int top = Math.max(mBounds.top - yOffset, displayContentRect.top + dividerWidth);
+            mTmpAdjustedBounds.set(mBounds);
+            mTmpAdjustedBounds.top =
+                    (int) (mAdjustImeAmount * top + (1 - mAdjustImeAmount) * mBounds.top);
+            mTmpAdjustedBounds.bottom = mTmpAdjustedBounds.top + mBounds.height();
+            mFullyAdjustedImeBounds.set(mBounds);
+            mFullyAdjustedImeBounds.top = top;
+            mFullyAdjustedImeBounds.bottom = top + mBounds.height();
         }
         return true;
     }
@@ -1007,7 +1005,7 @@
     /**
      * Updates the adjustment depending on it's current state.
      */
-    boolean updateAdjustedBounds() {
+    void updateAdjustedBounds() {
         boolean adjust = false;
         if (mMinimizeAmount != 0f) {
             adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
@@ -1018,7 +1016,7 @@
             mTmpAdjustedBounds.setEmpty();
             mLastContentBounds.setEmpty();
         }
-        return setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack());
+        setAdjustedBounds(mTmpAdjustedBounds);
     }
 
     boolean isAdjustedForMinimizedDockedStack() {
@@ -1030,6 +1028,9 @@
         pw.println(prefix + "mDeferDetach=" + mDeferDetach);
         pw.println(prefix + "mFullscreen=" + mFullscreen);
         pw.println(prefix + "mBounds=" + mBounds.toShortString());
+        if (!mAdjustedBounds.isEmpty()) {
+            pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
+        }
         for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) {
             mTasks.get(taskNdx).dump(prefix + "  ", pw);
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e325d6f..a4d5c95 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7443,7 +7443,7 @@
         }
     }
 
-    private void adjustForImeIfNeeded(final DisplayContent displayContent) {
+    void adjustForImeIfNeeded(final DisplayContent displayContent) {
         final WindowState imeWin = mInputMethodWindow;
         final TaskStack focusedStack =
                 mCurrentFocus != null ? mCurrentFocus.getStack() : null;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 329cbbd..db8f9bd 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -21,6 +21,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
@@ -1759,7 +1760,9 @@
         } else {
             clearAnimation();
         }
-
+        if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
+            mService.adjustForImeIfNeeded(mWin.mDisplayContent);
+        }
         return mAnimation != null;
     }