summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Haoran Zhang <haoranzhang@google.com> 2024-05-16 18:09:53 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-05-16 18:09:53 +0000
commite04f9a06228447142df0e4aa93486fff7604a68f (patch)
tree0b1c392698283047b18f9d97547391f1ae1c50bb
parentb14d1bd6090c2239ff65b4e7871ce305ec1a05c7 (diff)
parent8f71c728a3d8af38caa712cb8b0b6d9843e9d20c (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.java80
-rw-r--r--services/autofill/java/com/android/server/autofill/ViewState.java27
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);
}