New Autofill API: SaveInfo.setTriggerId()

Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases -t android.autofillservice.cts.SimpleSaveActivityTest#testExplicitySaveButton
Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases -t android.autofillservice.cts.SimpleSaveActivityTest#testExplicitySaveButtonWhenAppClearFields
Test: cts-tradefed run commandAndExit cts-dev -m CtsAutoFillServiceTestCases

Bug: 65118073
Fixes: 67006548

Change-Id: Id12179086567d014f35fe4177b041745fb19bafd
This commit is contained in:
Felipe Leme
2017-09-28 15:03:36 -07:00
parent 1174802b4c
commit 2fe3ade813
11 changed files with 294 additions and 60 deletions

View File

@@ -37251,6 +37251,7 @@ package android.service.autofill {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
field public static final int FLAG_DONT_SAVE_ON_FINISH = 2; // 0x2
field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
field public static final int NEGATIVE_BUTTON_STYLE_CANCEL = 0; // 0x0
field public static final int NEGATIVE_BUTTON_STYLE_REJECT = 1; // 0x1
@@ -37272,6 +37273,7 @@ package android.service.autofill {
method public android.service.autofill.SaveInfo.Builder setFlags(int);
method public android.service.autofill.SaveInfo.Builder setNegativeAction(int, android.content.IntentSender);
method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
method public android.service.autofill.SaveInfo.Builder setTriggerId(android.view.autofill.AutofillId);
method public android.service.autofill.SaveInfo.Builder setValidator(android.service.autofill.Validator);
}

View File

@@ -40346,6 +40346,7 @@ package android.service.autofill {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
field public static final int FLAG_DONT_SAVE_ON_FINISH = 2; // 0x2
field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
field public static final int NEGATIVE_BUTTON_STYLE_CANCEL = 0; // 0x0
field public static final int NEGATIVE_BUTTON_STYLE_REJECT = 1; // 0x1
@@ -40367,6 +40368,7 @@ package android.service.autofill {
method public android.service.autofill.SaveInfo.Builder setFlags(int);
method public android.service.autofill.SaveInfo.Builder setNegativeAction(int, android.content.IntentSender);
method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
method public android.service.autofill.SaveInfo.Builder setTriggerId(android.view.autofill.AutofillId);
method public android.service.autofill.SaveInfo.Builder setValidator(android.service.autofill.Validator);
}

View File

@@ -37546,6 +37546,7 @@ package android.service.autofill {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
field public static final int FLAG_DONT_SAVE_ON_FINISH = 2; // 0x2
field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
field public static final int NEGATIVE_BUTTON_STYLE_CANCEL = 0; // 0x0
field public static final int NEGATIVE_BUTTON_STYLE_REJECT = 1; // 0x1
@@ -37567,6 +37568,7 @@ package android.service.autofill {
method public android.service.autofill.SaveInfo.Builder setFlags(int);
method public android.service.autofill.SaveInfo.Builder setNegativeAction(int, android.content.IntentSender);
method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
method public android.service.autofill.SaveInfo.Builder setTriggerId(android.view.autofill.AutofillId);
method public android.service.autofill.SaveInfo.Builder setValidator(android.service.autofill.Validator);
}

View File

@@ -1879,7 +1879,7 @@ public class Activity extends ContextThemeWrapper
if (isFinishing()) {
if (mAutoFillResetNeeded) {
getAutofillManager().commit();
getAutofillManager().onActivityFinished();
} else if (mIntent != null
&& mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
// Activity was launched when user tapped a link in the Autofill Save UI - since

View File

@@ -65,7 +65,7 @@ import com.android.internal.os.SomeArgs;
* <li>The service replies through {@link FillCallback#onSuccess(FillResponse)}.
* <li>The Android System calls {@link #onDisconnected()} and unbinds from the
* {@code AutofillService}.
* <li>The Android System displays an UI affordance with the options sent by the service.
* <li>The Android System displays an autofill UI with the options sent by the service.
* <li>The user picks an option.
* <li>The proper views are autofilled.
* </ol>

View File

@@ -68,7 +68,7 @@ import java.util.Arrays;
* .build();
* </pre>
*
* <p>The save type flags are used to display the appropriate strings in the save UI affordance.
* <p>The save type flags are used to display the appropriate strings in the autofill save UI.
* You can pass multiple values, but try to keep it short if possible. In the above example, just
* {@code SaveInfo.SAVE_DATA_TYPE_PASSWORD} would be enough.
*
@@ -103,13 +103,17 @@ import java.util.Arrays;
* .build();
* </pre>
*
* <a name="TriggeringSaveRequest"></a>
* <h3>Triggering a save request</h3>
*
* <p>The {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} can be triggered after
* any of the following events:
* <ul>
* <li>The {@link Activity} finishes.
* <li>The app explicitly called {@link AutofillManager#commit()}.
* <li>All required views became invisible (if the {@link SaveInfo} was created with the
* <li>The app explicitly calls {@link AutofillManager#commit()}.
* <li>All required views become invisible (if the {@link SaveInfo} was created with the
* {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} flag).
* <li>The user clicks a specific view (defined by {@link Builder#setTriggerId(AutofillId)}.
* </ul>
*
* <p>But it is only triggered when all conditions below are met:
@@ -123,10 +127,13 @@ import java.util.Arrays;
* <li>There is no {@link Dataset} in the last {@link FillResponse} that completely matches the
* screen state (i.e., all required and optional fields in the dataset have the same value as
* the fields in the screen).
* <li>The user explicitly tapped the UI affordance asking to save data for autofill.
* <li>The user explicitly tapped the autofill save UI asking to save data for autofill.
* </ul>
*
* <p>The service can also customize some aspects of the save UI affordance:
* <a name="CustomizingSaveUI"></a>
* <h3>Customizing the autofill save UI</h3>
*
* <p>The service can also customize some aspects of the autofill save UI:
* <ul>
* <li>Add a simple subtitle by calling {@link Builder#setDescription(CharSequence)}.
* <li>Add a customized subtitle by calling
@@ -212,16 +219,25 @@ public final class SaveInfo implements Parcelable {
@interface SaveDataType{}
/**
* Usually {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
* is called once the {@link Activity} finishes. If this flag is set it is called once all
* saved views become invisible.
* Usually, a save request is only automatically <a href="#TriggeringSaveRequest">triggered</a>
* once the {@link Activity} finishes. If this flag is set, it is triggered once all saved views
* become invisible.
*/
public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x1;
/**
* By default, a save request is automatically <a href="#TriggeringSaveRequest">triggered</a>
* once the {@link Activity} finishes. If this flag is set, finishing the activity doesn't
* trigger a save request.
*
* <p>This flag is typically used in conjunction with {@link Builder#setTriggerId(AutofillId)}.
*/
public static final int FLAG_DONT_SAVE_ON_FINISH = 0x2;
/** @hide */
@IntDef(
flag = true,
value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE})
value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE, FLAG_DONT_SAVE_ON_FINISH})
@Retention(RetentionPolicy.SOURCE)
@interface SaveInfoFlags{}
@@ -236,6 +252,7 @@ public final class SaveInfo implements Parcelable {
private final InternalValidator mValidator;
private final InternalSanitizer[] mSanitizerKeys;
private final AutofillId[][] mSanitizerValues;
private final AutofillId mTriggerId;
private SaveInfo(Builder builder) {
mType = builder.mType;
@@ -259,6 +276,7 @@ public final class SaveInfo implements Parcelable {
mSanitizerValues[i] = builder.mSanitizers.valueAt(i);
}
}
mTriggerId = builder.mTriggerId;
}
/** @hide */
@@ -320,6 +338,12 @@ public final class SaveInfo implements Parcelable {
return mSanitizerValues;
}
/** @hide */
@Nullable
public AutofillId getTriggerId() {
return mTriggerId;
}
/**
* A builder for {@link SaveInfo} objects.
*/
@@ -338,6 +362,7 @@ public final class SaveInfo implements Parcelable {
private ArrayMap<InternalSanitizer, AutofillId[]> mSanitizers;
// Set used to validate against duplicate ids.
private ArraySet<AutofillId> mSanitizerIds;
private AutofillId mTriggerId;
/**
* Creates a new builder.
@@ -394,13 +419,15 @@ public final class SaveInfo implements Parcelable {
/**
* Sets flags changing the save behavior.
*
* @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or {@code 0}.
* @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE},
* {@link #FLAG_DONT_SAVE_ON_FINISH}, or {@code 0}.
* @return This builder.
*/
public @NonNull Builder setFlags(@SaveInfoFlags int flags) {
throwIfDestroyed();
mFlags = Preconditions.checkFlagsArgument(flags, FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE);
mFlags = Preconditions.checkFlagsArgument(flags,
FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE | FLAG_DONT_SAVE_ON_FINISH);
return this;
}
@@ -493,8 +520,8 @@ public final class SaveInfo implements Parcelable {
}
/**
* Sets an object used to validate the user input - if the input is not valid, the Save UI
* affordance is not shown.
* Sets an object used to validate the user input - if the input is not valid, the
* autofill save UI is not shown.
*
* <p>Typically used to validate credit card numbers. Examples:
*
@@ -520,7 +547,7 @@ public final class SaveInfo implements Parcelable {
* );
* </pre>
*
* <p><b>NOTE: </b>the example above is just for illustrative purposes; the same validator
* <p><b>Note:</b> the example above is just for illustrative purposes; the same validator
* could be created using a single regex for the {@code OR} part:
*
* <pre class="prettyprint">
@@ -615,6 +642,27 @@ public final class SaveInfo implements Parcelable {
return this;
}
/**
* Explicitly defines the view that should commit the autofill context when clicked.
*
* <p>Usually, the save request is only automatically
* <a href="#TriggeringSaveRequest">triggered</a> after the activity is
* finished or all relevant views become invisible, but there are scenarios where the
* autofill context is automatically commited too late
* &mdash;for example, when the activity manually clears the autofillable views when a
* button is tapped. This method can be used to trigger the autofill save UI earlier in
* these scenarios.
*
* <p><b>Note:</b> This method should only be used in scenarios where the automatic workflow
* is not enough, otherwise it could trigger the autofill save UI when it should not&mdash;
* for example, when the user entered invalid credentials for the autofillable views.
*/
public @NonNull Builder setTriggerId(@NonNull AutofillId id) {
throwIfDestroyed();
mTriggerId = Preconditions.checkNotNull(id);
return this;
}
/**
* Builds a new {@link SaveInfo} instance.
*
@@ -652,13 +700,14 @@ public final class SaveInfo implements Parcelable {
.append(", description=").append(mDescription)
.append(DebugUtils.flagsToString(SaveInfo.class, "NEGATIVE_BUTTON_STYLE_",
mNegativeButtonStyle))
.append(", mFlags=").append(mFlags)
.append(", mCustomDescription=").append(mCustomDescription)
.append(", validation=").append(mValidator)
.append(", flags=").append(mFlags)
.append(", customDescription=").append(mCustomDescription)
.append(", validator=").append(mValidator)
.append(", sanitizerKeys=")
.append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
.append(", sanitizerValues=")
.append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
.append(", triggerId=").append(mTriggerId)
.append("]").toString();
}
@@ -687,6 +736,7 @@ public final class SaveInfo implements Parcelable {
parcel.writeParcelableArray(mSanitizerValues[i], flags);
}
}
parcel.writeParcelable(mTriggerId, flags);
parcel.writeInt(mFlags);
}
@@ -727,6 +777,10 @@ public final class SaveInfo implements Parcelable {
builder.addSanitizer(sanitizers[i], autofillIds);
}
}
final AutofillId triggerId = parcel.readParcelable(null);
if (triggerId != null) {
builder.setTriggerId(triggerId);
}
builder.setFlags(parcel.readInt());
return builder.build();
}

View File

@@ -6397,6 +6397,60 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return null;
}
/**
* Set by {@link AutofillManager} if it needs to be notified when this view is clicked.
*/
private boolean mNotifyAutofillManagerOnClick;
/**
* Temporary variable used to make sure the autofill manager is not called twice on
* {@link #performClickInternal()}.
*/
private boolean mAlreadyNotifiedAutofillManagerOnClick;
/** @hide */
public void setNotifyAutofillManagerOnClick(boolean notify) {
mNotifyAutofillManagerOnClick = notify;
}
private void notifyAutofillManagerOnClick() {
if (!mNotifyAutofillManagerOnClick || mAlreadyNotifiedAutofillManagerOnClick) {
return;
}
// Must notify manager first to avoid scenarios where app has a listener
// that changes the state of views the autofill service might be interested on.
try {
getAutofillManager().notifyViewClicked(this);
} finally {
// Set it to already called so it's not called twice when
mAlreadyNotifiedAutofillManagerOnClick = true;
}
}
/**
* Entry point for {@link #performClick()} - other methods on View should call it instead of
* {@code performClick()} directly to make sure the autofill manager is notified when
* necessary (as subclasses could extend {@code performClick()} without calling the parent's
* method).
*/
private boolean performClickInternal() {
mAlreadyNotifiedAutofillManagerOnClick = false;
// Must notify autofill manager before performing the click actions to avoid scenarios where
// the app has a click listener that changes the state of views the autofill service might
// be interested on.
notifyAutofillManagerOnClick();
boolean performed;
try {
performed = performClick();
} finally {
// Reset it for next call.
mAlreadyNotifiedAutofillManagerOnClick = false;
}
return performed;
}
/**
* Call this view's OnClickListener, if it is defined. Performs all normal
* actions associated with clicking: reporting accessibility event, playing
@@ -6405,7 +6459,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @return True there was an assigned OnClickListener that was called, false
* otherwise is returned.
*/
// NOTE: other methods on View should not call this method directly, but performClickInternal()
// instead, to guarantee that the autofill manager is notified when necessary (as subclasses
// could extend this method without calling super.performClick()).
public boolean performClick() {
try {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
} finally {
// Reset it for next call.
mAlreadyNotifiedAutofillManagerOnClick = false;
}
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
@@ -11503,7 +11569,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK: {
if (isClickable()) {
performClick();
performClickInternal();
return true;
}
} break;
@@ -12615,7 +12681,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// This is a tap, so remove the longpress check
removeLongPressCallback();
if (!event.isCanceled()) {
return performClick();
return performClickInternal();
}
}
}
@@ -13187,7 +13253,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
performClickInternal();
}
}
}
@@ -18228,10 +18294,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
@SuppressWarnings({"UnusedDeclaration"})
public void outputDirtyFlags(String indent, boolean clear, int clearMask) {
Log.d("View", indent + this + " DIRTY(" + (mPrivateFlags & View.PFLAG_DIRTY_MASK) +
") DRAWN(" + (mPrivateFlags & PFLAG_DRAWN) + ")" + " CACHE_VALID(" +
(mPrivateFlags & View.PFLAG_DRAWING_CACHE_VALID) +
") INVALIDATED(" + (mPrivateFlags & PFLAG_INVALIDATED) + ")");
Log.d(VIEW_LOG_TAG, indent + this + " DIRTY("
+ (mPrivateFlags & View.PFLAG_DIRTY_MASK)
+ ") DRAWN(" + (mPrivateFlags & PFLAG_DRAWN) + ")" + " CACHE_VALID("
+ (mPrivateFlags & View.PFLAG_DRAWING_CACHE_VALID)
+ ") INVALIDATED(" + (mPrivateFlags & PFLAG_INVALIDATED) + ")");
if (clear) {
mPrivateFlags &= clearMask;
}
@@ -20008,7 +20075,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
@@ -25054,7 +25121,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private final class PerformClick implements Runnable {
@Override
public void run() {
performClick();
performClickInternal();
}
}

View File

@@ -91,10 +91,10 @@ import java.util.Objects;
* </ul>
*
* <p>When the service returns datasets, the Android System displays an autofill dataset picker
* UI affordance associated with the view, when the view is focused on and is part of a dataset.
* The application can be notified when the affordance is shown by registering an
* UI associated with the view, when the view is focused on and is part of a dataset.
* The application can be notified when the UI is shown by registering an
* {@link AutofillCallback} through {@link #registerCallback(AutofillCallback)}. When the user
* selects a dataset from the affordance, all views present in the dataset are autofilled, through
* selects a dataset from the UI, all views present in the dataset are autofilled, through
* calls to {@link View#autofill(AutofillValue)} or {@link View#autofill(SparseArray)}.
*
* <p>When the service returns ids of savable views, the Android System keeps track of changes
@@ -108,7 +108,7 @@ import java.util.Objects;
* </ul>
*
* <p>Finally, after the autofill context is commited (i.e., not cancelled), the Android System
* shows a save UI affordance if the value of savable views have changed. If the user selects the
* shows an autofill save UI if the value of savable views have changed. If the user selects the
* option to Save, the current value of the views is then sent to the autofill service.
*
* <p>It is safe to call into its methods from any thread.
@@ -311,6 +311,14 @@ public final class AutofillManager {
@GuardedBy("mLock")
@Nullable private ArraySet<AutofillId> mFillableIds;
/** If set, session is commited when the field is clicked. */
@GuardedBy("mLock")
@Nullable private AutofillId mSaveTriggerId;
/** If set, session is commited when the activity is finished; otherwise session is canceled. */
@GuardedBy("mLock")
private boolean mSaveOnFinish;
/** @hide */
public interface AutofillClient {
/**
@@ -834,6 +842,46 @@ public final class AutofillManager {
}
}
/**
* Called when a {@link View} is clicked. Currently only used by views that should trigger save.
*
* @hide
*/
public void notifyViewClicked(View view) {
final AutofillId id = view.getAutofillId();
if (sVerbose) Log.v(TAG, "notifyViewClicked(): id=" + id + ", trigger=" + mSaveTriggerId);
synchronized (mLock) {
if (mSaveTriggerId != null && mSaveTriggerId.equals(id)) {
if (sDebug) Log.d(TAG, "triggering commit by click of " + id);
commitLocked();
mMetricsLogger.action(MetricsEvent.AUTOFILL_SAVE_EXPLICITLY_TRIGGERED,
mContext.getPackageName());
}
}
}
/**
* Called by {@link android.app.Activity} to commit or cancel the session on finish.
*
* @hide
*/
public void onActivityFinished() {
if (!hasAutofillFeature()) {
return;
}
synchronized (mLock) {
if (mSaveOnFinish) {
commitLocked();
} else {
if (sDebug) Log.d(TAG, "Cancelling session on finish() as requested by service");
cancelLocked();
}
}
}
/**
* Called to indicate the current autofill context should be commited.
*
@@ -850,14 +898,17 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
if (!mEnabled && !isActiveLocked()) {
return;
}
finishSessionLocked();
commitLocked();
}
}
private void commitLocked() {
if (!mEnabled && !isActiveLocked()) {
return;
}
finishSessionLocked();
}
/**
* Called to indicate the current autofill context should be cancelled.
*
@@ -874,14 +925,17 @@ public final class AutofillManager {
return;
}
synchronized (mLock) {
if (!mEnabled && !isActiveLocked()) {
return;
}
cancelSessionLocked();
cancelLocked();
}
}
private void cancelLocked() {
if (!mEnabled && !isActiveLocked()) {
return;
}
cancelSessionLocked();
}
/** @hide */
public void disableOwnedAutofillServices() {
disableAutofillServices();
@@ -1038,6 +1092,7 @@ public final class AutofillManager {
mState = STATE_UNKNOWN;
mTrackedViews = null;
mFillableIds = null;
mSaveTriggerId = null;
}
private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
@@ -1289,12 +1344,15 @@ public final class AutofillManager {
/**
* Set the tracked views.
*
* @param trackedIds The views to be tracked
* @param trackedIds The views to be tracked.
* @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
* @param saveOnFinish Finish the session once the activity is finished.
* @param fillableIds Views that might anchor FillUI.
* @param saveTriggerId View that when clicked triggers commit().
*/
private void setTrackedViews(int sessionId, @Nullable AutofillId[] trackedIds,
boolean saveOnAllViewsInvisible, @Nullable AutofillId[] fillableIds) {
boolean saveOnAllViewsInvisible, boolean saveOnFinish,
@Nullable AutofillId[] fillableIds, @Nullable AutofillId saveTriggerId) {
synchronized (mLock) {
if (mEnabled && mSessionId == sessionId) {
if (saveOnAllViewsInvisible) {
@@ -1302,6 +1360,7 @@ public final class AutofillManager {
} else {
mTrackedViews = null;
}
mSaveOnFinish = saveOnFinish;
if (fillableIds != null) {
if (mFillableIds == null) {
mFillableIds = new ArraySet<>(fillableIds.length);
@@ -1314,10 +1373,30 @@ public final class AutofillManager {
+ ", mFillableIds" + mFillableIds);
}
}
if (mSaveTriggerId != null && !mSaveTriggerId.equals(saveTriggerId)) {
// Turn off trigger on previous view id.
setNotifyOnClickLocked(mSaveTriggerId, false);
}
if (saveTriggerId != null && !saveTriggerId.equals(mSaveTriggerId)) {
// Turn on trigger on new view id.
mSaveTriggerId = saveTriggerId;
setNotifyOnClickLocked(mSaveTriggerId, true);
}
}
}
}
private void setNotifyOnClickLocked(@NonNull AutofillId id, boolean notify) {
final View view = findView(id);
if (view == null) {
Log.w(TAG, "setNotifyOnClick(): invalid id: " + id);
return;
}
view.setNotifyAutofillManagerOnClick(notify);
}
private void setSaveUiState(int sessionId, boolean shown) {
if (sDebug) Log.d(TAG, "setSaveUiState(" + sessionId + "): " + shown);
synchronized (mLock) {
@@ -1504,6 +1583,8 @@ public final class AutofillManager {
pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
}
pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
}
private String getStateAsStringLocked() {
@@ -1752,7 +1833,7 @@ public final class AutofillManager {
* Callback for autofill related events.
*
* <p>Typically used for applications that display their own "auto-complete" views, so they can
* enable / disable such views when the autofill UI affordance is shown / hidden.
* enable / disable such views when the autofill UI is shown / hidden.
*/
public abstract static class AutofillCallback {
@@ -1762,26 +1843,26 @@ public final class AutofillManager {
public @interface AutofillEventType {}
/**
* The autofill input UI affordance associated with the view was shown.
* The autofill input UI associated with the view was shown.
*
* <p>If the view provides its own auto-complete UI affordance and its currently shown, it
* <p>If the view provides its own auto-complete UI and its currently shown, it
* should be hidden upon receiving this event.
*/
public static final int EVENT_INPUT_SHOWN = 1;
/**
* The autofill input UI affordance associated with the view was hidden.
* The autofill input UI associated with the view was hidden.
*
* <p>If the view provides its own auto-complete UI affordance that was hidden upon a
* <p>If the view provides its own auto-complete UI that was hidden upon a
* {@link #EVENT_INPUT_SHOWN} event, it could be shown again now.
*/
public static final int EVENT_INPUT_HIDDEN = 2;
/**
* The autofill input UI affordance associated with the view isn't shown because
* The autofill input UI associated with the view isn't shown because
* autofill is not available.
*
* <p>If the view provides its own auto-complete UI affordance but was not displaying it
* <p>If the view provides its own auto-complete UI but was not displaying it
* to avoid flickering, it could shown it upon receiving this event.
*/
public static final int EVENT_INPUT_UNAVAILABLE = 3;
@@ -1883,12 +1964,12 @@ public final class AutofillManager {
@Override
public void setTrackedViews(int sessionId, AutofillId[] ids,
boolean saveOnAllViewsInvisible, AutofillId[] fillableIds) {
boolean saveOnAllViewsInvisible, boolean saveOnFinish, AutofillId[] fillableIds,
AutofillId saveTriggerId) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
afm.post(() ->
afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible, fillableIds)
);
afm.post(() -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible,
saveOnFinish, fillableIds, saveTriggerId));
}
}

View File

@@ -53,7 +53,8 @@ oneway interface IAutoFillManagerClient {
* the session is finished automatically.
*/
void setTrackedViews(int sessionId, in @nullable AutofillId[] savableIds,
boolean saveOnAllViewsInvisible, in @nullable AutofillId[] fillableIds);
boolean saveOnAllViewsInvisible, boolean saveOnFinish,
in @nullable AutofillId[] fillableIds, in AutofillId saveTriggerId);
/**
* Requests showing the fill UI.

View File

@@ -4679,13 +4679,25 @@ message MetricsEvent {
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: P
ACTION_ZEN_ALLOW_ALARMS = 1162;
ACTION_ZEN_ALLOW_ALARMS = 1226;
// ACTION: DND Settings > Priority only allows > Media toggle
// SUBTYPE: 0 is off, 1 is on
// CATEGORY: SETTINGS
// OS: P
ACTION_ZEN_ALLOW_MEDIA = 1163;
ACTION_ZEN_ALLOW_MEDIA = 1227;
// An autofill service explicitly defined which view should commit the autofill context
// Package: Package of app that is autofilled
// OS: P
// Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION = 1228;
// The autofill context was commited when the user clicked a view explicitly marked by the
// service as committing it
// Package: Package of app that is autofilled
// OS: P
AUTOFILL_SAVE_EXPLICITLY_TRIGGERED = 1229;
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS

View File

@@ -1491,8 +1491,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
ArraySet<AutofillId> trackedViews = null;
boolean saveOnAllViewsInvisible = false;
boolean saveOnFinish = true;
final SaveInfo saveInfo = response.getSaveInfo();
final AutofillId saveTriggerId;
if (saveInfo != null) {
saveTriggerId = saveInfo.getTriggerId();
if (saveTriggerId != null) {
writeLog(MetricsEvent.AUTOFILL_EXPLICIT_SAVE_TRIGGER_DEFINITION);
}
saveOnAllViewsInvisible =
(saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
@@ -1509,6 +1515,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
Collections.addAll(trackedViews, saveInfo.getOptionalIds());
}
}
if ((saveInfo.getFlags() & SaveInfo.FLAG_DONT_SAVE_ON_FINISH) != 0) {
saveOnFinish = false;
}
} else {
saveTriggerId = null;
}
// Must also track that are part of datasets, otherwise the FillUI won't be hidden when
@@ -1533,10 +1545,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
try {
if (sVerbose) {
Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds);
Slog.v(TAG, "updateTrackedIdsLocked(): " + trackedViews + " => " + fillableIds
+ " (triggering on " + saveTriggerId + ")");
}
mClient.setTrackedViews(id, toArray(trackedViews), saveOnAllViewsInvisible,
toArray(fillableIds));
saveOnFinish, toArray(fillableIds), saveTriggerId);
} catch (RemoteException e) {
Slog.w(TAG, "Cannot set tracked ids", e);
}