IME bounds after DOWN->UP Motion event sequence

Bug: 170739594

Change-Id: I1aa8dd2ced7c017e57d3c14c02d5b78900a46caa
diff --git a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
index d7af5f1..08d573f 100644
--- a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
@@ -29,13 +29,15 @@
 import androidx.annotation.RequiresApi;
 import androidx.core.os.BuildCompat;
 
+import com.android.launcher3.util.UiThreadHelper;
+
 /**
  * Handles IME over all apps to be synchronously transitioning along with the passed in
  * root inset.
  */
 public class AllAppsInsetTransitionController {
 
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
     private static final String TAG = "AllAppsInsetTransitionController";
     private static final Interpolator LINEAR = new LinearInterpolator();
 
@@ -53,6 +55,13 @@
     private float mDown, mCurrent;
     private View mApps;
 
+    // Only purpose of these states is to keep track of fast fling transition
+    enum State {
+        RESET, DRAG_START_BOTTOM, FLING_END_TOP,
+        DRAG_START_TOP, FLING_END_BOTTOM
+    }
+    private State mState;
+
     public AllAppsInsetTransitionController(float allAppsHeight, View appsView) {
         mAllAppsHeight = allAppsHeight;
         mApps = appsView;
@@ -64,6 +73,11 @@
         WindowInsets insets = mApps.getRootWindowInsets();
         if (insets == null) return;
 
+        boolean imeVisible = insets.isVisible(WindowInsets.Type.ime());
+
+        if (DEBUG) {
+            Log.d(TAG, "\nhide imeVisible=" +  imeVisible);
+        }
         if (insets.isVisible(WindowInsets.Type.ime())) {
             mApps.getWindowInsetsController().hide(WindowInsets.Type.ime());
         }
@@ -78,20 +92,23 @@
     @RequiresApi(api = Build.VERSION_CODES.R)
     public void onDragStart(float progress) {
         if (!BuildCompat.isAtLeastR()) return;
-        onAnimationEnd(progress);
 
+        // Until getRootWindowInsets().isVisible(...) method returns correct value,
+        // only support InsetController based IME transition during swipe up and
+        // NOT swipe down
+        if (Float.compare(progress, 0f) == 0) return;
+
+        setState(true, false, progress);
         mDown = progress * mAllAppsHeight;
 
         // Below two values are sometimes incorrect. Possibly a platform bug
-        mDownInsetBottom = mApps.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
-        mShownAtDown = mApps.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+        // mDownInsetBottom = mApps.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
+        // mShownAtDown = mApps.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
 
-        // override this value based on what it should actually be.
-        mShownAtDown = Float.compare(progress, 1f) == 0 ? false : true;
-        mDownInsetBottom = mShownAtDown ? mShownInsetBottom : mHiddenInsetBottom;
         if (DEBUG) {
-            Log.d(TAG, "\nonDragStart mDownInsets=" + mDownInsetBottom
-                    + " mShownAtDown =" + mShownAtDown);
+            Log.d(TAG, "\nonDragStart progress=" +  progress
+                    + " mDownInsets=" + mDownInsetBottom
+                    + " mShownAtDown=" + mShownAtDown);
         }
 
         mApps.getWindowInsetsController().controlWindowInsetsAnimation(
@@ -103,37 +120,63 @@
                         if (DEBUG) {
                             Log.d(TAG, "Listener.onReady " + (mCurrentRequest == this));
                         }
-                        if (mCurrentRequest == this) {
-                            mAnimationController = controller;
-                        } else {
-                            controller.finish(mShownAtDown);
+                        if (controller != null) {
+                            if (mCurrentRequest == this && !handleFinishOnFling(controller)) {
+                                    mAnimationController = controller;
+                            } else {
+                                controller.finish(false /* just don't show */);
+                            }
                         }
                     }
 
                     @Override
                     public void onFinished(WindowInsetsAnimationController controller) {
                         // when screen lock happens, then this method get called
-                        mAnimationController.finish(false);
-                        mAnimationController = null;
                         if (DEBUG) {
-                            Log.d(TAG, "Listener.onFinished ctrl=" + controller);
+                            Log.d(TAG, "Listener.onFinished ctrl=" + controller
+                                    + " mAnimationController=" + mAnimationController);
+                        }
+                        if (mAnimationController != null) {
+                            mAnimationController.finish(true);
+                            mAnimationController = null;
                         }
                     }
 
                     @Override
                     public void onCancelled(@Nullable WindowInsetsAnimationController controller) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Listener.onCancelled ctrl=" + controller
+                                    + " mAnimationController=" + mAnimationController);
+                        }
+                        if (mState == State.DRAG_START_BOTTOM) {
+                            mApps.getWindowInsetsController().show(WindowInsets.Type.ime());
+                        }
                         mAnimationController = null;
                         if (controller != null) {
-                            controller.finish(mShownAtDown);
+                            controller.finish(true);
                         }
-                        if (DEBUG) {
-                            Log.d(TAG, "Listener.onCancelled ctrl=" + controller);
-                        }
+
                     }
                 });
     }
 
     /**
+     * If IME bounds after touch sequence finishes, call finish.
+     */
+    private boolean handleFinishOnFling(WindowInsetsAnimationController controller) {
+        if (!BuildCompat.isAtLeastR()) return false;
+
+        if (mState == State.FLING_END_TOP) {
+            controller.finish(true);
+            return true;
+        } else if (mState == State.FLING_END_BOTTOM) {
+            controller.finish(false);
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Handles the translation using the progress.
      *
      * @param progress value between 0..1
@@ -153,18 +196,17 @@
 
         int inset = (int) (mDownInsetBottom + (mDown - mCurrent) - shift);
 
-        if (DEBUG) {
-            Log.d(TAG, "updateInset mCurrent=" + mCurrent + " mDown="
-                    + mDown + " hidden=" + mHiddenInsetBottom
-                    + " shown=" + mShownInsetBottom
-                    + " mDownInsets.bottom=" + mDownInsetBottom + " inset:" + inset
-                    + " shift: " + shift);
-        }
         final int start = mShownAtDown ? mShownInsetBottom : mHiddenInsetBottom;
         final int end = mShownAtDown ? mHiddenInsetBottom : mShownInsetBottom;
         inset = Math.max(inset, mHiddenInsetBottom);
         inset = Math.min(inset, mShownInsetBottom);
-        Log.d(TAG, "updateInset inset:" + inset);
+        if (DEBUG || false) {
+            Log.d(TAG, "updateInset mCurrent=" + mCurrent + " mDown="
+                    + mDown + " hidden=" + mHiddenInsetBottom
+                    + " shown=" + mShownInsetBottom
+                    + " mDownInsets.bottom=" + mDownInsetBottom + " inset=" + inset
+                    + " shift= " + shift);
+        }
 
         mAnimationController.setInsetsAndAlpha(
                 Insets.of(0, 0, 0, inset),
@@ -179,19 +221,60 @@
     @RequiresApi(api = 30)
     public void onAnimationEnd(float progress) {
         if (DEBUG) {
+            Log.d(TAG, "onAnimationEnd progress=" + progress
+                    + " mAnimationController=" + mAnimationController);
+        }
+        if (mState == null) {
+            // only called when launcher restarting.
+            UiThreadHelper.hideKeyboardAsync(mApps.getContext(), mApps.getWindowToken());
+        }
+        setState(false, true, progress);
+        if (mAnimationController == null) {
+            return;
+        }
+
+        /* handle finish */
+        if (mState == State.FLING_END_TOP) {
+            mAnimationController.finish(true /* show */);
+        } else {
+            if (Float.compare(progress, 1f) == 0 /* bottom */) {
+                mAnimationController.finish(false /* gone */);
+            } else {
+                mAnimationController.finish(mShownAtDown);
+            }
+        }
+        /* handle finish */
+
+        if (DEBUG) {
             Log.d(TAG, "endTranslation progress=" + progress
                     + " mAnimationController=" + mAnimationController);
         }
-
-        if (mAnimationController == null) return;
-
-        if (Float.compare(progress, 1f) == 0 /* bottom */) {
-            mAnimationController.finish(false /* gone */);
-        }
-        if (Float.compare(progress, 0f) == 0 /* top */) {
-            mAnimationController.finish(true /* show */);
-        }
         mAnimationController = null;
         mCurrentRequest = null;
+        setState(false, false, progress);
+    }
+
+    private void setState(boolean start, boolean end, float progress) {
+        State state = State.RESET;
+        if (start && end) {
+            throw new IllegalStateException("drag start and end cannot happen in same call");
+        }
+        if (start) {
+            if (Float.compare(progress, 1f) == 0) {
+                state = State.DRAG_START_BOTTOM;
+            } else if (Float.compare(progress, 0f) == 0) {
+                state = State.DRAG_START_TOP;
+            }
+        } else if (end) {
+            if (Float.compare(progress, 1f) == 0 && mState == State.DRAG_START_TOP) {
+                state = State.FLING_END_BOTTOM;
+            } else if (Float.compare(progress, 0f) == 0 && mState == State.DRAG_START_BOTTOM) {
+                state = State.FLING_END_TOP;
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "setState " + mState + " -> " + state);
+        }
+        mState = state;
     }
 }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 9fd53e2..57c0ad9 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -266,7 +266,7 @@
         if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && BuildCompat.isAtLeastR()
                 && (mToState == ALL_APPS || mToState == NORMAL)) {
             mLauncher.getAllAppsController().getInsetController().onDragStart(
-                    mToState == ALL_APPS ? 0 : 1);
+                    mFromState == NORMAL ? 1f : 0f);
         }
     }