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