diff options
| author | 2024-05-16 18:09:53 +0000 | |
|---|---|---|
| committer | 2024-05-16 18:09:53 +0000 | |
| commit | e04f9a06228447142df0e4aa93486fff7604a68f (patch) | |
| tree | 0b1c392698283047b18f9d97547391f1ae1c50bb | |
| parent | b14d1bd6090c2239ff65b4e7871ce305ec1a05c7 (diff) | |
| parent | 8f71c728a3d8af38caa712cb8b0b6d9843e9d20c (diff) | |
Merge "[Autofill Framework] Add a cache to store the last non-empty value when field is cleared" into main
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/Session.java | 80 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/ViewState.java | 27 |
2 files changed, 92 insertions, 15 deletions
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 07b16c53ffe8..a8b123518db3 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -1168,6 +1168,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } + @GuardedBy("mLock") @Nullable private AutofillValue findValueFromThisSessionOnlyLocked(@NonNull AutofillId autofillId) { final ViewState state = mViewStates.get(autofillId); @@ -1176,9 +1177,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } AutofillValue value = state.getCurrentValue(); + + // Some app clears the form before navigating to another activities. In this case, use the + // cached value instead. + if (value == null || value.isEmpty()) { + AutofillValue candidateSaveValue = state.getCandidateSaveValue(); + if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { + if (sDebug) { + Slog.d(TAG, "findValueLocked(): current value for " + autofillId + + " is empty, using candidateSaveValue instead."); + } + return candidateSaveValue; + } + } if (value == null) { - if (sDebug) Slog.d(TAG, "findValueLocked(): no current value for " + autofillId); - value = getValueFromContextsLocked(autofillId); + if (sDebug) { + Slog.d(TAG, "findValueLocked(): no current value for " + autofillId + + ", checking value from previous fill contexts"); + value = getValueFromContextsLocked(autofillId); + } } return value; } @@ -3717,19 +3734,34 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState AutofillValue value = viewState.getCurrentValue(); if (value == null || value.isEmpty()) { - final AutofillValue initialValue = getValueFromContextsLocked(id); - if (initialValue != null) { - if (sDebug) { - Slog.d(TAG, "Value of required field " + id + " didn't change; " - + "using initial value (" + initialValue + ") instead"); + // Some apps clear the form before navigating to other activities. + // If current value is empty, consider fall back to last cached + // non-empty result first. + final AutofillValue candidateSaveValue = + viewState.getCandidateSaveValue(); + if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { + if (sVerbose) { + Slog.v(TAG, "current value is empty, using cached last non-empty " + + "value instead"); } - value = initialValue; + value = candidateSaveValue; } else { - if (sDebug) { - Slog.d(TAG, "empty value for required " + id ); + // If candidate save value is also empty, consider falling back to initial + // value in context. + final AutofillValue initialValue = getValueFromContextsLocked(id); + if (initialValue != null) { + if (sDebug) { + Slog.d(TAG, "Value of required field " + id + " didn't change; " + + "using initial value (" + initialValue + ") instead"); + } + value = initialValue; + } else { + if (sDebug) { + Slog.d(TAG, "empty value for required " + id); + } + allRequiredAreNotEmpty = false; + break; } - allRequiredAreNotEmpty = false; - break; } } @@ -3801,7 +3833,21 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState continue; } if ((viewState.getState() & ViewState.STATE_CHANGED) != 0) { - final AutofillValue currentValue = viewState.getCurrentValue(); + AutofillValue currentValue = viewState.getCurrentValue(); + if (currentValue == null || currentValue.isEmpty()) { + // Some apps clear the form before navigating to other activities. + // If current value is empty, consider fall back to last cached + // non-empty result instead. + final AutofillValue candidateSaveValue = + viewState.getCandidateSaveValue(); + if (candidateSaveValue != null && !candidateSaveValue.isEmpty()) { + if (sVerbose) { + Slog.v(TAG, "current value is empty, using cached last " + + "non-empty value instead"); + } + currentValue = candidateSaveValue; + } + } final AutofillValue value = getSanitizedValue(sanitizers, id, currentValue); if (value == null) { if (sDebug) { @@ -4714,14 +4760,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void updateViewStateAndUiOnValueChangedLocked(AutofillId id, AutofillValue value, ViewState viewState, int flags) { + // Cache the last non-empty value for save purpose. Some apps clear the form before + // navigating to other activities. if (mIgnoreViewStateResetToEmpty && (value == null || value.isEmpty()) && viewState.getCurrentValue() != null && viewState.getCurrentValue().isText() && viewState.getCurrentValue().getTextValue() != null && viewState.getCurrentValue().getTextValue().length() > 1) { if (sVerbose) { - Slog.v(TAG, "Ignoring view state reset to empty on id " + id); + Slog.v(TAG, "value is resetting to empty, caching the last non-empty value"); } - return; + viewState.setCandidateSaveValue(viewState.getCurrentValue()); + } else { + viewState.setCandidateSaveValue(null); } final String textValue; if (value == null || !value.isText()) { diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java index fec5aa531cdd..6ad0eb6ff54f 100644 --- a/services/autofill/java/com/android/server/autofill/ViewState.java +++ b/services/autofill/java/com/android/server/autofill/ViewState.java @@ -106,6 +106,15 @@ final class ViewState { */ private FillResponse mSecondaryFillResponse; private AutofillValue mCurrentValue; + + /** + * Some apps clear the form before navigating to another activity. The mCandidateSaveValue + * caches the value when a field with string longer than 2 characters are cleared. + * + * When showing save UI, if mCurrentValue of view state is empty, session would use + * mCandidateSaveValue to prompt save instead. + */ + private AutofillValue mCandidateSaveValue; private AutofillValue mAutofilledValue; private AutofillValue mSanitizedValue; private Rect mVirtualBounds; @@ -139,6 +148,18 @@ final class ViewState { mCurrentValue = value; } + /** + * Gets the candidate save value of the view. + */ + @Nullable + AutofillValue getCandidateSaveValue() { + return mCandidateSaveValue; + } + + void setCandidateSaveValue(AutofillValue value) { + mCandidateSaveValue = value; + } + @Nullable AutofillValue getAutofilledValue() { return mAutofilledValue; @@ -268,6 +289,9 @@ final class ViewState { if (mCurrentValue != null) { builder.append(", currentValue:" ).append(mCurrentValue); } + if (mCandidateSaveValue != null) { + builder.append(", candidateSaveValue:").append(mCandidateSaveValue); + } if (mAutofilledValue != null) { builder.append(", autofilledValue:" ).append(mAutofilledValue); } @@ -302,6 +326,9 @@ final class ViewState { if (mAutofilledValue != null) { pw.print(prefix); pw.print("autofilledValue:" ); pw.println(mAutofilledValue); } + if (mCandidateSaveValue != null) { + pw.print(prefix); pw.print("candidateSaveValue:"); pw.println(mCandidateSaveValue); + } if (mSanitizedValue != null) { pw.print(prefix); pw.print("sanitizedValue:" ); pw.println(mSanitizedValue); } |