summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java131
-rw-r--r--services/autofill/java/com/android/server/autofill/ViewState.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/FillUi.java3
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java53
4 files changed, 122 insertions, 67 deletions
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 53f85ea7b119..72029d178534 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2436,65 +2436,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
forceRemoveSelfLocked(AutofillManager.STATE_UNKNOWN_COMPAT_MODE);
return;
}
-
if (!Objects.equals(value, viewState.getCurrentValue())) {
- if ((value == null || value.isEmpty())
- && viewState.getCurrentValue() != null
- && viewState.getCurrentValue().isText()
- && viewState.getCurrentValue().getTextValue() != null
- && getSaveInfoLocked() != null) {
- final int length = viewState.getCurrentValue().getTextValue().length();
- if (sDebug) {
- Slog.d(TAG, "updateLocked(" + id + "): resetting value that was "
- + length + " chars long");
- }
- final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
- .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
- mMetricsLogger.write(log);
- }
-
- // Always update the internal state.
- viewState.setCurrentValue(value);
-
- // Must check if this update was caused by autofilling the view, in which
- // case we just update the value, but not the UI.
- final AutofillValue filledValue = viewState.getAutofilledValue();
- if (filledValue != null) {
- if (filledValue.equals(value)) {
- if (sVerbose) {
- Slog.v(TAG, "ignoring autofilled change on id " + id);
- }
- viewState.resetState(ViewState.STATE_CHANGED);
- return;
- }
- else {
- if ((viewState.id.equals(this.mCurrentViewId)) &&
- (viewState.getState() & ViewState.STATE_AUTOFILLED) != 0) {
- // Remove autofilled state once field is changed after autofilling.
- if (sVerbose) {
- Slog.v(TAG, "field changed after autofill on id " + id);
- }
- viewState.resetState(ViewState.STATE_AUTOFILLED);
- final ViewState currentView = mViewStates.get(mCurrentViewId);
- currentView.maybeCallOnFillReady(flags);
- }
- }
- }
-
- // Update the internal state...
- viewState.setState(ViewState.STATE_CHANGED);
-
- //..and the UI
- final String filterText;
- if (value == null || !value.isText()) {
- filterText = null;
- } else {
- final CharSequence text = value.getTextValue();
- // Text should never be null, but it doesn't hurt to check to avoid a
- // system crash...
- filterText = (text == null) ? null : text.toString();
- }
- getUiForShowing().filterFillUi(filterText, this);
+ logIfViewClearedLocked(id, value, viewState);
+ updateViewStateAndUiOnValueChangedLocked(id, value, viewState, flags);
}
break;
case ACTION_VIEW_ENTERED:
@@ -2573,6 +2517,68 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return ArrayUtils.contains(response.getIgnoredIds(), id);
}
+ @GuardedBy("mLock")
+ private void logIfViewClearedLocked(AutofillId id, AutofillValue value, ViewState viewState) {
+ if ((value == null || value.isEmpty())
+ && viewState.getCurrentValue() != null
+ && viewState.getCurrentValue().isText()
+ && viewState.getCurrentValue().getTextValue() != null
+ && getSaveInfoLocked() != null) {
+ final int length = viewState.getCurrentValue().getTextValue().length();
+ if (sDebug) {
+ Slog.d(TAG, "updateLocked(" + id + "): resetting value that was "
+ + length + " chars long");
+ }
+ final LogMaker log = newLogMaker(MetricsEvent.AUTOFILL_VALUE_RESET)
+ .addTaggedData(MetricsEvent.FIELD_AUTOFILL_PREVIOUS_LENGTH, length);
+ mMetricsLogger.write(log);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value,
+ ViewState viewState, int flags) {
+ viewState.setCurrentValue(value);
+
+ final String filterText;
+ if (value == null || !value.isText()) {
+ filterText = null;
+ } else {
+ final CharSequence text = value.getTextValue();
+ // Text should never be null, but it doesn't hurt to check to avoid a
+ // system crash...
+ filterText = (text == null) ? null : text.toString();
+ }
+
+ final AutofillValue filledValue = viewState.getAutofilledValue();
+ if (filledValue != null) {
+ if (filledValue.equals(value)) {
+ // When the update is caused by autofilling the view, just update the
+ // value, not the UI.
+ if (sVerbose) {
+ Slog.v(TAG, "ignoring autofilled change on id " + id);
+ }
+ viewState.resetState(ViewState.STATE_CHANGED);
+ return;
+ } else if ((viewState.id.equals(this.mCurrentViewId))
+ && (viewState.getState() & ViewState.STATE_AUTOFILLED) != 0) {
+ // Remove autofilled state once field is changed after autofilling.
+ if (sVerbose) {
+ Slog.v(TAG, "field changed after autofill on id " + id);
+ }
+ viewState.resetState(ViewState.STATE_AUTOFILLED);
+ final ViewState currentView = mViewStates.get(mCurrentViewId);
+ currentView.maybeCallOnFillReady(flags);
+ }
+ } else if (viewState.id.equals(this.mCurrentViewId)
+ && (viewState.getState() & ViewState.STATE_INLINE_SHOWN) != 0) {
+ requestShowInlineSuggestionsLocked(viewState.getResponse(), filterText);
+ }
+
+ viewState.setState(ViewState.STATE_CHANGED);
+ getUiForShowing().filterFillUi(filterText, this);
+ }
+
@Override
public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId,
@Nullable AutofillValue value) {
@@ -2602,7 +2608,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (response.supportsInlineSuggestions()) {
synchronized (mLock) {
- if (requestShowInlineSuggestionsLocked(response)) {
+ if (requestShowInlineSuggestionsLocked(response, filterText)) {
+ final ViewState currentView = mViewStates.get(mCurrentViewId);
+ currentView.setState(ViewState.STATE_INLINE_SHOWN);
//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);
@@ -2645,7 +2653,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
/**
* Returns whether we made a request to show inline suggestions.
*/
- private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response) {
+ private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
+ @Nullable String filterText) {
final List<Dataset> datasets = response.getDatasets();
if (datasets == null) {
Log.w(TAG, "response returned null datasets");
@@ -2663,7 +2672,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
InlineSuggestionsResponse inlineSuggestionsResponse =
InlineSuggestionFactory.createInlineSuggestionsResponse(request,
response.getRequestId(),
- datasets.toArray(new Dataset[]{}), response.getInlineActions(),
+ datasets.toArray(new Dataset[]{}), filterText, response.getInlineActions(),
mCurrentViewId, mContext, this, () -> {
synchronized (mLock) {
requestHideFillUi(mCurrentViewId);
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 84886f83027d..f7c24f080fa4 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -74,6 +74,8 @@ final class ViewState {
public static final int STATE_AUTOFILLED_ONCE = 0x800;
/** View triggered the latest augmented autofill request. */
public static final int STATE_TRIGGERED_AUGMENTED_AUTOFILL = 0x1000;
+ /** Inline suggestions were shown for this View. */
+ public static final int STATE_INLINE_SHOWN = 0x2000;
public final AutofillId id;
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 57961423061f..5dc43ef8ad56 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -313,6 +313,8 @@ final class FillUi {
Slog.e(TAG, "Error inflating remote views", e);
continue;
}
+ // TODO: Extract the shared filtering logic here and in FillUi to a common
+ // method.
final DatasetFieldFilter filter = dataset.getFilter(index);
Pattern filterPattern = null;
String valueText = null;
@@ -602,6 +604,7 @@ final class FillUi {
* Returns whether this item matches the value input by the user so it can be included
* in the filtered datasets.
*/
+ // TODO: Extract the shared filtering logic here and in FillUi to a common method.
public boolean matches(CharSequence filterText) {
if (TextUtils.isEmpty(filterText)) {
// Always show item when the user input is empty
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 95a4a191a52e..5f6e47b04113 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -17,6 +17,7 @@
package com.android.server.autofill.ui;
import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -24,10 +25,12 @@ import android.content.Context;
import android.os.RemoteException;
import android.service.autofill.Dataset;
import android.service.autofill.InlinePresentation;
+import android.text.TextUtils;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.View;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
import android.view.inline.InlinePresentationSpec;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
@@ -42,6 +45,7 @@ import com.android.server.UiThread;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
+import java.util.regex.Pattern;
public final class InlineSuggestionFactory {
private static final String TAG = "InlineSuggestionFactory";
@@ -69,17 +73,19 @@ public final class InlineSuggestionFactory {
@NonNull Runnable onErrorCallback) {
if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
- datasets, /* inlineActions= */ null, autofillId, context, onErrorCallback,
+ datasets, /* filterText= */ null, /* inlineActions= */ null, autofillId, context,
+ onErrorCallback,
(dataset, filedIndex) -> (v -> inlineSuggestionUiCallback.autofill(dataset)));
}
/**
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
- * autofill service.
+ * autofill service, potentially filtering the datasets.
*/
public static InlineSuggestionsResponse createInlineSuggestionsResponse(
@NonNull InlineSuggestionsRequest request, int requestId,
@NonNull Dataset[] datasets,
+ @Nullable String filterText,
@Nullable List<InlinePresentation> inlineActions,
@NonNull AutofillId autofillId,
@NonNull Context context,
@@ -87,15 +93,15 @@ public final class InlineSuggestionFactory {
@NonNull Runnable onErrorCallback) {
if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, datasets,
- inlineActions, autofillId, context, onErrorCallback,
+ filterText, inlineActions, autofillId, context, onErrorCallback,
(dataset, filedIndex) -> (v -> client.fill(requestId, filedIndex, dataset)));
}
private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
boolean isAugmented, @NonNull InlineSuggestionsRequest request,
- @NonNull Dataset[] datasets, @Nullable List<InlinePresentation> inlineActions,
- @NonNull AutofillId autofillId, @NonNull Context context,
- @NonNull Runnable onErrorCallback,
+ @NonNull Dataset[] datasets, @Nullable String filterText,
+ @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId,
+ @NonNull Context context, @NonNull Runnable onErrorCallback,
@NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
@@ -113,6 +119,9 @@ public final class InlineSuggestionFactory {
Slog.w(TAG, "InlinePresentation not found in dataset");
return null;
}
+ if (!includeDataset(dataset, fieldIndex, filterText)) {
+ continue;
+ }
InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
fieldIndex, mergedInlinePresentation(request, i, inlinePresentation),
inlineSuggestionUi, onClickListenerFactory);
@@ -129,6 +138,38 @@ public final class InlineSuggestionFactory {
return new InlineSuggestionsResponse(inlineSuggestions);
}
+ // TODO: Extract the shared filtering logic here and in FillUi to a common method.
+ private static boolean includeDataset(Dataset dataset, int fieldIndex,
+ @Nullable String filterText) {
+ // Show everything when the user input is empty.
+ if (TextUtils.isEmpty(filterText)) {
+ return true;
+ }
+
+ final String constraintLowerCase = filterText.toString().toLowerCase();
+
+ // Use the filter provided by the service, if available.
+ final Dataset.DatasetFieldFilter filter = dataset.getFilter(fieldIndex);
+ if (filter != null) {
+ Pattern filterPattern = filter.pattern;
+ if (filterPattern == null) {
+ if (sVerbose) {
+ Slog.v(TAG, "Explicitly disabling filter for dataset id" + dataset.getId());
+ }
+ return true;
+ }
+ return filterPattern.matcher(constraintLowerCase).matches();
+ }
+
+ final AutofillValue value = dataset.getFieldValues().get(fieldIndex);
+ if (value == null || !value.isText()) {
+ return dataset.getAuthentication() == null;
+ }
+ final String valueText = value.getTextValue().toString().toLowerCase();
+ return valueText.toLowerCase().startsWith(constraintLowerCase);
+ }
+
+
private static InlineSuggestion createInlineAction(boolean isAugmented,
@NonNull Context context,
@NonNull InlinePresentation inlinePresentation,