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.testcore;
17 
18 import static android.autofillservice.cts.testcore.Helper.createInlinePresentation;
19 import static android.autofillservice.cts.testcore.Helper.createPresentation;
20 import static android.autofillservice.cts.testcore.Helper.getAutofillIds;
21 
22 import static com.google.common.truth.Truth.assertWithMessage;
23 
24 import android.app.assist.AssistStructure;
25 import android.app.assist.AssistStructure.ViewNode;
26 import android.content.IntentSender;
27 import android.os.Bundle;
28 import android.service.autofill.Dataset;
29 import android.service.autofill.FillCallback;
30 import android.service.autofill.FillContext;
31 import android.service.autofill.FillResponse;
32 import android.service.autofill.InlinePresentation;
33 import android.service.autofill.SaveInfo;
34 import android.service.autofill.UserData;
35 import android.util.Log;
36 import android.util.Pair;
37 import android.view.autofill.AutofillId;
38 import android.view.autofill.AutofillValue;
39 import android.widget.RemoteViews;
40 
41 import androidx.annotation.NonNull;
42 import androidx.annotation.Nullable;
43 
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.function.Function;
50 import java.util.regex.Pattern;
51 
52 /**
53  * Helper class used to produce a {@link FillResponse} based on expected fields that should be
54  * present in the {@link AssistStructure}.
55  *
56  * <p>Typical usage:
57  *
58  * <pre class="prettyprint">
59  * InstrumentedAutoFillService.setFillResponse(new CannedFillResponse.Builder()
60  *               .addDataset(new CannedDataset.Builder("dataset_name")
61  *                   .setField("resource_id1", AutofillValue.forText("value1"))
62  *                   .setField("resource_id2", AutofillValue.forText("value2"))
63  *                   .build())
64  *               .build());
65  * </pre class="prettyprint">
66  */
67 public final class CannedFillResponse {
68 
69     private static final String TAG = CannedFillResponse.class.getSimpleName();
70 
71     private final ResponseType mResponseType;
72     private final List<CannedDataset> mDatasets;
73     private final String mFailureMessage;
74     private final int mSaveType;
75     private final String[] mRequiredSavableIds;
76     private final String[] mOptionalSavableIds;
77     private final AutofillId[] mRequiredSavableAutofillIds;
78     private final CharSequence mSaveDescription;
79     private final Bundle mExtras;
80     private final RemoteViews mPresentation;
81     private final InlinePresentation mInlinePresentation;
82     private final RemoteViews mHeader;
83     private final RemoteViews mFooter;
84     private final IntentSender mAuthentication;
85     private final String[] mAuthenticationIds;
86     private final String[] mIgnoredIds;
87     private final int mNegativeActionStyle;
88     private final IntentSender mNegativeActionListener;
89     private final int mPositiveActionStyle;
90     private final int mSaveInfoFlags;
91     private final int mFillResponseFlags;
92     private final AutofillId mSaveTriggerId;
93     private final long mDisableDuration;
94     private final String[] mFieldClassificationIds;
95     private final boolean mFieldClassificationIdsOverflow;
96     private final SaveInfoDecorator mSaveInfoDecorator;
97     private final UserData mUserData;
98     private final DoubleVisitor<List<FillContext>, FillResponse.Builder> mVisitor;
99     private DoubleVisitor<List<FillContext>, SaveInfo.Builder> mSaveInfoVisitor;
100     private final int[] mCancelIds;
101 
CannedFillResponse(Builder builder)102     private CannedFillResponse(Builder builder) {
103         mResponseType = builder.mResponseType;
104         mDatasets = builder.mDatasets;
105         mFailureMessage = builder.mFailureMessage;
106         mRequiredSavableIds = builder.mRequiredSavableIds;
107         mRequiredSavableAutofillIds = builder.mRequiredSavableAutofillIds;
108         mOptionalSavableIds = builder.mOptionalSavableIds;
109         mSaveDescription = builder.mSaveDescription;
110         mSaveType = builder.mSaveType;
111         mExtras = builder.mExtras;
112         mPresentation = builder.mPresentation;
113         mInlinePresentation = builder.mInlinePresentation;
114         mHeader = builder.mHeader;
115         mFooter = builder.mFooter;
116         mAuthentication = builder.mAuthentication;
117         mAuthenticationIds = builder.mAuthenticationIds;
118         mIgnoredIds = builder.mIgnoredIds;
119         mNegativeActionStyle = builder.mNegativeActionStyle;
120         mNegativeActionListener = builder.mNegativeActionListener;
121         mPositiveActionStyle = builder.mPositiveActionStyle;
122         mSaveInfoFlags = builder.mSaveInfoFlags;
123         mFillResponseFlags = builder.mFillResponseFlags;
124         mSaveTriggerId = builder.mSaveTriggerId;
125         mDisableDuration = builder.mDisableDuration;
126         mFieldClassificationIds = builder.mFieldClassificationIds;
127         mFieldClassificationIdsOverflow = builder.mFieldClassificationIdsOverflow;
128         mSaveInfoDecorator = builder.mSaveInfoDecorator;
129         mUserData = builder.mUserData;
130         mVisitor = builder.mVisitor;
131         mSaveInfoVisitor = builder.mSaveInfoVisitor;
132         mCancelIds = builder.mCancelIds;
133     }
134 
135     /**
136      * Constant used to pass a {@code null} response to the
137      * {@link FillCallback#onSuccess(FillResponse)} method.
138      */
139     public static final CannedFillResponse NO_RESPONSE =
140             new Builder(ResponseType.NULL).build();
141 
142     /**
143      * Constant used to fail the test when an expected request was made.
144      */
145     public static final CannedFillResponse NO_MOAR_RESPONSES =
146             new Builder(ResponseType.NO_MORE).build();
147 
148     /**
149      * Constant used to emulate a timeout by not calling any method on {@link FillCallback}.
150      */
151     public static final CannedFillResponse DO_NOT_REPLY_RESPONSE =
152             new Builder(ResponseType.TIMEOUT).build();
153 
154     /**
155      * Constant used to call {@link FillCallback#onFailure(CharSequence)} method.
156      */
157     public static final CannedFillResponse FAIL =
158             new Builder(ResponseType.FAILURE).build();
159 
getFailureMessage()160     public String getFailureMessage() {
161         return mFailureMessage;
162     }
163 
getResponseType()164     public ResponseType getResponseType() {
165         return mResponseType;
166     }
167 
168     /**
169      * Creates a new response, replacing the dataset field ids by the real ids from the assist
170      * structure.
171      */
asFillResponse(@ullable List<FillContext> contexts, @NonNull Function<String, ViewNode> nodeResolver)172     public FillResponse asFillResponse(@Nullable List<FillContext> contexts,
173             @NonNull Function<String, ViewNode> nodeResolver) {
174         final FillResponse.Builder builder = new FillResponse.Builder()
175                 .setFlags(mFillResponseFlags);
176         if (mDatasets != null) {
177             for (CannedDataset cannedDataset : mDatasets) {
178                 final Dataset dataset = cannedDataset.asDataset(nodeResolver);
179                 assertWithMessage("Cannot create datase").that(dataset).isNotNull();
180                 builder.addDataset(dataset);
181             }
182         }
183         final SaveInfo.Builder saveInfoBuilder;
184         if (mRequiredSavableIds != null || mOptionalSavableIds != null
185                 || mRequiredSavableAutofillIds != null || mSaveInfoDecorator != null) {
186             if (mRequiredSavableAutofillIds != null) {
187                 saveInfoBuilder = new SaveInfo.Builder(mSaveType, mRequiredSavableAutofillIds);
188             } else {
189                 saveInfoBuilder = mRequiredSavableIds == null || mRequiredSavableIds.length == 0
190                         ? new SaveInfo.Builder(mSaveType)
191                             : new SaveInfo.Builder(mSaveType,
192                                     getAutofillIds(nodeResolver, mRequiredSavableIds));
193             }
194 
195             saveInfoBuilder.setFlags(mSaveInfoFlags);
196 
197             if (mOptionalSavableIds != null) {
198                 saveInfoBuilder.setOptionalIds(getAutofillIds(nodeResolver, mOptionalSavableIds));
199             }
200             if (mSaveDescription != null) {
201                 saveInfoBuilder.setDescription(mSaveDescription);
202             }
203             if (mNegativeActionListener != null) {
204                 saveInfoBuilder.setNegativeAction(mNegativeActionStyle, mNegativeActionListener);
205             }
206 
207             saveInfoBuilder.setPositiveAction(mPositiveActionStyle);
208 
209             if (mSaveTriggerId != null) {
210                 saveInfoBuilder.setTriggerId(mSaveTriggerId);
211             }
212         } else if (mSaveInfoFlags != 0) {
213             saveInfoBuilder = new SaveInfo.Builder(mSaveType).setFlags(mSaveInfoFlags);
214         } else {
215             saveInfoBuilder = null;
216         }
217         if (saveInfoBuilder != null) {
218             // TODO: merge decorator and visitor
219             if (mSaveInfoDecorator != null) {
220                 mSaveInfoDecorator.decorate(saveInfoBuilder, nodeResolver);
221             }
222             if (mSaveInfoVisitor != null) {
223                 Log.d(TAG, "Visiting saveInfo " + saveInfoBuilder);
224                 mSaveInfoVisitor.visit(contexts, saveInfoBuilder);
225             }
226             final SaveInfo saveInfo = saveInfoBuilder.build();
227             Log.d(TAG, "saveInfo:" + saveInfo);
228             builder.setSaveInfo(saveInfo);
229         }
230         if (mIgnoredIds != null) {
231             builder.setIgnoredIds(getAutofillIds(nodeResolver, mIgnoredIds));
232         }
233         if (mAuthenticationIds != null) {
234             builder.setAuthentication(getAutofillIds(nodeResolver, mAuthenticationIds),
235                     mAuthentication, mPresentation, mInlinePresentation);
236         }
237         if (mDisableDuration > 0) {
238             builder.disableAutofill(mDisableDuration);
239         }
240         if (mFieldClassificationIdsOverflow) {
241             final int length = UserData.getMaxFieldClassificationIdsSize() + 1;
242             final AutofillId[] fieldIds = new AutofillId[length];
243             for (int i = 0; i < length; i++) {
244                 fieldIds[i] = new AutofillId(i);
245             }
246             builder.setFieldClassificationIds(fieldIds);
247         } else if (mFieldClassificationIds != null) {
248             builder.setFieldClassificationIds(
249                     getAutofillIds(nodeResolver, mFieldClassificationIds));
250         }
251         if (mExtras != null) {
252             builder.setClientState(mExtras);
253         }
254         if (mHeader != null) {
255             builder.setHeader(mHeader);
256         }
257         if (mFooter != null) {
258             builder.setFooter(mFooter);
259         }
260         if (mUserData != null) {
261             builder.setUserData(mUserData);
262         }
263         if (mVisitor != null) {
264             Log.d(TAG, "Visiting " + builder);
265             mVisitor.visit(contexts, builder);
266         }
267         builder.setPresentationCancelIds(mCancelIds);
268 
269         final FillResponse response = builder.build();
270         Log.v(TAG, "Response: " + response);
271         return response;
272     }
273 
274     @Override
toString()275     public String toString() {
276         return "CannedFillResponse: [type=" + mResponseType
277                 + ",datasets=" + mDatasets
278                 + ", requiredSavableIds=" + Arrays.toString(mRequiredSavableIds)
279                 + ", optionalSavableIds=" + Arrays.toString(mOptionalSavableIds)
280                 + ", requiredSavableAutofillIds=" + Arrays.toString(mRequiredSavableAutofillIds)
281                 + ", saveInfoFlags=" + mSaveInfoFlags
282                 + ", fillResponseFlags=" + mFillResponseFlags
283                 + ", failureMessage=" + mFailureMessage
284                 + ", saveDescription=" + mSaveDescription
285                 + ", hasPresentation=" + (mPresentation != null)
286                 + ", hasInlinePresentation=" + (mInlinePresentation != null)
287                 + ", hasHeader=" + (mHeader != null)
288                 + ", hasFooter=" + (mFooter != null)
289                 + ", hasAuthentication=" + (mAuthentication != null)
290                 + ", authenticationIds=" + Arrays.toString(mAuthenticationIds)
291                 + ", ignoredIds=" + Arrays.toString(mIgnoredIds)
292                 + ", saveTriggerId=" + mSaveTriggerId
293                 + ", disableDuration=" + mDisableDuration
294                 + ", fieldClassificationIds=" + Arrays.toString(mFieldClassificationIds)
295                 + ", fieldClassificationIdsOverflow=" + mFieldClassificationIdsOverflow
296                 + ", saveInfoDecorator=" + mSaveInfoDecorator
297                 + ", userData=" + mUserData
298                 + ", visitor=" + mVisitor
299                 + ", saveInfoVisitor=" + mSaveInfoVisitor
300                 + "]";
301     }
302 
303     public enum ResponseType {
304         NORMAL,
305         NULL,
306         NO_MORE,
307         TIMEOUT,
308         FAILURE,
309         DELAY
310     }
311 
312     public static final class Builder {
313         private final List<CannedDataset> mDatasets = new ArrayList<>();
314         private final ResponseType mResponseType;
315         private String mFailureMessage;
316         private String[] mRequiredSavableIds;
317         private String[] mOptionalSavableIds;
318         private AutofillId[] mRequiredSavableAutofillIds;
319         private CharSequence mSaveDescription;
320         public int mSaveType = -1;
321         private Bundle mExtras;
322         private RemoteViews mPresentation;
323         private InlinePresentation mInlinePresentation;
324         private RemoteViews mFooter;
325         private RemoteViews mHeader;
326         private IntentSender mAuthentication;
327         private String[] mAuthenticationIds;
328         private String[] mIgnoredIds;
329         private int mNegativeActionStyle;
330         private IntentSender mNegativeActionListener;
331         private int mPositiveActionStyle;
332         private int mSaveInfoFlags;
333         private int mFillResponseFlags;
334         private AutofillId mSaveTriggerId;
335         private long mDisableDuration;
336         private String[] mFieldClassificationIds;
337         private boolean mFieldClassificationIdsOverflow;
338         private SaveInfoDecorator mSaveInfoDecorator;
339         private UserData mUserData;
340         private DoubleVisitor<List<FillContext>, FillResponse.Builder> mVisitor;
341         private DoubleVisitor<List<FillContext>, SaveInfo.Builder> mSaveInfoVisitor;
342         private int[] mCancelIds;
343 
Builder(ResponseType type)344         public Builder(ResponseType type) {
345             mResponseType = type;
346         }
347 
Builder()348         public Builder() {
349             this(ResponseType.NORMAL);
350         }
351 
addDataset(CannedDataset dataset)352         public Builder addDataset(CannedDataset dataset) {
353             assertWithMessage("already set failure").that(mFailureMessage).isNull();
354             mDatasets.add(dataset);
355             return this;
356         }
357 
358         /**
359          * Sets the required savable ids based on their {@code resourceId}.
360          */
setRequiredSavableIds(int type, String... ids)361         public Builder setRequiredSavableIds(int type, String... ids) {
362             mSaveType = type;
363             mRequiredSavableIds = ids;
364             return this;
365         }
366 
setSaveInfoFlags(int flags)367         public Builder setSaveInfoFlags(int flags) {
368             mSaveInfoFlags = flags;
369             return this;
370         }
371 
setFillResponseFlags(int flags)372         public Builder setFillResponseFlags(int flags) {
373             mFillResponseFlags = flags;
374             return this;
375         }
376 
377         /**
378          * Sets the optional savable ids based on they {@code resourceId}.
379          */
setOptionalSavableIds(String... ids)380         public Builder setOptionalSavableIds(String... ids) {
381             mOptionalSavableIds = ids;
382             return this;
383         }
384 
385         /**
386          * Sets the description passed to the {@link SaveInfo}.
387          */
setSaveDescription(CharSequence description)388         public Builder setSaveDescription(CharSequence description) {
389             mSaveDescription = description;
390             return this;
391         }
392 
393         /**
394          * Sets the extra passed to {@link
395          * android.service.autofill.FillResponse.Builder#setClientState(Bundle)}.
396          */
setExtras(Bundle data)397         public Builder setExtras(Bundle data) {
398             mExtras = data;
399             return this;
400         }
401 
402         /**
403          * Sets the view to present the response in the UI.
404          */
setPresentation(RemoteViews presentation)405         public Builder setPresentation(RemoteViews presentation) {
406             mPresentation = presentation;
407             return this;
408         }
409 
410         /**
411          * Sets the view to present the response in the UI.
412          */
setInlinePresentation(InlinePresentation inlinePresentation)413         public Builder setInlinePresentation(InlinePresentation inlinePresentation) {
414             mInlinePresentation = inlinePresentation;
415             return this;
416         }
417 
418         /**
419          * Sets views to present the response in the UI by the type.
420          */
setPresentation(String message, boolean inlineMode)421         public Builder setPresentation(String message, boolean inlineMode) {
422             mPresentation = createPresentation(message);
423             if (inlineMode) {
424                 mInlinePresentation = createInlinePresentation(message);
425             }
426             return this;
427         }
428 
429         /**
430          * Sets the authentication intent.
431          */
setAuthentication(IntentSender authentication, String... ids)432         public Builder setAuthentication(IntentSender authentication, String... ids) {
433             mAuthenticationIds = ids;
434             mAuthentication = authentication;
435             return this;
436         }
437 
438         /**
439          * Sets the ignored fields based on resource ids.
440          */
setIgnoreFields(String...ids)441         public Builder setIgnoreFields(String...ids) {
442             mIgnoredIds = ids;
443             return this;
444         }
445 
446         /**
447          * Sets the negative action spec.
448          */
setNegativeAction(int style, IntentSender listener)449         public Builder setNegativeAction(int style, IntentSender listener) {
450             mNegativeActionStyle = style;
451             mNegativeActionListener = listener;
452             return this;
453         }
454 
455         /**
456          * Sets the positive action spec.
457          */
setPositiveAction(int style)458         public Builder setPositiveAction(int style) {
459             mPositiveActionStyle = style;
460             return this;
461         }
462 
build()463         public CannedFillResponse build() {
464             return new CannedFillResponse(this);
465         }
466 
467         /**
468          * Sets the response to call {@link FillCallback#onFailure(CharSequence)}.
469          */
returnFailure(String message)470         public Builder returnFailure(String message) {
471             assertWithMessage("already added datasets").that(mDatasets).isEmpty();
472             mFailureMessage = message;
473             return this;
474         }
475 
476         /**
477          * Sets the view that explicitly triggers save.
478          */
setSaveTriggerId(AutofillId id)479         public Builder setSaveTriggerId(AutofillId id) {
480             assertWithMessage("already set").that(mSaveTriggerId).isNull();
481             mSaveTriggerId = id;
482             return this;
483         }
484 
disableAutofill(long duration)485         public Builder disableAutofill(long duration) {
486             assertWithMessage("already set").that(mDisableDuration).isEqualTo(0L);
487             mDisableDuration = duration;
488             return this;
489         }
490 
491         /**
492          * Sets the ids used for field classification.
493          */
setFieldClassificationIds(String... ids)494         public Builder setFieldClassificationIds(String... ids) {
495             assertWithMessage("already set").that(mFieldClassificationIds).isNull();
496             mFieldClassificationIds = ids;
497             return this;
498         }
499 
500         /**
501          * Forces the service to throw an exception when setting the fields classification ids.
502          */
setFieldClassificationIdsOverflow()503         public Builder setFieldClassificationIdsOverflow() {
504             mFieldClassificationIdsOverflow = true;
505             return this;
506         }
507 
setHeader(RemoteViews header)508         public Builder setHeader(RemoteViews header) {
509             assertWithMessage("already set").that(mHeader).isNull();
510             mHeader = header;
511             return this;
512         }
513 
setFooter(RemoteViews footer)514         public Builder setFooter(RemoteViews footer) {
515             assertWithMessage("already set").that(mFooter).isNull();
516             mFooter = footer;
517             return this;
518         }
519 
setSaveInfoDecorator(SaveInfoDecorator decorator)520         public Builder setSaveInfoDecorator(SaveInfoDecorator decorator) {
521             assertWithMessage("already set").that(mSaveInfoDecorator).isNull();
522             mSaveInfoDecorator = decorator;
523             return this;
524         }
525 
526         /**
527          * Sets the package-specific UserData.
528          *
529          * <p>Overrides the default UserData for field classification.
530          */
setUserData(UserData userData)531         public Builder setUserData(UserData userData) {
532             assertWithMessage("already set").that(mUserData).isNull();
533             mUserData = userData;
534             return this;
535         }
536 
537         /**
538          * Sets a generic visitor for the "real" request and response.
539          *
540          * <p>Typically used in cases where the test need to infer data from the request to build
541          * the response.
542          */
setVisitor( @onNull DoubleVisitor<List<FillContext>, FillResponse.Builder> visitor)543         public Builder setVisitor(
544                 @NonNull DoubleVisitor<List<FillContext>, FillResponse.Builder> visitor) {
545             mVisitor = visitor;
546             return this;
547         }
548 
549         /**
550          * Sets a generic visitor for the "real" request and save info.
551          *
552          * <p>Typically used in cases where the test need to infer data from the request to build
553          * the response.
554          */
setSaveInfoVisitor( @onNull DoubleVisitor<List<FillContext>, SaveInfo.Builder> visitor)555         public Builder setSaveInfoVisitor(
556                 @NonNull DoubleVisitor<List<FillContext>, SaveInfo.Builder> visitor) {
557             mSaveInfoVisitor = visitor;
558             return this;
559         }
560 
561         /**
562          * Sets targets that cancel current session
563          */
setPresentationCancelIds(int[] ids)564         public Builder setPresentationCancelIds(int[] ids) {
565             mCancelIds = ids;
566             return this;
567         }
568     }
569 
570     /**
571      * Helper class used to produce a {@link Dataset} based on expected fields that should be
572      * present in the {@link AssistStructure}.
573      *
574      * <p>Typical usage:
575      *
576      * <pre class="prettyprint">
577      * InstrumentedAutoFillService.setFillResponse(new CannedFillResponse.Builder()
578      *               .addDataset(new CannedDataset.Builder("dataset_name")
579      *                   .setField("resource_id1", AutofillValue.forText("value1"))
580      *                   .setField("resource_id2", AutofillValue.forText("value2"))
581      *                   .build())
582      *               .build());
583      * </pre class="prettyprint">
584      */
585     public static class CannedDataset {
586         private final Map<String, AutofillValue> mFieldValues;
587         private final Map<String, RemoteViews> mFieldPresentations;
588         private final Map<String, InlinePresentation> mFieldInlinePresentations;
589         private final Map<String, InlinePresentation> mFieldInlineTooltipPresentations;
590         private final Map<String, Pair<Boolean, Pattern>> mFieldFilters;
591         private final RemoteViews mPresentation;
592         private final InlinePresentation mInlinePresentation;
593         private final InlinePresentation mInlineTooltipPresentation;
594         private final IntentSender mAuthentication;
595         private final String mId;
596 
CannedDataset(Builder builder)597         private CannedDataset(Builder builder) {
598             mFieldValues = builder.mFieldValues;
599             mFieldPresentations = builder.mFieldPresentations;
600             mFieldInlinePresentations = builder.mFieldInlinePresentations;
601             mFieldInlineTooltipPresentations = builder.mFieldInlineTooltipPresentations;
602             mFieldFilters = builder.mFieldFilters;
603             mPresentation = builder.mPresentation;
604             mInlinePresentation = builder.mInlinePresentation;
605             mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
606             mAuthentication = builder.mAuthentication;
607             mId = builder.mId;
608         }
609 
610         /**
611          * Creates a new dataset, replacing the field ids by the real ids from the assist structure.
612          */
asDataset(Function<String, ViewNode> nodeResolver)613         public Dataset asDataset(Function<String, ViewNode> nodeResolver) {
614             final Dataset.Builder builder = mPresentation != null
615                     ? new Dataset.Builder(mPresentation)
616                     : new Dataset.Builder();
617             if (mInlinePresentation != null) {
618                 if (mInlineTooltipPresentation != null) {
619                     builder.setInlinePresentation(mInlinePresentation, mInlineTooltipPresentation);
620                 } else {
621                     builder.setInlinePresentation(mInlinePresentation);
622                 }
623             }
624 
625             if (mFieldValues != null) {
626                 for (Map.Entry<String, AutofillValue> entry : mFieldValues.entrySet()) {
627                     final String id = entry.getKey();
628                     final ViewNode node = nodeResolver.apply(id);
629                     if (node == null) {
630                         throw new AssertionError("No node with resource id " + id);
631                     }
632                     final AutofillId autofillId = node.getAutofillId();
633                     final AutofillValue value = entry.getValue();
634                     final RemoteViews presentation = mFieldPresentations.get(id);
635                     final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(id);
636                     final InlinePresentation tooltipPresentation =
637                             mFieldInlineTooltipPresentations.get(id);
638                     final Pair<Boolean, Pattern> filter = mFieldFilters.get(id);
639                     if (presentation != null) {
640                         if (filter == null) {
641                             if (inlinePresentation != null) {
642                                 if (tooltipPresentation != null) {
643                                     builder.setValue(autofillId, value, presentation,
644                                             inlinePresentation, tooltipPresentation);
645                                 } else {
646                                     builder.setValue(autofillId, value, presentation,
647                                             inlinePresentation);
648                                 }
649                             } else {
650                                 builder.setValue(autofillId, value, presentation);
651                             }
652                         } else {
653                             if (inlinePresentation != null) {
654                                 if (tooltipPresentation != null) {
655                                     builder.setValue(autofillId, value, filter.second, presentation,
656                                             inlinePresentation, tooltipPresentation);
657                                 } else {
658                                     builder.setValue(autofillId, value, filter.second, presentation,
659                                             inlinePresentation);
660                                 }
661                             } else {
662                                 builder.setValue(autofillId, value, filter.second, presentation);
663                             }
664                         }
665                     } else {
666                         if (inlinePresentation != null) {
667                             if (tooltipPresentation != null) {
668                                 throw new IllegalStateException("presentation can not be null");
669                             } else {
670                                 builder.setFieldInlinePresentation(autofillId, value,
671                                         filter != null ? filter.second : null, inlinePresentation);
672                             }
673                         } else {
674                             if (filter == null) {
675                                 builder.setValue(autofillId, value);
676                             } else {
677                                 builder.setValue(autofillId, value, filter.second);
678                             }
679                         }
680                     }
681                 }
682             }
683             builder.setId(mId).setAuthentication(mAuthentication);
684             return builder.build();
685         }
686 
687         @Override
toString()688         public String toString() {
689             return "CannedDataset " + mId + " : [hasPresentation=" + (mPresentation != null)
690                     + ", hasInlinePresentation=" + (mInlinePresentation != null)
691                     + ", fieldPresentations=" + (mFieldPresentations)
692                     + ", fieldInlinePresentations=" + (mFieldInlinePresentations)
693                     + ", fieldTooltipInlinePresentations=" + (mFieldInlineTooltipPresentations)
694                     + ", hasAuthentication=" + (mAuthentication != null)
695                     + ", fieldValues=" + mFieldValues
696                     + ", fieldFilters=" + mFieldFilters + "]";
697         }
698 
699         public static class Builder {
700             private final Map<String, AutofillValue> mFieldValues = new HashMap<>();
701             private final Map<String, RemoteViews> mFieldPresentations = new HashMap<>();
702             private final Map<String, InlinePresentation> mFieldInlinePresentations =
703                     new HashMap<>();
704             private final Map<String, InlinePresentation> mFieldInlineTooltipPresentations =
705                     new HashMap<>();
706             private final Map<String, Pair<Boolean, Pattern>> mFieldFilters = new HashMap<>();
707 
708             private RemoteViews mPresentation;
709             private InlinePresentation mInlinePresentation;
710             private IntentSender mAuthentication;
711             private String mId;
712             private InlinePresentation mInlineTooltipPresentation;
713 
Builder()714             public Builder() {
715 
716             }
717 
Builder(RemoteViews presentation)718             public Builder(RemoteViews presentation) {
719                 mPresentation = presentation;
720             }
721 
722             /**
723              * Sets the canned value of a text field based on its {@code id}.
724              *
725              * <p>The meaning of the id is defined by the object using the canned dataset.
726              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
727              * {@link IdMode}.
728              */
setField(String id, String text)729             public Builder setField(String id, String text) {
730                 return setField(id, AutofillValue.forText(text));
731             }
732 
733             /**
734              * Sets the canned value of a text field based on its {@code id}.
735              *
736              * <p>The meaning of the id is defined by the object using the canned dataset.
737              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
738              * {@link IdMode}.
739              */
setField(String id, String text, Pattern filter)740             public Builder setField(String id, String text, Pattern filter) {
741                 return setField(id, AutofillValue.forText(text), true, filter);
742             }
743 
setUnfilterableField(String id, String text)744             public Builder setUnfilterableField(String id, String text) {
745                 return setField(id, AutofillValue.forText(text), false, null);
746             }
747 
748             /**
749              * Sets the canned value of a list field based on its its {@code id}.
750              *
751              * <p>The meaning of the id is defined by the object using the canned dataset.
752              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
753              * {@link IdMode}.
754              */
setField(String id, int index)755             public Builder setField(String id, int index) {
756                 return setField(id, AutofillValue.forList(index));
757             }
758 
759             /**
760              * Sets the canned value of a toggle field based on its {@code id}.
761              *
762              * <p>The meaning of the id is defined by the object using the canned dataset.
763              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
764              * {@link IdMode}.
765              */
setField(String id, boolean toggled)766             public Builder setField(String id, boolean toggled) {
767                 return setField(id, AutofillValue.forToggle(toggled));
768             }
769 
770             /**
771              * Sets the canned value of a date field based on its {@code id}.
772              *
773              * <p>The meaning of the id is defined by the object using the canned dataset.
774              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
775              * {@link IdMode}.
776              */
setField(String id, long date)777             public Builder setField(String id, long date) {
778                 return setField(id, AutofillValue.forDate(date));
779             }
780 
781             /**
782              * Sets the canned value of a date field based on its {@code id}.
783              *
784              * <p>The meaning of the id is defined by the object using the canned dataset.
785              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
786              * {@link IdMode}.
787              */
setField(String id, AutofillValue value)788             public Builder setField(String id, AutofillValue value) {
789                 mFieldValues.put(id, value);
790                 return this;
791             }
792 
793             /**
794              * Sets the canned value of a date field based on its {@code id}.
795              *
796              * <p>The meaning of the id is defined by the object using the canned dataset.
797              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
798              * {@link IdMode}.
799              */
setField(String id, AutofillValue value, boolean filterable, Pattern filter)800             public Builder setField(String id, AutofillValue value, boolean filterable,
801                     Pattern filter) {
802                 setField(id, value);
803                 mFieldFilters.put(id, new Pair<>(filterable, filter));
804                 return this;
805             }
806 
807             /**
808              * Sets the canned value of a field based on its {@code id}.
809              *
810              * <p>The meaning of the id is defined by the object using the canned dataset.
811              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
812              * {@link IdMode}.
813              */
setField(String id, String text, RemoteViews presentation)814             public Builder setField(String id, String text, RemoteViews presentation) {
815                 setField(id, text);
816                 mFieldPresentations.put(id, presentation);
817                 return this;
818             }
819 
820             /**
821              * Sets the canned value of a field based on its {@code id}.
822              *
823              * <p>The meaning of the id is defined by the object using the canned dataset.
824              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
825              * {@link IdMode}.
826              */
setField(String id, String text, RemoteViews presentation, Pattern filter)827             public Builder setField(String id, String text, RemoteViews presentation,
828                     Pattern filter) {
829                 setField(id, text, presentation);
830                 mFieldFilters.put(id, new Pair<>(true, filter));
831                 return this;
832             }
833 
834             /**
835              * Sets the canned value of a field based on its {@code id}.
836              *
837              * <p>The meaning of the id is defined by the object using the canned dataset.
838              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
839              * {@link IdMode}.
840              */
setField(String id, String text, RemoteViews presentation, InlinePresentation inlinePresentation)841             public Builder setField(String id, String text, RemoteViews presentation,
842                     InlinePresentation inlinePresentation) {
843                 setField(id, text);
844                 mFieldPresentations.put(id, presentation);
845                 mFieldInlinePresentations.put(id, inlinePresentation);
846                 return this;
847             }
848 
849             /**
850              * Sets the canned value of a field based on its {@code id}.
851              *
852              * <p>The meaning of the id is defined by the object using the canned dataset.
853              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
854              * {@link IdMode}.
855              */
setField(String id, String text, RemoteViews presentation, InlinePresentation inlinePresentation, InlinePresentation inlineTooltipPresentation)856             public Builder setField(String id, String text, RemoteViews presentation,
857                     InlinePresentation inlinePresentation,
858                     InlinePresentation inlineTooltipPresentation) {
859                 setField(id, text, presentation, inlinePresentation);
860                 mFieldInlineTooltipPresentations.put(id, inlineTooltipPresentation);
861                 return this;
862             }
863 
864             /**
865              * Sets the canned value of a field based on its {@code id}.
866              *
867              * <p>The meaning of the id is defined by the object using the canned dataset.
868              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
869              * {@link IdMode}.
870              */
setField(String id, String text, RemoteViews presentation, InlinePresentation inlinePresentation, Pattern filter)871             public Builder setField(String id, String text, RemoteViews presentation,
872                     InlinePresentation inlinePresentation, Pattern filter) {
873                 setField(id, text, presentation, inlinePresentation);
874                 mFieldFilters.put(id, new Pair<>(true, filter));
875                 return this;
876             }
877 
878             /**
879              * Sets the canned value of a field based on its {@code id}.
880              *
881              * <p>The meaning of the id is defined by the object using the canned dataset.
882              * For example, {@link InstrumentedAutoFillService.Replier} resolves the id based on
883              * {@link IdMode}.
884              */
setField(String id, String text, RemoteViews presentation, InlinePresentation inlinePresentation, InlinePresentation inlineTooltipPresentation, Pattern filter)885             public Builder setField(String id, String text, RemoteViews presentation,
886                     InlinePresentation inlinePresentation,
887                     InlinePresentation inlineTooltipPresentation,
888                     Pattern filter) {
889                 setField(id, text, presentation, inlinePresentation, inlineTooltipPresentation);
890                 mFieldFilters.put(id, new Pair<>(true, filter));
891 
892                 return this;
893             }
894 
895             /**
896              * Sets the view to present the response in the UI.
897              */
setPresentation(RemoteViews presentation)898             public Builder setPresentation(RemoteViews presentation) {
899                 mPresentation = presentation;
900                 return this;
901             }
902 
903             /**
904              * Sets the view to present the response in the UI.
905              */
setInlinePresentation(InlinePresentation inlinePresentation)906             public Builder setInlinePresentation(InlinePresentation inlinePresentation) {
907                 mInlinePresentation = inlinePresentation;
908                 return this;
909             }
910 
911             /**
912              * Sets the inline tooltip to present the response in the UI.
913              */
setInlineTooltipPresentation(InlinePresentation tooltip)914             public Builder setInlineTooltipPresentation(InlinePresentation tooltip) {
915                 mInlineTooltipPresentation = tooltip;
916                 return this;
917             }
918 
setPresentation(String message, boolean inlineMode)919             public Builder setPresentation(String message, boolean inlineMode) {
920                 mPresentation = createPresentation(message);
921                 if (inlineMode) {
922                     mInlinePresentation = createInlinePresentation(message);
923                 }
924                 return this;
925             }
926 
927             /**
928              * Sets the authentication intent.
929              */
setAuthentication(IntentSender authentication)930             public Builder setAuthentication(IntentSender authentication) {
931                 mAuthentication = authentication;
932                 return this;
933             }
934 
935             /**
936              * Sets the name.
937              */
setId(String id)938             public Builder setId(String id) {
939                 mId = id;
940                 return this;
941             }
942 
943             /**
944              * Builds the canned dataset.
945              */
build()946             public CannedDataset build() {
947                 return new CannedDataset(this);
948             }
949         }
950     }
951 
952     public interface SaveInfoDecorator {
decorate(SaveInfo.Builder builder, Function<String, ViewNode> nodeResolver)953         void decorate(SaveInfo.Builder builder, Function<String, ViewNode> nodeResolver);
954     }
955 }
956