Optionally schedule a delayed deletion of suggestions in sample keyboard

Test: manual
Bug: 157515522
Change-Id: Ia5517e8caff538fcc21bf69f15370a3bb2aea5de
diff --git a/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/AutofillImeService.java b/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/AutofillImeService.java
index 7b7971f..32f765a 100644
--- a/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/AutofillImeService.java
+++ b/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/AutofillImeService.java
@@ -102,6 +102,9 @@
                 Toast.LENGTH_SHORT).show();
     };
 
+    private ResponseState mResponseState = ResponseState.RESET;
+    private Runnable mDelayedDeletion;
+
     @Override
     public View onCreateInputView() {
         mInputView = (InputView) LayoutInflater.from(this).inflate(R.layout.input_view, null);
@@ -122,21 +125,51 @@
         if(mKeyboard != null) {
             mKeyboard.reset();
         }
-        if (mInputView != null) {
+        if (mResponseState == ResponseState.FINISH_INPUT) {
+            mResponseState = ResponseState.START_INPUT;
+        } else {
+            mResponseState = ResponseState.RESET;
+        }
+    }
+
+    @Override
+    public void onFinishInput() {
+        super.onFinishInput();
+        if (mResponseState == ResponseState.RECEIVE_RESPONSE) {
+            mResponseState = ResponseState.FINISH_INPUT;
+        } else {
+            mResponseState = ResponseState.RESET;
+        }
+    }
+
+    private void cancelDelayedDeletion(String msg) {
+        if(mDelayedDeletion != null) {
+            Log.d(TAG, msg + " canceling delayed deletion");
+            mHandler.removeCallbacks(mDelayedDeletion);
+            mDelayedDeletion = null;
+        }
+    }
+
+    private void scheduleDelayedDeletion() {
+        if (mInputView != null && mDelayedDeletion == null) {
             // We delay the deletion of the suggestions from previous input connection, to avoid
             // the flicker caused by deleting them and immediately showing new suggestions for
             // the current input connection.
-            Log.d(TAG, "onStartInput scheduling a delayed deletion of inline suggestions");
+            Log.d(TAG, "Scheduling a delayed deletion of inline suggestions");
             mDelayedDeletion = () -> {
-                Log.d(TAG, "onStartInput deleting inline suggestions");
+                Log.d(TAG, "Executing scheduled deleting inline suggestions");
                 mDelayedDeletion = null;
-                updateInlineSuggestionStrip(Collections.emptyList());
+                clearInlineSuggestionStrip();
             };
             mHandler.postDelayed(mDelayedDeletion, 200);
         }
     }
 
-    private Runnable mDelayedDeletion;
+    private void clearInlineSuggestionStrip() {
+        if (mInputView != null) {
+            updateInlineSuggestionStrip(Collections.emptyList());
+        }
+    }
 
     @Override
     public void onStartInputView(EditorInfo info, boolean restarting) {
@@ -153,7 +186,7 @@
             // called. If the framework is changed to not resend, then we need to cache the
             // inline suggestion views locally and re-attach them when the IME is shown again by
             // onStartInputView.
-            updateInlineSuggestionStrip(Collections.emptyList());
+            clearInlineSuggestionStrip();
         }
     }
 
@@ -227,22 +260,29 @@
 
     @Override
     public boolean onInlineSuggestionsResponse(InlineSuggestionsResponse response) {
-        Log.d(TAG, "onInlineSuggestionsResponse() called");
-        if(mDelayedDeletion != null) {
-            Log.d(TAG, "onInlineSuggestionsResponse unscheduling delayed deletion");
-            mHandler.removeCallbacks(mDelayedDeletion);
-        }
-        onInlineSuggestionsResponseInternal(response);
+        Log.d(TAG,
+                "onInlineSuggestionsResponse() called: " + response.getInlineSuggestions().size());
+        cancelDelayedDeletion("onInlineSuggestionsResponse");
+        final List<InlineSuggestion> inlineSuggestions = response.getInlineSuggestions();
+        mResponseState = ResponseState.RECEIVE_RESPONSE;
+        mHandler.post(() -> {
+            if (mResponseState == ResponseState.START_INPUT && inlineSuggestions.isEmpty()) {
+                scheduleDelayedDeletion();
+            } else {
+                inflateThenShowSuggestions(inlineSuggestions);
+            }
+            mResponseState = ResponseState.RESET;
+        });
         return true;
     }
 
     private void updateInlineSuggestionStrip(List<SuggestionItem> suggestionItems) {
+        Log.d(TAG, "Actually updating the suggestion strip: " + suggestionItems.size());
         mPinnedSuggestionsStart.removeAllViews();
         mScrollableSuggestions.removeAllViews();
         mPinnedSuggestionsEnd.removeAllViews();
 
-        final int size = suggestionItems.size();
-        if (size <= 0) {
+        if (suggestionItems.isEmpty()) {
             return;
         }
 
@@ -251,8 +291,7 @@
                 getColor(R.color.suggestion_strip_background));
         mSuggestionStrip.setVisibility(View.VISIBLE);
 
-        for (int i = 0; i < size; i++) {
-            final SuggestionItem suggestionItem = suggestionItems.get(i);
+        for (SuggestionItem suggestionItem : suggestionItems) {
             if (suggestionItem == null) {
                 continue;
             }
@@ -292,18 +331,8 @@
         handler.postDelayed(mMoveScrollableSuggestionsDown, MOVE_SUGGESTIONS_DOWN_TIMEOUT);
     }
 
-    private void onInlineSuggestionsResponseInternal(InlineSuggestionsResponse response) {
-        Log.d(TAG, "onInlineSuggestionsResponseInternal() called. Suggestion="
-                + response.getInlineSuggestions().size());
-
-        final List<InlineSuggestion> inlineSuggestions = response.getInlineSuggestions();
-
+    private void inflateThenShowSuggestions( List<InlineSuggestion> inlineSuggestions) {
         final int totalSuggestionsCount = inlineSuggestions.size();
-        if (totalSuggestionsCount <= 0) {
-            updateInlineSuggestionStrip(Collections.emptyList());
-            return;
-        }
-
         final Map<Integer, SuggestionItem> suggestionMap = Collections.synchronizedMap((
                 new TreeMap<>()));
         final ExecutorService executor = Executors.newSingleThreadExecutor();
@@ -355,4 +384,11 @@
             mIsPinned = isPinned;
         }
     }
+
+    enum ResponseState {
+        RESET,
+        RECEIVE_RESPONSE,
+        FINISH_INPUT,
+        START_INPUT,
+    }
 }