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