From 0500ea20fa8f3df07231e379878f86e15eab4a17 Mon Sep 17 00:00:00 2001 From: Feng Cao Date: Mon, 1 Jun 2020 00:30:03 -0700 Subject: [PATCH] Update the sample IME to solve some flickers * This, together with the change on the framework side ag/11685298, gets rid of the flicker on the notification pull down/up case, as well as switching from username to password. * The idea is to not clear the suggestions onInputFinish. Instead, we schedule a delayed deletion of the suggestions on InputStart, which will get canceled if there is new suggestions coming to the IME within the timeout. This gets rid of the flicker on the two cases mentioned in previous bullet. Test: manual test Bug: 157515522 Change-Id: I60d9181de3230d468224e2df08fee59b741d6828 --- .../autofillkeyboard/AutofillImeService.java | 57 +++++++++++++++---- .../android/autofillkeyboard/Keyboard.java | 5 ++ 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/AutofillImeService.java b/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/AutofillImeService.java index 73e5a92ea..7b7971f82 100644 --- a/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/AutofillImeService.java +++ b/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/AutofillImeService.java @@ -23,6 +23,7 @@ import android.graphics.drawable.Icon; import android.inputmethodservice.InputMethodService; import android.os.Bundle; import android.os.Handler; +import android.os.Looper; import android.util.Log; import android.util.Size; import android.util.TypedValue; @@ -66,6 +67,7 @@ public class AutofillImeService extends InputMethodService { private static final long MOVE_SUGGESTIONS_DOWN_TIMEOUT = 10000; private InputView mInputView; + private Keyboard mKeyboard; private Decoder mDecoder; private ViewGroup mSuggestionStrip; @@ -74,6 +76,8 @@ public class AutofillImeService extends InputMethodService { private InlineContentClipView mScrollableSuggestionsClip; private ViewGroup mScrollableSuggestions; + private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final Runnable mMoveScrollableSuggestionsToBg = () -> { mScrollableSuggestionsClip.setZOrderedOnTop(false); Toast.makeText(AutofillImeService.this, "Chips moved to bg - not clickable", @@ -101,6 +105,13 @@ public class AutofillImeService extends InputMethodService { @Override public View onCreateInputView() { mInputView = (InputView) LayoutInflater.from(this).inflate(R.layout.input_view, null); + mKeyboard = Keyboard.qwerty(this); + mInputView.addView(mKeyboard.inflateKeyboardView(LayoutInflater.from(this), mInputView)); + mSuggestionStrip = mInputView.findViewById(R.id.suggestion_strip); + mPinnedSuggestionsStart = mInputView.findViewById(R.id.pinned_suggestions_start); + mPinnedSuggestionsEnd = mInputView.findViewById(R.id.pinned_suggestions_end); + mScrollableSuggestionsClip = mInputView.findViewById(R.id.scrollable_suggestions_clip); + mScrollableSuggestions = mInputView.findViewById(R.id.scrollable_suggestions); return mInputView; } @@ -108,23 +119,42 @@ public class AutofillImeService extends InputMethodService { public void onStartInput(EditorInfo attribute, boolean restarting) { super.onStartInput(attribute, restarting); mDecoder = new Decoder(getCurrentInputConnection()); + if(mKeyboard != null) { + mKeyboard.reset(); + } + if (mInputView != 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"); + mDelayedDeletion = () -> { + Log.d(TAG, "onStartInput deleting inline suggestions"); + mDelayedDeletion = null; + updateInlineSuggestionStrip(Collections.emptyList()); + }; + mHandler.postDelayed(mDelayedDeletion, 200); + } } + private Runnable mDelayedDeletion; + @Override public void onStartInputView(EditorInfo info, boolean restarting) { super.onStartInputView(info, restarting); + } - mInputView.removeAllViews(); - Keyboard keyboard = Keyboard.qwerty(this); - mInputView.addView(keyboard.inflateKeyboardView(LayoutInflater.from(this), mInputView)); - - mSuggestionStrip = mInputView.findViewById(R.id.suggestion_strip); - mPinnedSuggestionsStart = mInputView.findViewById(R.id.pinned_suggestions_start); - mPinnedSuggestionsEnd = mInputView.findViewById(R.id.pinned_suggestions_end); - mScrollableSuggestionsClip = mInputView.findViewById(R.id.scrollable_suggestions_clip); - mScrollableSuggestions = mInputView.findViewById(R.id.scrollable_suggestions); - - updateInlineSuggestionStrip(Collections.emptyList()); + @Override + public void onFinishInputView(boolean finishingInput) { + super.onFinishInputView(finishingInput); + if (!finishingInput) { + // This runs when the IME is hide (but not finished). We need to clear the suggestions. + // Otherwise, they will stay on the screen for a bit after the IME window disappears. + // TODO: right now the framework resends the suggestions when onStartInputView is + // 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()); + } } @Override @@ -198,6 +228,10 @@ public class AutofillImeService extends InputMethodService { @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); return true; } @@ -209,7 +243,6 @@ public class AutofillImeService extends InputMethodService { final int size = suggestionItems.size(); if (size <= 0) { - mSuggestionStrip.setVisibility(View.GONE); return; } diff --git a/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/Keyboard.java b/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/Keyboard.java index e7b279e1f..334cdf5dd 100644 --- a/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/Keyboard.java +++ b/samples/AutofillKeyboard/src/com/example/android/autofillkeyboard/Keyboard.java @@ -102,6 +102,11 @@ final class Keyboard { return mKeyboardView; } + void reset() { + mState = 0; + mapKeys(); + } + private void mapKeys() { for (int i = 0; i < mKeyMapping.size(); i++) { TextView softkey = mKeyboardView.findViewById(mKeyMapping.keyAt(i));