Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 908126d4 authored by Feng Cao's avatar Feng Cao
Browse files

Clear inline suggestions before onFinishInput

* The problem with sending empty response to IME is that the IME
  may want to handle the following two cases differently:
  a) all suggestions are filtered out due to user typing: ime may
     want to immediately delete the existing suggestions to make
     place for other types of things, such as IME's own word
     completion or next word prediction.
  b) the current input connection is finished and a new connection
     will be created with the same field or a different field:
     ime may want to delay removing the suggestions so that if there
     is new inline suggestions coming soon after for the next
     connection, the UI transition can be smoothed out by skipping
     the gap of deleting the old suggestions and showing the new
     suggestions.
* We used to rely on the IME impl to clear the suggestions when input
  is finished. That was done to give the IME the flexibility to
  smooth out the UI updates. Otherwise in case the input connection
  is finished and immediately started again on the same field,
  and there is another non-empty suggestion coming after short after,
  it would cause UI flicker. Because the suggsetion chips would
  disappear for a short moment and then appear again.
* The previously implemented solution was to have the IME impl post a
  delayed deletion of the suggestions when onFinishInput is called.
* In this patch, we get around this issue by synchronously clearing
  the inline suggestions right before the onFinishInput. Then the
  IME impl can post a callback to the main thread to do the actual
  delection. And in the callback it can check whether onFinishInput
  and onStartInput was called right before to determine whether
  it needs to delay the delection or delete immediately.
* Also done in this patch is to clear existing inline suggestions,
  if any, before IME creating a new callback connection to the
  framework.

Test: atest android.autofillservice.cts.inline
Bug:  157515522

Change-Id: I6fd5d294cf8676a24b8576ea554824608672ce49
parent 997150ee
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import com.android.internal.view.IInlineSuggestionsResponseCallback;
import com.android.internal.view.InlineSuggestionsRequestInfo;

import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -58,6 +59,9 @@ import java.util.function.Supplier;
class InlineSuggestionSession {
    private static final String TAG = "ImsInlineSuggestionSession";

    static final InlineSuggestionsResponse EMPTY_RESPONSE = new InlineSuggestionsResponse(
            Collections.emptyList());

    @NonNull
    private final Handler mMainThreadHandler;
    @NonNull
@@ -72,6 +76,10 @@ class InlineSuggestionSession {
    private final Supplier<IBinder> mHostInputTokenSupplier;
    @NonNull
    private final Consumer<InlineSuggestionsResponse> mResponseConsumer;
    // Indicate whether the previous call to the mResponseConsumer is empty or not. If it hasn't
    // been called yet, the value would be null.
    @Nullable
    private Boolean mPreviousResponseIsEmpty;


    /**
@@ -142,6 +150,7 @@ class InlineSuggestionSession {
    @MainThread
    void invalidate() {
        if (mResponseCallback != null) {
            consumeInlineSuggestionsResponse(EMPTY_RESPONSE);
            mResponseCallback.invalidate();
            mResponseCallback = null;
        }
@@ -188,6 +197,17 @@ class InlineSuggestionSession {
        if (DEBUG) {
            Log.d(TAG, "IME receives response: " + response.getInlineSuggestions().size());
        }
        consumeInlineSuggestionsResponse(response);
    }

    @MainThread
    void consumeInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
        boolean isResponseEmpty = response.getInlineSuggestions().isEmpty();
        if (isResponseEmpty && Boolean.TRUE.equals(mPreviousResponseIsEmpty)) {
            // No-op if both the previous response and current response are empty.
            return;
        }
        mPreviousResponseIsEmpty = isResponseEmpty;
        mResponseConsumer.accept(response);
    }

+1 −0
Original line number Diff line number Diff line
@@ -213,6 +213,7 @@ class InlineSuggestionSessionController {
        mImeInputViewStarted = false;
        mImeInputStarted = false;
        if (mSession != null && mSession.shouldSendImeStatus()) {
            mSession.consumeInlineSuggestionsResponse(InlineSuggestionSession.EMPTY_RESPONSE);
            try {
                mSession.getRequestCallback().onInputMethodFinishInput();
            } catch (RemoteException e) {