Avoid FloatingToolbar flickers

by:
 1. Restricting 'moving hide' -- where we hide the toolbar if the
    toolbar is moving.
 2. Hide the toolbar when transitioning to 'select all' -- where the
    toolbar is refreshed.

Bug: 32910217
Bug: 30418276
Test: bit FrameworksCoreTests:android.widget.TextViewActivityTest
bit CtsWidgetTestCases:android.widget.cts.TextViewTest

Change-Id: I1f44ee765d74bbcf08e6e7cd635f76d1e8f6305b
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index f21545f..04a8265 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1387,7 +1387,7 @@
         if (mTextActionMode != null) {
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_MOVE:
-                    hideFloatingToolbar();
+                    hideFloatingToolbar(ActionMode.DEFAULT_HIDE_DURATION);
                     break;
                 case MotionEvent.ACTION_UP:  // fall through
                 case MotionEvent.ACTION_CANCEL:
@@ -1396,10 +1396,10 @@
         }
     }
 
-    private void hideFloatingToolbar() {
+    void hideFloatingToolbar(int duration) {
         if (mTextActionMode != null) {
             mTextView.removeCallbacks(mShowFloatingToolbar);
-            mTextActionMode.hide(ActionMode.DEFAULT_HIDE_DURATION);
+            mTextActionMode.hide(duration);
         }
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9a92489..d277616 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -374,6 +374,8 @@
     private static final int KEY_DOWN_HANDLED_BY_KEY_LISTENER = 1;
     private static final int KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD = 2;
 
+    private static final int FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY = 500;
+
     // System wide time for last cut, copy or text changed action.
     static long sLastCutCopyOrTextChangedTime;
 
@@ -11138,6 +11140,10 @@
     }
 
     boolean selectAllText() {
+        if (mEditor != null) {
+            // Hide the toolbar before changing the selection to avoid flickering.
+            mEditor.hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
+        }
         final int length = mText.length();
         Selection.setSelection((Spannable) mText, 0, length);
         return length > 0;
diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java
index ff211b6..497e7b0 100644
--- a/core/java/com/android/internal/view/FloatingActionMode.java
+++ b/core/java/com/android/internal/view/FloatingActionMode.java
@@ -295,6 +295,8 @@
      */
     private static final class FloatingToolbarVisibilityHelper {
 
+        private static final long MIN_SHOW_DURATION_FOR_MOVE_HIDE = 500;
+
         private final FloatingToolbar mToolbar;
 
         private boolean mHideRequested;
@@ -304,6 +306,8 @@
 
         private boolean mActive;
 
+        private long mLastShowTime;
+
         public FloatingToolbarVisibilityHelper(FloatingToolbar toolbar) {
             mToolbar = Preconditions.checkNotNull(toolbar);
         }
@@ -327,7 +331,13 @@
         }
 
         public void setMoving(boolean moving) {
-            mMoving = moving;
+            // Avoid unintended flickering by allowing the toolbar to show long enough before
+            // triggering the 'moving' flag - which signals a hide.
+            final boolean showingLongEnough =
+                System.currentTimeMillis() - mLastShowTime > MIN_SHOW_DURATION_FOR_MOVE_HIDE;
+            if (!moving || showingLongEnough) {
+                mMoving = moving;
+            }
         }
 
         public void setOutOfBounds(boolean outOfBounds) {
@@ -347,6 +357,7 @@
                 mToolbar.hide();
             } else {
                 mToolbar.show();
+                mLastShowTime = System.currentTimeMillis();
             }
         }
     }