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