Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.autofillservice.cts;
     17 
     18 import static android.autofillservice.cts.Helper.getAutofillIds;
     19 
     20 import static com.google.common.truth.Truth.assertWithMessage;
     21 
     22 import android.app.assist.AssistStructure;
     23 import android.app.assist.AssistStructure.ViewNode;
     24 import android.content.IntentSender;
     25 import android.os.Bundle;
     26 import android.service.autofill.Dataset;
     27 import android.service.autofill.FillCallback;
     28 import android.service.autofill.FillContext;
     29 import android.service.autofill.FillResponse;
     30 import android.service.autofill.SaveInfo;
     31 import android.service.autofill.UserData;
     32 import android.util.Log;
     33 import android.util.Pair;
     34 import android.view.autofill.AutofillId;
     35 import android.view.autofill.AutofillValue;
     36 import android.widget.RemoteViews;
     37 
     38 import androidx.annotation.NonNull;
     39 import androidx.annotation.Nullable;
     40 
     41 import java.util.ArrayList;
     42 import java.util.Arrays;
     43 import java.util.HashMap;
     44 import java.util.List;
     45 import java.util.Map;
     46 import java.util.function.Function;
     47 import java.util.regex.Pattern;
     48 
     49 /**
     50  * Helper class used to produce a {@link FillResponse} based on expected fields that should be
     51  * present in the {@link AssistStructure}.
     52  *
     53  * <p>Typical usage:
     54  *
     55  * <pre class="prettyprint">
     56  * InstrumentedAutoFillService.setFillResponse(new CannedFillResponse.Builder()
     57  *               .addDataset(new CannedDataset.Builder("dataset_name")
     58  *                   .setField("resource_id1", AutofillValue.forText("value1"))
     59  *                   .setField("resource_id2", AutofillValue.forText("value2"))
     60  *                   .build())
     61  *               .build());
     62  * </pre class="prettyprint">
     63  */
     64 public final class CannedFillResponse {
     65 
     66     private static final String TAG = CannedFillResponse.class.getSimpleName();
     67 
     68     private final ResponseType mResponseType;
     69     private final List<CannedDataset> mDatasets;
     70     private final String mFailureMessage;
     71     private final int mSaveType;
     72     private final String[] mRequiredSavableIds;
     73     private final String[] mOptionalSavableIds;
     74     private final AutofillId[] mRequiredSavableAutofillIds;
     75     private final String mSaveDescription;
     76     private final Bundle mExtras;
     77     private final RemoteViews mPresentation;
     78     private final RemoteViews mHeader;
     79     private final RemoteViews mFooter;
     80     private final IntentSender mAuthentication;
     81     private final String[] mAuthenticationIds;
     82     private final String[] mIgnoredIds;
     83     private final int mNegativeActionStyle;
     84     private final IntentSender mNegativeActionListener;
     85     private final int mSaveInfoFlags;
     86     private final int mFillResponseFlags;
     87     private final AutofillId mSaveTriggerId;
     88     private final long mDisableDuration;
     89     private final String[] mFieldClassificationIds;
     90     private final boolean mFieldClassificationIdsOverflow;
     91     private final SaveInfoDecorator mSaveInfoDecorator;
     92     private final UserData mUserData;
     93     private final DoubleVisitor<List<FillContext>, FillResponse.Builder> mVisitor;
     94     private DoubleVisitor<List<FillContext>, SaveInfo.Builder> mSaveInfoVisitor;
     95 
     96     private CannedFillResponse(Builder builder) {
     97         mResponseType = builder.mResponseType;
     98         mDatasets = builder.mDatasets;
     99         mFailureMessage = builder.mFailureMessage;
    100         mRequiredSavableIds = builder.mRequiredSavableIds;
    101         mRequiredSavableAutofillIds = builder.mRequiredSavableAutofillIds;
    102         mOptionalSavableIds = builder.mOptionalSavableIds;
    103         mSaveDescription = builder.mSaveDescription;
    104         mSaveType = builder.mSaveType;
    105         mExtras = builder.mExtras;
    106         mPresentation = builder.mPresentation;
    107         mHeader = builder.mHeader;
    108         mFooter = builder.mFooter;
    109         mAuthentication = builder.mAuthentication;
    110         mAuthenticationIds = builder.mAuthenticationIds;
    111         mIgnoredIds = builder.mIgnoredIds;
    112         mNegativeActionStyle = builder.mNegativeActionStyle;
    113         mNegativeActionListener = builder.mNegativeActionListener;
    114         mSaveInfoFlags = builder.mSaveInfoFlags;
    115         mFillResponseFlags = builder.mFillResponseFlags;
    116         mSaveTriggerId = builder.mSaveTriggerId;
    117         mDisableDuration = builder.mDisableDuration;
    118         mFieldClassificationIds = builder.mFieldClassificationIds;
    119         mFieldClassificationIdsOverflow = builder.mFieldClassificationIdsOverflow;
    120         mSaveInfoDecorator = builder.mSaveInfoDecorator;
    121         mUserData = builder.mUserData;
    122         mVisitor = builder.mVisitor;
    123         mSaveInfoVisitor = builder.mSaveInfoVisitor;
    124     }
    125 
    126     /**
    127      * Constant used to pass a {@code null} response to the
    128      * {@link FillCallback#onSuccess(FillResponse)} method.
    129      */
    130     public static final CannedFillResponse NO_RESPONSE =
    131             new Builder(ResponseType.NULL).build();
    132 
    133     /**
    134      * Constant used to fail the test when an expected request was made.
    135      */
    136     public static final CannedFillResponse NO_MOAR_RESPONSES =
    137             new Builder(ResponseType.NO_MORE).build();
    138 
    139     /**
    140      * Constant used to emulate a timeout by not calling any method on {@link FillCallback}.
    141      */
    142     public static final CannedFillResponse DO_NOT_REPLY_RESPONSE =
    143             new Builder(ResponseType.TIMEOUT).build();
    144 
    145     /**
    146      * Constant used to call {@link FillCallback#onFailure(CharSequence)} method.
    147      */
    148     public static final CannedFillResponse FAIL =
    149             new Builder(ResponseType.FAILURE).build();
    150 
    151     public String getFailureMessage() {
    152         return mFailureMessage;
    153     }
    154 
    155     public ResponseType getResponseType() {
    156         return mResponseType;
    157     }
    158 
    159     /**
    160      * Creates a new response, replacing the dataset field ids by the real ids from the assist
    161      * structure.
    162      */
    163     public FillResponse asFillResponse(@Nullable List<FillContext> contexts,
    164             @NonNull Function<String, ViewNode> nodeResolver) {
    165         final FillResponse.Builder builder = new FillResponse.Builder()
    166                 .setFlags(mFillResponseFlags);
    167         if (mDatasets != null) {
    168             for (CannedDataset cannedDataset : mDatasets) {
    169                 final Dataset dataset = cannedDataset.asDataset(nodeResolver);
    170                 assertWithMessage("Cannot create datase").that(dataset).isNotNull();
    171                 builder.addDataset(dataset);
    172             }
    173         }
    174         final SaveInfo.Builder saveInfoBuilder;
    175         if (mRequiredSavableIds != null || mOptionalSavableIds != null
    176                 || mRequiredSavableAutofillIds != null || mSaveInfoDecorator != null) {
    177             if (mRequiredSavableAutofillIds != null) {
    178                 saveInfoBuilder = new SaveInfo.Builder(mSaveType, mRequiredSavableAutofillIds);
    179             } else {
    180                 saveInfoBuilder = mRequiredSavableIds == null || mRequiredSavableIds.length == 0
    181                         ? new SaveInfo.Builder(mSaveType)
    182                             : new SaveInfo.Builder(mSaveType,
    183                                     getAutofillIds(nodeResolver, mRequiredSavableIds));
    184             }
    185 
    186             saveInfoBuilder.setFlags(mSaveInfoFlags);
    187 
    188             if (mOptionalSavableIds != null) {
    189                 saveInfoBuilder.setOptionalIds(getAutofillIds(nodeResolver, mOptionalSavableIds));
    190             }
    191             if (mSaveDescription != null) {
    192                 saveInfoBuilder.setDescription(mSaveDescription);
    193             }
    194             if (mNegativeActionListener != null) {
    195                 saveInfoBuilder.setNegativeAction(mNegativeActionStyle, mNegativeActionListener);
    196             }
    197             if (mSaveTriggerId != null) {
    198                 saveInfoBuilder.setTriggerId(mSaveTriggerId);
    199             }
    200         } else if (mSaveInfoFlags != 0) {
    201             saveInfoBuilder = new SaveInfo.Builder(mSaveType).setFlags(mSaveInfoFlags);
    202         } else {
    203             saveInfoBuilder = null;
    204         }
    205         if (saveInfoBuilder != null) {
    206             // TODO: merge decorator and visitor
    207             if (mSaveInfoDecorator != null) {
    208                 mSaveInfoDecorator.decorate(saveInfoBuilder, nodeResolver);
    209             }
    210             if (mSaveInfoVisitor != null) {
    211                 Log.d(TAG, "Visiting saveInfo " + saveInfoBuilder);
    212                 mSaveInfoVisitor.visit(contexts, saveInfoBuilder);
    213             }
    214             final SaveInfo saveInfo = saveInfoBuilder.build();
    215             Log.d(TAG, "saveInfo:" + saveInfo);
    216             builder.setSaveInfo(saveInfo);
    217         }
    218         if (mIgnoredIds != null) {
    219             builder.setIgnoredIds(getAutofillIds(nodeResolver, mIgnoredIds));
    220         }
    221         if (mAuthenticationIds != null) {
    222             builder.setAuthentication(getAutofillIds(nodeResolver, mAuthenticationIds),
    223                     mAuthentication, mPresentation);
    224         }
    225         if (mDisableDuration > 0) {
    226             builder.disableAutofill(mDisableDuration);
    227         }
    228         if (mFieldClassificationIdsOverflow) {
    229             final int length = UserData.getMaxFieldClassificationIdsSize() + 1;
    230             final AutofillId[] fieldIds = new AutofillId[length];
    231             for (int i = 0; i < length; i++) {
    232                 fieldIds[i] = new AutofillId(i);
    233             }
    234             builder.setFieldClassificationIds(fieldIds);
    235         } else if (mFieldClassificationIds != null) {
    236             builder.setFieldClassificationIds(
    237                     getAutofillIds(nodeResolver, mFieldClassificationIds));
    238         }
    239         if (mExtras != null) {
    240             builder.setClientState(mExtras);
    241         }
    242         if (mHeader != null) {
    243             builder.setHeader(mHeader);
    244         }
    245         if (mFooter != null) {
    246             builder.setFooter(mFooter);
    247         }
    248         if (mUserData != null) {
    249             builder.setUserData(mUserData);
    250         }
    251         if (mVisitor != null) {
    252             Log.d(TAG, "Visiting " + builder);
    253             mVisitor.visit(contexts, builder);
    254         }
    255 
    256         final FillResponse response = builder.build();
    257         Log.v(TAG, "Response: " + response);
    258         return response;
    259     }
    260 
    261     @Override
    262     public String toString() {
    263         return "CannedFillResponse: [type=" + mResponseType
    264                 + ",datasets=" + mDatasets
    265                 + ", requiredSavableIds=" + Arrays.toString(mRequiredSavableIds)
    266                 + ", optionalSavableIds=" + Arrays.toString(mOptionalSavableIds)
    267                 + ", requiredSavableAutofillIds=" + Arrays.toString(mRequiredSavableAutofillIds)
    268                 + ", saveInfoFlags=" + mSaveInfoFlags
    269                 + ", fillResponseFlags=" + mFillResponseFlags
    270                 + ", failureMessage=" + mFailureMessage
    271                 + ", saveDescription=" + mSaveDescription
    272                 + ", hasPresentation=" + (mPresentation != null)
    273                 + ", hasHeader=" + (mHeader != null)
    274                 + ", hasFooter=" + (mFooter != null)
    275                 + ", hasAuthentication=" + (mAuthentication != null)
    276                 + ", authenticationIds=" + Arrays.toString(mAuthenticationIds)
    277                 + ", ignoredIds=" + Arrays.toString(mIgnoredIds)
    278                 + ", saveTriggerId=" + mSaveTriggerId
    279                 + ", disableDuration=" + mDisableDuration
    280                 + ", fieldClassificationIds=" + Arrays.toString(mFieldClassificationIds)
    281                 + ", fieldClassificationIdsOverflow=" + mFieldClassificationIdsOverflow
    282                 + ", saveInfoDecorator=" + mSaveInfoDecorator
    283                 + ", userData=" + mUserData
    284                 + ", visitor=" + mVisitor
    285                 + ", saveInfoVisitor=" + mSaveInfoVisitor
    286                 + "]";
    287     }
    288 
    289     public enum ResponseType {
    290         NORMAL,
    291         NULL,
    292         NO_MORE,
    293         TIMEOUT,
    294         FAILURE
    295     }
    296 
    297     public static final class Builder {
    298         private final List<CannedDataset> mDatasets = new ArrayList<>();
    299         private final ResponseType mResponseType;
    300         private String mFailureMessage;
    301         private String[] mRequiredSavableIds;
    302         private String[] mOptionalSavableIds;
    303         private AutofillId[] mRequiredSavableAutofillIds;
    304         private String mSaveDescription;
    305         public int mSaveType = -1;
    306         private Bundle mExtras;
    307         private RemoteViews mPresentation;
    308         private RemoteViews mFooter;
    309         private RemoteViews mHeader;
    310         private IntentSender mAuthentication;
    311         private String[] mAuthenticationIds;
    312         private String[] mIgnoredIds;
    313         private int mNegativeActionStyle;
    314         private IntentSender mNegativeActionListener;
    315         private int mSaveInfoFlags;
    316         private int mFillResponseFlags;
    317         private AutofillId mSaveTriggerId;
    318         private long mDisableDuration;
    319         private String[] mFieldClassificationIds;
    320         private boolean mFieldClassificationIdsOverflow;
    321         private SaveInfoDecorator mSaveInfoDecorator;
    322         private UserData mUserData;
    323         private DoubleVisitor<List<FillContext>, FillResponse.Builder> mVisitor;
    324         private DoubleVisitor<List<FillContext>, SaveInfo.Builder> mSaveInfoVisitor;
    325 
    326         public Builder(ResponseType type) {
    327             mResponseType = type;
    328         }
    329 
    330         public Builder() {
    331             this(ResponseType.NORMAL);
    332         }
    333 
    334         public Builder addDataset(CannedDataset dataset) {
    335             assertWithMessage("already set failure").that(mFailureMessage).isNull();
    336             mDatasets.add(dataset);
    337             return this;
    338         }
    339 
    340         /**
    341          * Sets the required savable ids based on their {@code resourceId}.
    342          */
    343         public Builder setRequiredSavableIds(int type, String... ids) {
    344             mSaveType = type;
    345             mRequiredSavableIds = ids;
    346             return this;
    347         }
    348 
    349         public Builder setSaveInfoFlags(int flags) {
    350             mSaveInfoFlags = flags;
    351             return this;
    352         }
    353 
    354         public Builder setFillResponseFlags(int flags) {
    355             mFillResponseFlags = flags;
    356             return this;
    357         }
    358 
    359         /**
    360          * Sets the optional savable ids based on they {@code resourceId}.
    361          */
    362         public Builder setOptionalSavableIds(String... ids) {
    363             mOptionalSavableIds = ids;
    364             return this;
    365         }
    366 
    367         /**
    368          * Sets the description passed to the {@link SaveInfo}.
    369          */
    370         public Builder setSaveDescription(String description) {
    371             mSaveDescription = description;
    372             return this;
    373         }
    374 
    375         /**
    376          * Sets the extra passed to {@link
    377          * android.service.autofill.FillResponse.Builder#setClientState(Bundle)}.
    378          */
    379         public Builder setExtras(Bundle data) {
    380             mExtras = data;
    381             return this;
    382         }
    383 
    384         /**
    385          * Sets the view to present the response in the UI.
    386          */
    387         public Builder setPresentation(RemoteViews presentation) {
    388             mPresentation = presentation;
    389             return this;
    390         }
    391 
    392         /**
    393          * Sets the authentication intent.
    394          */
    395         public Builder setAuthentication(IntentSender authentication, String... ids) {
    396             mAuthenticationIds = ids;
    397             mAuthentication = authentication;
    398             return this;
    399         }
    400 
    401         /**
    402          * Sets the ignored fields based on resource ids.
    403          */
    404         public Builder setIgnoreFields(String...ids) {
    405             mIgnoredIds = ids;
    406             return this;
    407         }
    408 
    409         /**
    410          * Sets the negative action spec.
    411          */
    412         public Builder setNegativeAction(int style, IntentSender listener) {
    413             mNegativeActionStyle = style;
    414             mNegativeActionListener = listener;
    415             return this;
    416         }
    417 
    418         public CannedFillResponse build() {
    419             return new CannedFillResponse(this);
    420         }
    421 
    422         /**
    423          * Sets the response to call {@link FillCallback#onFailure(CharSequence)}.
    424          */
    425         public Builder returnFailure(String message) {
    426             assertWithMessage("already added datasets").that(mDatasets).isEmpty();
    427             mFailureMessage = message;
    428             return this;
    429         }
    430 
    431         /**
    432          * Sets the view that explicitly triggers save.
    433          */
    434         public Builder setSaveTriggerId(AutofillId id) {
    435             assertWithMessage("already set").that(mSaveTriggerId).isNull();
    436             mSaveTriggerId = id;
    437             return this;
    438         }
    439 
    440         public Builder disableAutofill(long duration) {
    441             assertWithMessage("already set").that(mDisableDuration).isEqualTo(0L);
    442             mDisableDuration = duration;
    443             return this;
    444         }
    445 
    446         /**
    447          * Sets the ids used for field classification.
    448          */
    449         public Builder setFieldClassificationIds(String... ids) {
    450             assertWithMessage("already set").that(mFieldClassificationIds).isNull();
    451             mFieldClassificationIds = ids;
    452             return this;
    453         }
    454 
    455         /**
    456          * Forces the service to throw an exception when setting the fields classification ids.
    457          */
    458         public Builder setFieldClassificationIdsOverflow() {
    459             mFieldClassificationIdsOverflow = true;
    460             return this;
    461         }
    462 
    463         public Builder setHeader(RemoteViews header) {
    464             assertWithMessage("already set").that(mHeader).isNull();
    465             mHeader = header;
    466             return this;
    467         }
    468 
    469         public Builder setFooter(RemoteViews footer) {
    470             assertWithMessage("already set").that(mFooter).isNull();
    471             mFooter = footer;
    472             return this;
    473         }
    474 
    475         public Builder setSaveInfoDecorator(SaveInfoDecorator decorator) {
    476             assertWithMessage("already set").that(mSaveInfoDecorator).isNull();
    477             mSaveInfoDecorator = decorator;
    478             return this;
    479         }
    480 
    481         /**
    482          * Sets the package-specific UserData.
    483          *
    484          * <p>Overrides the default UserData for field classification.
    485          */
    486         public Builder setUserData(UserData userData) {
    487             assertWithMessage("already set").that(mUserData).isNull();
    488             mUserData = userData;
    489             return this;
    490         }
    491 
    492         /**
    493          * Sets a generic visitor for the "real" request and response.
    494          *
    495          * <p>Typically used in cases where the test need to infer data from the request to build
    496          * the response.
    497          */
    498         public Builder setVisitor(
    499                 @NonNull DoubleVisitor<List<FillContext>, FillResponse.Builder> visitor) {
    500             mVisitor = visitor;
    501             return this;
    502         }
    503 
    504         /**
    505          * Sets a generic visitor for the "real" request and save info.
    506          *
    507          * <p>Typically used in cases where the test need to infer data from the request to build
    508          * the response.
    509          */
    510         public Builder setSaveInfoVisitor(
    511                 @NonNull DoubleVisitor<List<FillContext>, SaveInfo.Builder> visitor) {
    512             mSaveInfoVisitor = visitor;
    513             return this;
    514         }
    515     }
    516 
    517     /**
    518      * Helper class used to produce a {@link Dataset} based on expected fields that should be
    519      * present in the {@link AssistStructure}.
    520      *
    521      * <p>Typical usage:
    522      *
    523      * <pre class="prettyprint">
    524      * InstrumentedAutoFillService.setFillResponse(new CannedFillResponse.Builder()
    525      *               .addDataset(new CannedDataset.Builder("dataset_name")
    526      *                   .setField("resource_id1", AutofillValue.forText("value1"))
    527      *                   .setField("resource_id2", AutofillValue.forText("value2"))
    528      *                   .build())
    529      *               .build());
    530      * </pre class="prettyprint">
    531      */
    532     public static class CannedDataset {
    533         private final Map<String, AutofillValue> mFieldValues;
    534         private final Map<String, RemoteViews> mFieldPresentations;
    535         private final Map<String, Pair<Boolean, Pattern>> mFieldFilters;
    536         private final RemoteViews mPresentation;
    537         private final IntentSender mAuthentication;
    538         private final String mId;
    539 
    540         private CannedDataset(Builder builder) {
    541             mFieldValues = builder.mFieldValues;
    542             mFieldPresentations = builder.mFieldPresentations;
    543             mFieldFilters = builder.mFieldFilters;
    544             mPresentation = builder.mPresentation;
    545             mAuthentication = builder.mAuthentication;
    546             mId = builder.mId;
    547         }
    548 
    549         /**
    550          * Creates a new dataset, replacing the field ids by the real ids from the assist structure.
    551          */
    552         Dataset asDataset(Function<String, ViewNode> nodeResolver) {
    553             final Dataset.Builder builder = (mPresentation == null)
    554                     ? new Dataset.Builder()
    555                     : new Dataset.Builder(mPresentation);
    556 
    557             if (mFieldValues != null) {
    558                 for (Map.Entry<String, AutofillValue> entry : mFieldValues.entrySet()) {
    559                     final String id = entry.getKey();
    560                     final ViewNode node = nodeResolver.apply(id);
    561                     if (node == null) {
    562                         throw new AssertionError("No node with resource id " + id);
    563                     }
    564                     final AutofillId autofillId = node.getAutofillId();
    565                     final AutofillValue value = entry.getValue();
    566                     final RemoteViews presentation = mFieldPresentations.get(id);
    567                     final Pair<Boolean, Pattern> filter = mFieldFilters.get(id);
    568                     if (presentation != null) {
    569                         if (filter == null) {
    570                             builder.setValue(autofillId, value, presentation);
    571                         } else {
    572                             builder.setValue(autofillId, value, filter.second, presentation);
    573                         }
    574                     } else {
    575                         if (filter == null) {
    576                             builder.setValue(autofillId, value);
    577                         } else {
    578                             builder.setValue(autofillId, value, filter.second);
    579                         }
    580                     }
    581                 }
    582             }
    583             builder.setId(mId).setAuthentication(mAuthentication);
    584             return builder.build();
    585         }
    586 
    587         @Override
    588         public String toString() {
    589             return "CannedDataset " + mId + " : [hasPresentation=" + (mPresentation != null)
    590                     + ", fieldPresentations=" + (mFieldPresentations)
    591                     + ", hasAuthentication=" + (mAuthentication != null)
    592                     + ", fieldValues=" + mFieldValues
    593                     + ", fieldFilters=" + mFieldFilters + "]";
    594         }
    595 
    596         public static class Builder {
    597             private final Map<String, AutofillValue> mFieldValues = new HashMap<>();
    598             private final Map<String, RemoteViews> mFieldPresentations = new HashMap<>();
    599             private final Map<String, Pair<Boolean, Pattern>> mFieldFilters = new HashMap<>();
    600 
    601             private RemoteViews mPresentation;
    602             private IntentSender mAuthentication;
    603             private String mId;
    604 
    605             public Builder() {
    606 
    607             }
    608 
    609             public Builder(RemoteViews presentation) {
    610                 mPresentation = presentation;
    611             }
    612 
    613             /**
    614              * Sets the canned value of a text field based on its {@code id}.
    615              *
    616              * <p>The meaning of the id is defined by the object using the canned dataset.
    617              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
    618              * {@link IdMode}.
    619              */
    620             public Builder setField(String id, String text) {
    621                 return setField(id, AutofillValue.forText(text));
    622             }
    623 
    624             /**
    625              * Sets the canned value of a text field based on its {@code id}.
    626              *
    627              * <p>The meaning of the id is defined by the object using the canned dataset.
    628              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
    629              * {@link IdMode}.
    630              */
    631             public Builder setField(String id, String text, Pattern filter) {
    632                 return setField(id, AutofillValue.forText(text), true, filter);
    633             }
    634 
    635             public Builder setUnfilterableField(String id, String text) {
    636                 return setField(id, AutofillValue.forText(text), false, null);
    637             }
    638 
    639             /**
    640              * Sets the canned value of a list field based on its its {@code id}.
    641              *
    642              * <p>The meaning of the id is defined by the object using the canned dataset.
    643              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
    644              * {@link IdMode}.
    645              */
    646             public Builder setField(String id, int index) {
    647                 return setField(id, AutofillValue.forList(index));
    648             }
    649 
    650             /**
    651              * Sets the canned value of a toggle field based on its {@code id}.
    652              *
    653              * <p>The meaning of the id is defined by the object using the canned dataset.
    654              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
    655              * {@link IdMode}.
    656              */
    657             public Builder setField(String id, boolean toggled) {
    658                 return setField(id, AutofillValue.forToggle(toggled));
    659             }
    660 
    661             /**
    662              * Sets the canned value of a date field based on its {@code id}.
    663              *
    664              * <p>The meaning of the id is defined by the object using the canned dataset.
    665              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
    666              * {@link IdMode}.
    667              */
    668             public Builder setField(String id, long date) {
    669                 return setField(id, AutofillValue.forDate(date));
    670             }
    671 
    672             /**
    673              * Sets the canned value of a date field based on its {@code id}.
    674              *
    675              * <p>The meaning of the id is defined by the object using the canned dataset.
    676              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
    677              * {@link IdMode}.
    678              */
    679             public Builder setField(String id, AutofillValue value) {
    680                 mFieldValues.put(id, value);
    681                 return this;
    682             }
    683 
    684             /**
    685              * Sets the canned value of a date field based on its {@code id}.
    686              *
    687              * <p>The meaning of the id is defined by the object using the canned dataset.
    688              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
    689              * {@link IdMode}.
    690              */
    691             public Builder setField(String id, AutofillValue value, boolean filterable,
    692                     Pattern filter) {
    693                 setField(id, value);
    694                 mFieldFilters.put(id, new Pair<>(filterable, filter));
    695                 return this;
    696             }
    697 
    698             /**
    699              * Sets the canned value of a field based on its {@code id}.
    700              *
    701              * <p>The meaning of the id is defined by the object using the canned dataset.
    702              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
    703              * {@link IdMode}.
    704              */
    705             public Builder setField(String id, String text, RemoteViews presentation) {
    706                 setField(id, text);
    707                 mFieldPresentations.put(id, presentation);
    708                 return this;
    709             }
    710 
    711             /**
    712              * Sets the canned value of a field based on its {@code id}.
    713              *
    714              * <p>The meaning of the id is defined by the object using the canned dataset.
    715              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
    716              * {@link IdMode}.
    717              */
    718             public Builder setField(String id, String text, RemoteViews presentation,
    719                     Pattern filter) {
    720                 setField(id, text, presentation);
    721                 mFieldFilters.put(id, new Pair<>(true, filter));
    722                 return this;
    723             }
    724 
    725             /**
    726              * Sets the view to present the response in the UI.
    727              */
    728             public Builder setPresentation(RemoteViews presentation) {
    729                 mPresentation = presentation;
    730                 return this;
    731             }
    732 
    733             /**
    734              * Sets the authentication intent.
    735              */
    736             public Builder setAuthentication(IntentSender authentication) {
    737                 mAuthentication = authentication;
    738                 return this;
    739             }
    740 
    741             /**
    742              * Sets the name.
    743              */
    744             public Builder setId(String id) {
    745                 mId = id;
    746                 return this;
    747             }
    748 
    749             public CannedDataset build() {
    750                 return new CannedDataset(this);
    751             }
    752         }
    753     }
    754 
    755     interface SaveInfoDecorator {
    756         void decorate(SaveInfo.Builder builder, Function<String, ViewNode> nodeResolver);
    757     }
    758 }
    759