Improved documentation for AutofillService package:
am: 2ef19c1d73
Change-Id: Iac283d34da149829a25313d0910f2564b347e332
This commit is contained in:
@@ -19,24 +19,230 @@ import android.annotation.CallSuper;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.Settings;
|
||||
|
||||
import com.android.internal.os.HandlerCaller;
|
||||
import android.annotation.SdkConstant;
|
||||
import android.app.Activity;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.app.Service;import android.content.Intent;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.IBinder;
|
||||
import android.os.ICancellationSignal;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewStructure;
|
||||
import android.view.autofill.AutofillId;
|
||||
import android.view.autofill.AutofillManager;
|
||||
import android.view.autofill.AutofillValue;
|
||||
|
||||
import com.android.internal.os.SomeArgs;
|
||||
|
||||
/**
|
||||
* Top-level service of the current autofill service for a given user.
|
||||
* An {@code AutofillService} is a service used to automatically fill the contents of the screen
|
||||
* on behalf of a given user - for more information about autofill, read
|
||||
* <a href="{@docRoot}preview/features/autofill.html">Autofill Framework</a>.
|
||||
*
|
||||
* <p>Apps providing autofill capabilities must extend this service.
|
||||
* <p>An {@code AutofillService} is only bound to the Android System for autofill purposes if:
|
||||
* <ol>
|
||||
* <li>It requires the {@code android.permission.BIND_AUTOFILL_SERVICE} permission in its
|
||||
* manifest.
|
||||
* <li>The user explicitly enables it using Android Settings (the
|
||||
* {@link Settings#ACTION_REQUEST_SET_AUTOFILL_SERVICE} intent can be used to launch such
|
||||
* Settings screen).
|
||||
* </ol>
|
||||
*
|
||||
* <h3>Basic usage</h3>
|
||||
*
|
||||
* <p>The basic autofill process is defined by the workflow below:
|
||||
* <ol>
|
||||
* <li>User focus an editable {@link View}.
|
||||
* <li>View calls {@link AutofillManager#notifyViewEntered(android.view.View)}.
|
||||
* <li>A {@link ViewStructure} representing all views in the screen is created.
|
||||
* <li>The Android System binds to the service and calls {@link #onConnected()}.
|
||||
* <li>The service receives the view structure through the
|
||||
* {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)}.
|
||||
* <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 user picks an option.
|
||||
* <li>The proper views are autofilled.
|
||||
* </ol>
|
||||
*
|
||||
* <p>This workflow was designed to minimize the time the Android System is bound to the service;
|
||||
* for each call, it: binds to service, waits for the reply, and unbinds right away. Furthermore,
|
||||
* those calls are considered stateless: if the service needs to keep state between calls, it must
|
||||
* do its own state management (keeping in mind that the service's process might be killed by the
|
||||
* Android System when unbound; for example, if the device is running low in memory).
|
||||
*
|
||||
* <p>Typically, the
|
||||
* {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} will:
|
||||
* <ol>
|
||||
* <li>Parse the view structure looking for autofillable views (for example, using
|
||||
* {@link android.app.assist.AssistStructure.ViewNode#getAutofillHints()}.
|
||||
* <li>Match the autofillable views with the user's data.
|
||||
* <li>Create a {@link Dataset} for each set of user's data that match those fields.
|
||||
* <li>Fill the dataset(s) with the proper {@link AutofillId}s and {@link AutofillValue}s.
|
||||
* <li>Add the dataset(s) to the {@link FillResponse} passed to
|
||||
* {@link FillCallback#onSuccess(FillResponse)}.
|
||||
* </ol>
|
||||
*
|
||||
* <p>For example, for a login screen with username and password views where the user only has one
|
||||
* account in the service, the response could be:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .addDataset(new Dataset.Builder()
|
||||
* .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer"))
|
||||
* .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer"))
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* <p>But if the user had 2 accounts instead, the response could be:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .addDataset(new Dataset.Builder()
|
||||
* .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer"))
|
||||
* .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer"))
|
||||
* .build())
|
||||
* .addDataset(new Dataset.Builder()
|
||||
* .setValue(id1, AutofillValue.forText("flanders"), createPresentation("flanders"))
|
||||
* .setValue(id2, AutofillValue.forText("OkelyDokelyDo"), createPresentation("password for flanders"))
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* <p>If the service does not find any autofillable view in the view structure, it should pass
|
||||
* {@code null} to {@link FillCallback#onSuccess(FillResponse)}; if the service encountered an error
|
||||
* processing the request, it should call {@link FillCallback#onFailure(CharSequence)}. For
|
||||
* performance reasons, it's paramount that the service calls either
|
||||
* {@link FillCallback#onSuccess(FillResponse)} or {@link FillCallback#onFailure(CharSequence)} for
|
||||
* each {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} received - if it
|
||||
* doesn't, the request will eventually time out and be discarded by the Android System.
|
||||
*
|
||||
* <h3>Saving user data</h3>
|
||||
*
|
||||
* <p>If the service is also interested on saving the data filled by the user, it must set a
|
||||
* {@link SaveInfo} object in the {@link FillResponse}. See {@link SaveInfo} for more details and
|
||||
* examples.
|
||||
*
|
||||
* <h3>User authentication</h3>
|
||||
*
|
||||
* <p>The service can provide an extra degree of security by requiring the user to authenticate
|
||||
* before an app can be autofilled. The authentication is typically required in 2 scenarios:
|
||||
* <ul>
|
||||
* <li>To unlock the user data (for example, using a master password or fingerprint
|
||||
* authentication) - see
|
||||
* {@link FillResponse.Builder#setAuthentication(AutofillId[], android.content.IntentSender, android.widget.RemoteViews)}.
|
||||
* <li>To unlock a specific dataset (for example, by providing a CVC for a credit card) - see
|
||||
* {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}.
|
||||
* </ul>
|
||||
*
|
||||
* <p>When using authentication, it is recommended to encrypt only the sensitive data and leave
|
||||
* labels unencrypted, so they can be used on presentation views. For example, if the user has a
|
||||
* home and a work address, the {@code Home} and {@code Work} labels should be stored unencrypted
|
||||
* (since they don't have any sensitive data) while the address data per se could be stored in an
|
||||
* encrypted storage. Then when the user chooses the {@code Home} dataset, the platform starts
|
||||
* the authentication flow, and the service can decrypt the sensitive data.
|
||||
*
|
||||
* <p>The authentication mechanism can also be used in scenarios where the service needs multiple
|
||||
* steps to determine the datasets that can fill a screen. For example, when autofilling a financial
|
||||
* app where the user has accounts for multiple banks, the workflow could be:
|
||||
*
|
||||
* <ol>
|
||||
* <li>The first {@link FillResponse} contains datasets with the credentials for the financial
|
||||
* app, plus a "fake" dataset whose presentation says "Tap here for banking apps credentials".
|
||||
* <li>When the user selects the fake dataset, the service displays a dialog with available
|
||||
* banking apps.
|
||||
* <li>When the user select a banking app, the service replies with a new {@link FillResponse}
|
||||
* containing the datasets for that bank.
|
||||
* </ol>
|
||||
*
|
||||
* <p>Another example of multiple-steps dataset selection is when the service stores the user
|
||||
* credentials in "vaults": the first response would contain fake datasets with the vault names,
|
||||
* and the subsequent response would contain the app credentials stored in that vault.
|
||||
*
|
||||
* <h3>Data partitioning</h3>
|
||||
*
|
||||
* <p>The autofillable views in a screen should be grouped in logical groups called "partitions".
|
||||
* Typical partitions are:
|
||||
* <ul>
|
||||
* <li>Credentials (username/email address, password).
|
||||
* <li>Address (street, city, state, zip code, etc).
|
||||
* <li>Payment info (credit card number, expiration date, and verification code).
|
||||
* </ul>
|
||||
* <p>For security reasons, when a screen has more than one partition, it's paramount that the
|
||||
* contents of a dataset do not spawn multiple partitions, specially when one of the partitions
|
||||
* contains data that is not specific to the application being autofilled. For example, a dataset
|
||||
* should not contain fields for username, password, and credit card information. The reason for
|
||||
* this rule is that a malicious app could draft a view structure where the credit card fields
|
||||
* are not visible, so when the user selects a dataset from the username UI, the credit card info is
|
||||
* released to the application without the user knowledge. Similar, it's recommended to always
|
||||
* protect a dataset that contains sensitive information by requiring dataset authentication
|
||||
* (see {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}).
|
||||
*
|
||||
* <p>When the service detects that a screen have multiple partitions, it should return a
|
||||
* {@link FillResponse} with just the datasets for the partition that originated the request (i.e.,
|
||||
* the partition that has the {@link android.app.assist.AssistStructure.ViewNode} whose
|
||||
* {@link android.app.assist.AssistStructure.ViewNode#isFocused()} returns {@code true}); then if
|
||||
* the user selects a field from a different partition, the Android System will make another
|
||||
* {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} call for that partition,
|
||||
* and so on.
|
||||
*
|
||||
* <p>Notice that when the user autofill a partition with the data provided by the service and the
|
||||
* user did not change these fields, the autofilled value is sent back to the service in the
|
||||
* subsequent calls (and can be obtained by calling
|
||||
* {@link android.app.assist.AssistStructure.ViewNode#getAutofillValue()}). This is useful in the
|
||||
* cases where the service must create datasets for a partition based on the choice made in a
|
||||
* previous partition. For example, the 1st response for a screen that have credentials and address
|
||||
* partitions could be:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .addDataset(new Dataset.Builder() // partition 1 (credentials)
|
||||
* .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer"))
|
||||
* .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer"))
|
||||
* .build())
|
||||
* .addDataset(new Dataset.Builder() // partition 1 (credentials)
|
||||
* .setValue(id1, AutofillValue.forText("flanders"), createPresentation("flanders"))
|
||||
* .setValue(id2, AutofillValue.forText("OkelyDokelyDo"), createPresentation("password for flanders"))
|
||||
* .build())
|
||||
* .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_PASSWORD,
|
||||
* new AutofillId[] { id1, id2 })
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* <p>Then if the user selected {@code flanders}, the service would get a new
|
||||
* {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} call, with the values of
|
||||
* the fields {@code id1} and {@code id2} prepopulated, so the service could then fetch the address
|
||||
* for the Flanders account and return the following {@link FillResponse} for the address partition:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .addDataset(new Dataset.Builder() // partition 2 (address)
|
||||
* .setValue(id3, AutofillValue.forText("744 Evergreen Terrace"), createPresentation("744 Evergreen Terrace")) // street
|
||||
* .setValue(id4, AutofillValue.forText("Springfield"), createPresentation("Springfield")) // city
|
||||
* .build())
|
||||
* .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_PASSWORD | SaveInfo.SAVE_DATA_TYPE_ADDRESS,
|
||||
* new AutofillId[] { id1, id2 }) // username and password
|
||||
* .setOptionalIds(new AutofillId[] { id3, id4 }) // state and zipcode
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* <p>When the service returns multiple {@link FillResponse}, the last one overrides the previous;
|
||||
* that's why the {@link SaveInfo} in the 2nd request above has the info for both partitions.
|
||||
*
|
||||
* <h3>Ignoring views</h3>
|
||||
*
|
||||
* <p>If the service find views that cannot be autofilled (for example, a text field representing
|
||||
* the response to a Captcha challenge), it should mark those views as ignored by
|
||||
* calling {@link FillResponse.Builder#setIgnoredIds(AutofillId...)} so the system does not trigger
|
||||
* a new {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback)} when these views are
|
||||
* focused.
|
||||
*/
|
||||
public abstract class AutofillService extends Service {
|
||||
private static final String TAG = "AutofillService";
|
||||
@@ -132,11 +338,6 @@ public abstract class AutofillService extends Service {
|
||||
|
||||
private HandlerCaller mHandlerCaller;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <strong>NOTE: </strong>if overridden, it must call {@code super.onCreate()}.
|
||||
*/
|
||||
@CallSuper
|
||||
@Override
|
||||
public void onCreate() {
|
||||
@@ -162,8 +363,7 @@ public abstract class AutofillService extends Service {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Android system do decide if an {@link Activity} can be autofilled by the
|
||||
* service.
|
||||
* Called by the Android system do decide if a screen can be autofilled by the service.
|
||||
*
|
||||
* <p>Service must call one of the {@link FillCallback} methods (like
|
||||
* {@link FillCallback#onSuccess(FillResponse)}
|
||||
@@ -181,7 +381,7 @@ public abstract class AutofillService extends Service {
|
||||
@NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
|
||||
|
||||
/**
|
||||
* Called when user requests service to save the fields of an {@link Activity}.
|
||||
* Called when user requests service to save the fields of a screen.
|
||||
*
|
||||
* <p>Service must call one of the {@link SaveCallback} methods (like
|
||||
* {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
|
||||
@@ -226,7 +426,7 @@ public abstract class AutofillService extends Service {
|
||||
* @return The history or {@code null} if there are no events.
|
||||
*/
|
||||
@Nullable public final FillEventHistory getFillEventHistory() {
|
||||
AutofillManager afm = getSystemService(AutofillManager.class);
|
||||
final AutofillManager afm = getSystemService(AutofillManager.class);
|
||||
|
||||
if (afm == null) {
|
||||
return null;
|
||||
|
||||
@@ -31,17 +31,23 @@ import com.android.internal.util.Preconditions;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A set of data that can be used to autofill an {@link android.app.Activity}.
|
||||
* A dataset object represents a group of key/value pairs used to autofill parts of a screen.
|
||||
*
|
||||
* <p>It contains:
|
||||
* <p>In its simplest form, a dataset contains one or more key / value pairs (comprised of
|
||||
* {@link AutofillId} and {@link AutofillValue} respectively); and one or more
|
||||
* {@link RemoteViews presentation} for these pairs (a pair could have its own
|
||||
* {@link RemoteViews presentation}, or use the default {@link RemoteViews presentation} associated
|
||||
* with the whole dataset). When an autofill service returns datasets in a {@link FillResponse}
|
||||
* and the screen input is focused in a view that is present in at least one of these datasets,
|
||||
* the Android System displays a UI affordance containing the {@link RemoteViews presentation} of
|
||||
* all datasets pairs that have that view's {@link AutofillId}. Then, when the user selects a
|
||||
* dataset from the affordance, all views in that dataset are autofilled.
|
||||
*
|
||||
* <ol>
|
||||
* <li>A list of values for input fields.
|
||||
* <li>A presentation view to visualize.
|
||||
* <li>An optional intent to authenticate.
|
||||
* </ol>
|
||||
* <p>In a more sophisticated form, the dataset value can be protected until the user authenticates
|
||||
* the dataset - see {@link Dataset.Builder#setAuthentication(IntentSender)}.
|
||||
*
|
||||
* @see android.service.autofill.FillResponse for examples.
|
||||
* @see android.service.autofill.AutofillService for more information and examples about the
|
||||
* role of datasets in the autofill workflow.
|
||||
*/
|
||||
public final class Dataset implements Parcelable {
|
||||
|
||||
@@ -113,7 +119,7 @@ public final class Dataset implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for {@link Dataset} objects. You must to provide at least
|
||||
* A builder for {@link Dataset} objects. You must provide at least
|
||||
* one value for a field or set an authentication intent.
|
||||
*/
|
||||
public static final class Builder {
|
||||
@@ -175,9 +181,9 @@ public final class Dataset implements Parcelable {
|
||||
* credit card information without the CVV for the data set in the {@link FillResponse
|
||||
* response} then the returned data set should contain the CVV entry.
|
||||
*
|
||||
* <p></><strong>Note:</strong> Do not make the provided pending intent
|
||||
* <p><b>NOTE:</b> Do not make the provided pending intent
|
||||
* immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
|
||||
* platform needs to fill in the authentication arguments.</p>
|
||||
* platform needs to fill in the authentication arguments.
|
||||
*
|
||||
* @param authentication Intent to an activity with your authentication flow.
|
||||
* @return This builder.
|
||||
@@ -191,7 +197,7 @@ public final class Dataset implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id for the dataset.
|
||||
* Sets the id for the dataset so its usage history can be retrieved later.
|
||||
*
|
||||
* <p>The id of the last selected dataset can be read from
|
||||
* {@link AutofillService#getFillEventHistory()}. If the id is not set it will not be clear
|
||||
@@ -214,13 +220,12 @@ public final class Dataset implements Parcelable {
|
||||
*
|
||||
* @param id id returned by {@link
|
||||
* android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
|
||||
* @param value value to be auto filled. Pass {@code null} if you do not have the value
|
||||
* @param value value to be autofilled. Pass {@code null} if you do not have the value
|
||||
* but the target view is a logical part of the dataset. For example, if
|
||||
* the dataset needs an authentication and you have no access to the value.
|
||||
* Filtering matches any user typed string to {@code null} values.
|
||||
* @return This builder.
|
||||
* @throws IllegalStateException if the builder was constructed without a presentation
|
||||
* ({@link RemoteViews}).
|
||||
* @throws IllegalStateException if the builder was constructed without a
|
||||
* {@link RemoteViews presentation}.
|
||||
*/
|
||||
public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
|
||||
throwIfDestroyed();
|
||||
@@ -232,7 +237,8 @@ public final class Dataset implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of a field, using a custom presentation to visualize it.
|
||||
* Sets the value of a field, using a custom {@link RemoteViews presentation} to
|
||||
* visualize it.
|
||||
*
|
||||
* @param id id returned by {@link
|
||||
* android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
|
||||
@@ -272,10 +278,12 @@ public final class Dataset implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Dataset} instance. You should not interact
|
||||
* with this builder once this method is called. It is required
|
||||
* that you specified at least one field. Also it is mandatory to
|
||||
* provide a presentation view to visualize the data set in the UI.
|
||||
* Creates a new {@link Dataset} instance.
|
||||
*
|
||||
* <p>You should not interact with this builder once this method is called.
|
||||
*
|
||||
* <p>It is required that you specify at least one field before calling this method. It's
|
||||
* also mandatory to provide a presentation view to visualize the data set in the UI.
|
||||
*
|
||||
* @return The built dataset.
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,6 @@ import android.util.ArrayMap;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.autofill.AutofillId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
@@ -32,7 +33,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class represents a request to an {@link AutofillService autofill provider}
|
||||
* This class represents a request to an autofill service
|
||||
* to interpret the screen and provide information to the system which views are
|
||||
* interesting for saving and what are the possible ways to fill the inputs on
|
||||
* the screen if applicable.
|
||||
@@ -40,8 +41,29 @@ import java.util.List;
|
||||
* @see AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
|
||||
*/
|
||||
public final class FillRequest implements Parcelable {
|
||||
|
||||
/**
|
||||
* Indicates autofill was explicitly requested by the user.
|
||||
*
|
||||
* <p>Users typically make an explicit request to autofill a screen in two situations:
|
||||
* <ul>
|
||||
* <li>The app disabled autofill (using {@link View#setImportantForAutofill(int)}.
|
||||
* <li>The service could not figure out how to autofill a screen (but the user knows the
|
||||
* service has data for that app).
|
||||
* </ul>
|
||||
*
|
||||
* <p>This flag is particularly useful for the second case. For example, the service could offer
|
||||
* a complex UI where the user can map which screen views belong to each user data, or it could
|
||||
* offer a simpler UI where the user picks the data for just the view used to trigger the
|
||||
* request (that would be the view whose
|
||||
* {@link android.app.assist.AssistStructure.ViewNode#isFocused()} method returns {@code true}).
|
||||
*
|
||||
* <p>An explicit autofill request is triggered when the
|
||||
* {@link android.view.autofill.AutofillManager#requestAutofill(View)} or
|
||||
* {@link android.view.autofill.AutofillManager#requestAutofill(View, int, android.graphics.Rect)}
|
||||
* is called. For example, standard {@link android.widget.TextView} views that use
|
||||
* an {@link android.widget.Editor} shows an {@code AUTOFILL} option in the overflow menu that
|
||||
* triggers such request.
|
||||
*/
|
||||
public static final int FLAG_MANUAL_REQUEST = 0x1;
|
||||
|
||||
@@ -79,14 +101,14 @@ public final class FillRequest implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The unique id of this request.
|
||||
* Gets the unique id of this request.
|
||||
*/
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The flags associated with this request.
|
||||
* Gets the flags associated with this request.
|
||||
*
|
||||
* @see #FLAG_MANUAL_REQUEST
|
||||
*/
|
||||
@@ -95,7 +117,7 @@ public final class FillRequest implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The contexts associated with each previous fill request.
|
||||
* Gets the contexts associated with each previous fill request.
|
||||
*/
|
||||
public @NonNull List<FillContext> getFillContexts() {
|
||||
return mContexts;
|
||||
@@ -104,10 +126,10 @@ public final class FillRequest implements Parcelable {
|
||||
/**
|
||||
* Gets the extra client state returned from the last {@link
|
||||
* AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
|
||||
* fill request}.
|
||||
* <p>
|
||||
* Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
|
||||
* save request} is made the client state is cleared.
|
||||
* fill request}, so the service can use it for state management.
|
||||
*
|
||||
* <p>Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
|
||||
* save request} is made, the client state is cleared.
|
||||
*
|
||||
* @return The client state.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.view.autofill.Helper.sDebug;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.content.IntentSender;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
@@ -36,100 +37,7 @@ import java.util.Arrays;
|
||||
* Response for a {@link
|
||||
* AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
|
||||
*
|
||||
* <p>The response typically contains one or more {@link Dataset}s, each representing a set of
|
||||
* fields that can be autofilled together, and the Android system displays a dataset picker UI
|
||||
* affordance that the user must use before the {@link android.app.Activity} is filled with
|
||||
* the dataset.
|
||||
*
|
||||
* <p>For example, for a login page with username/password where the user only has one account in
|
||||
* the response could be:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .add(new Dataset.Builder(createPresentation())
|
||||
* .setValue(id1, AutofillValue.forText("homer"))
|
||||
* .setValue(id2, AutofillValue.forText("D'OH!"))
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* <p>If the user had 2 accounts, each with its own user-provided names, the response could be:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .add(new Dataset.Builder(createFirstPresentation())
|
||||
* .setValue(id1, AutofillValue.forText("homer"))
|
||||
* .setValue(id2, AutofillValue.forText("D'OH!"))
|
||||
* .build())
|
||||
* .add(new Dataset.Builder(createSecondPresentation())
|
||||
* .setValue(id1, AutofillValue.forText("elbarto")
|
||||
* .setValue(id2, AutofillValue.forText("cowabonga")
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* If the service is interested on saving the user-edited data back, it must set a {@link SaveInfo}
|
||||
* in the {@link FillResponse}. Typically, the {@link SaveInfo} contains the same ids as the
|
||||
* {@link Dataset}, but other combinations are possible - see {@link SaveInfo} for more details
|
||||
*
|
||||
* <p>If the service has multiple {@link Dataset}s for different sections of the activity,
|
||||
* for example, a user section for which there are two datasets followed by an address
|
||||
* section for which there are two datasets for each user user, then it should "partition"
|
||||
* the activity in sections and populate the response with just a subset of the data that would
|
||||
* fulfill the first section (the name in our example); then once the user fills the first
|
||||
* section and taps a field from the next section (the address in our example), the Android
|
||||
* system would issue another request for that section, and so on. Note that if the user
|
||||
* chooses to populate the first section with a service provided dataset, the subsequent request
|
||||
* would contain the populated values so you don't try to provide suggestions for the first
|
||||
* section but ony for the second one based on the context of what was already filled. For
|
||||
* example, the first response could be:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .add(new Dataset.Builder(createFirstPresentation())
|
||||
* .setValue(id1, AutofillValue.forText("Homer"))
|
||||
* .setValue(id2, AutofillValue.forText("Simpson"))
|
||||
* .build())
|
||||
* .add(new Dataset.Builder(createSecondPresentation())
|
||||
* .setValue(id1, AutofillValue.forText("Bart"))
|
||||
* .setValue(id2, AutofillValue.forText("Simpson"))
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* <p>Then after the user picks the second dataset and taps the street field to
|
||||
* trigger another autofill request, the second response could be:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .add(new Dataset.Builder(createThirdPresentation())
|
||||
* .setValue(id3, AutofillValue.forText("742 Evergreen Terrace"))
|
||||
* .setValue(id4, AutofillValue.forText("Springfield"))
|
||||
* .build())
|
||||
* .add(new Dataset.Builder(createFourthPresentation())
|
||||
* .setValue(id3, AutofillValue.forText("Springfield Power Plant"))
|
||||
* .setValue(id4, AutofillValue.forText("Springfield"))
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* <p>The service could require user authentication at the {@link FillResponse} or the
|
||||
* {@link Dataset} level, prior to autofilling an activity - see
|
||||
* {@link FillResponse.Builder#setAuthentication(AutofillId[], IntentSender, RemoteViews)} and
|
||||
* {@link Dataset.Builder#setAuthentication(IntentSender)}.
|
||||
*
|
||||
* <p>It is recommended that you encrypt only the sensitive data but leave the labels unencrypted
|
||||
* which would allow you to provide a dataset presentation views with labels and if the user
|
||||
* chooses one of them challenge the user to authenticate. For example, if the user has a
|
||||
* home and a work address the Home and Work labels could be stored unencrypted as they don't
|
||||
* have any sensitive data while the address data is in an encrypted storage. If the user
|
||||
* chooses Home, then the platform will start your authentication flow. If you encrypt all
|
||||
* data and require auth at the response level the user will have to interact with the fill
|
||||
* UI to trigger a request for the datasets (as they don't see the presentation views for the
|
||||
* possible options) which will start your auth flow and after successfully authenticating
|
||||
* the user will be presented with the Home and Work options to pick one. Hence, you have
|
||||
* flexibility how to implement your auth while storing labels non-encrypted and data
|
||||
* encrypted provides a better user experience.
|
||||
* <p>See the main {@link AutofillService} documentation for more details and examples.
|
||||
*/
|
||||
public final class FillResponse implements Parcelable {
|
||||
|
||||
@@ -221,7 +129,7 @@ public final class FillResponse implements Parcelable {
|
||||
private boolean mDestroyed;
|
||||
|
||||
/**
|
||||
* Requires a fill response authentication before autofilling the activity with
|
||||
* Requires a fill response authentication before autofilling the screen with
|
||||
* any data set in this response.
|
||||
*
|
||||
* <p>This is typically useful when a user interaction is required to unlock their
|
||||
@@ -230,16 +138,16 @@ public final class FillResponse implements Parcelable {
|
||||
* auth on the data set level leading to a better user experience. Note that if you
|
||||
* use sensitive data as a label, for example an email address, then it should also
|
||||
* be encrypted. The provided {@link android.app.PendingIntent intent} must be an
|
||||
* activity which implements your authentication flow. Also if you provide an auth
|
||||
* {@link Activity} which implements your authentication flow. Also if you provide an auth
|
||||
* intent you also need to specify the presentation view to be shown in the fill UI
|
||||
* for the user to trigger your authentication flow.
|
||||
*
|
||||
* <p>When a user triggers autofill, the system launches the provided intent
|
||||
* whose extras will have the {@link AutofillManager#EXTRA_ASSIST_STRUCTURE screen
|
||||
* content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
|
||||
* client state}. Once you complete your authentication flow you should set the activity
|
||||
* result to {@link android.app.Activity#RESULT_OK} and provide the fully populated
|
||||
* {@link FillResponse response} by setting it to the {@link
|
||||
* client state}. Once you complete your authentication flow you should set the
|
||||
* {@link Activity} result to {@link android.app.Activity#RESULT_OK} and provide the fully
|
||||
* populated {@link FillResponse response} by setting it to the {@link
|
||||
* AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra.
|
||||
* For example, if you provided an empty {@link FillResponse resppnse} because the
|
||||
* user's data was locked and marked that the response needs an authentication then
|
||||
@@ -286,8 +194,8 @@ public final class FillResponse implements Parcelable {
|
||||
* {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
|
||||
* FillCallback)} requests.
|
||||
*
|
||||
* <p>This is typically used when the service cannot autofill the view; for example, an
|
||||
* {@code EditText} representing a captcha.
|
||||
* <p>This is typically used when the service cannot autofill the view; for example, a
|
||||
* text field representing the result of a Captcha challenge.
|
||||
*/
|
||||
public Builder setIgnoredIds(AutofillId...ids) {
|
||||
mIgnoredIds = ids;
|
||||
@@ -316,8 +224,6 @@ public final class FillResponse implements Parcelable {
|
||||
/**
|
||||
* Sets the {@link SaveInfo} associated with this response.
|
||||
*
|
||||
* <p>See {@link FillResponse} for more info.
|
||||
*
|
||||
* @return This builder.
|
||||
*/
|
||||
public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) {
|
||||
@@ -335,7 +241,7 @@ public final class FillResponse implements Parcelable {
|
||||
* fill requests and the subsequent save request.
|
||||
*
|
||||
* <p>If this method is called on multiple {@link FillResponse} objects for the same
|
||||
* activity, just the latest bundle is passed back to the service.
|
||||
* screen, just the latest bundle is passed back to the service.
|
||||
*
|
||||
* <p>Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
|
||||
* save request} is made the client state is cleared.
|
||||
@@ -350,9 +256,10 @@ public final class FillResponse implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new {@link FillResponse} instance. You must provide at least
|
||||
* one dataset or some savable ids or an authentication with a presentation
|
||||
* view.
|
||||
* Builds a new {@link FillResponse} instance.
|
||||
*
|
||||
* <p>You must provide at least one dataset or some savable ids or an authentication with a
|
||||
* presentation view.
|
||||
*
|
||||
* @return A built response.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.view.autofill.Helper.sDebug;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.content.IntentSender;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
@@ -45,70 +46,91 @@ import java.util.Arrays;
|
||||
* two pieces of information:
|
||||
*
|
||||
* <ol>
|
||||
* <li>The type of user data that would be saved (like passoword or credit card info).
|
||||
* <li>The type(s) of user data (like password or credit card info) that would be saved.
|
||||
* <li>The minimum set of views (represented by their {@link AutofillId}) that need to be changed
|
||||
* to trigger a save request.
|
||||
* </ol>
|
||||
*
|
||||
* Typically, the {@link SaveInfo} contains the same {@code id}s as the {@link Dataset}:
|
||||
* <p>Typically, the {@link SaveInfo} contains the same {@code id}s as the {@link Dataset}:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .add(new Dataset.Builder(createPresentation())
|
||||
* .setValue(id1, AutofillValue.forText("homer"))
|
||||
* .setValue(id2, AutofillValue.forText("D'OH!"))
|
||||
* .build())
|
||||
* .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_PASSWORD, new int[] {id1, id2})
|
||||
* .build())
|
||||
* .build();
|
||||
* new FillResponse.Builder()
|
||||
* .addDataset(new Dataset.Builder()
|
||||
* .setValue(id1, AutofillValue.forText("homer"), createPresentation("homer")) // username
|
||||
* .setValue(id2, AutofillValue.forText("D'OH!"), createPresentation("password for homer")) // password
|
||||
* .build())
|
||||
* .setSaveInfo(new SaveInfo.Builder(
|
||||
* SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD,
|
||||
* new AutofillId[] { id1, id2 }).build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* There might be cases where the {@link AutofillService} knows how to fill the
|
||||
* {@link android.app.Activity}, but the user has no data for it. In that case, the
|
||||
* {@link FillResponse} should contain just the {@link SaveInfo}, but no {@link Dataset}s:
|
||||
* <p>The save type flags are used to display the appropriate strings in the save UI affordance.
|
||||
* 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.
|
||||
*
|
||||
* <p>There might be cases where the {@link AutofillService} knows how to fill the screen,
|
||||
* but the user has no data for it. In that case, the {@link FillResponse} should contain just the
|
||||
* {@link SaveInfo}, but no {@link Dataset Datasets}:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_PASSWORD, new int[] {id1, id2})
|
||||
* .build())
|
||||
* .build();
|
||||
* new FillResponse.Builder()
|
||||
* .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_PASSWORD,
|
||||
* new AutofillId[] { id1, id2 }).build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* <p>There might be cases where the user data in the {@link AutofillService} is enough
|
||||
* to populate some fields but not all, and the service would still be interested on saving the
|
||||
* other fields. In this scenario, the service could set the
|
||||
* other fields. In that case, the service could set the
|
||||
* {@link SaveInfo.Builder#setOptionalIds(AutofillId[])} as well:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* new FillResponse.Builder()
|
||||
* .add(new Dataset.Builder(createPresentation())
|
||||
* .setValue(id1, AutofillValue.forText("742 Evergreen Terrace")) // street
|
||||
* .setValue(id2, AutofillValue.forText("Springfield")) // city
|
||||
* .build())
|
||||
* .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_ADDRESS, new int[] {id1, id2})
|
||||
* .setOptionalIds(new int[] {id3, id4}) // state and zipcode
|
||||
* .build())
|
||||
* .addDataset(new Dataset.Builder()
|
||||
* .setValue(id1, AutofillValue.forText("742 Evergreen Terrace"),
|
||||
* createPresentation("742 Evergreen Terrace")) // street
|
||||
* .setValue(id2, AutofillValue.forText("Springfield"),
|
||||
* createPresentation("Springfield")) // city
|
||||
* .build())
|
||||
* .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_DATA_TYPE_ADDRESS,
|
||||
* new AutofillId[] { id1, id2 }) // street and city
|
||||
* .setOptionalIds(new AutofillId[] { id3, id4 }) // state and zipcode
|
||||
* .build())
|
||||
* .build();
|
||||
* </pre>
|
||||
*
|
||||
* The
|
||||
* {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
|
||||
* is triggered after a call to {@link AutofillManager#commit()}, but only when all conditions
|
||||
* below are met:
|
||||
* <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
|
||||
* {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} flag).
|
||||
* </ul>
|
||||
*
|
||||
* <ol>
|
||||
* <p>But it is only triggered when all conditions below are met:
|
||||
* <ul>
|
||||
* <li>The {@link SaveInfo} associated with the {@link FillResponse} is not {@code null}.
|
||||
* <li>The {@link AutofillValue} of all required views (as set by the {@code requiredIds} passed
|
||||
* to {@link SaveInfo.Builder} constructor are not empty.
|
||||
* <li>The {@link AutofillValue}s of all required views (as set by the {@code requiredIds} passed
|
||||
* to the {@link SaveInfo.Builder} constructor are not empty.
|
||||
* <li>The {@link AutofillValue} of at least one view (be it required or optional) has changed
|
||||
* (i.e., it's not the same value passed in a {@link Dataset}).
|
||||
* <li>The user explicitly tapped the affordance asking to save data for autofill.
|
||||
* </ol>
|
||||
* (i.e., it's neither the same value passed in a {@link Dataset}, nor the initial value
|
||||
* presented in the view).
|
||||
* <li>The user explicitly tapped the UI affordance asking to save data for autofill.
|
||||
* </ul>
|
||||
*
|
||||
* <p>The service can also customize some aspects of the save UI affordance:
|
||||
* <ul>
|
||||
* <li>Add a subtitle by calling {@link Builder#setDescription(CharSequence)}.
|
||||
* <li>Customize the button used to reject the save request by calling
|
||||
* {@link Builder#setNegativeAction(int, IntentSender)}.
|
||||
* </ul>
|
||||
*/
|
||||
public final class SaveInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* Type used on when the service can save the contents of an activity, but cannot describe what
|
||||
* Type used when the service can save the contents of a screen, but cannot describe what
|
||||
* the content is for.
|
||||
*/
|
||||
public static final int SAVE_DATA_TYPE_GENERIC = 0x0;
|
||||
@@ -181,8 +203,8 @@ public final class SaveInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* Usually {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
|
||||
* is called once the activity finishes. If this flag is set it is called once all saved views
|
||||
* become invisible.
|
||||
* is called once the {@link Activity} finishes. If this flag is set it is called once all
|
||||
* saved views become invisible.
|
||||
*/
|
||||
public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x1;
|
||||
|
||||
@@ -294,9 +316,9 @@ public final class SaveInfo implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flags changing the save behavior.
|
||||
* Sets flags changing the save behavior.
|
||||
*
|
||||
* @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or 0.
|
||||
* @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or {@code 0}.
|
||||
* @return This builder.
|
||||
*/
|
||||
public @NonNull Builder setFlags(@SaveInfoFlags int flags) {
|
||||
|
||||
Reference in New Issue
Block a user