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