Merge "Reset Autofill dialog if the content is changed" into tm-qpr-dev

This commit is contained in:
Joanne Chung
2022-12-15 14:01:22 +00:00
committed by Android (Google) Code Review
3 changed files with 221 additions and 95 deletions

View File

@@ -111,6 +111,12 @@ public final class FillRequest implements Parcelable {
*/ */
public static final @RequestFlags int FLAG_IME_SHOWING = 0x80; public static final @RequestFlags int FLAG_IME_SHOWING = 0x80;
/**
* Indicates whether autofill session should reset the fill dialog state.
* @hide
*/
public static final @RequestFlags int FLAG_RESET_FILL_DIALOG_STATE = 0x100;
/** @hide */ /** @hide */
public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE; public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE;
@@ -208,7 +214,8 @@ public final class FillRequest implements Parcelable {
FLAG_PASSWORD_INPUT_TYPE, FLAG_PASSWORD_INPUT_TYPE,
FLAG_VIEW_NOT_FOCUSED, FLAG_VIEW_NOT_FOCUSED,
FLAG_SUPPORTS_FILL_DIALOG, FLAG_SUPPORTS_FILL_DIALOG,
FLAG_IME_SHOWING FLAG_IME_SHOWING,
FLAG_RESET_FILL_DIALOG_STATE
}) })
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@DataClass.Generated.Member @DataClass.Generated.Member
@@ -236,6 +243,8 @@ public final class FillRequest implements Parcelable {
return "FLAG_SUPPORTS_FILL_DIALOG"; return "FLAG_SUPPORTS_FILL_DIALOG";
case FLAG_IME_SHOWING: case FLAG_IME_SHOWING:
return "FLAG_IME_SHOWING"; return "FLAG_IME_SHOWING";
case FLAG_RESET_FILL_DIALOG_STATE:
return "FLAG_RESET_FILL_DIALOG_STATE";
default: return Integer.toHexString(value); default: return Integer.toHexString(value);
} }
} }
@@ -312,7 +321,8 @@ public final class FillRequest implements Parcelable {
| FLAG_PASSWORD_INPUT_TYPE | FLAG_PASSWORD_INPUT_TYPE
| FLAG_VIEW_NOT_FOCUSED | FLAG_VIEW_NOT_FOCUSED
| FLAG_SUPPORTS_FILL_DIALOG | FLAG_SUPPORTS_FILL_DIALOG
| FLAG_IME_SHOWING); | FLAG_IME_SHOWING
| FLAG_RESET_FILL_DIALOG_STATE);
this.mInlineSuggestionsRequest = inlineSuggestionsRequest; this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
this.mDelayedFillIntentSender = delayedFillIntentSender; this.mDelayedFillIntentSender = delayedFillIntentSender;
@@ -473,7 +483,8 @@ public final class FillRequest implements Parcelable {
| FLAG_PASSWORD_INPUT_TYPE | FLAG_PASSWORD_INPUT_TYPE
| FLAG_VIEW_NOT_FOCUSED | FLAG_VIEW_NOT_FOCUSED
| FLAG_SUPPORTS_FILL_DIALOG | FLAG_SUPPORTS_FILL_DIALOG
| FLAG_IME_SHOWING); | FLAG_IME_SHOWING
| FLAG_RESET_FILL_DIALOG_STATE);
this.mInlineSuggestionsRequest = inlineSuggestionsRequest; this.mInlineSuggestionsRequest = inlineSuggestionsRequest;
this.mDelayedFillIntentSender = delayedFillIntentSender; this.mDelayedFillIntentSender = delayedFillIntentSender;
@@ -495,10 +506,10 @@ public final class FillRequest implements Parcelable {
}; };
@DataClass.Generated( @DataClass.Generated(
time = 1647856966565L, time = 1663290803064L,
codegenVersion = "1.0.23", codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
@Deprecated @Deprecated
private void __metadata() {} private void __metadata() {}

View File

@@ -19,6 +19,7 @@ package android.view.autofill;
import static android.service.autofill.FillRequest.FLAG_IME_SHOWING; import static android.service.autofill.FillRequest.FLAG_IME_SHOWING;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
import static android.service.autofill.FillRequest.FLAG_RESET_FILL_DIALOG_STATE;
import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.view.ContentInfo.SOURCE_AUTOFILL; import static android.view.ContentInfo.SOURCE_AUTOFILL;
@@ -715,7 +716,7 @@ public final class AutofillManager {
* Autofill will automatically trigger a fill request after activity * Autofill will automatically trigger a fill request after activity
* start if there is any field is autofillable. But if there is a field that * start if there is any field is autofillable. But if there is a field that
* triggered autofill, it is unnecessary to trigger again through * triggered autofill, it is unnecessary to trigger again through
* AutofillManager#notifyViewEnteredForActivityStarted. * AutofillManager#notifyViewEnteredForFillDialog.
*/ */
private AtomicBoolean mIsFillRequested; private AtomicBoolean mIsFillRequested;
@@ -728,6 +729,10 @@ public final class AutofillManager {
private final String[] mFillDialogEnabledHints; private final String[] mFillDialogEnabledHints;
// Tracked all views that have appeared, including views that there are no
// dataset in responses. Used to avoid request pre-fill request again and again.
private final ArraySet<AutofillId> mAllTrackedViews = new ArraySet<>();
/** @hide */ /** @hide */
public interface AutofillClient { public interface AutofillClient {
/** /**
@@ -1173,6 +1178,16 @@ public final class AutofillManager {
* @hide * @hide
*/ */
public void notifyViewEnteredForFillDialog(View v) { public void notifyViewEnteredForFillDialog(View v) {
synchronized (mLock) {
if (mTrackedViews != null) {
// To support the fill dialog can show for the autofillable Views in
// different pages but in the same Activity. We need to reset the
// mIsFillRequested flag to allow asking for a new FillRequest when
// user switches to other page
mTrackedViews.checkViewState(v.getAutofillId());
}
}
// Skip if the fill request has been performed for a view. // Skip if the fill request has been performed for a view.
if (mIsFillRequested.get()) { if (mIsFillRequested.get()) {
return; return;
@@ -1299,6 +1314,10 @@ public final class AutofillManager {
} }
mForAugmentedAutofillOnly = false; mForAugmentedAutofillOnly = false;
} }
if ((flags & FLAG_SUPPORTS_FILL_DIALOG) != 0) {
flags |= FLAG_RESET_FILL_DIALOG_STATE;
}
updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags); updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
} }
addEnteredIdLocked(id); addEnteredIdLocked(id);
@@ -2165,6 +2184,7 @@ public final class AutofillManager {
mIsFillRequested.set(false); mIsFillRequested.set(false);
mShowAutofillDialogCalled = false; mShowAutofillDialogCalled = false;
mFillDialogTriggerIds = null; mFillDialogTriggerIds = null;
mAllTrackedViews.clear();
if (resetEnteredIds) { if (resetEnteredIds) {
mEnteredIds = null; mEnteredIds = null;
} }
@@ -2702,14 +2722,9 @@ public final class AutofillManager {
+ ", mFillableIds=" + mFillableIds + ", mFillableIds=" + mFillableIds
+ ", mEnabled=" + mEnabled + ", mEnabled=" + mEnabled
+ ", mSessionId=" + mSessionId); + ", mSessionId=" + mSessionId);
} }
if (mEnabled && mSessionId == sessionId) { if (mEnabled && mSessionId == sessionId) {
if (saveOnAllViewsInvisible) {
mTrackedViews = new TrackedViews(trackedIds);
} else {
mTrackedViews = null;
}
mSaveOnFinish = saveOnFinish; mSaveOnFinish = saveOnFinish;
if (fillableIds != null) { if (fillableIds != null) {
if (mFillableIds == null) { if (mFillableIds == null) {
@@ -2731,6 +2746,27 @@ public final class AutofillManager {
mSaveTriggerId = saveTriggerId; mSaveTriggerId = saveTriggerId;
setNotifyOnClickLocked(mSaveTriggerId, true); setNotifyOnClickLocked(mSaveTriggerId, true);
} }
if (!saveOnAllViewsInvisible) {
trackedIds = null;
}
final ArraySet<AutofillId> allFillableIds = new ArraySet<>();
if (mFillableIds != null) {
allFillableIds.addAll(mFillableIds);
}
if (trackedIds != null) {
for (AutofillId id : trackedIds) {
id.resetSessionId();
allFillableIds.add(id);
}
}
if (!allFillableIds.isEmpty()) {
mTrackedViews = new TrackedViews(trackedIds, Helper.toArray(allFillableIds));
} else {
mTrackedViews = null;
}
} }
} }
} }
@@ -3502,10 +3538,19 @@ public final class AutofillManager {
*/ */
private class TrackedViews { private class TrackedViews {
/** Visible tracked views */ /** Visible tracked views */
@Nullable private ArraySet<AutofillId> mVisibleTrackedIds; @NonNull private final ArraySet<AutofillId> mVisibleTrackedIds;
/** Invisible tracked views */ /** Invisible tracked views */
@Nullable private ArraySet<AutofillId> mInvisibleTrackedIds; @NonNull private final ArraySet<AutofillId> mInvisibleTrackedIds;
/** Visible tracked views for fill dialog */
@NonNull private final ArraySet<AutofillId> mVisibleDialogTrackedIds;
/** Invisible tracked views for fill dialog */
@NonNull private final ArraySet<AutofillId> mInvisibleDialogTrackedIds;
boolean mHasNewTrackedView;
boolean mIsTrackedSaveView;
/** /**
* Check if set is null or value is in set. * Check if set is null or value is in set.
@@ -3571,43 +3616,65 @@ public final class AutofillManager {
* *
* @param trackedIds The views to be tracked * @param trackedIds The views to be tracked
*/ */
TrackedViews(@Nullable AutofillId[] trackedIds) { TrackedViews(@Nullable AutofillId[] trackedIds, @Nullable AutofillId[] allTrackedIds) {
final AutofillClient client = getClient(); mVisibleTrackedIds = new ArraySet<>();
if (!ArrayUtils.isEmpty(trackedIds) && client != null) { mInvisibleTrackedIds = new ArraySet<>();
final boolean[] isVisible; if (!ArrayUtils.isEmpty(trackedIds)) {
mIsTrackedSaveView = true;
initialTrackedViews(trackedIds, mVisibleTrackedIds, mInvisibleTrackedIds);
}
if (client.autofillClientIsVisibleForAutofill()) { mVisibleDialogTrackedIds = new ArraySet<>();
if (sVerbose) Log.v(TAG, "client is visible, check tracked ids"); mInvisibleDialogTrackedIds = new ArraySet<>();
isVisible = client.autofillClientGetViewVisibility(trackedIds); if (!ArrayUtils.isEmpty(allTrackedIds)) {
} else { initialTrackedViews(allTrackedIds, mVisibleDialogTrackedIds,
// All false mInvisibleDialogTrackedIds);
isVisible = new boolean[trackedIds.length]; mAllTrackedViews.addAll(Arrays.asList(allTrackedIds));
}
final int numIds = trackedIds.length;
for (int i = 0; i < numIds; i++) {
final AutofillId id = trackedIds[i];
id.resetSessionId();
if (isVisible[i]) {
mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
} else {
mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
}
}
} }
if (sVerbose) { if (sVerbose) {
Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): " Log.v(TAG, "TrackedViews(trackedIds=" + Arrays.toString(trackedIds) + "): "
+ " mVisibleTrackedIds=" + mVisibleTrackedIds + " mVisibleTrackedIds=" + mVisibleTrackedIds
+ " mInvisibleTrackedIds=" + mInvisibleTrackedIds); + " mInvisibleTrackedIds=" + mInvisibleTrackedIds
+ " allTrackedIds=" + Arrays.toString(allTrackedIds)
+ " mVisibleDialogTrackedIds=" + mVisibleDialogTrackedIds
+ " mInvisibleDialogTrackedIds=" + mInvisibleDialogTrackedIds);
} }
if (mVisibleTrackedIds == null) { if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
} }
} }
private void initialTrackedViews(AutofillId[] trackedIds,
@NonNull ArraySet<AutofillId> visibleSet,
@NonNull ArraySet<AutofillId> invisibleSet) {
final boolean[] isVisible;
final AutofillClient client = getClient();
if (ArrayUtils.isEmpty(trackedIds) || client == null) {
return;
}
if (client.autofillClientIsVisibleForAutofill()) {
if (sVerbose) Log.v(TAG, "client is visible, check tracked ids");
isVisible = client.autofillClientGetViewVisibility(trackedIds);
} else {
// All false
isVisible = new boolean[trackedIds.length];
}
final int numIds = trackedIds.length;
for (int i = 0; i < numIds; i++) {
final AutofillId id = trackedIds[i];
id.resetSessionId();
if (isVisible[i]) {
addToSet(visibleSet, id);
} else {
addToSet(invisibleSet, id);
}
}
}
/** /**
* Called when a {@link View view's} visibility changes. * Called when a {@link View view's} visibility changes.
* *
@@ -3624,22 +3691,37 @@ public final class AutofillManager {
if (isClientVisibleForAutofillLocked()) { if (isClientVisibleForAutofillLocked()) {
if (isVisible) { if (isVisible) {
if (isInSet(mInvisibleTrackedIds, id)) { if (isInSet(mInvisibleTrackedIds, id)) {
mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id); removeFromSet(mInvisibleTrackedIds, id);
mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id); addToSet(mVisibleTrackedIds, id);
}
if (isInSet(mInvisibleDialogTrackedIds, id)) {
removeFromSet(mInvisibleDialogTrackedIds, id);
addToSet(mVisibleDialogTrackedIds, id);
} }
} else { } else {
if (isInSet(mVisibleTrackedIds, id)) { if (isInSet(mVisibleTrackedIds, id)) {
mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id); removeFromSet(mVisibleTrackedIds, id);
mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id); addToSet(mInvisibleTrackedIds, id);
}
if (isInSet(mVisibleDialogTrackedIds, id)) {
removeFromSet(mVisibleDialogTrackedIds, id);
addToSet(mInvisibleDialogTrackedIds, id);
} }
} }
} }
if (mVisibleTrackedIds == null) { if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
if (sVerbose) { if (sVerbose) {
Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleTrackedIds); Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleTrackedIds);
} }
finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
}
if (mVisibleDialogTrackedIds.isEmpty()) {
if (sVerbose) {
Log.v(TAG, "No more visible ids. Invisible = " + mInvisibleDialogTrackedIds);
}
processNoVisibleTrackedAllViews();
} }
} }
@@ -3653,66 +3735,66 @@ public final class AutofillManager {
// The visibility of the views might have changed while the client was not be visible, // The visibility of the views might have changed while the client was not be visible,
// hence update the visibility state for all views. // hence update the visibility state for all views.
AutofillClient client = getClient(); AutofillClient client = getClient();
ArraySet<AutofillId> updatedVisibleTrackedIds = null;
ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
if (client != null) { if (client != null) {
if (sVerbose) { if (sVerbose) {
Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + mInvisibleTrackedIds
+ " vis=" + mVisibleTrackedIds); + " vis=" + mVisibleTrackedIds);
} }
if (mInvisibleTrackedIds != null) {
final ArrayList<AutofillId> orderedInvisibleIds =
new ArrayList<>(mInvisibleTrackedIds);
final boolean[] isVisible = client.autofillClientGetViewVisibility(
Helper.toArray(orderedInvisibleIds));
final int numInvisibleTrackedIds = orderedInvisibleIds.size(); onVisibleForAutofillChangedInternalLocked(mVisibleTrackedIds, mInvisibleTrackedIds);
for (int i = 0; i < numInvisibleTrackedIds; i++) { onVisibleForAutofillChangedInternalLocked(
final AutofillId id = orderedInvisibleIds.get(i); mVisibleDialogTrackedIds, mInvisibleDialogTrackedIds);
if (isVisible[i]) {
updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
if (sDebug) {
Log.d(TAG, "onVisibleForAutofill() " + id + " became visible");
}
} else {
updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
}
}
}
if (mVisibleTrackedIds != null) {
final ArrayList<AutofillId> orderedVisibleIds =
new ArrayList<>(mVisibleTrackedIds);
final boolean[] isVisible = client.autofillClientGetViewVisibility(
Helper.toArray(orderedVisibleIds));
final int numVisibleTrackedIds = orderedVisibleIds.size();
for (int i = 0; i < numVisibleTrackedIds; i++) {
final AutofillId id = orderedVisibleIds.get(i);
if (isVisible[i]) {
updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
} else {
updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
if (sDebug) {
Log.d(TAG, "onVisibleForAutofill() " + id + " became invisible");
}
}
}
}
mInvisibleTrackedIds = updatedInvisibleTrackedIds;
mVisibleTrackedIds = updatedVisibleTrackedIds;
} }
if (mVisibleTrackedIds == null) { if (mIsTrackedSaveView && mVisibleTrackedIds.isEmpty()) {
if (sVerbose) { if (sVerbose) {
Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids"); Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
} }
finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED); finishSessionLocked(/* commitReason= */ COMMIT_REASON_VIEW_CHANGED);
} }
if (mVisibleDialogTrackedIds.isEmpty()) {
if (sVerbose) {
Log.v(TAG, "onVisibleForAutofillChangedLocked(): no more visible ids");
}
processNoVisibleTrackedAllViews();
}
}
void onVisibleForAutofillChangedInternalLocked(@NonNull ArraySet<AutofillId> visibleSet,
@NonNull ArraySet<AutofillId> invisibleSet) {
// The visibility of the views might have changed while the client was not be visible,
// hence update the visibility state for all views.
if (sVerbose) {
Log.v(TAG, "onVisibleForAutofillChangedLocked(): inv= " + invisibleSet
+ " vis=" + visibleSet);
}
ArraySet<AutofillId> allTrackedIds = new ArraySet<>();
allTrackedIds.addAll(visibleSet);
allTrackedIds.addAll(invisibleSet);
if (!allTrackedIds.isEmpty()) {
visibleSet.clear();
invisibleSet.clear();
initialTrackedViews(Helper.toArray(allTrackedIds), visibleSet, invisibleSet);
}
}
private void processNoVisibleTrackedAllViews() {
mShowAutofillDialogCalled = false;
}
void checkViewState(AutofillId id) {
if (mAllTrackedViews.contains(id)) {
return;
}
// Add the id as tracked to avoid triggering fill request again and again.
mAllTrackedViews.add(id);
if (mHasNewTrackedView) {
return;
}
// First one new tracks view
mIsFillRequested.set(false);
mHasNewTrackedView = true;
} }
} }

