Finish supporting floating-IME alpha in split-screen

There are a couple situations that cause floating IME:
zero-sized frame due to fullscreenIme and the IME itself
providing a frame that doesn't generate insets.

This CL adds detection for an IME frame that doesn't provide
insets and it also allows the subsidiary IME processers (like
divider) to disable alpha animation. This is necessary because
when divider adjust for ime, there is no content behind the
ime, so we can't have it fade in/out; however, if the divider
doesn't adjust, we want the fade in/out so it matches the
ime behavior when not in split.

Bug: 160915523
Test: Use fullscreen IME as well as making gboard floating.
      Open ime in either top or bottom for both cases.
Change-Id: If6fd192c6e7f843a958d98bf40c4d0866d818394
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
index 9db389e..5ef6b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
@@ -126,18 +126,20 @@
     }
 
     @Override
-    public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
-            boolean imeShouldShow, SurfaceControl.Transaction t) {
+    @ImeAnimationFlags
+    public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
+            boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) {
         mHiddenTop = hiddenTop;
         mShownTop = shownTop;
         mTargetShown = imeShouldShow;
         if (!isDividerVisible()) {
-            return;
+            return 0;
         }
         final boolean splitIsVisible = !getView().isHidden();
         mSecondaryHasFocus = getSecondaryHasFocus(displayId);
         final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
-                && !getLayout().mDisplayLayout.isLandscape() && !mSplits.mDivider.isMinimized();
+                && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
+                && !mSplits.mDivider.isMinimized();
         if (mLastAdjustTop < 0) {
             mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
         } else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) {
@@ -155,7 +157,7 @@
         if (mPaused) {
             mPausedTargetAdjusted = targetAdjusted;
             if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState());
-            return;
+            return (targetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0;
         }
         mTargetAdjusted = targetAdjusted;
         updateDimTargets();
@@ -174,6 +176,7 @@
         } else {
             mAdjustedWhileHidden = true;
         }
+        return (mTargetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0;
     }
 
     private void updateImeAdjustState() {
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 1ce98eb..926f653 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.IntDef;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Point;
@@ -136,12 +137,16 @@
         }
     }
 
