1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.view.contentcapture; 17 18 import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString; 19 import static android.view.contentcapture.ContentCaptureManager.DEBUG; 20 import static android.view.contentcapture.ContentCaptureManager.NO_SESSION_ID; 21 import static android.view.contentcapture.flags.Flags.FLAG_CCAPI_BAKLAVA_ENABLED; 22 23 import android.annotation.FlaggedApi; 24 import android.annotation.IntDef; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.SystemApi; 28 import android.graphics.Insets; 29 import android.graphics.Rect; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.text.Selection; 33 import android.text.Spannable; 34 import android.text.SpannableString; 35 import android.util.Log; 36 import android.view.autofill.AutofillId; 37 import android.view.inputmethod.BaseInputConnection; 38 39 import java.io.PrintWriter; 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 42 import java.util.ArrayList; 43 import java.util.List; 44 import java.util.Objects; 45 46 /** @hide */ 47 @SystemApi 48 public final class ContentCaptureEvent implements Parcelable { 49 50 private static final String TAG = ContentCaptureEvent.class.getSimpleName(); 51 52 /** @hide */ 53 public static final int TYPE_SESSION_FINISHED = -2; 54 /** @hide */ 55 public static final int TYPE_SESSION_STARTED = -1; 56 57 /** 58 * Called when a node has been added to the screen and is visible to the user. 59 * 60 * On API level 33, this event may be re-sent with additional information if a view's children 61 * have changed, e.g. scrolling Views inside of a ListView. This information will be stored in 62 * the extras Bundle associated with the event's ViewNode. Within the Bundle, the 63 * "android.view.ViewStructure.extra.ACTIVE_CHILDREN_IDS" key may be used to get a list of 64 * Autofill IDs of active child views, and the 65 * "android.view.ViewStructure.extra.FIRST_ACTIVE_POSITION" key may be used to get the 0-based 66 * position of the first active child view in the list relative to the positions of child views 67 * in the container View's dataset. 68 * 69 * <p>The metadata of the node is available through {@link #getViewNode()}. 70 */ 71 public static final int TYPE_VIEW_APPEARED = 1; 72 73 /** 74 * Called when one or more nodes have been removed from the screen and is not visible to the 75 * user anymore. 76 * 77 * <p>To get the id(s), first call {@link #getIds()} - if it returns {@code null}, then call 78 * {@link #getId()}. 79 */ 80 public static final int TYPE_VIEW_DISAPPEARED = 2; 81 82 /** 83 * Called when the text of a node has been changed. 84 * 85 * <p>The id of the node is available through {@link #getId()}, and the new text is 86 * available through {@link #getText()}. 87 */ 88 public static final int TYPE_VIEW_TEXT_CHANGED = 3; 89 90 /** 91 * Called before events (such as {@link #TYPE_VIEW_APPEARED} and/or 92 * {@link #TYPE_VIEW_DISAPPEARED}) representing a view hierarchy are sent. 93 * 94 * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent 95 * if the initial view hierarchy doesn't initially have any view that's important for content 96 * capture. 97 */ 98 public static final int TYPE_VIEW_TREE_APPEARING = 4; 99 100 /** 101 * Called after events (such as {@link #TYPE_VIEW_APPEARED} and/or 102 * {@link #TYPE_VIEW_DISAPPEARED}) representing a view hierarchy were sent. 103 * 104 * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent 105 * if the initial view hierarchy doesn't initially have any view that's important for content 106 * capture. 107 */ 108 public static final int TYPE_VIEW_TREE_APPEARED = 5; 109 110 /** 111 * Called after a call to 112 * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}. 113 * 114 * <p>The passed context is available through {@link #getContentCaptureContext()}. 115 */ 116 public static final int TYPE_CONTEXT_UPDATED = 6; 117 118 /** 119 * Called after the session is ready, typically after the activity resumed and the 120 * initial views appeared 121 */ 122 public static final int TYPE_SESSION_RESUMED = 7; 123 124 /** 125 * Called after the session is paused, typically after the activity paused and the 126 * views disappeared. 127 */ 128 public static final int TYPE_SESSION_PAUSED = 8; 129 130 /** 131 * Called when the view's insets are changed. The new insets associated with the 132 * event may then be retrieved by calling {@link #getInsets()} 133 */ 134 public static final int TYPE_VIEW_INSETS_CHANGED = 9; 135 136 /** 137 * Called before {@link #TYPE_VIEW_TREE_APPEARING}, or after the size of the window containing 138 * the views changed. 139 */ 140 public static final int TYPE_WINDOW_BOUNDS_CHANGED = 10; 141 142 /** 143 * Called to flush a semantics meaningful view changes status to Intelligence Service. 144 */ 145 @FlaggedApi(FLAG_CCAPI_BAKLAVA_ENABLED) 146 public static final int TYPE_SESSION_FLUSH = 11; 147 148 /** @hide */ 149 @IntDef(prefix = { "TYPE_" }, value = { 150 TYPE_VIEW_APPEARED, 151 TYPE_VIEW_DISAPPEARED, 152 TYPE_VIEW_TEXT_CHANGED, 153 TYPE_VIEW_TREE_APPEARING, 154 TYPE_VIEW_TREE_APPEARED, 155 TYPE_CONTEXT_UPDATED, 156 TYPE_SESSION_PAUSED, 157 TYPE_SESSION_RESUMED, 158 TYPE_VIEW_INSETS_CHANGED, 159 TYPE_WINDOW_BOUNDS_CHANGED, 160 TYPE_SESSION_FLUSH, 161 }) 162 @Retention(RetentionPolicy.SOURCE) 163 public @interface EventType{} 164 165 /** @hide */ 166 public static final int MAX_INVALID_VALUE = -1; 167 168 private final int mSessionId; 169 private final int mType; 170 private final long mEventTime; 171 private @Nullable AutofillId mId; 172 private @Nullable ArrayList<AutofillId> mIds; 173 private @Nullable ViewNode mNode; 174 private @Nullable CharSequence mText; 175 private int mParentSessionId = NO_SESSION_ID; 176 private @Nullable ContentCaptureContext mClientContext; 177 private @Nullable Insets mInsets; 178 private @Nullable Rect mBounds; 179 180 private int mComposingStart = MAX_INVALID_VALUE; 181 private int mComposingEnd = MAX_INVALID_VALUE; 182 private int mSelectionStartIndex = MAX_INVALID_VALUE; 183 private int mSelectionEndIndex = MAX_INVALID_VALUE; 184 185 /** Only used in the main Content Capture session, no need to parcel */ 186 private boolean mTextHasComposingSpan; 187 188 /** @hide */ ContentCaptureEvent(int sessionId, int type, long eventTime)189 public ContentCaptureEvent(int sessionId, int type, long eventTime) { 190 mSessionId = sessionId; 191 mType = type; 192 mEventTime = eventTime; 193 } 194 195 /** @hide */ ContentCaptureEvent(int sessionId, int type)196 public ContentCaptureEvent(int sessionId, int type) { 197 this(sessionId, type, System.currentTimeMillis()); 198 } 199 200 /** @hide */ setAutofillId(@onNull AutofillId id)201 public ContentCaptureEvent setAutofillId(@NonNull AutofillId id) { 202 mId = Objects.requireNonNull(id); 203 return this; 204 } 205 206 /** @hide */ setAutofillIds(@onNull ArrayList<AutofillId> ids)207 public ContentCaptureEvent setAutofillIds(@NonNull ArrayList<AutofillId> ids) { 208 mIds = Objects.requireNonNull(ids); 209 return this; 210 } 211 212 /** 213 * Adds an autofill id to the this event, merging the single id into a list if necessary. 214 * 215 * @hide 216 */ addAutofillId(@onNull AutofillId id)217 public ContentCaptureEvent addAutofillId(@NonNull AutofillId id) { 218 Objects.requireNonNull(id); 219 if (mIds == null) { 220 mIds = new ArrayList<>(); 221 if (mId == null) { 222 Log.w(TAG, "addAutofillId(" + id + ") called without an initial id"); 223 } else { 224 mIds.add(mId); 225 mId = null; 226 } 227 } 228 mIds.add(id); 229 return this; 230 } 231 232 /** 233 * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. 234 * 235 * @hide 236 */ setParentSessionId(int parentSessionId)237 public ContentCaptureEvent setParentSessionId(int parentSessionId) { 238 mParentSessionId = parentSessionId; 239 return this; 240 } 241 242 /** 243 * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. 244 * 245 * @hide 246 */ setClientContext(@onNull ContentCaptureContext clientContext)247 public ContentCaptureEvent setClientContext(@NonNull ContentCaptureContext clientContext) { 248 mClientContext = clientContext; 249 return this; 250 } 251 252 /** @hide */ 253 @NonNull getSessionId()254 public int getSessionId() { 255 return mSessionId; 256 } 257 258 /** 259 * Used by {@link #TYPE_SESSION_STARTED} and {@link #TYPE_SESSION_FINISHED}. 260 * 261 * @hide 262 */ 263 @Nullable getParentSessionId()264 public int getParentSessionId() { 265 return mParentSessionId; 266 } 267 268 /** 269 * Gets the {@link ContentCaptureContext} set calls to 270 * {@link ContentCaptureSession#setContentCaptureContext(ContentCaptureContext)}. 271 * 272 * <p>Only set on {@link #TYPE_CONTEXT_UPDATED} events. 273 */ 274 @Nullable getContentCaptureContext()275 public ContentCaptureContext getContentCaptureContext() { 276 return mClientContext; 277 } 278 279 /** @hide */ 280 @NonNull setViewNode(@onNull ViewNode node)281 public ContentCaptureEvent setViewNode(@NonNull ViewNode node) { 282 mNode = Objects.requireNonNull(node); 283 return this; 284 } 285 286 /** @hide */ 287 @NonNull setText(@ullable CharSequence text)288 public ContentCaptureEvent setText(@Nullable CharSequence text) { 289 mText = text; 290 return this; 291 } 292 293 /** @hide */ 294 @NonNull setComposingIndex(int start, int end)295 public ContentCaptureEvent setComposingIndex(int start, int end) { 296 mComposingStart = start; 297 mComposingEnd = end; 298 return this; 299 } 300 301 /** @hide */ 302 @NonNull hasComposingSpan()303 public boolean hasComposingSpan() { 304 return mComposingStart > MAX_INVALID_VALUE; 305 } 306 307 /** @hide */ 308 @NonNull setSelectionIndex(int start, int end)309 public ContentCaptureEvent setSelectionIndex(int start, int end) { 310 mSelectionStartIndex = start; 311 mSelectionEndIndex = end; 312 return this; 313 } 314 hasSameComposingSpan(@onNull ContentCaptureEvent other)315 boolean hasSameComposingSpan(@NonNull ContentCaptureEvent other) { 316 return mComposingStart == other.mComposingStart && mComposingEnd == other.mComposingEnd; 317 } 318 hasSameSelectionSpan(@onNull ContentCaptureEvent other)319 boolean hasSameSelectionSpan(@NonNull ContentCaptureEvent other) { 320 return mSelectionStartIndex == other.mSelectionStartIndex 321 && mSelectionEndIndex == other.mSelectionEndIndex; 322 } 323 getComposingStart()324 private int getComposingStart() { 325 return mComposingStart; 326 } 327 getComposingEnd()328 private int getComposingEnd() { 329 return mComposingEnd; 330 } 331 getSelectionStart()332 private int getSelectionStart() { 333 return mSelectionStartIndex; 334 } 335 getSelectionEnd()336 private int getSelectionEnd() { 337 return mSelectionEndIndex; 338 } 339 restoreComposingSpan()340 private void restoreComposingSpan() { 341 if (mComposingStart <= MAX_INVALID_VALUE 342 || mComposingEnd <= MAX_INVALID_VALUE) { 343 return; 344 } 345 if (mText instanceof Spannable) { 346 BaseInputConnection.setComposingSpans((Spannable) mText, mComposingStart, 347 mComposingEnd); 348 } else { 349 Log.w(TAG, "Text is not a Spannable."); 350 } 351 } 352 restoreSelectionSpans()353 private void restoreSelectionSpans() { 354 if (mSelectionStartIndex <= MAX_INVALID_VALUE 355 || mSelectionEndIndex <= MAX_INVALID_VALUE) { 356 return; 357 } 358 359 if (mText instanceof SpannableString) { 360 SpannableString ss = (SpannableString) mText; 361 ss.setSpan(Selection.SELECTION_START, mSelectionStartIndex, mSelectionStartIndex, 0); 362 ss.setSpan(Selection.SELECTION_END, mSelectionEndIndex, mSelectionEndIndex, 0); 363 } else { 364 Log.w(TAG, "Text is not a SpannableString."); 365 } 366 } 367 368 /** @hide */ 369 @NonNull setInsets(@onNull Insets insets)370 public ContentCaptureEvent setInsets(@NonNull Insets insets) { 371 mInsets = insets; 372 return this; 373 } 374 375 /** @hide */ 376 @NonNull setBounds(@onNull Rect bounds)377 public ContentCaptureEvent setBounds(@NonNull Rect bounds) { 378 mBounds = bounds; 379 return this; 380 } 381 382 /** 383 * Gets the type of the event. 384 * 385 * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED}, 386 * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_VIEW_TREE_APPEARING}, 387 * {@link #TYPE_VIEW_TREE_APPEARED}, {@link #TYPE_CONTEXT_UPDATED}, 388 * {@link #TYPE_SESSION_RESUMED}, or {@link #TYPE_SESSION_PAUSED}. 389 */ getType()390 public @EventType int getType() { 391 return mType; 392 } 393 394 /** 395 * Gets when the event was generated, in millis since epoch. 396 */ getEventTime()397 public long getEventTime() { 398 return mEventTime; 399 } 400 401 /** 402 * Gets the whole metadata of the node associated with the event. 403 * 404 * <p>Only set on {@link #TYPE_VIEW_APPEARED} events. 405 */ 406 @Nullable getViewNode()407 public ViewNode getViewNode() { 408 return mNode; 409 } 410 411 /** 412 * Gets the {@link AutofillId} of the node associated with the event. 413 * 414 * <p>Only set on {@link #TYPE_VIEW_DISAPPEARED} (when the event contains just one node - if 415 * it contains more than one, this method returns {@code null} and the actual ids should be 416 * retrived by {@link #getIds()}) and {@link #TYPE_VIEW_TEXT_CHANGED} events. 417 */ 418 @Nullable getId()419 public AutofillId getId() { 420 return mId; 421 } 422 423 /** 424 * Gets the {@link AutofillId AutofillIds} of the nodes associated with the event. 425 * 426 * <p>Only set on {@link #TYPE_VIEW_DISAPPEARED}, when the event contains more than one node 427 * (if it contains just one node, it's returned by {@link #getId()} instead. 428 */ 429 @Nullable getIds()430 public List<AutofillId> getIds() { 431 return mIds; 432 } 433 434 /** 435 * Gets the current text of the node associated with the event. 436 * 437 * <p>Only set on {@link #TYPE_VIEW_TEXT_CHANGED} events. 438 */ 439 @Nullable getText()440 public CharSequence getText() { 441 return mText; 442 } 443 444 /** 445 * Gets the rectangle of the insets associated with the event. Valid insets will only be 446 * returned if the type of the event is {@link #TYPE_VIEW_INSETS_CHANGED}, otherwise they 447 * will be null. 448 */ 449 @Nullable getInsets()450 public Insets getInsets() { 451 return mInsets; 452 } 453 454 /** 455 * Gets the {@link Rect} bounds of the window associated with the event. Valid bounds will only 456 * be returned if the type of the event is {@link #TYPE_WINDOW_BOUNDS_CHANGED}, otherwise they 457 * will be null. 458 */ 459 @Nullable getBounds()460 public Rect getBounds() { 461 return mBounds; 462 } 463 464 /** 465 * Merges event of the same type, either {@link #TYPE_VIEW_TEXT_CHANGED} 466 * or {@link #TYPE_VIEW_DISAPPEARED}. 467 * 468 * @hide 469 */ mergeEvent(@onNull ContentCaptureEvent event)470 public void mergeEvent(@NonNull ContentCaptureEvent event) { 471 Objects.requireNonNull(event); 472 final int eventType = event.getType(); 473 if (mType != eventType) { 474 Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType) + ") cannot be merged " 475 + "with different eventType=" + getTypeAsString(mType)); 476 return; 477 } 478 479 if (eventType == TYPE_VIEW_DISAPPEARED) { 480 final List<AutofillId> ids = event.getIds(); 481 final AutofillId id = event.getId(); 482 if (ids != null) { 483 if (id != null) { 484 Log.w(TAG, "got TYPE_VIEW_DISAPPEARED event with both id and ids: " + event); 485 } 486 for (int i = 0; i < ids.size(); i++) { 487 addAutofillId(ids.get(i)); 488 } 489 return; 490 } 491 if (id != null) { 492 addAutofillId(id); 493 return; 494 } 495 throw new IllegalArgumentException("mergeEvent(): got " 496 + "TYPE_VIEW_DISAPPEARED event with neither id or ids: " + event); 497 } else if (eventType == TYPE_VIEW_TEXT_CHANGED) { 498 setText(event.getText()); 499 setComposingIndex(event.getComposingStart(), event.getComposingEnd()); 500 setSelectionIndex(event.getSelectionStart(), event.getSelectionEnd()); 501 } else { 502 Log.e(TAG, "mergeEvent(" + getTypeAsString(eventType) 503 + ") does not support this event type."); 504 } 505 } 506 507 /** @hide */ dump(@onNull PrintWriter pw)508 public void dump(@NonNull PrintWriter pw) { 509 pw.print("type="); pw.print(getTypeAsString(mType)); 510 pw.print(", time="); pw.print(mEventTime); 511 if (mId != null) { 512 pw.print(", id="); pw.print(mId); 513 } 514 if (mIds != null) { 515 pw.print(", ids="); pw.print(mIds); 516 } 517 if (mNode != null) { 518 pw.print(", mNode.id="); pw.print(mNode.getAutofillId()); 519 } 520 if (mSessionId != NO_SESSION_ID) { 521 pw.print(", sessionId="); pw.print(mSessionId); 522 } 523 if (mParentSessionId != NO_SESSION_ID) { 524 pw.print(", parentSessionId="); pw.print(mParentSessionId); 525 } 526 if (mText != null) { 527 pw.print(", text="); pw.println(getSanitizedString(mText)); 528 } 529 if (mClientContext != null) { 530 pw.print(", context="); mClientContext.dump(pw); pw.println(); 531 } 532 if (mInsets != null) { 533 pw.print(", insets="); pw.println(mInsets); 534 } 535 if (mBounds != null) { 536 pw.print(", bounds="); pw.println(mBounds); 537 } 538 if (mComposingStart > MAX_INVALID_VALUE) { 539 pw.print(", composing("); pw.print(mComposingStart); 540 pw.print(", "); pw.print(mComposingEnd); pw.print(")"); 541 } 542 if (mSelectionStartIndex > MAX_INVALID_VALUE) { 543 pw.print(", selection("); pw.print(mSelectionStartIndex); 544 pw.print(", "); pw.print(mSelectionEndIndex); pw.print(")"); 545 } 546 } 547 548 @NonNull 549 @Override toString()550 public String toString() { 551 final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=") 552 .append(getTypeAsString(mType)); 553 string.append(", session=").append(mSessionId); 554 if (mType == TYPE_SESSION_STARTED && mParentSessionId != NO_SESSION_ID) { 555 string.append(", parent=").append(mParentSessionId); 556 } 557 if (mId != null) { 558 string.append(", id=").append(mId); 559 } 560 if (mIds != null) { 561 string.append(", ids=").append(mIds); 562 } 563 if (mNode != null) { 564 final String className = mNode.getClassName(); 565 string.append(", class=").append(className); 566 string.append(", id=").append(mNode.getAutofillId()); 567 if (mNode.getText() != null) { 568 string.append(", text=") 569 .append(DEBUG ? mNode.getText() : getSanitizedString(mNode.getText())); 570 } 571 } 572 if (mText != null) { 573 string.append(", text=") 574 .append(DEBUG ? mText : getSanitizedString(mText)); 575 } 576 if (mClientContext != null) { 577 string.append(", context=").append(mClientContext); 578 } 579 if (mInsets != null) { 580 string.append(", insets=").append(mInsets); 581 } 582 if (mBounds != null) { 583 string.append(", bounds=").append(mBounds); 584 } 585 if (mComposingStart > MAX_INVALID_VALUE) { 586 string.append(", composing=[") 587 .append(mComposingStart).append(",").append(mComposingEnd).append("]"); 588 } 589 if (mSelectionStartIndex > MAX_INVALID_VALUE) { 590 string.append(", selection=[") 591 .append(mSelectionStartIndex).append(",") 592 .append(mSelectionEndIndex).append("]"); 593 } 594 return string.append(']').toString(); 595 } 596 597 @Override describeContents()598 public int describeContents() { 599 return 0; 600 } 601 602 @Override writeToParcel(Parcel parcel, int flags)603 public void writeToParcel(Parcel parcel, int flags) { 604 parcel.writeInt(mSessionId); 605 parcel.writeInt(mType); 606 parcel.writeLong(mEventTime); 607 parcel.writeParcelable(mId, flags); 608 parcel.writeTypedList(mIds); 609 ViewNode.writeToParcel(parcel, mNode, flags); 610 parcel.writeCharSequence(mText); 611 if (mType == TYPE_SESSION_STARTED || mType == TYPE_SESSION_FINISHED) { 612 parcel.writeInt(mParentSessionId); 613 } 614 if (mType == TYPE_SESSION_STARTED || mType == TYPE_CONTEXT_UPDATED) { 615 parcel.writeParcelable(mClientContext, flags); 616 } 617 if (mType == TYPE_VIEW_INSETS_CHANGED) { 618 parcel.writeParcelable(mInsets, flags); 619 } 620 if (mType == TYPE_WINDOW_BOUNDS_CHANGED) { 621 parcel.writeParcelable(mBounds, flags); 622 } 623 if (mType == TYPE_VIEW_TEXT_CHANGED) { 624 parcel.writeInt(mComposingStart); 625 parcel.writeInt(mComposingEnd); 626 parcel.writeInt(mSelectionStartIndex); 627 parcel.writeInt(mSelectionEndIndex); 628 } 629 } 630 631 public static final @android.annotation.NonNull Parcelable.Creator<ContentCaptureEvent> CREATOR = 632 new Parcelable.Creator<ContentCaptureEvent>() { 633 634 @Override 635 @NonNull 636 public ContentCaptureEvent createFromParcel(Parcel parcel) { 637 final int sessionId = parcel.readInt(); 638 final int type = parcel.readInt(); 639 final long eventTime = parcel.readLong(); 640 final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, type, eventTime); 641 final AutofillId id = parcel.readParcelable(null, android.view.autofill.AutofillId.class); 642 if (id != null) { 643 event.setAutofillId(id); 644 } 645 final ArrayList<AutofillId> ids = parcel.createTypedArrayList(AutofillId.CREATOR); 646 if (ids != null) { 647 event.setAutofillIds(ids); 648 } 649 final ViewNode node = ViewNode.readFromParcel(parcel); 650 if (node != null) { 651 event.setViewNode(node); 652 } 653 event.setText(parcel.readCharSequence()); 654 if (type == TYPE_SESSION_STARTED || type == TYPE_SESSION_FINISHED) { 655 event.setParentSessionId(parcel.readInt()); 656 } 657 if (type == TYPE_SESSION_STARTED || type == TYPE_CONTEXT_UPDATED) { 658 event.setClientContext(parcel.readParcelable(null, android.view.contentcapture.ContentCaptureContext.class)); 659 } 660 if (type == TYPE_VIEW_INSETS_CHANGED) { 661 event.setInsets(parcel.readParcelable(null, android.graphics.Insets.class)); 662 } 663 if (type == TYPE_WINDOW_BOUNDS_CHANGED) { 664 event.setBounds(parcel.readParcelable(null, android.graphics.Rect.class)); 665 } 666 if (type == TYPE_VIEW_TEXT_CHANGED) { 667 event.setComposingIndex(parcel.readInt(), parcel.readInt()); 668 event.restoreComposingSpan(); 669 event.setSelectionIndex(parcel.readInt(), parcel.readInt()); 670 event.restoreSelectionSpans(); 671 } 672 return event; 673 } 674 675 @Override 676 @NonNull 677 public ContentCaptureEvent[] newArray(int size) { 678 return new ContentCaptureEvent[size]; 679 } 680 }; 681 682 /** @hide */ getTypeAsString(@ventType int type)683 public static String getTypeAsString(@EventType int type) { 684 switch (type) { 685 case TYPE_SESSION_STARTED: 686 return "SESSION_STARTED"; 687 case TYPE_SESSION_FINISHED: 688 return "SESSION_FINISHED"; 689 case TYPE_SESSION_RESUMED: 690 return "SESSION_RESUMED"; 691 case TYPE_SESSION_PAUSED: 692 return "SESSION_PAUSED"; 693 case TYPE_VIEW_APPEARED: 694 return "VIEW_APPEARED"; 695 case TYPE_VIEW_DISAPPEARED: 696 return "VIEW_DISAPPEARED"; 697 case TYPE_VIEW_TEXT_CHANGED: 698 return "VIEW_TEXT_CHANGED"; 699 case TYPE_VIEW_TREE_APPEARING: 700 return "VIEW_TREE_APPEARING"; 701 case TYPE_VIEW_TREE_APPEARED: 702 return "VIEW_TREE_APPEARED"; 703 case TYPE_CONTEXT_UPDATED: 704 return "CONTEXT_UPDATED"; 705 case TYPE_VIEW_INSETS_CHANGED: 706 return "VIEW_INSETS_CHANGED"; 707 case TYPE_WINDOW_BOUNDS_CHANGED: 708 return "TYPE_WINDOW_BOUNDS_CHANGED"; 709 case TYPE_SESSION_FLUSH: 710 return "TYPE_SESSION_FLUSH"; 711 default: 712 return "UKNOWN_TYPE: " + type; 713 } 714 } 715 } 716