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 
CannedFillResponse(Builder builder)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 
getFailureMessage()151     public String getFailureMessage() {
152         return mFailureMessage;
153     }
154 
getResponseType()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      */
asFillResponse(@ullable List<FillContext> contexts, @NonNull Function<String, ViewNode> nodeResolver)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
toString()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 
Builder(ResponseType type)326         public Builder(ResponseType type) {
327             mResponseType = type;
328         }
329 
Builder()330         public Builder() {
331             this(ResponseType.NORMAL);
332         }
333 
addDataset(CannedDataset dataset)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          */
setRequiredSavableIds(int type, String... ids)343         public Builder setRequiredSavableIds(int type, String... ids) {
344             mSaveType = type;
345             mRequiredSavableIds = ids;
346             return this;
347         }
348 
setSaveInfoFlags(int flags)349         public Builder setSaveInfoFlags(int flags) {
350             mSaveInfoFlags = flags;
351             return this;
352         }
353 
setFillResponseFlags(int flags)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          */
setOptionalSavableIds(String... ids)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          */
setSaveDescription(String description)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          */
setExtras(Bundle data)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          */
setPresentation(RemoteViews presentation)387         public Builder setPresentation(RemoteViews presentation) {
388             mPresentation = presentation;
389             return this;
390         }
391 
392         /**
393          * Sets the authentication intent.
394          */
setAuthentication(IntentSender authentication, String... ids)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          */
setIgnoreFields(String...ids)404         public Builder setIgnoreFields(String...ids) {
405             mIgnoredIds = ids;
406             return this;
407         }
408 
409         /**
410          * Sets the negative action spec.
411          */
setNegativeAction(int style, IntentSender listener)412         public Builder setNegativeAction(int style, IntentSender listener) {
413             mNegativeActionStyle = style;
414             mNegativeActionListener = listener;
415             return this;
416         }
417 
build()418         public CannedFillResponse build() {
419             return new CannedFillResponse(this);
420         }
421 
422         /**
423          * Sets the response to call {@link FillCallback#onFailure(CharSequence)}.
424          */
returnFailure(String message)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          */
setSaveTriggerId(AutofillId id)434         public Builder setSaveTriggerId(AutofillId id) {
435             assertWithMessage("already set").that(mSaveTriggerId).isNull();
436             mSaveTriggerId = id;
437             return this;
438         }
439 
disableAutofill(long duration)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          */
setFieldClassificationIds(String... ids)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          */
setFieldClassificationIdsOverflow()458         public Builder setFieldClassificationIdsOverflow() {
459             mFieldClassificationIdsOverflow = true;
460             return this;
461         }
462 
setHeader(RemoteViews header)463         public Builder setHeader(RemoteViews header) {
464             assertWithMessage("already set").that(mHeader).isNull();
465             mHeader = header;
466             return this;
467         }
468 
setFooter(RemoteViews footer)469         public Builder setFooter(RemoteViews footer) {
470             assertWithMessage("already set").that(mFooter).isNull();
471             mFooter = footer;
472             return this;
473         }
474 
setSaveInfoDecorator(SaveInfoDecorator decorator)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          */
setUserData(UserData userData)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          */
setVisitor( @onNull DoubleVisitor<List<FillContext>, FillResponse.Builder> visitor)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          */
setSaveInfoVisitor( @onNull DoubleVisitor<List<FillContext>, SaveInfo.Builder> visitor)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 
CannedDataset(Builder builder)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          */
asDataset(Function<String, ViewNode> nodeResolver)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
toString()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 
Builder()605             public Builder() {
606 
607             }
608 
Builder(RemoteViews presentation)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              */
setField(String id, String text)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              */
setField(String id, String text, Pattern filter)631             public Builder setField(String id, String text, Pattern filter) {
632                 return setField(id, AutofillValue.forText(text), true, filter);
633             }
634 
setUnfilterableField(String id, String text)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              */
setField(String id, int index)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              */
setField(String id, boolean toggled)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              */
setField(String id, long date)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              */
setField(String id, AutofillValue value)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              */
setField(String id, AutofillValue value, boolean filterable, Pattern filter)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              */
setField(String id, String text, RemoteViews presentation)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              */
setField(String id, String text, RemoteViews presentation, Pattern filter)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              */
setPresentation(RemoteViews presentation)728             public Builder setPresentation(RemoteViews presentation) {
729                 mPresentation = presentation;
730                 return this;
731             }
732 
733             /**
734              * Sets the authentication intent.
735              */
setAuthentication(IntentSender authentication)736             public Builder setAuthentication(IntentSender authentication) {
737                 mAuthentication = authentication;
738                 return this;
739             }
740 
741             /**
742              * Sets the name.
743              */
setId(String id)744             public Builder setId(String id) {
745                 mId = id;
746                 return this;
747             }
748 
build()749             public CannedDataset build() {
750                 return new CannedDataset(this);
751             }
752         }
753     }
754 
755     interface SaveInfoDecorator {
decorate(SaveInfo.Builder builder, Function<String, ViewNode> nodeResolver)756         void decorate(SaveInfo.Builder builder, Function<String, ViewNode> nodeResolver);
757     }
758 }
759