-    private void dispatchStartPositioning(int displayId, int hiddenTop, int shownTop,
-            boolean show, SurfaceControl.Transaction t) {
+    @ImePositionProcessor.ImeAnimationFlags
+    private int dispatchStartPositioning(int displayId, int hiddenTop, int shownTop,
+            boolean show, boolean isFloating, SurfaceControl.Transaction t) {
         synchronized (mPositionProcessors) {
+            int flags = 0;
             for (ImePositionProcessor pp : mPositionProcessors) {
-                pp.onImeStartPositioning(displayId, hiddenTop, shownTop, show, t);
+                flags |= pp.onImeStartPositioning(
+                        displayId, hiddenTop, shownTop, show, isFloating, t);
             }
+            return flags;
         }
     }
 
@@ -184,6 +189,7 @@
         int mRotation = Surface.ROTATION_0;
         boolean mImeShowing = false;
         final Rect mImeFrame = new Rect();
+        boolean mAnimateAlpha = true;
 
         PerDisplay(int displayId, int initialRotation) {
             mDisplayId = displayId;
@@ -268,15 +274,29 @@
             return mImeFrame.top + (int) surfaceOffset;
         }
 
+        private boolean calcIsFloating(InsetsSource imeSource) {
+            final Rect frame = imeSource.getFrame();
+            if (frame.height() == 0) {
+                return true;
+            }
+            // Some Floating Input Methods will still report a frame, but the frame is actually
+            // a nav-bar inset created by WM and not part of the IME (despite being reported as
+            // an IME inset). For now, we assume that no non-floating IME will be <= this nav bar
+            // frame height so any reported frame that is <= nav-bar frame height is assumed to
+            // be floating.
+            return frame.height() <= mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId)
+                    .navBarFrameHeight();
+        }
+
         private void startAnimation(final boolean show, final boolean forceRestart) {
             final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
             if (imeSource == null || mImeSourceControl == null) {
                 return;
             }
             final Rect newFrame = imeSource.getFrame();
-            final boolean isFloating = newFrame.height() == 0 && show;
+            final boolean isFloating = calcIsFloating(imeSource) && show;
             if (isFloating) {
-                // This is likely a "floating" or "expanded" IME, so to get animations, just
+                // This is a "floating" or "expanded" IME, so to get animations, just
                 // pretend the ime has some size just below the screen.
                 mImeFrame.set(newFrame);
                 final int floatingInset = (int) (
@@ -329,7 +349,8 @@
                 SurfaceControl.Transaction t = mTransactionPool.acquire();
                 float value = (float) animation.getAnimatedValue();
                 t.setPosition(mImeSourceControl.getLeash(), x, value);
-                final float alpha = isFloating ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
+                final float alpha = (mAnimateAlpha || isFloating)
+                        ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
                 t.setAlpha(mImeSourceControl.getLeash(), alpha);
                 dispatchPositionChanged(mDisplayId, imeTop(value), t);
                 t.apply();
@@ -342,16 +363,18 @@
                 public void onAnimationStart(Animator animation) {
                     SurfaceControl.Transaction t = mTransactionPool.acquire();
                     t.setPosition(mImeSourceControl.getLeash(), x, startY);
-                    final float alpha = isFloating ? (startY - hiddenY) / (shownY - hiddenY) : 1.f;
-                    t.setAlpha(mImeSourceControl.getLeash(), alpha);
                     if (DEBUG) {
                         Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
                                 + imeTop(hiddenY) + "->" + imeTop(shownY)
                                 + " showing:" + (mAnimationDirection == DIRECTION_SHOW));
                     }
-                    dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
-                            imeTop(shownY), mAnimationDirection == DIRECTION_SHOW,
-                            t);
+                    int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
+                            imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t);
+                    mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0;
+                    final float alpha = (mAnimateAlpha || isFloating)
+                            ? (startY - hiddenY) / (shownY - hiddenY)
+                            : 1.f;
+                    t.setAlpha(mImeSourceControl.getLeash(), alpha);
                     if (mAnimationDirection == DIRECTION_SHOW) {
                         t.show(mImeSourceControl.getLeash());
                     }
@@ -414,15 +437,33 @@
      */
     public interface ImePositionProcessor {
         /**
+         * Indicates that ime shouldn't animate alpha. It will always be opaque. Used when stuff
+         * behind the IME shouldn't be visible (for example during split-screen adjustment where
+         * there is nothing behind the ime).
+         */
+        int IME_ANIMATION_NO_ALPHA = 1;
+
+        /** @hide */
+        @IntDef(prefix = { "IME_ANIMATION_" }, value = {
+                IME_ANIMATION_NO_ALPHA,
+        })
+        @interface ImeAnimationFlags {}
+
+        /**
          * Called when the IME position is starting to animate.
          *
          * @param hiddenTop The y position of the top of the IME surface when it is hidden.
          * @param shownTop  The y position of the top of the IME surface when it is shown.
          * @param showing   {@code true} when we are animating from hidden to shown, {@code false}
          *                  when animating from shown to hidden.
+         * @param isFloating {@code true} when the ime is a floating ime (doesn't inset).
+         * @return flags that may alter how ime itself is animated (eg. no-alpha).
          */
-        default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
-                boolean showing, SurfaceControl.Transaction t) {}
+        @ImeAnimationFlags
+        default int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
+                boolean showing, boolean isFloating, SurfaceControl.Transaction t) {
+            return 0;
+        }
 
         /**
          * Called when the ime position changed. This is expected to be a synchronous call on the
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
index cfec1c0..a341f305 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
@@ -79,6 +79,7 @@
     private final Rect mStableInsets = new Rect();
     private boolean mHasNavigationBar = false;
     private boolean mHasStatusBar = false;
+    private int mNavBarFrameHeight = 0;
 
     /**
      * Create empty layout.
@@ -146,6 +147,7 @@
         if (mHasStatusBar) {
             convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar);
         }
+        mNavBarFrameHeight = getNavigationBarFrameHeight(res, mWidth > mHeight);
     }
 
     /**
@@ -214,6 +216,11 @@
         return mWidth > mHeight;
     }
 
+    /** Get the navbar frame height (used by ime). */
+    public int navBarFrameHeight() {
+        return mNavBarFrameHeight;
+    }
+
     /** Gets the orientation of this layout */
     public int getOrientation() {
         return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
@@ -483,6 +490,7 @@
             } else {
                 return res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode);
             }
+
         } else {
             if (navBarSide == NAV_BAR_BOTTOM) {
                 return res.getDimensionPixelSize(landscape
@@ -493,4 +501,11 @@
             }
         }
     }
+
+    /** @see com.android.server.wm.DisplayPolicy#getNavigationBarFrameHeight */
+    public static int getNavigationBarFrameHeight(Resources res, boolean landscape) {
+        return res.getDimensionPixelSize(landscape
+                ? R.dimen.navigation_bar_frame_height_landscape
+                : R.dimen.navigation_bar_frame_height);
+    }
 }