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