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 17 package android.service.autofill; 18 19 import static android.view.autofill.Helper.sVerbose; 20 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.IntentSender; 25 import android.os.Bundle; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.util.ArrayMap; 29 import android.util.ArraySet; 30 import android.util.Log; 31 import android.view.autofill.AutofillId; 32 import android.view.autofill.AutofillManager; 33 34 import com.android.internal.util.ArrayUtils; 35 import com.android.internal.util.Preconditions; 36 37 import java.lang.annotation.Retention; 38 import java.lang.annotation.RetentionPolicy; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.Collections; 42 import java.util.List; 43 import java.util.Map; 44 import java.util.Set; 45 46 /** 47 * Describes what happened after the last 48 * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)} 49 * call. 50 * 51 * <p>This history is typically used to keep track of previous user actions to optimize further 52 * requests. For example, the service might return email addresses in alphabetical order by 53 * default, but change that order based on the address the user picked on previous requests. 54 * 55 * <p>The history is not persisted over reboots, and it's cleared every time the service 56 * replies to a 57 * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)} 58 * by calling {@link FillCallback#onSuccess(FillResponse)} or 59 * {@link FillCallback#onFailure(CharSequence)} (if the service doesn't call any of these methods, 60 * the history will clear out after some pre-defined time). 61 */ 62 public final class FillEventHistory implements Parcelable { 63 private static final String TAG = "FillEventHistory"; 64 65 /** 66 * Not in parcel. The ID of the autofill session that created the {@link FillResponse}. 67 */ 68 private final int mSessionId; 69 70 @Nullable private final Bundle mClientState; 71 @Nullable List<Event> mEvents; 72 73 /** @hide */ getSessionId()74 public int getSessionId() { 75 return mSessionId; 76 } 77 78 /** 79 * Returns the client state set in the previous {@link FillResponse}. 80 * 81 * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous 82 * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)} 83 * , which is not necessary the same app being autofilled now. 84 * 85 * @deprecated use {@link #getEvents()} then {@link Event#getClientState()} instead. 86 */ 87 @Deprecated getClientState()88 @Nullable public Bundle getClientState() { 89 return mClientState; 90 } 91 92 /** 93 * Returns the events occurred after the latest call to 94 * {@link FillCallback#onSuccess(FillResponse)}. 95 * 96 * @return The list of events or {@code null} if non occurred. 97 */ getEvents()98 @Nullable public List<Event> getEvents() { 99 return mEvents; 100 } 101 102 /** 103 * @hide 104 */ addEvent(Event event)105 public void addEvent(Event event) { 106 if (mEvents == null) { 107 mEvents = new ArrayList<>(1); 108 } 109 mEvents.add(event); 110 } 111 112 /** 113 * @hide 114 */ FillEventHistory(int sessionId, @Nullable Bundle clientState)115 public FillEventHistory(int sessionId, @Nullable Bundle clientState) { 116 mClientState = clientState; 117 mSessionId = sessionId; 118 } 119 120 @Override toString()121 public String toString() { 122 return mEvents == null ? "no events" : mEvents.toString(); 123 } 124 125 @Override describeContents()126 public int describeContents() { 127 return 0; 128 } 129 130 @Override writeToParcel(Parcel parcel, int flags)131 public void writeToParcel(Parcel parcel, int flags) { 132 parcel.writeBundle(mClientState); 133 if (mEvents == null) { 134 parcel.writeInt(0); 135 } else { 136 parcel.writeInt(mEvents.size()); 137 138 int numEvents = mEvents.size(); 139 for (int i = 0; i < numEvents; i++) { 140 Event event = mEvents.get(i); 141 parcel.writeInt(event.mEventType); 142 parcel.writeString(event.mDatasetId); 143 parcel.writeBundle(event.mClientState); 144 parcel.writeStringList(event.mSelectedDatasetIds); 145 parcel.writeArraySet(event.mIgnoredDatasetIds); 146 parcel.writeTypedList(event.mChangedFieldIds); 147 parcel.writeStringList(event.mChangedDatasetIds); 148 149 parcel.writeTypedList(event.mManuallyFilledFieldIds); 150 if (event.mManuallyFilledFieldIds != null) { 151 final int size = event.mManuallyFilledFieldIds.size(); 152 for (int j = 0; j < size; j++) { 153 parcel.writeStringList(event.mManuallyFilledDatasetIds.get(j)); 154 } 155 } 156 final AutofillId[] detectedFields = event.mDetectedFieldIds; 157 parcel.writeParcelableArray(detectedFields, flags); 158 if (detectedFields != null) { 159 FieldClassification.writeArrayToParcel(parcel, 160 event.mDetectedFieldClassifications); 161 } 162 parcel.writeInt(event.mSaveDialogNotShowReason); 163 } 164 } 165 } 166 167 /** 168 * Description of an event that occured after the latest call to 169 * {@link FillCallback#onSuccess(FillResponse)}. 170 */ 171 public static final class Event { 172 /** 173 * A dataset was selected. The dataset selected can be read from {@link #getDatasetId()}. 174 * 175 * <p><b>Note: </b>on Android {@link android.os.Build.VERSION_CODES#O}, this event was also 176 * incorrectly reported after a 177 * {@link Dataset.Builder#setAuthentication(IntentSender) dataset authentication} was 178 * selected and the service returned a dataset in the 179 * {@link AutofillManager#EXTRA_AUTHENTICATION_RESULT} of the activity launched from that 180 * {@link IntentSender}. This behavior was fixed on Android 181 * {@link android.os.Build.VERSION_CODES#O_MR1}. 182 */ 183 public static final int TYPE_DATASET_SELECTED = 0; 184 185 /** 186 * A {@link Dataset.Builder#setAuthentication(IntentSender) dataset authentication} was 187 * selected. The dataset authenticated can be read from {@link #getDatasetId()}. 188 */ 189 public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; 190 191 /** 192 * A {@link FillResponse.Builder#setAuthentication(android.view.autofill.AutofillId[], 193 * IntentSender, android.widget.RemoteViews) fill response authentication} was selected. 194 */ 195 public static final int TYPE_AUTHENTICATION_SELECTED = 2; 196 197 /** A save UI was shown. */ 198 public static final int TYPE_SAVE_SHOWN = 3; 199 200 /** 201 * A committed autofill context for which the autofill service provided datasets. 202 * 203 * <p>This event is useful to track: 204 * <ul> 205 * <li>Which datasets (if any) were selected by the user 206 * ({@link #getSelectedDatasetIds()}). 207 * <li>Which datasets (if any) were NOT selected by the user 208 * ({@link #getIgnoredDatasetIds()}). 209 * <li>Which fields in the selected datasets were changed by the user after the dataset 210 * was selected ({@link #getChangedFields()}. 211 * <li>Which fields match the {@link UserData} set by the service. 212 * </ul> 213 * 214 * <p><b>Note: </b>This event is only generated when: 215 * <ul> 216 * <li>The autofill context is committed. 217 * <li>The service provides at least one dataset in the 218 * {@link FillResponse fill responses} associated with the context. 219 * <li>The last {@link FillResponse fill responses} associated with the context has the 220 * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} flag. 221 * </ul> 222 * 223 * <p>See {@link android.view.autofill.AutofillManager} for more information about autofill 224 * contexts. 225 */ 226 public static final int TYPE_CONTEXT_COMMITTED = 4; 227 228 /** 229 * A dataset selector was shown. 230 * 231 * <p>This event is fired whenever the autofill UI was presented to the user.</p> 232 */ 233 public static final int TYPE_DATASETS_SHOWN = 5; 234 235 /** @hide */ 236 @IntDef(prefix = { "TYPE_" }, value = { 237 TYPE_DATASET_SELECTED, 238 TYPE_DATASET_AUTHENTICATION_SELECTED, 239 TYPE_AUTHENTICATION_SELECTED, 240 TYPE_SAVE_SHOWN, 241 TYPE_CONTEXT_COMMITTED, 242 TYPE_DATASETS_SHOWN 243 }) 244 @Retention(RetentionPolicy.SOURCE) 245 @interface EventIds{} 246 247 /** No reason for save dialog. */ 248 public static final int NO_SAVE_UI_REASON_NONE = 0; 249 250 /** The SaveInfo associated with the FillResponse is null. */ 251 public static final int NO_SAVE_UI_REASON_NO_SAVE_INFO = 1; 252 253 /** The service asked to delay save. */ 254 public static final int NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG = 2; 255 256 /** There was empty value for required ids. */ 257 public static final int NO_SAVE_UI_REASON_HAS_EMPTY_REQUIRED = 3; 258 259 /** No value has been changed. */ 260 public static final int NO_SAVE_UI_REASON_NO_VALUE_CHANGED = 4; 261 262 /** Fields failed validation. */ 263 public static final int NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED = 5; 264 265 /** All fields matched contents of datasets. */ 266 public static final int NO_SAVE_UI_REASON_DATASET_MATCH = 6; 267 268 /** @hide */ 269 @IntDef(prefix = { "NO_SAVE_UI_REASON_" }, value = { 270 NO_SAVE_UI_REASON_NONE, 271 NO_SAVE_UI_REASON_NO_SAVE_INFO, 272 NO_SAVE_UI_REASON_WITH_DELAY_SAVE_FLAG, 273 NO_SAVE_UI_REASON_HAS_EMPTY_REQUIRED, 274 NO_SAVE_UI_REASON_NO_VALUE_CHANGED, 275 NO_SAVE_UI_REASON_FIELD_VALIDATION_FAILED, 276 NO_SAVE_UI_REASON_DATASET_MATCH 277 }) 278 @Retention(RetentionPolicy.SOURCE) 279 public @interface NoSaveReason{} 280 281 @EventIds private final int mEventType; 282 @Nullable private final String mDatasetId; 283 @Nullable private final Bundle mClientState; 284 285 // Note: mSelectedDatasetIds is stored as List<> instead of Set because Session already 286 // stores it as List 287 @Nullable private final List<String> mSelectedDatasetIds; 288 @Nullable private final ArraySet<String> mIgnoredDatasetIds; 289 290 @Nullable private final ArrayList<AutofillId> mChangedFieldIds; 291 @Nullable private final ArrayList<String> mChangedDatasetIds; 292 293 @Nullable private final ArrayList<AutofillId> mManuallyFilledFieldIds; 294 @Nullable private final ArrayList<ArrayList<String>> mManuallyFilledDatasetIds; 295 296 @Nullable private final AutofillId[] mDetectedFieldIds; 297 @Nullable private final FieldClassification[] mDetectedFieldClassifications; 298 299 @NoSaveReason private final int mSaveDialogNotShowReason; 300 301 /** 302 * Returns the type of the event. 303 * 304 * @return The type of the event 305 */ getType()306 public int getType() { 307 return mEventType; 308 } 309 310 /** 311 * Returns the id of dataset the id was on. 312 * 313 * @return The id of dataset, or {@code null} the event is not associated with a dataset. 314 */ getDatasetId()315 @Nullable public String getDatasetId() { 316 return mDatasetId; 317 } 318 319 /** 320 * Returns the client state from the {@link FillResponse} used to generate this event. 321 * 322 * <p><b>Note: </b>the state is associated with the app that was autofilled in the previous 323 * {@link 324 * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}, 325 * which is not necessary the same app being autofilled now. 326 */ getClientState()327 @Nullable public Bundle getClientState() { 328 return mClientState; 329 } 330 331 /** 332 * Returns which datasets were selected by the user. 333 * 334 * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. 335 */ getSelectedDatasetIds()336 @NonNull public Set<String> getSelectedDatasetIds() { 337 return mSelectedDatasetIds == null ? Collections.emptySet() 338 : new ArraySet<>(mSelectedDatasetIds); 339 } 340 341 /** 342 * Returns which datasets were NOT selected by the user. 343 * 344 * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. 345 */ getIgnoredDatasetIds()346 @NonNull public Set<String> getIgnoredDatasetIds() { 347 return mIgnoredDatasetIds == null ? Collections.emptySet() : mIgnoredDatasetIds; 348 } 349 350 /** 351 * Returns which fields in the selected datasets were changed by the user after the dataset 352 * was selected. 353 * 354 * <p>For example, server provides: 355 * 356 * <pre class="prettyprint"> 357 * FillResponse response = new FillResponse.Builder() 358 * .addDataset(new Dataset.Builder(presentation1) 359 * .setId("4815") 360 * .setValue(usernameId, AutofillValue.forText("MrPlow")) 361 * .build()) 362 * .addDataset(new Dataset.Builder(presentation2) 363 * .setId("162342") 364 * .setValue(passwordId, AutofillValue.forText("D'OH")) 365 * .build()) 366 * .build(); 367 * </pre> 368 * 369 * <p>User select both datasets (for username and password) but after the fields are 370 * autofilled, user changes them to: 371 * 372 * <pre class="prettyprint"> 373 * username = "ElBarto"; 374 * password = "AyCaramba"; 375 * </pre> 376 * 377 * <p>Then the result is the following map: 378 * 379 * <pre class="prettyprint"> 380 * usernameId => "4815" 381 * passwordId => "162342" 382 * </pre> 383 * 384 * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. 385 * 386 * @return map map whose key is the id of the change fields, and value is the id of 387 * dataset that has that field and was selected by the user. 388 */ getChangedFields()389 @NonNull public Map<AutofillId, String> getChangedFields() { 390 if (mChangedFieldIds == null || mChangedDatasetIds == null) { 391 return Collections.emptyMap(); 392 } 393 394 final int size = mChangedFieldIds.size(); 395 final ArrayMap<AutofillId, String> changedFields = new ArrayMap<>(size); 396 for (int i = 0; i < size; i++) { 397 changedFields.put(mChangedFieldIds.get(i), mChangedDatasetIds.get(i)); 398 } 399 return changedFields; 400 } 401 402 /** 403 * Gets the <a href="AutofillService.html#FieldClassification">field classification</a> 404 * results. 405 * 406 * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the 407 * service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...) 408 * field classification}. 409 */ getFieldsClassification()410 @NonNull public Map<AutofillId, FieldClassification> getFieldsClassification() { 411 if (mDetectedFieldIds == null) { 412 return Collections.emptyMap(); 413 } 414 final int size = mDetectedFieldIds.length; 415 final ArrayMap<AutofillId, FieldClassification> map = new ArrayMap<>(size); 416 for (int i = 0; i < size; i++) { 417 final AutofillId id = mDetectedFieldIds[i]; 418 final FieldClassification fc = mDetectedFieldClassifications[i]; 419 if (sVerbose) { 420 Log.v(TAG, "getFieldsClassification[" + i + "]: id=" + id + ", fc=" + fc); 421 } 422 map.put(id, fc); 423 } 424 return map; 425 } 426 427 /** 428 * Returns which fields were available on datasets provided by the service but manually 429 * entered by the user. 430 * 431 * <p>For example, server provides: 432 * 433 * <pre class="prettyprint"> 434 * FillResponse response = new FillResponse.Builder() 435 * .addDataset(new Dataset.Builder(presentation1) 436 * .setId("4815") 437 * .setValue(usernameId, AutofillValue.forText("MrPlow")) 438 * .setValue(passwordId, AutofillValue.forText("AyCaramba")) 439 * .build()) 440 * .addDataset(new Dataset.Builder(presentation2) 441 * .setId("162342") 442 * .setValue(usernameId, AutofillValue.forText("ElBarto")) 443 * .setValue(passwordId, AutofillValue.forText("D'OH")) 444 * .build()) 445 * .addDataset(new Dataset.Builder(presentation3) 446 * .setId("108") 447 * .setValue(usernameId, AutofillValue.forText("MrPlow")) 448 * .setValue(passwordId, AutofillValue.forText("D'OH")) 449 * .build()) 450 * .build(); 451 * </pre> 452 * 453 * <p>User doesn't select a dataset but manually enters: 454 * 455 * <pre class="prettyprint"> 456 * username = "MrPlow"; 457 * password = "D'OH"; 458 * </pre> 459 * 460 * <p>Then the result is the following map: 461 * 462 * <pre class="prettyprint"> 463 * usernameId => { "4815", "108"} 464 * passwordId => { "162342", "108" } 465 * </pre> 466 * 467 * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. 468 * 469 * @return map map whose key is the id of the manually-entered field, and value is the 470 * ids of the datasets that have that value but were not selected by the user. 471 */ getManuallyEnteredField()472 @NonNull public Map<AutofillId, Set<String>> getManuallyEnteredField() { 473 if (mManuallyFilledFieldIds == null || mManuallyFilledDatasetIds == null) { 474 return Collections.emptyMap(); 475 } 476 477 final int size = mManuallyFilledFieldIds.size(); 478 final Map<AutofillId, Set<String>> manuallyFilledFields = new ArrayMap<>(size); 479 for (int i = 0; i < size; i++) { 480 final AutofillId fieldId = mManuallyFilledFieldIds.get(i); 481 final ArrayList<String> datasetIds = mManuallyFilledDatasetIds.get(i); 482 manuallyFilledFields.put(fieldId, new ArraySet<>(datasetIds)); 483 } 484 return manuallyFilledFields; 485 } 486 487 /** 488 * Returns the reason why a save dialog was not shown. 489 * 490 * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. For the other 491 * event types, the reason is set to NO_SAVE_UI_REASON_NONE. 492 * 493 * @return The reason why a save dialog was not shown. 494 */ 495 @NoSaveReason getNoSaveUiReason()496 public int getNoSaveUiReason() { 497 return mSaveDialogNotShowReason; 498 } 499 500 /** 501 * Creates a new event. 502 * 503 * @param eventType The type of the event 504 * @param datasetId The dataset the event was on, or {@code null} if the event was on the 505 * whole response. 506 * @param clientState The client state associated with the event. 507 * @param selectedDatasetIds The ids of datasets selected by the user. 508 * @param ignoredDatasetIds The ids of datasets NOT select by the user. 509 * @param changedFieldIds The ids of fields changed by the user. 510 * @param changedDatasetIds The ids of the datasets that havd values matching the 511 * respective entry on {@code changedFieldIds}. 512 * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user 513 * and belonged to datasets. 514 * @param manuallyFilledDatasetIds The ids of datasets that had values matching the 515 * respective entry on {@code manuallyFilledFieldIds}. 516 * @param detectedFieldClassifications the field classification matches. 517 * 518 * @throws IllegalArgumentException If the length of {@code changedFieldIds} and 519 * {@code changedDatasetIds} doesn't match. 520 * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and 521 * {@code manuallyFilledDatasetIds} doesn't match. 522 * 523 * @hide 524 */ Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, @Nullable List<String> selectedDatasetIds, @Nullable ArraySet<String> ignoredDatasetIds, @Nullable ArrayList<AutofillId> changedFieldIds, @Nullable ArrayList<String> changedDatasetIds, @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications)525 public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, 526 @Nullable List<String> selectedDatasetIds, 527 @Nullable ArraySet<String> ignoredDatasetIds, 528 @Nullable ArrayList<AutofillId> changedFieldIds, 529 @Nullable ArrayList<String> changedDatasetIds, 530 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, 531 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, 532 @Nullable AutofillId[] detectedFieldIds, 533 @Nullable FieldClassification[] detectedFieldClassifications) { 534 this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds, 535 changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, 536 manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, 537 NO_SAVE_UI_REASON_NONE); 538 } 539 540 /** 541 * Creates a new event. 542 * 543 * @param eventType The type of the event 544 * @param datasetId The dataset the event was on, or {@code null} if the event was on the 545 * whole response. 546 * @param clientState The client state associated with the event. 547 * @param selectedDatasetIds The ids of datasets selected by the user. 548 * @param ignoredDatasetIds The ids of datasets NOT select by the user. 549 * @param changedFieldIds The ids of fields changed by the user. 550 * @param changedDatasetIds The ids of the datasets that havd values matching the 551 * respective entry on {@code changedFieldIds}. 552 * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user 553 * and belonged to datasets. 554 * @param manuallyFilledDatasetIds The ids of datasets that had values matching the 555 * respective entry on {@code manuallyFilledFieldIds}. 556 * @param detectedFieldClassifications the field classification matches. 557 * @param saveDialogNotShowReason The reason why a save dialog was not shown. 558 * 559 * @throws IllegalArgumentException If the length of {@code changedFieldIds} and 560 * {@code changedDatasetIds} doesn't match. 561 * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and 562 * {@code manuallyFilledDatasetIds} doesn't match. 563 * 564 * @hide 565 */ Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, @Nullable List<String> selectedDatasetIds, @Nullable ArraySet<String> ignoredDatasetIds, @Nullable ArrayList<AutofillId> changedFieldIds, @Nullable ArrayList<String> changedDatasetIds, @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications, int saveDialogNotShowReason)566 public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, 567 @Nullable List<String> selectedDatasetIds, 568 @Nullable ArraySet<String> ignoredDatasetIds, 569 @Nullable ArrayList<AutofillId> changedFieldIds, 570 @Nullable ArrayList<String> changedDatasetIds, 571 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, 572 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, 573 @Nullable AutofillId[] detectedFieldIds, 574 @Nullable FieldClassification[] detectedFieldClassifications, 575 int saveDialogNotShowReason) { 576 mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN, 577 "eventType"); 578 mDatasetId = datasetId; 579 mClientState = clientState; 580 mSelectedDatasetIds = selectedDatasetIds; 581 mIgnoredDatasetIds = ignoredDatasetIds; 582 if (changedFieldIds != null) { 583 Preconditions.checkArgument(!ArrayUtils.isEmpty(changedFieldIds) 584 && changedDatasetIds != null 585 && changedFieldIds.size() == changedDatasetIds.size(), 586 "changed ids must have same length and not be empty"); 587 } 588 mChangedFieldIds = changedFieldIds; 589 mChangedDatasetIds = changedDatasetIds; 590 if (manuallyFilledFieldIds != null) { 591 Preconditions.checkArgument(!ArrayUtils.isEmpty(manuallyFilledFieldIds) 592 && manuallyFilledDatasetIds != null 593 && manuallyFilledFieldIds.size() == manuallyFilledDatasetIds.size(), 594 "manually filled ids must have same length and not be empty"); 595 } 596 mManuallyFilledFieldIds = manuallyFilledFieldIds; 597 mManuallyFilledDatasetIds = manuallyFilledDatasetIds; 598 599 mDetectedFieldIds = detectedFieldIds; 600 mDetectedFieldClassifications = detectedFieldClassifications; 601 602 mSaveDialogNotShowReason = Preconditions.checkArgumentInRange(saveDialogNotShowReason, 603 NO_SAVE_UI_REASON_NONE, NO_SAVE_UI_REASON_DATASET_MATCH, 604 "saveDialogNotShowReason"); 605 } 606 607 @Override toString()608 public String toString() { 609 return "FillEvent [datasetId=" + mDatasetId 610 + ", type=" + mEventType 611 + ", selectedDatasets=" + mSelectedDatasetIds 612 + ", ignoredDatasetIds=" + mIgnoredDatasetIds 613 + ", changedFieldIds=" + mChangedFieldIds 614 + ", changedDatasetsIds=" + mChangedDatasetIds 615 + ", manuallyFilledFieldIds=" + mManuallyFilledFieldIds 616 + ", manuallyFilledDatasetIds=" + mManuallyFilledDatasetIds 617 + ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds) 618 + ", detectedFieldClassifications =" 619 + Arrays.toString(mDetectedFieldClassifications) 620 + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason 621 + "]"; 622 } 623 } 624 625 public static final @android.annotation.NonNull Parcelable.Creator<FillEventHistory> CREATOR = 626 new Parcelable.Creator<FillEventHistory>() { 627 @Override 628 public FillEventHistory createFromParcel(Parcel parcel) { 629 FillEventHistory selection = new FillEventHistory(0, parcel.readBundle()); 630 631 final int numEvents = parcel.readInt(); 632 for (int i = 0; i < numEvents; i++) { 633 final int eventType = parcel.readInt(); 634 final String datasetId = parcel.readString(); 635 final Bundle clientState = parcel.readBundle(); 636 final ArrayList<String> selectedDatasetIds = parcel.createStringArrayList(); 637 @SuppressWarnings("unchecked") 638 final ArraySet<String> ignoredDatasets = 639 (ArraySet<String>) parcel.readArraySet(null); 640 final ArrayList<AutofillId> changedFieldIds = 641 parcel.createTypedArrayList(AutofillId.CREATOR); 642 final ArrayList<String> changedDatasetIds = parcel.createStringArrayList(); 643 644 final ArrayList<AutofillId> manuallyFilledFieldIds = 645 parcel.createTypedArrayList(AutofillId.CREATOR); 646 final ArrayList<ArrayList<String>> manuallyFilledDatasetIds; 647 if (manuallyFilledFieldIds != null) { 648 final int size = manuallyFilledFieldIds.size(); 649 manuallyFilledDatasetIds = new ArrayList<>(size); 650 for (int j = 0; j < size; j++) { 651 manuallyFilledDatasetIds.add(parcel.createStringArrayList()); 652 } 653 } else { 654 manuallyFilledDatasetIds = null; 655 } 656 final AutofillId[] detectedFieldIds = parcel.readParcelableArray(null, 657 AutofillId.class); 658 final FieldClassification[] detectedFieldClassifications = 659 (detectedFieldIds != null) 660 ? FieldClassification.readArrayFromParcel(parcel) 661 : null; 662 final int saveDialogNotShowReason = parcel.readInt(); 663 664 selection.addEvent(new Event(eventType, datasetId, clientState, 665 selectedDatasetIds, ignoredDatasets, 666 changedFieldIds, changedDatasetIds, 667 manuallyFilledFieldIds, manuallyFilledDatasetIds, 668 detectedFieldIds, detectedFieldClassifications, 669 saveDialogNotShowReason)); 670 } 671 return selection; 672 } 673 674 @Override 675 public FillEventHistory[] newArray(int size) { 676 return new FillEventHistory[size]; 677 } 678 }; 679 } 680