View File

@@ -24,6 +24,7 @@ import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
import static android.service.autofill.FillEventHistory.Event.UI_TYPE_UNKNOWN; import static android.service.autofill.FillEventHistory.Event.UI_TYPE_UNKNOWN;
import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
import static android.service.autofill.FillRequest.FLAG_RESET_FILL_DIALOG_STATE;
import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG; import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED; import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
@@ -411,6 +412,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock") @GuardedBy("mLock")
private boolean mStartedLogEventWithoutFocus; private boolean mStartedLogEventWithoutFocus;
/**
* Keeps the fill dialog trigger ids of the last response. This invalidates
* the trigger ids of the previous response.
*/
@Nullable
@GuardedBy("mLock")
private AutofillId[] mLastFillDialogTriggerIds;
void onSwitchInputMethodLocked() { void onSwitchInputMethodLocked() {
// One caveat is that for the case where the focus is on a field for which regular autofill // One caveat is that for the case where the focus is on a field for which regular autofill
// returns null, and augmented autofill is triggered, and then the user switches the input // returns null, and augmented autofill is triggered, and then the user switches the input
@@ -1171,6 +1180,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return; return;
} }
mLastFillDialogTriggerIds = response.getFillDialogTriggerIds();
final int flags = response.getFlags(); final int flags = response.getFlags();
if ((flags & FillResponse.FLAG_DELAY_FILL) != 0) { if ((flags & FillResponse.FLAG_DELAY_FILL) != 0) {
Slog.v(TAG, "Service requested to wait for delayed fill response."); Slog.v(TAG, "Service requested to wait for delayed fill response.");
@@ -1277,6 +1288,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
+ (timedOut ? "timeout" : "failure")); + (timedOut ? "timeout" : "failure"));
} }
mService.resetLastResponse(); mService.resetLastResponse();
mLastFillDialogTriggerIds = null;
final LogMaker requestLog = mRequestLogs.get(requestId); final LogMaker requestLog = mRequestLogs.get(requestId);
if (requestLog == null) { if (requestLog == null) {
Slog.w(TAG, "onFillRequestFailureOrTimeout(): no log for id " + requestId); Slog.w(TAG, "onFillRequestFailureOrTimeout(): no log for id " + requestId);
@@ -2973,6 +2985,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} }
} }
if ((flags & FLAG_RESET_FILL_DIALOG_STATE) != 0) {
if (sDebug) Log.d(TAG, "force to reset fill dialog state");
mSessionFlags.mFillDialogDisabled = false;
}
switch(action) { switch(action) {
case ACTION_START_SESSION: case ACTION_START_SESSION:
// View is triggering autofill. // View is triggering autofill.
@@ -3402,10 +3419,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
} }
private boolean isFillDialogUiEnabled() { private boolean isFillDialogUiEnabled() {
// TODO read from Settings or somewhere
final boolean isSettingsEnabledFillDialog = true;
synchronized (mLock) { synchronized (mLock) {
return isSettingsEnabledFillDialog && !mSessionFlags.mFillDialogDisabled; return !mSessionFlags.mFillDialogDisabled;
} }
} }
@@ -3431,14 +3446,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
AutofillId filledId, String filterText, int flags) { AutofillId filledId, String filterText, int flags) {
if (!isFillDialogUiEnabled()) { if (!isFillDialogUiEnabled()) {
// Unsupported fill dialog UI // Unsupported fill dialog UI
if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled");
return false; return false;
} }
if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) { if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
// IME is showing, fallback to normal suggestions UI // IME is showing, fallback to normal suggestions UI
if (sDebug) Log.w(TAG, "requestShowFillDialog: IME is showing");
return false; return false;
} }
synchronized (mLock) {
if (mLastFillDialogTriggerIds == null
|| !ArrayUtils.contains(mLastFillDialogTriggerIds, filledId)) {
// Last fill dialog triggered ids are changed.
if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed.");
return false;
}
}
final Drawable serviceIcon = getServiceIcon(); final Drawable serviceIcon = getServiceIcon();
getUiForShowing().showFillDialog(filledId, response, filterText, getUiForShowing().showFillDialog(filledId, response, filterText,
@@ -4278,6 +4304,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (mSessionFlags.mAugmentedAutofillOnly) { if (mSessionFlags.mAugmentedAutofillOnly) {
pw.print(prefix); pw.println("For Augmented Autofill Only"); pw.print(prefix); pw.println("For Augmented Autofill Only");
} }
if (mSessionFlags.mFillDialogDisabled) {
pw.print(prefix); pw.println("Fill Dialog disabled");
}
if (mLastFillDialogTriggerIds != null) {
pw.print(prefix); pw.println("Last Fill Dialog trigger ids: ");
pw.println(mSelectedDatasetIds);
}
if (mAugmentedAutofillDestroyer != null) { if (mAugmentedAutofillDestroyer != null) {
pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer"); pw.print(prefix); pw.println("has mAugmentedAutofillDestroyer");
} }