From f4d947bdc63958990bda6a2880d6bd5f7e5325a7 Mon Sep 17 00:00:00 2001 From: Joanne Chung Date: Tue, 9 Jun 2020 15:27:24 +0800 Subject: [PATCH] Notify the registered AutofillCallback for inline. AutofillInlineSuggestionsRequestSession controls the inline communication with the IME, we can centralize the UI show and hide notification here. Use a variable mPreviousHasNonPinSuggestionShow to save the previous status, when the display status change, e.g. from no suggestion to has suggestions we can notify inline ui shown event. Bug: 157762527 Test: atest android.autofillservice.cts.inline Test: atest CtsAutoFillServiceTestCases:DatasetFilteringDropdownTest Test: atest CtsAutoFillServiceTestCases:InlineFilteringTest Test: atest CtsAutoFillServiceTestCases:LoginActivityTest Test: Manual. Write a test app and register AutofillCallback, make sure the callback is called as expected. Change-Id: I8ee38008874c4a6f110635e8e4107a4eef72ed14 --- .../AutofillInlineSessionController.java | 9 ++- ...tofillInlineSuggestionsRequestSession.java | 43 +++++++++++++- .../com/android/server/autofill/Session.java | 58 ++++++++++--------- .../server/autofill/ui/InlineFillUi.java | 15 +++++ 4 files changed, 96 insertions(+), 29 deletions(-) diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java index 0ec8654f2a204..83c89bf7f698d 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java @@ -45,6 +45,8 @@ final class AutofillInlineSessionController { private final Object mLock; @NonNull private final Handler mHandler; + @NonNull + private final InlineFillUi.InlineUiEventCallback mUiCallback; @Nullable @GuardedBy("mLock") @@ -54,12 +56,14 @@ final class AutofillInlineSessionController { private InlineFillUi mInlineFillUi; AutofillInlineSessionController(InputMethodManagerInternal inputMethodManagerInternal, - int userId, ComponentName componentName, Handler handler, Object lock) { + int userId, ComponentName componentName, Handler handler, Object lock, + InlineFillUi.InlineUiEventCallback callback) { mInputMethodManagerInternal = inputMethodManagerInternal; mUserId = userId; mComponentName = componentName; mHandler = handler; mLock = lock; + mUiCallback = callback; } @@ -82,7 +86,8 @@ final class AutofillInlineSessionController { // TODO(b/151123764): consider reusing the same AutofillInlineSession object for the // same field. mSession = new AutofillInlineSuggestionsRequestSession(mInputMethodManagerInternal, mUserId, - mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras); + mComponentName, mHandler, mLock, autofillId, requestConsumer, uiExtras, + mUiCallback); mSession.onCreateInlineSuggestionsRequestLocked(); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java index 48895ad42e99d..68eeb0a3ca2e0 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java @@ -29,6 +29,7 @@ import android.os.Handler; import android.os.RemoteException; import android.util.Slog; import android.view.autofill.AutofillId; +import android.view.inputmethod.InlineSuggestion; import android.view.inputmethod.InlineSuggestionsRequest; import android.view.inputmethod.InlineSuggestionsResponse; @@ -40,6 +41,7 @@ import com.android.server.autofill.ui.InlineFillUi; import com.android.server.inputmethod.InputMethodManagerInternal; import java.lang.ref.WeakReference; +import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -65,6 +67,8 @@ final class AutofillInlineSuggestionsRequestSession { private final Handler mHandler; @NonNull private final Bundle mUiExtras; + @NonNull + private final InlineFillUi.InlineUiEventCallback mUiCallback; @GuardedBy("mLock") @NonNull @@ -97,18 +101,22 @@ final class AutofillInlineSuggestionsRequestSession { @GuardedBy("mLock") private boolean mDestroyed = false; + @GuardedBy("mLock") + private boolean mPreviousHasNonPinSuggestionShow; AutofillInlineSuggestionsRequestSession( @NonNull InputMethodManagerInternal inputMethodManagerInternal, int userId, @NonNull ComponentName componentName, @NonNull Handler handler, @NonNull Object lock, @NonNull AutofillId autofillId, - @NonNull Consumer requestConsumer, @NonNull Bundle uiExtras) { + @NonNull Consumer requestConsumer, @NonNull Bundle uiExtras, + @NonNull InlineFillUi.InlineUiEventCallback callback) { mInputMethodManagerInternal = inputMethodManagerInternal; mUserId = userId; mComponentName = componentName; mHandler = handler; mLock = lock; mUiExtras = uiExtras; + mUiCallback = callback; mAutofillId = autofillId; mImeRequestConsumer = requestConsumer; @@ -217,6 +225,7 @@ final class AutofillInlineSuggestionsRequestSession { // No-op if both the previous response and current response are empty. return; } + maybeNotifyFillUiEventLocked(response.getInlineSuggestions()); updateResponseToImeUncheckLocked(response); mPreviousResponseIsNotEmpty = !isEmptyResponse; } @@ -238,6 +247,38 @@ final class AutofillInlineSuggestionsRequestSession { } } + @GuardedBy("mLock") + private void maybeNotifyFillUiEventLocked(@NonNull List suggestions) { + if (mDestroyed) { + return; + } + boolean hasSuggestionToShow = false; + for (int i = 0; i < suggestions.size(); i++) { + InlineSuggestion suggestion = suggestions.get(i); + // It is possible we don't have any match result but we still have pinned + // suggestions. Only notify we have non-pinned suggestions to show + if (!suggestion.getInfo().isPinned()) { + hasSuggestionToShow = true; + break; + } + } + if (sDebug) { + Slog.d(TAG, "maybeNotifyFillUiEventLoked(): hasSuggestionToShow=" + hasSuggestionToShow + + ", mPreviousHasNonPinSuggestionShow=" + mPreviousHasNonPinSuggestionShow); + } + // Use mPreviousHasNonPinSuggestionShow to save previous status, if the display status + // change, we can notify the event. + if (hasSuggestionToShow && !mPreviousHasNonPinSuggestionShow) { + // From no suggestion to has suggestions to show + mUiCallback.notifyInlineUiShown(mAutofillId); + } else if (!hasSuggestionToShow && mPreviousHasNonPinSuggestionShow) { + // From has suggestions to no suggestions to show + mUiCallback.notifyInlineUiHidden(mAutofillId); + } + // Update the latest status + mPreviousHasNonPinSuggestionShow = hasSuggestionToShow; + } + /** * Handles the {@code request} and {@code callback} received from the IME. * diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index a9a0ab69f6335..fa090697c67c3 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -831,7 +831,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState setClientLocked(client); mInlineSessionController = new AutofillInlineSessionController(inputMethodManagerInternal, - userId, componentName, handler, mLock); + userId, componentName, handler, mLock, + new InlineFillUi.InlineUiEventCallback() { + @Override + public void notifyInlineUiShown(AutofillId autofillId) { + notifyFillUiShown(autofillId); + } + + @Override + public void notifyInlineUiHidden(AutofillId autofillId) { + notifyFillUiHidden(autofillId); + } + }); mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED) .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags)); @@ -1317,6 +1328,26 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState this, intentSender, intent)); } + private void notifyFillUiHidden(@NonNull AutofillId autofillId) { + synchronized (mLock) { + try { + mClient.notifyFillUiHidden(this.id, autofillId); + } catch (RemoteException e) { + Slog.e(TAG, "Error sending fill UI hidden notification", e); + } + } + } + + private void notifyFillUiShown(@NonNull AutofillId autofillId) { + synchronized (mLock) { + try { + mClient.notifyFillUiShown(this.id, autofillId); + } catch (RemoteException e) { + Slog.e(TAG, "Error sending fill UI shown notification", e); + } + } + } + private void doStartIntentSender(IntentSender intentSender, Intent intent) { try { synchronized (mLock) { @@ -2698,11 +2729,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // TODO(b/156099633): remove this once framework gets out of business of resending // inline suggestions when IME visibility changes. mInlineSessionController.hideInlineSuggestionsUiLocked(viewState.id); - try { - mClient.notifyFillUiHidden(this.id, viewState.id); - } catch (RemoteException e) { - Slog.e(TAG, "Error requesting to hide fill UI", e); - } viewState.resetState(ViewState.STATE_CHANGED); return; } else if ((viewState.id.equals(this.mCurrentViewId)) @@ -2729,11 +2755,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // TODO: we should be able to replace this with controller#filterInlineFillUiLocked // to accomplish filtering for augmented autofill. mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); - try { - mClient.notifyFillUiHidden(this.id, mCurrentViewId); - } catch (RemoteException e) { - Slog.e(TAG, "Error sending fill UI hidden notification", e); - } } } @@ -2819,11 +2840,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (requestShowInlineSuggestionsLocked(response, filterText)) { final ViewState currentView = mViewStates.get(mCurrentViewId); currentView.setState(ViewState.STATE_INLINE_SHOWN); - try { - mClient.notifyFillUiShown(this.id, mCurrentViewId); - } catch (RemoteException e) { - Slog.e(TAG, "Error sending fill UI shown notification", e); - } //TODO(b/137800469): Fix it to log showed only when IME asks for inflation, // rather than here where framework sends back the response. mService.logDatasetShown(id, mClientState); @@ -2894,11 +2910,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState synchronized (mLock) { mInlineSessionController.hideInlineSuggestionsUiLocked( focusedId); - try { - mClient.notifyFillUiHidden(this.id, focusedId); - } catch (RemoteException e) { - Slog.e(TAG, "Error sending fill UI hidden notification", e); - } } }, remoteRenderService); return mInlineSessionController.setInlineFillUiLocked(inlineFillUi); @@ -3410,11 +3421,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (mCurrentViewId != null) { mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId); - try { - mClient.notifyFillUiHidden(this.id, mCurrentViewId); - } catch (RemoteException e) { - Slog.e(TAG, "Error sending fill UI hidden notification", e); - } } autoFillApp(dataset); return; diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java index a3d0fb955da42..636e66f0e90ef 100644 --- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java @@ -297,4 +297,19 @@ public final class InlineFillUi { */ void startIntentSender(@NonNull IntentSender intentSender, @NonNull Intent intent); } + + /** + * Callback for inline suggestion Ui related events. + */ + public interface InlineUiEventCallback { + /** + * Callback to notify inline ui is shown. + */ + void notifyInlineUiShown(@NonNull AutofillId autofillId); + + /** + * Callback to notify inline ui is hidden. + */ + void notifyInlineUiHidden(@NonNull AutofillId autofillId); + } }