1 /* 2 * Copyright (C) 2016 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 17 package android.service.autofill; 18 19 import static android.view.autofill.Helper.sDebug; 20 21 import android.annotation.Hide; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.SuppressLint; 26 import android.annotation.SystemApi; 27 import android.annotation.TestApi; 28 import android.content.ClipData; 29 import android.content.Intent; 30 import android.content.IntentSender; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.util.ArrayMap; 34 import android.view.autofill.AutofillId; 35 import android.view.autofill.AutofillManager; 36 import android.view.autofill.AutofillValue; 37 import android.widget.RemoteViews; 38 39 import com.android.internal.util.Preconditions; 40 41 import java.lang.annotation.Retention; 42 import java.lang.annotation.RetentionPolicy; 43 import java.util.ArrayList; 44 import java.util.Objects; 45 import java.util.regex.Pattern; 46 47 /** 48 * <p>A <code>Dataset</code> object represents a group of fields (key / value pairs) used 49 * to autofill parts of a screen. 50 * 51 * <p>For more information about the role of datasets in the autofill workflow, read 52 * <a href="/guide/topics/text/autofill-services">Build autofill services</a> and the 53 * <code><a href="/reference/android/service/autofill/AutofillService">AutofillService</a></code> 54 * documentation. 55 * 56 * <a name="BasicUsage"></a> 57 * <h3>Basic usage</h3> 58 * 59 * <p>In its simplest form, a dataset contains one or more fields (comprised of 60 * an {@link AutofillId id}, a {@link AutofillValue value}, and an optional filter 61 * {@link Pattern regex}); and one or more {@link RemoteViews presentations} for these fields 62 * (each field could have its own {@link RemoteViews presentation}, or use the default 63 * {@link RemoteViews presentation} associated with the whole dataset). 64 * 65 * <p>When an autofill service returns datasets in a {@link FillResponse} 66 * and the screen input is focused in a view that is present in at least one of these datasets, 67 * the Android System displays a UI containing the {@link RemoteViews presentation} of 68 * all datasets pairs that have that view's {@link AutofillId}. Then, when the user selects a 69 * dataset from the UI, all views in that dataset are autofilled. 70 * 71 * <p>If both the current Input Method and autofill service supports inline suggestions, the Dataset 72 * can be shown by the keyboard as a suggestion. To use this feature, the Dataset should contain 73 * an {@link InlinePresentation} representing how the inline suggestion UI will be rendered. 74 * 75 * <a name="FillDialogUI"></a> 76 * <h3>Fill Dialog UI</h3> 77 * 78 * <p>The fill dialog UI is a more conspicuous and efficient interface than dropdown UI. If autofill 79 * suggestions are available when the user clicks on a field that supports filling the dialog UI, 80 * Autofill will pop up a fill dialog. The dialog will take up a larger area to display the 81 * datasets, so it is easy for users to pay attention to the datasets and selecting a dataset. 82 * If the user focuses on the view before suggestions are available, will fall back to dropdown UI 83 * or inline suggestions. 84 * 85 * <a name="Authentication"></a> 86 * <h3>Dataset authentication</h3> 87 * 88 * <p>In a more sophisticated form, the dataset values can be protected until the user authenticates 89 * the dataset—in that case, when a dataset is selected by the user, the Android System 90 * launches an intent set by the service to "unlock" the dataset. 91 * 92 * <p>For example, when a data set contains credit card information (such as number, 93 * expiration date, and verification code), you could provide a dataset presentation saying 94 * "Tap to authenticate". Then when the user taps that option, you would launch an activity asking 95 * the user to enter the credit card code, and if the user enters a valid code, you could then 96 * "unlock" the dataset. 97 * 98 * <p>You can also use authenticated datasets to offer an interactive UI for the user. For example, 99 * if the activity being autofilled is an account creation screen, you could use an authenticated 100 * dataset to automatically generate a random password for the user. 101 * 102 * <p>See {@link Dataset.Builder#setAuthentication(IntentSender)} for more details about the dataset 103 * authentication mechanism. 104 * 105 * <a name="Filtering"></a> 106 * <h3>Filtering</h3> 107 * <p>The autofill UI automatically changes which values are shown based on value of the view 108 * anchoring it, following the rules below: 109 * <ol> 110 * <li>If the view's {@link android.view.View#getAutofillValue() autofill value} is not 111 * {@link AutofillValue#isText() text} or is empty, all datasets are shown. 112 * <li>Datasets that have a filter regex (set through {@link Field.Builder#setFilter(Pattern)} 113 * and {@link Dataset.Builder#setField(AutofillId, Field)}) and whose regex matches the view's 114 * text value converted to lower case are shown. 115 * <li>Datasets that do not require authentication, have a field value that is 116 * {@link AutofillValue#isText() text} and whose {@link AutofillValue#getTextValue() value} starts 117 * with the lower case value of the view's text are shown. 118 * <li>All other datasets are hidden. 119 * </ol> 120 * <p>Note: If user enters four or more characters, all datasets will be hidden</p> 121 * 122 */ 123 public final class Dataset implements Parcelable { 124 /** 125 * This dataset is picked because of unknown reason. 126 * @hide 127 */ 128 public static final int PICK_REASON_UNKNOWN = 0; 129 /** 130 * This dataset is picked because pcc wasn't enabled. 131 * @hide 132 */ 133 public static final int PICK_REASON_NO_PCC = 1; 134 /** 135 * This dataset is picked because provider gave this dataset. 136 * @hide 137 */ 138 public static final int PICK_REASON_PROVIDER_DETECTION_ONLY = 2; 139 /** 140 * This dataset is picked because provider detection was preferred. However, provider also made 141 * this dataset available for PCC detected types, so they could've been picked up by PCC 142 * detection. This however doesn't imply that this dataset would've been chosen for sure. For 143 * eg, if PCC Detection was preferred, and PCC detected other field types, which wasn't 144 * applicable to this dataset, it wouldn't have been shown. 145 * @hide 146 */ 147 public static final int PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC = 3; 148 /** 149 * This dataset is picked because of PCC detection was chosen. 150 * @hide 151 */ 152 public static final int PICK_REASON_PCC_DETECTION_ONLY = 4; 153 /** 154 * This dataset is picked because of PCC Detection was preferred. However, Provider also gave 155 * this dataset, so if PCC wasn't enabled, this dataset would've been eligible anyway. 156 * @hide 157 */ 158 public static final int PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER = 5; 159 160 /** 161 * Reason why the dataset was eligible for autofill. 162 * @hide 163 */ 164 @IntDef(prefix = { "PICK_REASON_" }, value = { 165 PICK_REASON_UNKNOWN, 166 PICK_REASON_NO_PCC, 167 PICK_REASON_PROVIDER_DETECTION_ONLY, 168 PICK_REASON_PROVIDER_DETECTION_PREFERRED_WITH_PCC, 169 PICK_REASON_PCC_DETECTION_ONLY, 170 PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER, 171 }) 172 @Retention(RetentionPolicy.SOURCE) 173 public @interface DatasetEligibleReason{} 174 175 private @DatasetEligibleReason int mEligibleReason; 176 177 private final ArrayList<AutofillId> mFieldIds; 178 private final ArrayList<AutofillValue> mFieldValues; 179 private final ArrayList<RemoteViews> mFieldPresentations; 180 private final ArrayList<RemoteViews> mFieldDialogPresentations; 181 private final ArrayList<InlinePresentation> mFieldInlinePresentations; 182 private final ArrayList<InlinePresentation> mFieldInlineTooltipPresentations; 183 private final ArrayList<DatasetFieldFilter> mFieldFilters; 184 private final ArrayList<String> mAutofillDatatypes; 185 186 @Nullable private final ClipData mFieldContent; 187 private final RemoteViews mPresentation; 188 private final RemoteViews mDialogPresentation; 189 @Nullable private final InlinePresentation mInlinePresentation; 190 @Nullable private final InlinePresentation mInlineTooltipPresentation; 191 private final IntentSender mAuthentication; 192 193 @Nullable private Intent mCredentialFillInIntent; 194 195 @Nullable String mId; 196 197 /** 198 * Constructor to copy the dataset, but replaces the AutofillId with the given input. 199 * Useful to modify the field type, and provide autofillId. 200 * @hide 201 */ Dataset( ArrayList<AutofillId> fieldIds, ArrayList<AutofillValue> fieldValues, ArrayList<RemoteViews> fieldPresentations, ArrayList<RemoteViews> fieldDialogPresentations, ArrayList<InlinePresentation> fieldInlinePresentations, ArrayList<InlinePresentation> fieldInlineTooltipPresentations, ArrayList<DatasetFieldFilter> fieldFilters, ArrayList<String> autofillDatatypes, ClipData fieldContent, RemoteViews presentation, RemoteViews dialogPresentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation inlineTooltipPresentation, @Nullable String id, IntentSender authentication)202 public Dataset( 203 ArrayList<AutofillId> fieldIds, 204 ArrayList<AutofillValue> fieldValues, 205 ArrayList<RemoteViews> fieldPresentations, 206 ArrayList<RemoteViews> fieldDialogPresentations, 207 ArrayList<InlinePresentation> fieldInlinePresentations, 208 ArrayList<InlinePresentation> fieldInlineTooltipPresentations, 209 ArrayList<DatasetFieldFilter> fieldFilters, 210 ArrayList<String> autofillDatatypes, 211 ClipData fieldContent, 212 RemoteViews presentation, 213 RemoteViews dialogPresentation, 214 @Nullable InlinePresentation inlinePresentation, 215 @Nullable InlinePresentation inlineTooltipPresentation, 216 @Nullable String id, 217 IntentSender authentication) { 218 mFieldIds = fieldIds; 219 mFieldValues = fieldValues; 220 mFieldPresentations = fieldPresentations; 221 mFieldDialogPresentations = fieldDialogPresentations; 222 mFieldInlinePresentations = fieldInlinePresentations; 223 mFieldInlineTooltipPresentations = fieldInlineTooltipPresentations; 224 mAutofillDatatypes = autofillDatatypes; 225 mFieldFilters = fieldFilters; 226 mFieldContent = fieldContent; 227 mPresentation = presentation; 228 mDialogPresentation = dialogPresentation; 229 mInlinePresentation = inlinePresentation; 230 mInlineTooltipPresentation = inlineTooltipPresentation; 231 mAuthentication = authentication; 232 mCredentialFillInIntent = null; 233 mId = id; 234 } 235 236 /** 237 * Constructor to copy the dataset, but replaces the AutofillId with the given input. 238 * Useful to modify the field type, and provide autofillId. 239 * @hide 240 */ Dataset(Dataset dataset, ArrayList<AutofillId> ids)241 public Dataset(Dataset dataset, ArrayList<AutofillId> ids) { 242 mFieldIds = ids; 243 mFieldValues = dataset.mFieldValues; 244 mFieldPresentations = dataset.mFieldPresentations; 245 mFieldDialogPresentations = dataset.mFieldDialogPresentations; 246 mFieldInlinePresentations = dataset.mFieldInlinePresentations; 247 mFieldInlineTooltipPresentations = dataset.mFieldInlineTooltipPresentations; 248 mFieldFilters = dataset.mFieldFilters; 249 mFieldContent = dataset.mFieldContent; 250 mPresentation = dataset.mPresentation; 251 mDialogPresentation = dataset.mDialogPresentation; 252 mInlinePresentation = dataset.mInlinePresentation; 253 mInlineTooltipPresentation = dataset.mInlineTooltipPresentation; 254 mAuthentication = dataset.mAuthentication; 255 mCredentialFillInIntent = dataset.mCredentialFillInIntent; 256 mId = dataset.mId; 257 mAutofillDatatypes = dataset.mAutofillDatatypes; 258 } 259 Dataset(Builder builder)260 private Dataset(Builder builder) { 261 mFieldIds = builder.mFieldIds; 262 mFieldValues = builder.mFieldValues; 263 mFieldPresentations = builder.mFieldPresentations; 264 mFieldDialogPresentations = builder.mFieldDialogPresentations; 265 mFieldInlinePresentations = builder.mFieldInlinePresentations; 266 mFieldInlineTooltipPresentations = builder.mFieldInlineTooltipPresentations; 267 mFieldFilters = builder.mFieldFilters; 268 mFieldContent = builder.mFieldContent; 269 mPresentation = builder.mPresentation; 270 mDialogPresentation = builder.mDialogPresentation; 271 mInlinePresentation = builder.mInlinePresentation; 272 mInlineTooltipPresentation = builder.mInlineTooltipPresentation; 273 mAuthentication = builder.mAuthentication; 274 mCredentialFillInIntent = builder.mCredentialFillInIntent; 275 mId = builder.mId; 276 mAutofillDatatypes = builder.mAutofillDatatypes; 277 } 278 279 /** @hide */ 280 @TestApi 281 @SuppressLint({"ConcreteCollection", "NullableCollection"}) getAutofillDatatypes()282 public @Nullable ArrayList<String> getAutofillDatatypes() { 283 return mAutofillDatatypes; 284 } 285 286 /** @hide */ 287 @TestApi 288 @SuppressLint({"ConcreteCollection", "NullableCollection"}) getFieldIds()289 public @Nullable ArrayList<AutofillId> getFieldIds() { 290 return mFieldIds; 291 } 292 293 /** @hide */ 294 @TestApi 295 @SuppressLint({"ConcreteCollection", "NullableCollection"}) getFieldValues()296 public @Nullable ArrayList<AutofillValue> getFieldValues() { 297 return mFieldValues; 298 } 299 300 /** @hide */ 301 @TestApi getFieldPresentation(int index)302 public @Nullable RemoteViews getFieldPresentation(int index) { 303 final RemoteViews customPresentation = mFieldPresentations.get(index); 304 return customPresentation != null ? customPresentation : mPresentation; 305 } 306 307 /** @hide */ 308 @TestApi getFieldDialogPresentation(int index)309 public @Nullable RemoteViews getFieldDialogPresentation(int index) { 310 final RemoteViews customPresentation = mFieldDialogPresentations.get(index); 311 return customPresentation != null ? customPresentation : mDialogPresentation; 312 } 313 314 /** @hide */ 315 @TestApi getFieldInlinePresentation(int index)316 public @Nullable InlinePresentation getFieldInlinePresentation(int index) { 317 final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index); 318 return inlinePresentation != null ? inlinePresentation : mInlinePresentation; 319 } 320 321 /** @hide */ 322 @TestApi getFieldInlineTooltipPresentation(int index)323 public @Nullable InlinePresentation getFieldInlineTooltipPresentation(int index) { 324 final InlinePresentation inlineTooltipPresentation = 325 mFieldInlineTooltipPresentations.get(index); 326 return inlineTooltipPresentation != null 327 ? inlineTooltipPresentation : mInlineTooltipPresentation; 328 } 329 330 /** @hide */ 331 @TestApi getFilter(int index)332 public @Nullable DatasetFieldFilter getFilter(int index) { 333 return mFieldFilters.get(index); 334 } 335 336 /** 337 * Returns the content to be filled for a non-text suggestion. This is only applicable to 338 * augmented autofill. The target field for the content is available via {@link #getFieldIds()} 339 * (guaranteed to have a single field id set when the return value here is non-null). See 340 * {@link Builder#setContent(AutofillId, ClipData)} for more info. 341 * 342 * @hide 343 */ 344 @TestApi getFieldContent()345 public @Nullable ClipData getFieldContent() { 346 return mFieldContent; 347 } 348 349 /** @hide */ 350 @TestApi getAuthentication()351 public @Nullable IntentSender getAuthentication() { 352 return mAuthentication; 353 } 354 355 /** @hide */ 356 @Hide getCredentialFillInIntent()357 public @Nullable Intent getCredentialFillInIntent() { 358 return mCredentialFillInIntent; 359 } 360 361 /** @hide */ 362 @Hide setCredentialFillInIntent(Intent intent)363 public void setCredentialFillInIntent(Intent intent) { 364 mCredentialFillInIntent = intent; 365 } 366 367 /** @hide */ 368 @TestApi isEmpty()369 public boolean isEmpty() { 370 return mFieldIds == null || mFieldIds.isEmpty(); 371 } 372 373 @Override toString()374 public String toString() { 375 if (!sDebug) return super.toString(); 376 377 final StringBuilder builder = new StringBuilder("Dataset["); 378 if (mId == null) { 379 builder.append("noId"); 380 } else { 381 // Cannot disclose id because it could contain PII. 382 builder.append("id=").append(mId.length()).append("_chars"); 383 } 384 if (mFieldIds != null) { 385 builder.append(", fieldIds=").append(mFieldIds); 386 } 387 if (mFieldValues != null) { 388 builder.append(", fieldValues=").append(mFieldValues); 389 } 390 if (mFieldContent != null) { 391 builder.append(", fieldContent=").append(mFieldContent); 392 } 393 if (mFieldPresentations != null) { 394 builder.append(", fieldPresentations=").append(mFieldPresentations.size()); 395 } 396 if (mFieldDialogPresentations != null) { 397 builder.append(", fieldDialogPresentations=").append(mFieldDialogPresentations.size()); 398 } 399 if (mFieldInlinePresentations != null) { 400 builder.append(", fieldInlinePresentations=").append(mFieldInlinePresentations.size()); 401 } 402 if (mFieldInlineTooltipPresentations != null) { 403 builder.append(", fieldInlineTooltipInlinePresentations=").append( 404 mFieldInlineTooltipPresentations.size()); 405 } 406 if (mFieldFilters != null) { 407 builder.append(", fieldFilters=").append(mFieldFilters.size()); 408 } 409 if (mPresentation != null) { 410 builder.append(", hasPresentation"); 411 } 412 if (mDialogPresentation != null) { 413 builder.append(", hasDialogPresentation"); 414 } 415 if (mInlinePresentation != null) { 416 builder.append(", hasInlinePresentation"); 417 } 418 if (mInlineTooltipPresentation != null) { 419 builder.append(", hasInlineTooltipPresentation"); 420 } 421 if (mAuthentication != null) { 422 builder.append(", hasAuthentication"); 423 } 424 if (mCredentialFillInIntent != null) { 425 builder.append(", hasAuthenticationExtras"); 426 } 427 if (mAutofillDatatypes != null) { 428 builder.append(", autofillDatatypes=").append(mAutofillDatatypes); 429 } 430 return builder.append(']').toString(); 431 } 432 433 /** 434 * Gets the id of this dataset. 435 * 436 * @return The id of this dataset or {@code null} if not set 437 * 438 * @hide 439 */ 440 @TestApi getId()441 public @Nullable String getId() { 442 return mId; 443 } 444 445 /** 446 * Sets the reason as to why this dataset is eligible 447 * @hide 448 */ setEligibleReasonReason(@atasetEligibleReason int eligibleReason)449 public void setEligibleReasonReason(@DatasetEligibleReason int eligibleReason) { 450 this.mEligibleReason = eligibleReason; 451 } 452 453 /** 454 * Get the reason as to why this dataset is eligible. 455 * @hide 456 */ getEligibleReason()457 public @DatasetEligibleReason int getEligibleReason() { 458 return mEligibleReason; 459 } 460 461 /** 462 * A builder for {@link Dataset} objects. You must provide at least 463 * one value for a field or set an authentication intent. 464 */ 465 public static final class Builder { 466 private ArrayList<AutofillId> mFieldIds = new ArrayList<>(); 467 private ArrayList<AutofillValue> mFieldValues = new ArrayList(); 468 private ArrayList<RemoteViews> mFieldPresentations = new ArrayList(); 469 private ArrayList<RemoteViews> mFieldDialogPresentations = new ArrayList(); 470 private ArrayList<InlinePresentation> mFieldInlinePresentations = new ArrayList(); 471 private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations = new ArrayList(); 472 private ArrayList<DatasetFieldFilter> mFieldFilters = new ArrayList(); 473 private ArrayList<String> mAutofillDatatypes = new ArrayList(); 474 @Nullable private ClipData mFieldContent; 475 private RemoteViews mPresentation; 476 private RemoteViews mDialogPresentation; 477 @Nullable private InlinePresentation mInlinePresentation; 478 @Nullable private InlinePresentation mInlineTooltipPresentation; 479 private IntentSender mAuthentication; 480 481 private Intent mCredentialFillInIntent; 482 private boolean mDestroyed; 483 @Nullable private String mId; 484 485 /** 486 * Usually, a field will be associated with a single autofill id and/or datatype. 487 * There could be null field value corresponding to different autofill ids or datatye 488 * values, but the implementation is ok with duplicating that information. 489 * This map is just for the purpose of optimization, to reduce the size of the pelled data 490 * over the binder transaction. 491 */ 492 private ArrayMap<Field, Integer> mFieldToIndexdMap = new ArrayMap<>(); 493 494 /** 495 * Creates a new builder. 496 * 497 * @param presentation The presentation used to visualize this dataset. 498 * @deprecated Use {@link #Builder(Presentations)} instead. 499 */ 500 @Deprecated Builder(@onNull RemoteViews presentation)501 public Builder(@NonNull RemoteViews presentation) { 502 Objects.requireNonNull(presentation, "presentation must be non-null"); 503 mPresentation = presentation; 504 } 505 506 /** 507 * Creates a new builder. 508 * 509 * <p>Only called by augmented autofill. 510 * 511 * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset 512 * as inline suggestions. If the dataset supports inline suggestions, 513 * this should not be null. 514 * @hide 515 * @deprecated Use {@link #Builder(Presentations)} instead. 516 */ 517 @SystemApi 518 @Deprecated Builder(@onNull InlinePresentation inlinePresentation)519 public Builder(@NonNull InlinePresentation inlinePresentation) { 520 Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null"); 521 mInlinePresentation = inlinePresentation; 522 } 523 524 /** 525 * Creates a new builder. 526 * 527 * @param presentations The presentations used to visualize this dataset. 528 */ Builder(@onNull Presentations presentations)529 public Builder(@NonNull Presentations presentations) { 530 Objects.requireNonNull(presentations, "presentations must be non-null"); 531 532 mPresentation = presentations.getMenuPresentation(); 533 mInlinePresentation = presentations.getInlinePresentation(); 534 mInlineTooltipPresentation = presentations.getInlineTooltipPresentation(); 535 mDialogPresentation = presentations.getDialogPresentation(); 536 } 537 538 /** 539 * Creates a new builder for a dataset where each field will be visualized independently. 540 * 541 * <p>When using this constructor, a presentation must be provided for each field through 542 * {@link #setField(AutofillId, Field)}. 543 */ Builder()544 public Builder() { 545 } 546 547 /** 548 * Sets the {@link InlinePresentation} used to visualize this dataset as inline suggestions. 549 * If the dataset supports inline suggestions this should not be null. 550 * 551 * @throws IllegalStateException if {@link #build()} was already called. 552 * 553 * @return this builder. 554 * @deprecated Use {@link #Builder(Presentations)} instead. 555 */ 556 @Deprecated setInlinePresentation( @onNull InlinePresentation inlinePresentation)557 public @NonNull Builder setInlinePresentation( 558 @NonNull InlinePresentation inlinePresentation) { 559 throwIfDestroyed(); 560 Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null"); 561 mInlinePresentation = inlinePresentation; 562 return this; 563 } 564 565 /** 566 * Visualizes this dataset as inline suggestions. 567 * 568 * @param inlinePresentation the {@link InlinePresentation} used to visualize this 569 * dataset as inline suggestions. If the dataset supports inline suggestions this 570 * should not be null. 571 * @param inlineTooltipPresentation the {@link InlinePresentation} used to show 572 * the tooltip for the {@code inlinePresentation}. 573 * 574 * @throws IllegalStateException if {@link #build()} was already called. 575 * 576 * @return this builder. 577 * @deprecated Use {@link #Builder(Presentations)} instead. 578 */ 579 @Deprecated setInlinePresentation( @onNull InlinePresentation inlinePresentation, @NonNull InlinePresentation inlineTooltipPresentation)580 public @NonNull Builder setInlinePresentation( 581 @NonNull InlinePresentation inlinePresentation, 582 @NonNull InlinePresentation inlineTooltipPresentation) { 583 throwIfDestroyed(); 584 Objects.requireNonNull(inlinePresentation, "inlinePresentation must be non-null"); 585 Objects.requireNonNull(inlineTooltipPresentation, 586 "inlineTooltipPresentation must be non-null"); 587 mInlinePresentation = inlinePresentation; 588 mInlineTooltipPresentation = inlineTooltipPresentation; 589 return this; 590 } 591 592 /** 593 * Triggers a custom UI before before autofilling the screen with the contents of this 594 * dataset. 595 * 596 * <p><b>Note:</b> Although the name of this method suggests that it should be used just for 597 * authentication flow, it can be used for other advanced flows; see {@link AutofillService} 598 * for examples. 599 * 600 * <p>This method is called when you need to provide an authentication 601 * UI for the data set. For example, when a data set contains credit card information 602 * (such as number, expiration date, and verification code), you can display UI 603 * asking for the verification code before filing in the data. Even if the 604 * data set is completely populated the system will launch the specified authentication 605 * intent and will need your approval to fill it in. Since the data set is "locked" 606 * until the user authenticates it, typically this data set name is masked 607 * (for example, "VISA....1234"). Typically you would want to store the data set 608 * labels non-encrypted and the actual sensitive data encrypted and not in memory. 609 * This allows showing the labels in the UI while involving the user if one of 610 * the items with these labels is chosen. Note that if you use sensitive data as 611 * a label, for example an email address, then it should also be encrypted.</p> 612 * 613 * <p>When a user triggers autofill, the system launches the provided intent 614 * whose extras will have the {@link 615 * android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen content}, 616 * and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE client 617 * state}. Once you complete your authentication flow you should set the activity 618 * result to {@link android.app.Activity#RESULT_OK} and provide the fully populated 619 * {@link Dataset dataset} or a fully-populated {@link FillResponse response} by 620 * setting it to the {@link 621 * android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra. If you 622 * provide a dataset in the result, it will replace the authenticated dataset and 623 * will be immediately filled in. An exception to this behavior is if the original 624 * dataset represents a pinned inline suggestion (i.e. any of the field in the dataset 625 * has a pinned inline presentation, see {@link InlinePresentation#isPinned()}), then 626 * the original dataset will not be replaced, 627 * so that it can be triggered as a pending intent again. 628 * If you provide a response, it will replace the 629 * current response and the UI will be refreshed. For example, if you provided 630 * credit card information without the CVV for the data set in the {@link FillResponse 631 * response} then the returned data set should contain the CVV entry. 632 * 633 * <p><b>Note:</b> Do not make the provided pending intent 634 * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the 635 * platform needs to fill in the authentication arguments. 636 * 637 * @param authentication Intent to an activity with your authentication flow. 638 * 639 * @throws IllegalStateException if {@link #build()} was already called. 640 * 641 * @return this builder. 642 * 643 * @see android.app.PendingIntent 644 */ setAuthentication(@ullable IntentSender authentication)645 public @NonNull Builder setAuthentication(@Nullable IntentSender authentication) { 646 throwIfDestroyed(); 647 mAuthentication = authentication; 648 return this; 649 } 650 651 /** 652 * Sets extras to be associated with the {@code authentication} intent sender, to be 653 * set on the intent that is fired through the intent sender. 654 * 655 * Autofill providers can set any extras they wish to receive directly on the intent 656 * that is used to create the {@code authentication}. This is an internal API, to be 657 * used by the platform to associate data with a given dataset. These extras will be 658 * merged with the {@code clientState} and sent as part of the fill in intent when 659 * the {@code authentication} intentSender is invoked. 660 * 661 * @hide 662 */ 663 @Hide setCredentialFillInIntent(@ullable Intent credentialFillInIntent)664 public @NonNull Builder setCredentialFillInIntent(@Nullable Intent credentialFillInIntent) { 665 throwIfDestroyed(); 666 mCredentialFillInIntent = credentialFillInIntent; 667 return this; 668 } 669 670 /** 671 * Sets the id for the dataset so its usage can be tracked. 672 * 673 * <p>Dataset usage can be tracked for 2 purposes: 674 * 675 * <ul> 676 * <li>For statistical purposes, the service can call 677 * {@link AutofillService#getFillEventHistory()} when handling {@link 678 * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)} 679 * calls. 680 * <li>For normal autofill workflow, the service can call 681 * {@link SaveRequest#getDatasetIds()} when handling 682 * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} calls. 683 * </ul> 684 * 685 * @param id id for this dataset or {@code null} to unset. 686 * 687 * @throws IllegalStateException if {@link #build()} was already called. 688 * 689 * @return this builder. 690 */ setId(@ullable String id)691 public @NonNull Builder setId(@Nullable String id) { 692 throwIfDestroyed(); 693 mId = id; 694 return this; 695 } 696 697 /** 698 * Sets the content for a field. 699 * 700 * <p>Only called by augmented autofill. 701 * 702 * <p>For a given field, either a {@link AutofillValue value} or content can be filled, but 703 * not both. Furthermore, when filling content, only a single field can be filled. 704 * 705 * <p>The provided {@link ClipData} can contain content URIs (e.g. a URI for an image). 706 * The augmented autofill provider setting the content here must itself have at least 707 * read permissions to any passed content URIs. If the user accepts the suggestion backed 708 * by the content URI(s), the platform will automatically grant read URI permissions to 709 * the app being autofilled, just before passing the content URI(s) to it. The granted 710 * permissions will be transient and tied to the lifecycle of the activity being filled 711 * (when the activity finishes, permissions will automatically be revoked by the platform). 712 * 713 * @param id id returned by 714 * {@link android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 715 * @param content content to be autofilled. Pass {@code null} if you do not have the content 716 * but the target view is a logical part of the dataset. For example, if the dataset needs 717 * authentication. 718 * 719 * @throws IllegalStateException if {@link #build()} was already called. 720 * @throws IllegalArgumentException if the provided content 721 * {@link ClipData.Item#getIntent() contains an intent} 722 * 723 * @return this builder. 724 * 725 * @hide 726 */ 727 @TestApi 728 @SystemApi 729 @SuppressLint("MissingGetterMatchingBuilder") setContent(@onNull AutofillId id, @Nullable ClipData content)730 public @NonNull Builder setContent(@NonNull AutofillId id, @Nullable ClipData content) { 731 throwIfDestroyed(); 732 if (content != null) { 733 for (int i = 0; i < content.getItemCount(); i++) { 734 Preconditions.checkArgument(content.getItemAt(i).getIntent() == null, 735 "Content items cannot contain an Intent: content=" + content); 736 } 737 } 738 setLifeTheUniverseAndEverything(id, null, null, null, null, null, null); 739 mFieldContent = content; 740 return this; 741 } 742 743 /** 744 * Sets the value of a field. 745 * 746 * <b>Note:</b> Prior to Android {@link android.os.Build.VERSION_CODES#P}, this method would 747 * throw an {@link IllegalStateException} if this builder was constructed without a 748 * {@link RemoteViews presentation}. Android {@link android.os.Build.VERSION_CODES#P} and 749 * higher removed this restriction because datasets used as an 750 * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT 751 * authentication result} do not need a presentation. But if you don't set the presentation 752 * in the constructor in a dataset that is meant to be shown to the user, the autofill UI 753 * for this field will not be displayed. 754 * 755 * <p><b>Note:</b> On Android {@link android.os.Build.VERSION_CODES#P} and 756 * higher, datasets that require authentication can be also be filtered by passing a 757 * {@link AutofillValue#forText(CharSequence) text value} as the {@code value} parameter. 758 * 759 * @param id id returned by {@link 760 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 761 * @param value value to be autofilled. Pass {@code null} if you do not have the value 762 * but the target view is a logical part of the dataset. For example, if 763 * the dataset needs authentication and you have no access to the value. 764 * 765 * @throws IllegalStateException if {@link #build()} was already called. 766 * 767 * @return this builder. 768 * @deprecated Use {@link #setField(AutofillId, Field)} instead. 769 */ 770 @Deprecated setValue(@onNull AutofillId id, @Nullable AutofillValue value)771 public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) { 772 throwIfDestroyed(); 773 setLifeTheUniverseAndEverything(id, value, null, null, null, null, null); 774 return this; 775 } 776 777 /** 778 * Sets the value of a field, using a custom {@link RemoteViews presentation} to 779 * visualize it. 780 * 781 * <p><b>Note:</b> On Android {@link android.os.Build.VERSION_CODES#P} and 782 * higher, datasets that require authentication can be also be filtered by passing a 783 * {@link AutofillValue#forText(CharSequence) text value} as the {@code value} parameter. 784 * 785 * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color 786 * or background color: Autofill on different platforms may have different themes. 787 * 788 * @param id id returned by {@link 789 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 790 * @param value the value to be autofilled. Pass {@code null} if you do not have the value 791 * but the target view is a logical part of the dataset. For example, if 792 * the dataset needs authentication and you have no access to the value. 793 * @param presentation the presentation used to visualize this field. 794 * 795 * @throws IllegalStateException if {@link #build()} was already called. 796 * 797 * @return this builder. 798 * @deprecated Use {@link #setField(AutofillId, Field)} instead. 799 */ 800 @Deprecated setValue(@onNull AutofillId id, @Nullable AutofillValue value, @NonNull RemoteViews presentation)801 public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, 802 @NonNull RemoteViews presentation) { 803 throwIfDestroyed(); 804 Objects.requireNonNull(presentation, "presentation cannot be null"); 805 setLifeTheUniverseAndEverything(id, value, presentation, null, null, null, null); 806 return this; 807 } 808 809 /** 810 * Sets the value of a field using an <a href="#Filtering">explicit filter</a>. 811 * 812 * <p>This method is typically used when the dataset requires authentication and the service 813 * does not know its value but wants to hide the dataset after the user enters a minimum 814 * number of characters. For example, if the dataset represents a credit card number and the 815 * service does not want to show the "Tap to authenticate" message until the user tapped 816 * 4 digits, in which case the filter would be {@code Pattern.compile("\\d.{4,}")}. 817 * 818 * <p><b>Note:</b> If the dataset requires authentication but the service knows its text 819 * value it's easier to filter by calling {@link #setValue(AutofillId, AutofillValue)} and 820 * use the value to filter. 821 * 822 * @param id id returned by {@link 823 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 824 * @param value the value to be autofilled. Pass {@code null} if you do not have the value 825 * but the target view is a logical part of the dataset. For example, if 826 * the dataset needs authentication and you have no access to the value. 827 * @param filter regex used to determine if the dataset should be shown in the autofill UI; 828 * when {@code null}, it disables filtering on that dataset (this is the recommended 829 * approach when {@code value} is not {@code null} and field contains sensitive data 830 * such as passwords). 831 * 832 * @return this builder. 833 * @throws IllegalStateException if the builder was constructed without a 834 * {@link RemoteViews presentation} or {@link #build()} was already called. 835 * @deprecated Use {@link #setField(AutofillId, Field)} instead. 836 */ 837 @Deprecated setValue(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter)838 public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, 839 @Nullable Pattern filter) { 840 throwIfDestroyed(); 841 Preconditions.checkState(mPresentation != null, 842 "Dataset presentation not set on constructor"); 843 setLifeTheUniverseAndEverything( 844 id, value, null, null, null, new DatasetFieldFilter(filter), null); 845 return this; 846 } 847 848 /** 849 * Sets the value of a field, using a custom {@link RemoteViews presentation} to 850 * visualize it and a <a href="#Filtering">explicit filter</a>. 851 * 852 * <p>This method is typically used when the dataset requires authentication and the service 853 * does not know its value but wants to hide the dataset after the user enters a minimum 854 * number of characters. For example, if the dataset represents a credit card number and the 855 * service does not want to show the "Tap to authenticate" message until the user tapped 856 * 4 digits, in which case the filter would be {@code Pattern.compile("\\d.{4,}")}. 857 * 858 * <p><b>Note:</b> If the dataset requires authentication but the service knows its text 859 * value it's easier to filter by calling 860 * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter. 861 * 862 * @param id id returned by {@link 863 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 864 * @param value the value to be autofilled. Pass {@code null} if you do not have the value 865 * but the target view is a logical part of the dataset. For example, if 866 * the dataset needs authentication and you have no access to the value. 867 * @param filter regex used to determine if the dataset should be shown in the autofill UI; 868 * when {@code null}, it disables filtering on that dataset (this is the recommended 869 * approach when {@code value} is not {@code null} and field contains sensitive data 870 * such as passwords). 871 * @param presentation the presentation used to visualize this field. 872 * 873 * @throws IllegalStateException if {@link #build()} was already called. 874 * 875 * @return this builder. 876 * @deprecated Use {@link #setField(AutofillId, Field)} instead. 877 */ 878 @Deprecated setValue(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull RemoteViews presentation)879 public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, 880 @Nullable Pattern filter, @NonNull RemoteViews presentation) { 881 throwIfDestroyed(); 882 Objects.requireNonNull(presentation, "presentation cannot be null"); 883 setLifeTheUniverseAndEverything(id, value, presentation, null, null, 884 new DatasetFieldFilter(filter), null); 885 return this; 886 } 887 888 /** 889 * Sets the value of a field, using a custom {@link RemoteViews presentation} to 890 * visualize it and an {@link InlinePresentation} to visualize it as an inline suggestion. 891 * 892 * <p><b>Note:</b> If the dataset requires authentication but the service knows its text 893 * value it's easier to filter by calling 894 * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter. 895 * 896 * @param id id returned by {@link 897 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 898 * @param value the value to be autofilled. Pass {@code null} if you do not have the value 899 * but the target view is a logical part of the dataset. For example, if 900 * the dataset needs authentication and you have no access to the value. 901 * @param presentation the presentation used to visualize this field. 902 * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset 903 * as inline suggestions. If the dataset supports inline suggestions, 904 * this should not be null. 905 * 906 * @throws IllegalStateException if {@link #build()} was already called. 907 * 908 * @return this builder. 909 * @deprecated Use {@link #setField(AutofillId, Field)} instead. 910 */ 911 @Deprecated setValue(@onNull AutofillId id, @Nullable AutofillValue value, @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation)912 public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, 913 @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) { 914 throwIfDestroyed(); 915 Objects.requireNonNull(presentation, "presentation cannot be null"); 916 Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null"); 917 setLifeTheUniverseAndEverything( 918 id, value, presentation, inlinePresentation, null, null, null); 919 return this; 920 } 921 922 /** 923 * Sets the value of a field, using a custom {@link RemoteViews presentation} to 924 * visualize it and an {@link InlinePresentation} to visualize it as an inline suggestion. 925 * 926 * @see #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation) 927 * 928 * @param id id returned by {@link 929 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 930 * @param value the value to be autofilled. Pass {@code null} if you do not have the value 931 * but the target view is a logical part of the dataset. For example, if 932 * the dataset needs authentication and you have no access to the value. 933 * @param presentation the presentation used to visualize this field. 934 * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset 935 * as inline suggestions. If the dataset supports inline suggestions, 936 * this should not be null. 937 * @param inlineTooltipPresentation The {@link InlinePresentation} used to show 938 * the tooltip for the {@code inlinePresentation}. 939 * 940 * @throws IllegalStateException if {@link #build()} was already called. 941 * 942 * @return this builder. 943 * @deprecated Use {@link #setField(AutofillId, Field)} instead. 944 */ 945 @Deprecated setValue(@onNull AutofillId id, @Nullable AutofillValue value, @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation, @NonNull InlinePresentation inlineTooltipPresentation)946 public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, 947 @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation, 948 @NonNull InlinePresentation inlineTooltipPresentation) { 949 throwIfDestroyed(); 950 Objects.requireNonNull(presentation, "presentation cannot be null"); 951 Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null"); 952 Objects.requireNonNull(inlineTooltipPresentation, 953 "inlineTooltipPresentation cannot be null"); 954 setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, 955 inlineTooltipPresentation, null, null); 956 return this; 957 } 958 959 /** 960 * Sets the value of a field, using a custom {@link RemoteViews presentation} to 961 * visualize it and a <a href="#Filtering">explicit filter</a>, and an 962 * {@link InlinePresentation} to visualize it as an inline suggestion. 963 * 964 * <p>This method is typically used when the dataset requires authentication and the service 965 * does not know its value but wants to hide the dataset after the user enters a minimum 966 * number of characters. For example, if the dataset represents a credit card number and the 967 * service does not want to show the "Tap to authenticate" message until the user tapped 968 * 4 digits, in which case the filter would be {@code Pattern.compile("\\d.{4,}")}. 969 * 970 * <p><b>Note:</b> If the dataset requires authentication but the service knows its text 971 * value it's easier to filter by calling 972 * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter. 973 * 974 * @param id id returned by {@link 975 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 976 * @param value the value to be autofilled. Pass {@code null} if you do not have the value 977 * but the target view is a logical part of the dataset. For example, if 978 * the dataset needs authentication and you have no access to the value. 979 * @param filter regex used to determine if the dataset should be shown in the autofill UI; 980 * when {@code null}, it disables filtering on that dataset (this is the recommended 981 * approach when {@code value} is not {@code null} and field contains sensitive data 982 * such as passwords). 983 * @param presentation the presentation used to visualize this field. 984 * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset 985 * as inline suggestions. If the dataset supports inline suggestions, this 986 * should not be null. 987 * 988 * @throws IllegalStateException if {@link #build()} was already called. 989 * 990 * @return this builder. 991 * @deprecated Use {@link #setField(AutofillId, Field)} instead. 992 */ 993 @Deprecated setValue(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation)994 public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, 995 @Nullable Pattern filter, @NonNull RemoteViews presentation, 996 @NonNull InlinePresentation inlinePresentation) { 997 throwIfDestroyed(); 998 Objects.requireNonNull(presentation, "presentation cannot be null"); 999 Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null"); 1000 setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null, 1001 new DatasetFieldFilter(filter), null); 1002 return this; 1003 } 1004 1005 /** 1006 * Sets the value of a field, using a custom {@link RemoteViews presentation} to 1007 * visualize it and a <a href="#Filtering">explicit filter</a>, and an 1008 * {@link InlinePresentation} to visualize it as an inline suggestion. 1009 * 1010 * @see #setValue(AutofillId, AutofillValue, Pattern, RemoteViews, InlinePresentation) 1011 * 1012 * @param id id returned by {@link 1013 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 1014 * @param value the value to be autofilled. Pass {@code null} if you do not have the value 1015 * but the target view is a logical part of the dataset. For example, if 1016 * the dataset needs authentication and you have no access to the value. 1017 * @param filter regex used to determine if the dataset should be shown in the autofill UI; 1018 * when {@code null}, it disables filtering on that dataset (this is the recommended 1019 * approach when {@code value} is not {@code null} and field contains sensitive data 1020 * such as passwords). 1021 * @param presentation the presentation used to visualize this field. 1022 * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset 1023 * as inline suggestions. If the dataset supports inline suggestions, this 1024 * should not be null. 1025 * @param inlineTooltipPresentation The {@link InlinePresentation} used to show 1026 * the tooltip for the {@code inlinePresentation}. 1027 * 1028 * @throws IllegalStateException if {@link #build()} was already called. 1029 * 1030 * @return this builder. 1031 * @deprecated Use {@link #setField(AutofillId, Field)} instead. 1032 */ 1033 @Deprecated setValue(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation, @NonNull InlinePresentation inlineTooltipPresentation)1034 public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value, 1035 @Nullable Pattern filter, @NonNull RemoteViews presentation, 1036 @NonNull InlinePresentation inlinePresentation, 1037 @NonNull InlinePresentation inlineTooltipPresentation) { 1038 throwIfDestroyed(); 1039 Objects.requireNonNull(presentation, "presentation cannot be null"); 1040 Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null"); 1041 Objects.requireNonNull(inlineTooltipPresentation, 1042 "inlineTooltipPresentation cannot be null"); 1043 setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, 1044 inlineTooltipPresentation, new DatasetFieldFilter(filter), null); 1045 return this; 1046 } 1047 1048 /** 1049 * Sets the value of a field. 1050 * 1051 * Before Android 13, this information could be provided using several overloaded 1052 * setValue(...) methods. This method replaces those with a Builder pattern. 1053 * For example, in the old workflow, the app sets a field would be: 1054 * <pre class="prettyprint"> 1055 * Dataset.Builder dataset = new Dataset.Builder(); 1056 * if (filter != null) { 1057 * if (presentation != null) { 1058 * if (inlinePresentation != null) { 1059 * dataset.setValue(id, value, filter, presentation, inlinePresentation) 1060 * } else { 1061 * dataset.setValue(id, value, filter, presentation); 1062 * } 1063 * } else { 1064 * dataset.setValue(id, value, filter); 1065 * } 1066 * } else { 1067 * if (presentation != null) { 1068 * if (inlinePresentation != null) { 1069 * dataset.setValue(id, value, presentation, inlinePresentation) 1070 * } else { 1071 * dataset.setValue(id, value, presentation); 1072 * } 1073 * } else { 1074 * dataset.setValue(id, value); 1075 * } 1076 * } 1077 * </pre> 1078 * <p>The new workflow would be: 1079 * <pre class="prettyprint"> 1080 * Field.Builder fieldBuilder = new Field.Builder(); 1081 * if (value != null) { 1082 * fieldBuilder.setValue(value); 1083 * } 1084 * if (filter != null) { 1085 * fieldBuilder.setFilter(filter); 1086 * } 1087 * Presentations.Builder presentationsBuilder = new Presentations.Builder(); 1088 * if (presentation != null) { 1089 * presentationsBuilder.setMenuPresentation(presentation); 1090 * } 1091 * if (inlinePresentation != null) { 1092 * presentationsBuilder.setInlinePresentation(inlinePresentation); 1093 * } 1094 * if (dialogPresentation != null) { 1095 * presentationsBuilder.setDialogPresentation(dialogPresentation); 1096 * } 1097 * fieldBuilder.setPresentations(presentationsBuilder.build()); 1098 * dataset.setField(id, fieldBuilder.build()); 1099 * </pre> 1100 * 1101 * @see Field 1102 * 1103 * @param id id returned by {@link 1104 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 1105 * @param field the fill information about the field. 1106 * 1107 * @throws IllegalStateException if {@link #build()} was already called. 1108 * 1109 * @return this builder. 1110 */ setField(@onNull AutofillId id, @Nullable Field field)1111 public @NonNull Builder setField(@NonNull AutofillId id, @Nullable Field field) { 1112 throwIfDestroyed(); 1113 1114 if (mFieldToIndexdMap.containsKey(field)) { 1115 int index = mFieldToIndexdMap.get(field); 1116 if (mFieldIds.get(index) == null) { 1117 mFieldIds.set(index, id); 1118 return this; 1119 } 1120 // if the Autofill Id is already set, ignore and proceed as if setting in a new 1121 // value. 1122 } 1123 int index; 1124 if (field == null) { 1125 index = setLifeTheUniverseAndEverything(id, null, null, null, null, null, null); 1126 } else { 1127 final DatasetFieldFilter filter = field.getDatasetFieldFilter(); 1128 final Presentations presentations = field.getPresentations(); 1129 if (presentations == null) { 1130 index = setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null, 1131 filter, null); 1132 } else { 1133 index = setLifeTheUniverseAndEverything(id, field.getValue(), 1134 presentations.getMenuPresentation(), 1135 presentations.getInlinePresentation(), 1136 presentations.getInlineTooltipPresentation(), filter, 1137 presentations.getDialogPresentation()); 1138 } 1139 } 1140 mFieldToIndexdMap.put(field, index); 1141 return this; 1142 } 1143 1144 /** 1145 * Adds a field to this Dataset with a specific type. This is used to send back Field 1146 * information when Autofilling with platform detections is on. 1147 * Platform detections are on when receiving a populated list from 1148 * FillRequest#getHints(). 1149 * 1150 * Populate every field/type known for this user for this app. 1151 * 1152 * For example, if getHints() contains "username" and "password", 1153 * a new Dataset should be created that calls this method twice, 1154 * one for the username, then another for the password (assuming 1155 * the only one credential pair is found for the user). If a user 1156 * has two credential pairs, then two Datasets should be created, 1157 * and so on. 1158 * 1159 * @param hint An autofill hint returned from {@link 1160 * FillRequest#getHints()}. 1161 * 1162 * @param field the fill information about the field. 1163 * 1164 * @throws IllegalStateException if {@link #build()} was already called 1165 * or this builder also contains AutofillId information 1166 * 1167 * @return this builder. 1168 */ setField(@onNull String hint, @NonNull Field field)1169 public @NonNull Dataset.Builder setField(@NonNull String hint, @NonNull Field field) { 1170 throwIfDestroyed(); 1171 1172 if (mFieldToIndexdMap.containsKey(field)) { 1173 int index = mFieldToIndexdMap.get(field); 1174 if (mAutofillDatatypes.get(index) == null) { 1175 mAutofillDatatypes.set(index, hint); 1176 return this; 1177 } 1178 // if the hint is already set, ignore and proceed as if setting in a new hint. 1179 } 1180 1181 int index; 1182 final DatasetFieldFilter filter = field.getDatasetFieldFilter(); 1183 final Presentations presentations = field.getPresentations(); 1184 if (presentations == null) { 1185 index = setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null, 1186 filter, null); 1187 } else { 1188 index = setLifeTheUniverseAndEverything(hint, field.getValue(), 1189 presentations.getMenuPresentation(), 1190 presentations.getInlinePresentation(), 1191 presentations.getInlineTooltipPresentation(), filter, 1192 presentations.getDialogPresentation()); 1193 } 1194 mFieldToIndexdMap.put(field, index); 1195 return this; 1196 } 1197 1198 /** 1199 * Adds a field to this Dataset that is relevant to all applicable hints. This is used to 1200 * provide field information when autofill with platform detections is enabled. 1201 * Platform detections are on when receiving a populated list from 1202 * FillRequest#getHints(). 1203 * 1204 * @param field the fill information about the field. 1205 * 1206 * @throws IllegalStateException if {@link #build()} was already called 1207 * or this builder also contains AutofillId information 1208 * 1209 * @return this builder. 1210 */ setFieldForAllHints(@onNull Field field)1211 public @NonNull Dataset.Builder setFieldForAllHints(@NonNull Field field) { 1212 return setField(AutofillManager.ANY_HINT, field); 1213 } 1214 1215 /** 1216 * Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an 1217 * {@link InlinePresentation} to visualize it as an inline suggestion. 1218 * 1219 * <p>Only called by augmented autofill. 1220 * 1221 * @param id id returned by {@link 1222 * android.app.assist.AssistStructure.ViewNode#getAutofillId()}. 1223 * @param value the value to be autofilled. Pass {@code null} if you do not have the value 1224 * but the target view is a logical part of the dataset. For example, if 1225 * the dataset needs authentication and you have no access to the value. 1226 * @param filter regex used to determine if the dataset should be shown in the autofill UI; 1227 * when {@code null}, it disables filtering on that dataset (this is the recommended 1228 * approach when {@code value} is not {@code null} and field contains sensitive data 1229 * such as passwords). 1230 * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset 1231 * as inline suggestions. If the dataset supports inline suggestions, this 1232 * should not be null. 1233 * 1234 * @throws IllegalStateException if {@link #build()} was already called. 1235 * 1236 * @return this builder. 1237 * @deprecated Use {@link #setField(AutofillId, Field)} instead. 1238 * @hide 1239 */ 1240 @Deprecated 1241 @SystemApi setFieldInlinePresentation(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull InlinePresentation inlinePresentation)1242 public @NonNull Builder setFieldInlinePresentation(@NonNull AutofillId id, 1243 @Nullable AutofillValue value, @Nullable Pattern filter, 1244 @NonNull InlinePresentation inlinePresentation) { 1245 throwIfDestroyed(); 1246 Objects.requireNonNull(inlinePresentation, "inlinePresentation cannot be null"); 1247 setLifeTheUniverseAndEverything(id, value, null, inlinePresentation, null, 1248 new DatasetFieldFilter(filter), null); 1249 return this; 1250 } 1251 1252 /** Returns the index at which this id was modified or inserted */ setLifeTheUniverseAndEverything(@onNull String datatype, @Nullable AutofillValue value, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation tooltip, @Nullable DatasetFieldFilter filter, @Nullable RemoteViews dialogPresentation)1253 private int setLifeTheUniverseAndEverything(@NonNull String datatype, 1254 @Nullable AutofillValue value, 1255 @Nullable RemoteViews presentation, 1256 @Nullable InlinePresentation inlinePresentation, 1257 @Nullable InlinePresentation tooltip, 1258 @Nullable DatasetFieldFilter filter, 1259 @Nullable RemoteViews dialogPresentation) { 1260 Objects.requireNonNull(datatype, "datatype cannot be null"); 1261 final int existingIdx = mAutofillDatatypes.indexOf(datatype); 1262 if (existingIdx >= 0) { 1263 mAutofillDatatypes.add(datatype); 1264 mFieldValues.set(existingIdx, value); 1265 mFieldPresentations.set(existingIdx, presentation); 1266 mFieldDialogPresentations.set(existingIdx, dialogPresentation); 1267 mFieldInlinePresentations.set(existingIdx, inlinePresentation); 1268 mFieldInlineTooltipPresentations.set(existingIdx, tooltip); 1269 mFieldFilters.set(existingIdx, filter); 1270 return existingIdx; 1271 } 1272 mFieldIds.add(null); 1273 mAutofillDatatypes.add(datatype); 1274 mFieldValues.add(value); 1275 mFieldPresentations.add(presentation); 1276 mFieldDialogPresentations.add(dialogPresentation); 1277 mFieldInlinePresentations.add(inlinePresentation); 1278 mFieldInlineTooltipPresentations.add(tooltip); 1279 mFieldFilters.add(filter); 1280 return mFieldIds.size() - 1; 1281 } 1282 1283 /** Returns the index at which this id was modified or inserted */ setLifeTheUniverseAndEverything(@onNull AutofillId id, @Nullable AutofillValue value, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation tooltip, @Nullable DatasetFieldFilter filter, @Nullable RemoteViews dialogPresentation)1284 private int setLifeTheUniverseAndEverything(@NonNull AutofillId id, 1285 @Nullable AutofillValue value, @Nullable RemoteViews presentation, 1286 @Nullable InlinePresentation inlinePresentation, 1287 @Nullable InlinePresentation tooltip, 1288 @Nullable DatasetFieldFilter filter, 1289 @Nullable RemoteViews dialogPresentation) { 1290 Objects.requireNonNull(id, "id cannot be null"); 1291 final int existingIdx = mFieldIds.indexOf(id); 1292 if (existingIdx >= 0) { 1293 mFieldValues.set(existingIdx, value); 1294 mFieldPresentations.set(existingIdx, presentation); 1295 mFieldDialogPresentations.set(existingIdx, dialogPresentation); 1296 mFieldInlinePresentations.set(existingIdx, inlinePresentation); 1297 mFieldInlineTooltipPresentations.set(existingIdx, tooltip); 1298 mFieldFilters.set(existingIdx, filter); 1299 return existingIdx; 1300 } 1301 mFieldIds.add(id); 1302 mAutofillDatatypes.add(null); 1303 mFieldValues.add(value); 1304 mFieldPresentations.add(presentation); 1305 mFieldDialogPresentations.add(dialogPresentation); 1306 mFieldInlinePresentations.add(inlinePresentation); 1307 mFieldInlineTooltipPresentations.add(tooltip); 1308 mFieldFilters.add(filter); 1309 return mFieldIds.size() - 1; 1310 } 1311 createFromParcel( @ullable AutofillId id, @Nullable String datatype, @Nullable AutofillValue value, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation tooltip, @Nullable DatasetFieldFilter filter, @Nullable RemoteViews dialogPresentation)1312 private void createFromParcel( 1313 @Nullable AutofillId id, @Nullable String datatype, 1314 @Nullable AutofillValue value, @Nullable RemoteViews presentation, 1315 @Nullable InlinePresentation inlinePresentation, 1316 @Nullable InlinePresentation tooltip, 1317 @Nullable DatasetFieldFilter filter, 1318 @Nullable RemoteViews dialogPresentation) { 1319 if (id != null) { 1320 final int existingIdx = mFieldIds.indexOf(id); 1321 if (existingIdx >= 0) { 1322 mFieldValues.set(existingIdx, value); 1323 mFieldPresentations.set(existingIdx, presentation); 1324 mFieldDialogPresentations.set(existingIdx, dialogPresentation); 1325 mFieldInlinePresentations.set(existingIdx, inlinePresentation); 1326 mFieldInlineTooltipPresentations.set(existingIdx, tooltip); 1327 mFieldFilters.set(existingIdx, filter); 1328 return; 1329 } 1330 } 1331 mFieldIds.add(id); 1332 mAutofillDatatypes.add(datatype); 1333 mFieldValues.add(value); 1334 mFieldPresentations.add(presentation); 1335 mFieldDialogPresentations.add(dialogPresentation); 1336 mFieldInlinePresentations.add(inlinePresentation); 1337 mFieldInlineTooltipPresentations.add(tooltip); 1338 mFieldFilters.add(filter); 1339 return; 1340 } 1341 1342 /** 1343 * Creates a new {@link Dataset} instance. 1344 * 1345 * <p>You should not interact with this builder once this method is called. 1346 * 1347 * @throws IllegalStateException if no field was set (through 1348 * {@link #setField(AutofillId, Field)}), or if {@link #build()} was already called. 1349 * 1350 * @return The built dataset. 1351 */ build()1352 public @NonNull Dataset build() { 1353 throwIfDestroyed(); 1354 mDestroyed = true; 1355 if (mFieldIds == null && mAutofillDatatypes == null) { 1356 throw new IllegalStateException("at least one of field or datatype must be set"); 1357 } 1358 if (mFieldIds != null && mAutofillDatatypes != null) { 1359 if (mFieldIds.size() == 0 && mAutofillDatatypes.size() == 0) { 1360 throw new IllegalStateException( 1361 "at least one of field or datatype must be set"); 1362 } 1363 } 1364 if (mFieldContent != null) { 1365 if (mFieldIds.size() > 1) { 1366 throw new IllegalStateException( 1367 "when filling content, only one field can be filled"); 1368 } 1369 if (mFieldValues.get(0) != null) { 1370 throw new IllegalStateException("cannot fill both content and values"); 1371 } 1372 } 1373 return new Dataset(this); 1374 } 1375 throwIfDestroyed()1376 private void throwIfDestroyed() { 1377 if (mDestroyed) { 1378 throw new IllegalStateException("Already called #build()"); 1379 } 1380 } 1381 } 1382 1383 ///////////////////////////////////// 1384 // Parcelable "contract" methods. // 1385 ///////////////////////////////////// 1386 1387 @Override describeContents()1388 public int describeContents() { 1389 return 0; 1390 } 1391 1392 @Override writeToParcel(Parcel parcel, int flags)1393 public void writeToParcel(Parcel parcel, int flags) { 1394 parcel.writeParcelable(mPresentation, flags); 1395 parcel.writeParcelable(mDialogPresentation, flags); 1396 parcel.writeParcelable(mInlinePresentation, flags); 1397 parcel.writeParcelable(mInlineTooltipPresentation, flags); 1398 parcel.writeTypedList(mFieldIds, flags); 1399 parcel.writeTypedList(mFieldValues, flags); 1400 parcel.writeTypedList(mFieldPresentations, flags); 1401 parcel.writeTypedList(mFieldDialogPresentations, flags); 1402 parcel.writeTypedList(mFieldInlinePresentations, flags); 1403 parcel.writeTypedList(mFieldInlineTooltipPresentations, flags); 1404 parcel.writeTypedList(mFieldFilters, flags); 1405 parcel.writeStringList(mAutofillDatatypes); 1406 parcel.writeParcelable(mFieldContent, flags); 1407 parcel.writeParcelable(mAuthentication, flags); 1408 parcel.writeString(mId); 1409 parcel.writeInt(mEligibleReason); 1410 parcel.writeTypedObject(mCredentialFillInIntent, flags); 1411 } 1412 1413 public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() { 1414 @Override 1415 public Dataset createFromParcel(Parcel parcel) { 1416 final RemoteViews presentation = parcel.readParcelable(null, 1417 android.widget.RemoteViews.class); 1418 final RemoteViews dialogPresentation = parcel.readParcelable(null, 1419 android.widget.RemoteViews.class); 1420 final InlinePresentation inlinePresentation = parcel.readParcelable(null, 1421 android.service.autofill.InlinePresentation.class); 1422 final InlinePresentation inlineTooltipPresentation = 1423 parcel.readParcelable(null, android.service.autofill.InlinePresentation.class); 1424 final ArrayList<AutofillId> ids = 1425 parcel.createTypedArrayList(AutofillId.CREATOR); 1426 final ArrayList<AutofillValue> values = 1427 parcel.createTypedArrayList(AutofillValue.CREATOR); 1428 final ArrayList<RemoteViews> presentations = 1429 parcel.createTypedArrayList(RemoteViews.CREATOR); 1430 final ArrayList<RemoteViews> dialogPresentations = 1431 parcel.createTypedArrayList(RemoteViews.CREATOR); 1432 final ArrayList<InlinePresentation> inlinePresentations = 1433 parcel.createTypedArrayList(InlinePresentation.CREATOR); 1434 final ArrayList<InlinePresentation> inlineTooltipPresentations = 1435 parcel.createTypedArrayList(InlinePresentation.CREATOR); 1436 final ArrayList<DatasetFieldFilter> filters = 1437 parcel.createTypedArrayList(DatasetFieldFilter.CREATOR); 1438 final ArrayList<String> autofillDatatypes = 1439 parcel.createStringArrayList(); 1440 final ClipData fieldContent = parcel.readParcelable(null, 1441 android.content.ClipData.class); 1442 final IntentSender authentication = parcel.readParcelable(null, 1443 android.content.IntentSender.class); 1444 final String datasetId = parcel.readString(); 1445 final int eligibleReason = parcel.readInt(); 1446 final Intent credentialFillInIntent = parcel.readTypedObject(Intent.CREATOR); 1447 1448 // Always go through the builder to ensure the data ingested by 1449 // the system obeys the contract of the builder to avoid attacks 1450 // using specially crafted parcels. 1451 final Builder builder; 1452 if (presentation != null || inlinePresentation != null || dialogPresentation != null) { 1453 final Presentations.Builder presentationsBuilder = new Presentations.Builder(); 1454 if (presentation != null) { 1455 presentationsBuilder.setMenuPresentation(presentation); 1456 } 1457 if (inlinePresentation != null) { 1458 presentationsBuilder.setInlinePresentation(inlinePresentation); 1459 } 1460 if (inlineTooltipPresentation != null) { 1461 presentationsBuilder.setInlineTooltipPresentation(inlineTooltipPresentation); 1462 } 1463 if (dialogPresentation != null) { 1464 presentationsBuilder.setDialogPresentation(dialogPresentation); 1465 } 1466 builder = new Builder(presentationsBuilder.build()); 1467 } else { 1468 builder = new Builder(); 1469 } 1470 1471 if (fieldContent != null) { 1472 builder.setContent(ids.get(0), fieldContent); 1473 } 1474 final int inlinePresentationsSize = inlinePresentations.size(); 1475 for (int i = 0; i < ids.size(); i++) { 1476 final AutofillId id = ids.get(i); 1477 final String datatype = autofillDatatypes.get(i); 1478 final AutofillValue value = values.get(i); 1479 final RemoteViews fieldPresentation = presentations.get(i); 1480 final RemoteViews fieldDialogPresentation = dialogPresentations.get(i); 1481 final InlinePresentation fieldInlinePresentation = 1482 i < inlinePresentationsSize ? inlinePresentations.get(i) : null; 1483 final InlinePresentation fieldInlineTooltipPresentation = 1484 i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null; 1485 final DatasetFieldFilter filter = filters.get(i); 1486 builder.createFromParcel(id, datatype, value, fieldPresentation, 1487 fieldInlinePresentation, fieldInlineTooltipPresentation, filter, 1488 fieldDialogPresentation); 1489 } 1490 builder.setAuthentication(authentication); 1491 builder.setCredentialFillInIntent(credentialFillInIntent); 1492 builder.setId(datasetId); 1493 Dataset dataset = builder.build(); 1494 dataset.mEligibleReason = eligibleReason; 1495 return dataset; 1496 } 1497 1498 @Override 1499 public Dataset[] newArray(int size) { 1500 return new Dataset[size]; 1501 } 1502 }; 1503 1504 /** 1505 * Helper class used to indicate when the service explicitly set a {@link Pattern} filter for a 1506 * dataset field‐ we cannot use a {@link Pattern} directly because then we wouldn't be 1507 * able to differentiate whether the service explicitly passed a {@code null} filter to disable 1508 * filter, or when it called the methods that does not take a filter {@link Pattern}. 1509 * 1510 * @hide 1511 */ 1512 @TestApi 1513 public static final class DatasetFieldFilter implements Parcelable { 1514 1515 /** @hide */ 1516 @Nullable 1517 public final Pattern pattern; 1518 DatasetFieldFilter(@ullable Pattern pattern)1519 DatasetFieldFilter(@Nullable Pattern pattern) { 1520 this.pattern = pattern; 1521 } 1522 getPattern()1523 public @Nullable Pattern getPattern() { 1524 return pattern; 1525 } 1526 1527 @Override toString()1528 public String toString() { 1529 if (!sDebug) return super.toString(); 1530 1531 // Cannot log pattern because it could contain PII 1532 return pattern == null ? "null" : pattern.pattern().length() + "_chars"; 1533 } 1534 1535 @Override describeContents()1536 public int describeContents() { 1537 return 0; 1538 } 1539 1540 @Override writeToParcel(@onNull Parcel parcel, int flags)1541 public void writeToParcel(@NonNull Parcel parcel, int flags) { 1542 parcel.writeSerializable(pattern); 1543 } 1544 1545 @SuppressWarnings("hiding") 1546 public static final @android.annotation.NonNull Creator<DatasetFieldFilter> CREATOR = 1547 new Creator<DatasetFieldFilter>() { 1548 1549 @Override 1550 public DatasetFieldFilter createFromParcel(Parcel parcel) { 1551 return new DatasetFieldFilter((Pattern) parcel.readSerializable(java.util.regex.Pattern.class.getClassLoader(), java.util.regex.Pattern.class)); 1552 } 1553 1554 @Override 1555 public DatasetFieldFilter[] newArray(int size) { 1556 return new DatasetFieldFilter[size]; 1557 } 1558 }; 1559 } 1560 } 1561