1 /* 2 * Copyright (C) 2008 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.view; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.content.Context; 23 import android.graphics.Rect; 24 import android.graphics.Region; 25 import android.os.Build; 26 import android.util.Log; 27 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.concurrent.CopyOnWriteArrayList; 31 import java.util.function.Consumer; 32 33 /** 34 * A view tree observer is used to register listeners that can be notified of global 35 * changes in the view tree. Such global events include, but are not limited to, 36 * layout of the whole tree, beginning of the drawing pass, touch mode change.... 37 * 38 * A ViewTreeObserver should never be instantiated by applications as it is provided 39 * by the views hierarchy. Refer to {@link android.view.View#getViewTreeObserver()} 40 * for more information. 41 */ 42 public final class ViewTreeObserver { 43 // Recursive listeners use CopyOnWriteArrayList 44 private CopyOnWriteArrayList<OnWindowFocusChangeListener> mOnWindowFocusListeners; 45 private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners; 46 private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; 47 @UnsupportedAppUsage 48 private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; 49 private CopyOnWriteArrayList<OnEnterAnimationCompleteListener> 50 mOnEnterAnimationCompleteListeners; 51 52 // Non-recursive listeners use CopyOnWriteArray 53 // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive 54 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 55 private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners; 56 @UnsupportedAppUsage 57 private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners; 58 @UnsupportedAppUsage 59 private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners; 60 private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners; 61 private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners; 62 private CopyOnWriteArray<Consumer<List<Rect>>> mGestureExclusionListeners; 63 64 // These listeners cannot be mutated during dispatch 65 private boolean mInDispatchOnDraw; 66 private ArrayList<OnDrawListener> mOnDrawListeners; 67 private static boolean sIllegalOnDrawModificationIsFatal; 68 69 // These listeners are one-shot 70 private ArrayList<Runnable> mOnFrameCommitListeners; 71 72 /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after 73 * that the listener will be immediately called. */ 74 private boolean mWindowShown; 75 76 private boolean mAlive = true; 77 78 /** 79 * Interface definition for a callback to be invoked when the view hierarchy is 80 * attached to and detached from its window. 81 */ 82 public interface OnWindowAttachListener { 83 /** 84 * Callback method to be invoked when the view hierarchy is attached to a window 85 */ onWindowAttached()86 public void onWindowAttached(); 87 88 /** 89 * Callback method to be invoked when the view hierarchy is detached from a window 90 */ onWindowDetached()91 public void onWindowDetached(); 92 } 93 94 /** 95 * Interface definition for a callback to be invoked when the view hierarchy's window 96 * focus state changes. 97 */ 98 public interface OnWindowFocusChangeListener { 99 /** 100 * Callback method to be invoked when the window focus changes in the view tree. 101 * 102 * @param hasFocus Set to true if the window is gaining focus, false if it is 103 * losing focus. 104 */ onWindowFocusChanged(boolean hasFocus)105 public void onWindowFocusChanged(boolean hasFocus); 106 } 107 108 /** 109 * Interface definition for a callback to be invoked when the focus state within 110 * the view tree changes. 111 */ 112 public interface OnGlobalFocusChangeListener { 113 /** 114 * Callback method to be invoked when the focus changes in the view tree. When 115 * the view tree transitions from touch mode to non-touch mode, oldFocus is null. 116 * When the view tree transitions from non-touch mode to touch mode, newFocus is 117 * null. When focus changes in non-touch mode (without transition from or to 118 * touch mode) either oldFocus or newFocus can be null. 119 * 120 * @param oldFocus The previously focused view, if any. 121 * @param newFocus The newly focused View, if any. 122 */ onGlobalFocusChanged(View oldFocus, View newFocus)123 public void onGlobalFocusChanged(View oldFocus, View newFocus); 124 } 125 126 /** 127 * Interface definition for a callback to be invoked when the global layout state 128 * or the visibility of views within the view tree changes. 129 */ 130 public interface OnGlobalLayoutListener { 131 /** 132 * Callback method to be invoked when the global layout state or the visibility of views 133 * within the view tree changes 134 */ onGlobalLayout()135 public void onGlobalLayout(); 136 } 137 138 /** 139 * Interface definition for a callback to be invoked when the view tree is about to be drawn. 140 */ 141 public interface OnPreDrawListener { 142 /** 143 * Callback method to be invoked when the view tree is about to be drawn. At this point, all 144 * views in the tree have been measured and given a frame. Clients can use this to adjust 145 * their scroll bounds or even to request a new layout before drawing occurs. 146 * 147 * @return Return true to proceed with the current drawing pass, or false to cancel. 148 * 149 * @see android.view.View#onMeasure 150 * @see android.view.View#onLayout 151 * @see android.view.View#onDraw 152 */ onPreDraw()153 public boolean onPreDraw(); 154 } 155 156 /** 157 * Interface definition for a callback to be invoked when the view tree is about to be drawn. 158 */ 159 public interface OnDrawListener { 160 /** 161 * <p>Callback method to be invoked when the view tree is about to be drawn. At this point, 162 * views cannot be modified in any way.</p> 163 * 164 * <p>Unlike with {@link OnPreDrawListener}, this method cannot be used to cancel the 165 * current drawing pass.</p> 166 * 167 * <p>An {@link OnDrawListener} listener <strong>cannot be added or removed</strong> 168 * from this method.</p> 169 * 170 * @see android.view.View#onMeasure 171 * @see android.view.View#onLayout 172 * @see android.view.View#onDraw 173 */ onDraw()174 public void onDraw(); 175 } 176 177 /** 178 * Interface definition for a callback to be invoked when the touch mode changes. 179 */ 180 public interface OnTouchModeChangeListener { 181 /** 182 * Callback method to be invoked when the touch mode changes. 183 * 184 * @param isInTouchMode True if the view hierarchy is now in touch mode, false otherwise. 185 */ onTouchModeChanged(boolean isInTouchMode)186 public void onTouchModeChanged(boolean isInTouchMode); 187 } 188 189 /** 190 * Interface definition for a callback to be invoked when 191 * something in the view tree has been scrolled. 192 */ 193 public interface OnScrollChangedListener { 194 /** 195 * Callback method to be invoked when something in the view tree 196 * has been scrolled. 197 */ onScrollChanged()198 public void onScrollChanged(); 199 } 200 201 /** 202 * Interface definition for a callback noting when a system window has been displayed. 203 * This is only used for non-Activity windows. Activity windows can use 204 * Activity.onEnterAnimationComplete() to get the same signal. 205 * @hide 206 */ 207 public interface OnWindowShownListener { 208 /** 209 * Callback method to be invoked when a non-activity window is fully shown. 210 */ onWindowShown()211 void onWindowShown(); 212 } 213 214 /** 215 * Parameters used with OnComputeInternalInsetsListener. 216 * 217 * We are not yet ready to commit to this API and support it, so 218 * @hide 219 */ 220 public final static class InternalInsetsInfo { 221 222 @UnsupportedAppUsage InternalInsetsInfo()223 public InternalInsetsInfo() { 224 } 225 226 /** 227 * Offsets from the frame of the window at which the content of 228 * windows behind it should be placed. 229 */ 230 @UnsupportedAppUsage 231 public final Rect contentInsets = new Rect(); 232 233 /** 234 * Offsets from the frame of the window at which windows behind it 235 * are visible. 236 */ 237 @UnsupportedAppUsage 238 public final Rect visibleInsets = new Rect(); 239 240 /** 241 * Touchable region defined relative to the origin of the frame of the window. 242 * Only used when {@link #setTouchableInsets(int)} is called with 243 * the option {@link #TOUCHABLE_INSETS_REGION}. 244 */ 245 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 246 public final Region touchableRegion = new Region(); 247 248 /** 249 * Option for {@link #setTouchableInsets(int)}: the entire window frame 250 * can be touched. 251 */ 252 public static final int TOUCHABLE_INSETS_FRAME = 0; 253 254 /** 255 * Option for {@link #setTouchableInsets(int)}: the area inside of 256 * the content insets can be touched. 257 */ 258 public static final int TOUCHABLE_INSETS_CONTENT = 1; 259 260 /** 261 * Option for {@link #setTouchableInsets(int)}: the area inside of 262 * the visible insets can be touched. 263 */ 264 public static final int TOUCHABLE_INSETS_VISIBLE = 2; 265 266 /** 267 * Option for {@link #setTouchableInsets(int)}: the area inside of 268 * the provided touchable region in {@link #touchableRegion} can be touched. 269 */ 270 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 271 public static final int TOUCHABLE_INSETS_REGION = 3; 272 273 /** 274 * Set which parts of the window can be touched: either 275 * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT}, 276 * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}. 277 */ 278 @UnsupportedAppUsage setTouchableInsets(int val)279 public void setTouchableInsets(int val) { 280 mTouchableInsets = val; 281 } 282 283 @UnsupportedAppUsage 284 int mTouchableInsets; 285 reset()286 void reset() { 287 contentInsets.setEmpty(); 288 visibleInsets.setEmpty(); 289 touchableRegion.setEmpty(); 290 mTouchableInsets = TOUCHABLE_INSETS_FRAME; 291 } 292 isEmpty()293 boolean isEmpty() { 294 return contentInsets.isEmpty() 295 && visibleInsets.isEmpty() 296 && touchableRegion.isEmpty() 297 && mTouchableInsets == TOUCHABLE_INSETS_FRAME; 298 } 299 300 @Override hashCode()301 public int hashCode() { 302 int result = contentInsets.hashCode(); 303 result = 31 * result + visibleInsets.hashCode(); 304 result = 31 * result + touchableRegion.hashCode(); 305 result = 31 * result + mTouchableInsets; 306 return result; 307 } 308 309 @Override equals(@ullable Object o)310 public boolean equals(@Nullable Object o) { 311 if (this == o) return true; 312 if (o == null || getClass() != o.getClass()) return false; 313 314 InternalInsetsInfo other = (InternalInsetsInfo)o; 315 return mTouchableInsets == other.mTouchableInsets && 316 contentInsets.equals(other.contentInsets) && 317 visibleInsets.equals(other.visibleInsets) && 318 touchableRegion.equals(other.touchableRegion); 319 } 320 321 @UnsupportedAppUsage set(InternalInsetsInfo other)322 void set(InternalInsetsInfo other) { 323 contentInsets.set(other.contentInsets); 324 visibleInsets.set(other.visibleInsets); 325 touchableRegion.set(other.touchableRegion); 326 mTouchableInsets = other.mTouchableInsets; 327 } 328 } 329 330 /** 331 * Interface definition for a callback to be invoked when layout has 332 * completed and the client can compute its interior insets. 333 * 334 * We are not yet ready to commit to this API and support it, so 335 * @hide 336 */ 337 public interface OnComputeInternalInsetsListener { 338 /** 339 * Callback method to be invoked when layout has completed and the 340 * client can compute its interior insets. 341 * 342 * @param inoutInfo Should be filled in by the implementation with 343 * the information about the insets of the window. This is called 344 * with whatever values the previous OnComputeInternalInsetsListener 345 * returned, if there are multiple such listeners in the window. 346 */ onComputeInternalInsets(InternalInsetsInfo inoutInfo)347 public void onComputeInternalInsets(InternalInsetsInfo inoutInfo); 348 } 349 350 /** 351 * @hide 352 */ 353 public interface OnEnterAnimationCompleteListener { onEnterAnimationComplete()354 public void onEnterAnimationComplete(); 355 } 356 357 /** 358 * Creates a new ViewTreeObserver. This constructor should not be called 359 */ ViewTreeObserver(Context context)360 ViewTreeObserver(Context context) { 361 sIllegalOnDrawModificationIsFatal = 362 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O; 363 } 364 365 /** 366 * Merges all the listeners registered on the specified observer with the listeners 367 * registered on this object. After this method is invoked, the specified observer 368 * will return false in {@link #isAlive()} and should not be used anymore. 369 * 370 * @param observer The ViewTreeObserver whose listeners must be added to this observer 371 */ merge(ViewTreeObserver observer)372 void merge(ViewTreeObserver observer) { 373 if (observer.mOnWindowAttachListeners != null) { 374 if (mOnWindowAttachListeners != null) { 375 mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners); 376 } else { 377 mOnWindowAttachListeners = observer.mOnWindowAttachListeners; 378 } 379 } 380 381 if (observer.mOnWindowFocusListeners != null) { 382 if (mOnWindowFocusListeners != null) { 383 mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners); 384 } else { 385 mOnWindowFocusListeners = observer.mOnWindowFocusListeners; 386 } 387 } 388 389 if (observer.mOnGlobalFocusListeners != null) { 390 if (mOnGlobalFocusListeners != null) { 391 mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners); 392 } else { 393 mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners; 394 } 395 } 396 397 if (observer.mOnGlobalLayoutListeners != null) { 398 if (mOnGlobalLayoutListeners != null) { 399 mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners); 400 } else { 401 mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners; 402 } 403 } 404 405 if (observer.mOnPreDrawListeners != null) { 406 if (mOnPreDrawListeners != null) { 407 mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners); 408 } else { 409 mOnPreDrawListeners = observer.mOnPreDrawListeners; 410 } 411 } 412 413 if (observer.mOnDrawListeners != null) { 414 if (mOnDrawListeners != null) { 415 mOnDrawListeners.addAll(observer.mOnDrawListeners); 416 } else { 417 mOnDrawListeners = observer.mOnDrawListeners; 418 } 419 } 420 421 if (observer.mOnFrameCommitListeners != null) { 422 if (mOnFrameCommitListeners != null) { 423 mOnFrameCommitListeners.addAll(observer.captureFrameCommitCallbacks()); 424 } else { 425 mOnFrameCommitListeners = observer.captureFrameCommitCallbacks(); 426 } 427 } 428 429 if (observer.mOnTouchModeChangeListeners != null) { 430 if (mOnTouchModeChangeListeners != null) { 431 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners); 432 } else { 433 mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners; 434 } 435 } 436 437 if (observer.mOnComputeInternalInsetsListeners != null) { 438 if (mOnComputeInternalInsetsListeners != null) { 439 mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners); 440 } else { 441 mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners; 442 } 443 } 444 445 if (observer.mOnScrollChangedListeners != null) { 446 if (mOnScrollChangedListeners != null) { 447 mOnScrollChangedListeners.addAll(observer.mOnScrollChangedListeners); 448 } else { 449 mOnScrollChangedListeners = observer.mOnScrollChangedListeners; 450 } 451 } 452 453 if (observer.mOnWindowShownListeners != null) { 454 if (mOnWindowShownListeners != null) { 455 mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners); 456 } else { 457 mOnWindowShownListeners = observer.mOnWindowShownListeners; 458 } 459 } 460 461 if (observer.mGestureExclusionListeners != null) { 462 if (mGestureExclusionListeners != null) { 463 mGestureExclusionListeners.addAll(observer.mGestureExclusionListeners); 464 } else { 465 mGestureExclusionListeners = observer.mGestureExclusionListeners; 466 } 467 } 468 469 observer.kill(); 470 } 471 472 /** 473 * Register a callback to be invoked when the view hierarchy is attached to a window. 474 * 475 * @param listener The callback to add 476 * 477 * @throws IllegalStateException If {@link #isAlive()} returns false 478 */ addOnWindowAttachListener(OnWindowAttachListener listener)479 public void addOnWindowAttachListener(OnWindowAttachListener listener) { 480 checkIsAlive(); 481 482 if (mOnWindowAttachListeners == null) { 483 mOnWindowAttachListeners 484 = new CopyOnWriteArrayList<OnWindowAttachListener>(); 485 } 486 487 mOnWindowAttachListeners.add(listener); 488 } 489 490 /** 491 * Remove a previously installed window attach callback. 492 * 493 * @param victim The callback to remove 494 * 495 * @throws IllegalStateException If {@link #isAlive()} returns false 496 * 497 * @see #addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener) 498 */ removeOnWindowAttachListener(OnWindowAttachListener victim)499 public void removeOnWindowAttachListener(OnWindowAttachListener victim) { 500 checkIsAlive(); 501 if (mOnWindowAttachListeners == null) { 502 return; 503 } 504 mOnWindowAttachListeners.remove(victim); 505 } 506 507 /** 508 * Register a callback to be invoked when the window focus state within the view tree changes. 509 * 510 * @param listener The callback to add 511 * 512 * @throws IllegalStateException If {@link #isAlive()} returns false 513 */ addOnWindowFocusChangeListener(OnWindowFocusChangeListener listener)514 public void addOnWindowFocusChangeListener(OnWindowFocusChangeListener listener) { 515 checkIsAlive(); 516 517 if (mOnWindowFocusListeners == null) { 518 mOnWindowFocusListeners 519 = new CopyOnWriteArrayList<OnWindowFocusChangeListener>(); 520 } 521 522 mOnWindowFocusListeners.add(listener); 523 } 524 525 /** 526 * Remove a previously installed window focus change callback. 527 * 528 * @param victim The callback to remove 529 * 530 * @throws IllegalStateException If {@link #isAlive()} returns false 531 * 532 * @see #addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener) 533 */ removeOnWindowFocusChangeListener(OnWindowFocusChangeListener victim)534 public void removeOnWindowFocusChangeListener(OnWindowFocusChangeListener victim) { 535 checkIsAlive(); 536 if (mOnWindowFocusListeners == null) { 537 return; 538 } 539 mOnWindowFocusListeners.remove(victim); 540 } 541 542 /** 543 * Register a callback to be invoked when the focus state within the view tree changes. 544 * 545 * @param listener The callback to add 546 * 547 * @throws IllegalStateException If {@link #isAlive()} returns false 548 */ addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener)549 public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener) { 550 checkIsAlive(); 551 552 if (mOnGlobalFocusListeners == null) { 553 mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>(); 554 } 555 556 mOnGlobalFocusListeners.add(listener); 557 } 558 559 /** 560 * Remove a previously installed focus change callback. 561 * 562 * @param victim The callback to remove 563 * 564 * @throws IllegalStateException If {@link #isAlive()} returns false 565 * 566 * @see #addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener) 567 */ removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim)568 public void removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim) { 569 checkIsAlive(); 570 if (mOnGlobalFocusListeners == null) { 571 return; 572 } 573 mOnGlobalFocusListeners.remove(victim); 574 } 575 576 /** 577 * Register a callback to be invoked when the global layout state or the visibility of views 578 * within the view tree changes 579 * 580 * @param listener The callback to add 581 * 582 * @throws IllegalStateException If {@link #isAlive()} returns false 583 */ addOnGlobalLayoutListener(OnGlobalLayoutListener listener)584 public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) { 585 checkIsAlive(); 586 587 if (mOnGlobalLayoutListeners == null) { 588 mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>(); 589 } 590 591 mOnGlobalLayoutListeners.add(listener); 592 } 593 594 /** 595 * Remove a previously installed global layout callback 596 * 597 * @param victim The callback to remove 598 * 599 * @throws IllegalStateException If {@link #isAlive()} returns false 600 * 601 * @deprecated Use #removeOnGlobalLayoutListener instead 602 * 603 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener) 604 */ 605 @Deprecated removeGlobalOnLayoutListener(OnGlobalLayoutListener victim)606 public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) { 607 removeOnGlobalLayoutListener(victim); 608 } 609 610 /** 611 * Remove a previously installed global layout callback 612 * 613 * @param victim The callback to remove 614 * 615 * @throws IllegalStateException If {@link #isAlive()} returns false 616 * 617 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener) 618 */ removeOnGlobalLayoutListener(OnGlobalLayoutListener victim)619 public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) { 620 checkIsAlive(); 621 if (mOnGlobalLayoutListeners == null) { 622 return; 623 } 624 mOnGlobalLayoutListeners.remove(victim); 625 } 626 627 /** 628 * Register a callback to be invoked when the view tree is about to be drawn 629 * 630 * @param listener The callback to add 631 * 632 * @throws IllegalStateException If {@link #isAlive()} returns false 633 */ addOnPreDrawListener(OnPreDrawListener listener)634 public void addOnPreDrawListener(OnPreDrawListener listener) { 635 checkIsAlive(); 636 637 if (mOnPreDrawListeners == null) { 638 mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>(); 639 } 640 641 mOnPreDrawListeners.add(listener); 642 } 643 644 /** 645 * Remove a previously installed pre-draw callback 646 * 647 * @param victim The callback to remove 648 * 649 * @throws IllegalStateException If {@link #isAlive()} returns false 650 * 651 * @see #addOnPreDrawListener(OnPreDrawListener) 652 */ removeOnPreDrawListener(OnPreDrawListener victim)653 public void removeOnPreDrawListener(OnPreDrawListener victim) { 654 checkIsAlive(); 655 if (mOnPreDrawListeners == null) { 656 return; 657 } 658 mOnPreDrawListeners.remove(victim); 659 } 660 661 /** 662 * Register a callback to be invoked when the view tree window has been shown 663 * 664 * @param listener The callback to add 665 * 666 * @throws IllegalStateException If {@link #isAlive()} returns false 667 * @hide 668 */ addOnWindowShownListener(OnWindowShownListener listener)669 public void addOnWindowShownListener(OnWindowShownListener listener) { 670 checkIsAlive(); 671 672 if (mOnWindowShownListeners == null) { 673 mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>(); 674 } 675 676 mOnWindowShownListeners.add(listener); 677 if (mWindowShown) { 678 listener.onWindowShown(); 679 } 680 } 681 682 /** 683 * Remove a previously installed window shown callback 684 * 685 * @param victim The callback to remove 686 * 687 * @throws IllegalStateException If {@link #isAlive()} returns false 688 * 689 * @see #addOnWindowShownListener(OnWindowShownListener) 690 * @hide 691 */ removeOnWindowShownListener(OnWindowShownListener victim)692 public void removeOnWindowShownListener(OnWindowShownListener victim) { 693 checkIsAlive(); 694 if (mOnWindowShownListeners == null) { 695 return; 696 } 697 mOnWindowShownListeners.remove(victim); 698 } 699 700 /** 701 * <p>Register a callback to be invoked when the view tree is about to be drawn.</p> 702 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from 703 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p> 704 * 705 * @param listener The callback to add 706 * 707 * @throws IllegalStateException If {@link #isAlive()} returns false 708 */ addOnDrawListener(OnDrawListener listener)709 public void addOnDrawListener(OnDrawListener listener) { 710 checkIsAlive(); 711 712 if (mOnDrawListeners == null) { 713 mOnDrawListeners = new ArrayList<OnDrawListener>(); 714 } 715 716 if (mInDispatchOnDraw) { 717 IllegalStateException ex = new IllegalStateException( 718 "Cannot call addOnDrawListener inside of onDraw"); 719 if (sIllegalOnDrawModificationIsFatal) { 720 throw ex; 721 } else { 722 Log.e("ViewTreeObserver", ex.getMessage(), ex); 723 } 724 } 725 mOnDrawListeners.add(listener); 726 } 727 728 /** 729 * <p>Remove a previously installed pre-draw callback.</p> 730 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from 731 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p> 732 * 733 * @param victim The callback to remove 734 * 735 * @throws IllegalStateException If {@link #isAlive()} returns false 736 * 737 * @see #addOnDrawListener(OnDrawListener) 738 */ removeOnDrawListener(OnDrawListener victim)739 public void removeOnDrawListener(OnDrawListener victim) { 740 checkIsAlive(); 741 if (mOnDrawListeners == null) { 742 return; 743 } 744 if (mInDispatchOnDraw) { 745 IllegalStateException ex = new IllegalStateException( 746 "Cannot call removeOnDrawListener inside of onDraw"); 747 if (sIllegalOnDrawModificationIsFatal) { 748 throw ex; 749 } else { 750 Log.e("ViewTreeObserver", ex.getMessage(), ex); 751 } 752 } 753 mOnDrawListeners.remove(victim); 754 } 755 756 /** 757 * Adds a frame commit callback. This callback will be invoked when the current rendering 758 * content has been rendered into a frame and submitted to the swap chain. The frame may 759 * not currently be visible on the display when this is invoked, but it has been submitted. 760 * This callback is useful in combination with {@link PixelCopy} to capture the current 761 * rendered content of the UI reliably. 762 * 763 * Note: Only works with hardware rendering. Does nothing otherwise. 764 * 765 * @param callback The callback to invoke when the frame is committed. 766 */ registerFrameCommitCallback(@onNull Runnable callback)767 public void registerFrameCommitCallback(@NonNull Runnable callback) { 768 checkIsAlive(); 769 if (mOnFrameCommitListeners == null) { 770 mOnFrameCommitListeners = new ArrayList<>(); 771 } 772 mOnFrameCommitListeners.add(callback); 773 } 774 captureFrameCommitCallbacks()775 @Nullable ArrayList<Runnable> captureFrameCommitCallbacks() { 776 ArrayList<Runnable> ret = mOnFrameCommitListeners; 777 mOnFrameCommitListeners = null; 778 return ret; 779 } 780 781 /** 782 * Attempts to remove the given callback from the list of pending frame complete callbacks. 783 * 784 * @param callback The callback to remove 785 * @return Whether or not the callback was removed. If this returns true the callback will 786 * not be invoked. If false is returned then the callback was either never added 787 * or may already be pending execution and was unable to be removed 788 */ unregisterFrameCommitCallback(@onNull Runnable callback)789 public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) { 790 checkIsAlive(); 791 if (mOnFrameCommitListeners == null) { 792 return false; 793 } 794 return mOnFrameCommitListeners.remove(callback); 795 } 796 797 /** 798 * Register a callback to be invoked when a view has been scrolled. 799 * 800 * @param listener The callback to add 801 * 802 * @throws IllegalStateException If {@link #isAlive()} returns false 803 */ addOnScrollChangedListener(OnScrollChangedListener listener)804 public void addOnScrollChangedListener(OnScrollChangedListener listener) { 805 checkIsAlive(); 806 807 if (mOnScrollChangedListeners == null) { 808 mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>(); 809 } 810 811 mOnScrollChangedListeners.add(listener); 812 } 813 814 /** 815 * Remove a previously installed scroll-changed callback 816 * 817 * @param victim The callback to remove 818 * 819 * @throws IllegalStateException If {@link #isAlive()} returns false 820 * 821 * @see #addOnScrollChangedListener(OnScrollChangedListener) 822 */ removeOnScrollChangedListener(OnScrollChangedListener victim)823 public void removeOnScrollChangedListener(OnScrollChangedListener victim) { 824 checkIsAlive(); 825 if (mOnScrollChangedListeners == null) { 826 return; 827 } 828 mOnScrollChangedListeners.remove(victim); 829 } 830 831 /** 832 * Register a callback to be invoked when the invoked when the touch mode changes. 833 * 834 * @param listener The callback to add 835 * 836 * @throws IllegalStateException If {@link #isAlive()} returns false 837 */ addOnTouchModeChangeListener(OnTouchModeChangeListener listener)838 public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) { 839 checkIsAlive(); 840 841 if (mOnTouchModeChangeListeners == null) { 842 mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>(); 843 } 844 845 mOnTouchModeChangeListeners.add(listener); 846 } 847 848 /** 849 * Remove a previously installed touch mode change callback 850 * 851 * @param victim The callback to remove 852 * 853 * @throws IllegalStateException If {@link #isAlive()} returns false 854 * 855 * @see #addOnTouchModeChangeListener(OnTouchModeChangeListener) 856 */ removeOnTouchModeChangeListener(OnTouchModeChangeListener victim)857 public void removeOnTouchModeChangeListener(OnTouchModeChangeListener victim) { 858 checkIsAlive(); 859 if (mOnTouchModeChangeListeners == null) { 860 return; 861 } 862 mOnTouchModeChangeListeners.remove(victim); 863 } 864 865 /** 866 * Register a callback to be invoked when the invoked when it is time to 867 * compute the window's internal insets. 868 * 869 * @param listener The callback to add 870 * 871 * @throws IllegalStateException If {@link #isAlive()} returns false 872 * 873 * We are not yet ready to commit to this API and support it, so 874 * @hide 875 */ 876 @UnsupportedAppUsage addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener)877 public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener) { 878 checkIsAlive(); 879 880 if (mOnComputeInternalInsetsListeners == null) { 881 mOnComputeInternalInsetsListeners = 882 new CopyOnWriteArray<OnComputeInternalInsetsListener>(); 883 } 884 885 mOnComputeInternalInsetsListeners.add(listener); 886 } 887 888 /** 889 * Remove a previously installed internal insets computation callback 890 * 891 * @param victim The callback to remove 892 * 893 * @throws IllegalStateException If {@link #isAlive()} returns false 894 * 895 * @see #addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener) 896 * 897 * We are not yet ready to commit to this API and support it, so 898 * @hide 899 */ 900 @UnsupportedAppUsage removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim)901 public void removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim) { 902 checkIsAlive(); 903 if (mOnComputeInternalInsetsListeners == null) { 904 return; 905 } 906 mOnComputeInternalInsetsListeners.remove(victim); 907 } 908 909 /** 910 * @hide 911 */ addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener)912 public void addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) { 913 checkIsAlive(); 914 if (mOnEnterAnimationCompleteListeners == null) { 915 mOnEnterAnimationCompleteListeners = 916 new CopyOnWriteArrayList<OnEnterAnimationCompleteListener>(); 917 } 918 mOnEnterAnimationCompleteListeners.add(listener); 919 } 920 921 /** 922 * @hide 923 */ removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener)924 public void removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) { 925 checkIsAlive(); 926 if (mOnEnterAnimationCompleteListeners == null) { 927 return; 928 } 929 mOnEnterAnimationCompleteListeners.remove(listener); 930 } 931 932 /** 933 * Add a listener to be notified when the tree's <em>transformed</em> gesture exclusion rects 934 * change. This could be the result of an animation or other layout change, or a view calling 935 * {@link View#setSystemGestureExclusionRects(List)}. 936 * 937 * @param listener listener to add 938 * @see View#setSystemGestureExclusionRects(List) 939 */ addOnSystemGestureExclusionRectsChangedListener( @onNull Consumer<List<Rect>> listener)940 public void addOnSystemGestureExclusionRectsChangedListener( 941 @NonNull Consumer<List<Rect>> listener) { 942 checkIsAlive(); 943 if (mGestureExclusionListeners == null) { 944 mGestureExclusionListeners = new CopyOnWriteArray<>(); 945 } 946 mGestureExclusionListeners.add(listener); 947 } 948 949 /** 950 * Unsubscribe the given listener from gesture exclusion rect changes. 951 * @see #addOnSystemGestureExclusionRectsChangedListener(Consumer) 952 * @see View#setSystemGestureExclusionRects(List) 953 */ removeOnSystemGestureExclusionRectsChangedListener( @onNull Consumer<List<Rect>> listener)954 public void removeOnSystemGestureExclusionRectsChangedListener( 955 @NonNull Consumer<List<Rect>> listener) { 956 checkIsAlive(); 957 if (mGestureExclusionListeners == null) { 958 return; 959 } 960 mGestureExclusionListeners.remove(listener); 961 } 962 checkIsAlive()963 private void checkIsAlive() { 964 if (!mAlive) { 965 throw new IllegalStateException("This ViewTreeObserver is not alive, call " 966 + "getViewTreeObserver() again"); 967 } 968 } 969 970 /** 971 * Indicates whether this ViewTreeObserver is alive. When an observer is not alive, 972 * any call to a method (except this one) will throw an exception. 973 * 974 * If an application keeps a long-lived reference to this ViewTreeObserver, it should 975 * always check for the result of this method before calling any other method. 976 * 977 * @return True if this object is alive and be used, false otherwise. 978 */ isAlive()979 public boolean isAlive() { 980 return mAlive; 981 } 982 983 /** 984 * Marks this ViewTreeObserver as not alive. After invoking this method, invoking 985 * any other method but {@link #isAlive()} and {@link #kill()} will throw an Exception. 986 * 987 * @hide 988 */ kill()989 private void kill() { 990 mAlive = false; 991 } 992 993 /** 994 * Notifies registered listeners that window has been attached/detached. 995 */ dispatchOnWindowAttachedChange(boolean attached)996 final void dispatchOnWindowAttachedChange(boolean attached) { 997 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to 998 // perform the dispatching. The iterator is a safe guard against listeners that 999 // could mutate the list by calling the various add/remove methods. This prevents 1000 // the array from being modified while we iterate it. 1001 final CopyOnWriteArrayList<OnWindowAttachListener> listeners 1002 = mOnWindowAttachListeners; 1003 if (listeners != null && listeners.size() > 0) { 1004 for (OnWindowAttachListener listener : listeners) { 1005 if (attached) listener.onWindowAttached(); 1006 else listener.onWindowDetached(); 1007 } 1008 } 1009 } 1010 1011 /** 1012 * Notifies registered listeners that window focus has changed. 1013 */ dispatchOnWindowFocusChange(boolean hasFocus)1014 final void dispatchOnWindowFocusChange(boolean hasFocus) { 1015 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to 1016 // perform the dispatching. The iterator is a safe guard against listeners that 1017 // could mutate the list by calling the various add/remove methods. This prevents 1018 // the array from being modified while we iterate it. 1019 final CopyOnWriteArrayList<OnWindowFocusChangeListener> listeners 1020 = mOnWindowFocusListeners; 1021 if (listeners != null && listeners.size() > 0) { 1022 for (OnWindowFocusChangeListener listener : listeners) { 1023 listener.onWindowFocusChanged(hasFocus); 1024 } 1025 } 1026 } 1027 1028 /** 1029 * Notifies registered listeners that focus has changed. 1030 */ 1031 @UnsupportedAppUsage dispatchOnGlobalFocusChange(View oldFocus, View newFocus)1032 final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) { 1033 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to 1034 // perform the dispatching. The iterator is a safe guard against listeners that 1035 // could mutate the list by calling the various add/remove methods. This prevents 1036 // the array from being modified while we iterate it. 1037 final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners; 1038 if (listeners != null && listeners.size() > 0) { 1039 for (OnGlobalFocusChangeListener listener : listeners) { 1040 listener.onGlobalFocusChanged(oldFocus, newFocus); 1041 } 1042 } 1043 } 1044 1045 /** 1046 * Notifies registered listeners that a global layout happened. This can be called 1047 * manually if you are forcing a layout on a View or a hierarchy of Views that are 1048 * not attached to a Window or in the GONE state. 1049 */ dispatchOnGlobalLayout()1050 public final void dispatchOnGlobalLayout() { 1051 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to 1052 // perform the dispatching. The iterator is a safe guard against listeners that 1053 // could mutate the list by calling the various add/remove methods. This prevents 1054 // the array from being modified while we iterate it. 1055 final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners; 1056 if (listeners != null && listeners.size() > 0) { 1057 CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start(); 1058 try { 1059 int count = access.size(); 1060 for (int i = 0; i < count; i++) { 1061 access.get(i).onGlobalLayout(); 1062 } 1063 } finally { 1064 listeners.end(); 1065 } 1066 } 1067 } 1068 1069 /** 1070 * Returns whether there are listeners for on pre-draw events. 1071 */ hasOnPreDrawListeners()1072 final boolean hasOnPreDrawListeners() { 1073 return mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0; 1074 } 1075 1076 /** 1077 * Notifies registered listeners that the drawing pass is about to start. If a 1078 * listener returns true, then the drawing pass is canceled and rescheduled. This can 1079 * be called manually if you are forcing the drawing on a View or a hierarchy of Views 1080 * that are not attached to a Window or in the GONE state. 1081 * 1082 * @return True if the current draw should be canceled and rescheduled, false otherwise. 1083 */ 1084 @SuppressWarnings("unchecked") dispatchOnPreDraw()1085 public final boolean dispatchOnPreDraw() { 1086 boolean cancelDraw = false; 1087 final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners; 1088 if (listeners != null && listeners.size() > 0) { 1089 CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start(); 1090 try { 1091 int count = access.size(); 1092 for (int i = 0; i < count; i++) { 1093 cancelDraw |= !(access.get(i).onPreDraw()); 1094 } 1095 } finally { 1096 listeners.end(); 1097 } 1098 } 1099 return cancelDraw; 1100 } 1101 1102 /** 1103 * Notifies registered listeners that the window is now shown 1104 * @hide 1105 */ 1106 @SuppressWarnings("unchecked") dispatchOnWindowShown()1107 public final void dispatchOnWindowShown() { 1108 mWindowShown = true; 1109 final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners; 1110 if (listeners != null && listeners.size() > 0) { 1111 CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start(); 1112 try { 1113 int count = access.size(); 1114 for (int i = 0; i < count; i++) { 1115 access.get(i).onWindowShown(); 1116 } 1117 } finally { 1118 listeners.end(); 1119 } 1120 } 1121 } 1122 1123 /** 1124 * Notifies registered listeners that the drawing pass is about to start. 1125 */ dispatchOnDraw()1126 public final void dispatchOnDraw() { 1127 if (mOnDrawListeners != null) { 1128 mInDispatchOnDraw = true; 1129 final ArrayList<OnDrawListener> listeners = mOnDrawListeners; 1130 int numListeners = listeners.size(); 1131 for (int i = 0; i < numListeners; ++i) { 1132 listeners.get(i).onDraw(); 1133 } 1134 mInDispatchOnDraw = false; 1135 } 1136 } 1137 1138 /** 1139 * Notifies registered listeners that the touch mode has changed. 1140 * 1141 * @param inTouchMode True if the touch mode is now enabled, false otherwise. 1142 */ 1143 @UnsupportedAppUsage dispatchOnTouchModeChanged(boolean inTouchMode)1144 final void dispatchOnTouchModeChanged(boolean inTouchMode) { 1145 final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners = 1146 mOnTouchModeChangeListeners; 1147 if (listeners != null && listeners.size() > 0) { 1148 for (OnTouchModeChangeListener listener : listeners) { 1149 listener.onTouchModeChanged(inTouchMode); 1150 } 1151 } 1152 } 1153 1154 /** 1155 * Notifies registered listeners that something has scrolled. 1156 */ 1157 @UnsupportedAppUsage dispatchOnScrollChanged()1158 final void dispatchOnScrollChanged() { 1159 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to 1160 // perform the dispatching. The iterator is a safe guard against listeners that 1161 // could mutate the list by calling the various add/remove methods. This prevents 1162 // the array from being modified while we iterate it. 1163 final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners; 1164 if (listeners != null && listeners.size() > 0) { 1165 CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start(); 1166 try { 1167 int count = access.size(); 1168 for (int i = 0; i < count; i++) { 1169 access.get(i).onScrollChanged(); 1170 } 1171 } finally { 1172 listeners.end(); 1173 } 1174 } 1175 } 1176 1177 /** 1178 * Returns whether there are listeners for computing internal insets. 1179 */ 1180 @UnsupportedAppUsage hasComputeInternalInsetsListeners()1181 final boolean hasComputeInternalInsetsListeners() { 1182 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = 1183 mOnComputeInternalInsetsListeners; 1184 return (listeners != null && listeners.size() > 0); 1185 } 1186 1187 /** 1188 * Calls all listeners to compute the current insets. 1189 */ 1190 @UnsupportedAppUsage dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo)1191 final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) { 1192 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to 1193 // perform the dispatching. The iterator is a safe guard against listeners that 1194 // could mutate the list by calling the various add/remove methods. This prevents 1195 // the array from being modified while we iterate it. 1196 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners = 1197 mOnComputeInternalInsetsListeners; 1198 if (listeners != null && listeners.size() > 0) { 1199 CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start(); 1200 try { 1201 int count = access.size(); 1202 for (int i = 0; i < count; i++) { 1203 access.get(i).onComputeInternalInsets(inoutInfo); 1204 } 1205 } finally { 1206 listeners.end(); 1207 } 1208 } 1209 } 1210 1211 /** 1212 * @hide 1213 */ dispatchOnEnterAnimationComplete()1214 public final void dispatchOnEnterAnimationComplete() { 1215 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to 1216 // perform the dispatching. The iterator is a safe guard against listeners that 1217 // could mutate the list by calling the various add/remove methods. This prevents 1218 // the array from being modified while we iterate it. 1219 final CopyOnWriteArrayList<OnEnterAnimationCompleteListener> listeners = 1220 mOnEnterAnimationCompleteListeners; 1221 if (listeners != null && !listeners.isEmpty()) { 1222 for (OnEnterAnimationCompleteListener listener : listeners) { 1223 listener.onEnterAnimationComplete(); 1224 } 1225 } 1226 } 1227 dispatchOnSystemGestureExclusionRectsChanged(@onNull List<Rect> rects)1228 void dispatchOnSystemGestureExclusionRectsChanged(@NonNull List<Rect> rects) { 1229 final CopyOnWriteArray<Consumer<List<Rect>>> listeners = mGestureExclusionListeners; 1230 if (listeners != null && listeners.size() > 0) { 1231 CopyOnWriteArray.Access<Consumer<List<Rect>>> access = listeners.start(); 1232 try { 1233 final int count = access.size(); 1234 for (int i = 0; i < count; i++) { 1235 access.get(i).accept(rects); 1236 } 1237 } finally { 1238 listeners.end(); 1239 } 1240 } 1241 } 1242 1243 /** 1244 * Copy on write array. This array is not thread safe, and only one loop can 1245 * iterate over this array at any given time. This class avoids allocations 1246 * until a concurrent modification happens. 1247 * 1248 * Usage: 1249 * 1250 * CopyOnWriteArray.Access<MyData> access = array.start(); 1251 * try { 1252 * for (int i = 0; i < access.size(); i++) { 1253 * MyData d = access.get(i); 1254 * } 1255 * } finally { 1256 * access.end(); 1257 * } 1258 */ 1259 static class CopyOnWriteArray<T> { 1260 private ArrayList<T> mData = new ArrayList<T>(); 1261 private ArrayList<T> mDataCopy; 1262 1263 private final Access<T> mAccess = new Access<T>(); 1264 1265 private boolean mStart; 1266 1267 static class Access<T> { 1268 private ArrayList<T> mData; 1269 private int mSize; 1270 get(int index)1271 T get(int index) { 1272 return mData.get(index); 1273 } 1274 size()1275 int size() { 1276 return mSize; 1277 } 1278 } 1279 CopyOnWriteArray()1280 CopyOnWriteArray() { 1281 } 1282 getArray()1283 private ArrayList<T> getArray() { 1284 if (mStart) { 1285 if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData); 1286 return mDataCopy; 1287 } 1288 return mData; 1289 } 1290 start()1291 Access<T> start() { 1292 if (mStart) throw new IllegalStateException("Iteration already started"); 1293 mStart = true; 1294 mDataCopy = null; 1295 mAccess.mData = mData; 1296 mAccess.mSize = mData.size(); 1297 return mAccess; 1298 } 1299 end()1300 void end() { 1301 if (!mStart) throw new IllegalStateException("Iteration not started"); 1302 mStart = false; 1303 if (mDataCopy != null) { 1304 mData = mDataCopy; 1305 mAccess.mData.clear(); 1306 mAccess.mSize = 0; 1307 } 1308 mDataCopy = null; 1309 } 1310 size()1311 int size() { 1312 return getArray().size(); 1313 } 1314 add(T item)1315 void add(T item) { 1316 getArray().add(item); 1317 } 1318 addAll(CopyOnWriteArray<T> array)1319 void addAll(CopyOnWriteArray<T> array) { 1320 getArray().addAll(array.mData); 1321 } 1322 remove(T item)1323 void remove(T item) { 1324 getArray().remove(item); 1325 } 1326 clear()1327 void clear() { 1328 getArray().clear(); 1329 } 1330 } 1331 } 1332