blob: e39bfa699952b2b6fb0393835fee6258c978b330 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.view;
18
Mathew Inwoode5ad5982018-08-17 15:07:52 +010019import android.annotation.UnsupportedAppUsage;
John Reckd94094e2016-09-08 14:12:26 -070020import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.graphics.Rect;
Jeff Brownfbf09772011-01-16 14:06:57 -080022import android.graphics.Region;
John Reckd94094e2016-09-08 14:12:26 -070023import android.os.Build;
24import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025
Chet Haase6e0ecb42010-11-03 19:41:18 -070026import java.util.ArrayList;
Chet Haase0f8ffd82012-06-07 07:48:48 -070027import java.util.concurrent.CopyOnWriteArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028
29/**
30 * A view tree observer is used to register listeners that can be notified of global
31 * changes in the view tree. Such global events include, but are not limited to,
32 * layout of the whole tree, beginning of the drawing pass, touch mode change....
33 *
34 * A ViewTreeObserver should never be instantiated by applications as it is provided
35 * by the views hierarchy. Refer to {@link android.view.View#getViewTreeObserver()}
36 * for more information.
37 */
38public final class ViewTreeObserver {
Romain Guyc39ed4a2012-06-12 12:06:46 -070039 // Recursive listeners use CopyOnWriteArrayList
Dianne Hackborn961cae92013-03-20 14:59:43 -070040 private CopyOnWriteArrayList<OnWindowFocusChangeListener> mOnWindowFocusListeners;
41 private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners;
Chet Haase0f8ffd82012-06-07 07:48:48 -070042 private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
Mathew Inwoode5ad5982018-08-17 15:07:52 +010043 @UnsupportedAppUsage
Chet Haase0f8ffd82012-06-07 07:48:48 -070044 private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
Filip Gruszczynski6eafa902014-11-14 14:24:37 -080045 private CopyOnWriteArrayList<OnEnterAnimationCompleteListener>
46 mOnEnterAnimationCompleteListeners;
Romain Guyc39ed4a2012-06-12 12:06:46 -070047
48 // Non-recursive listeners use CopyOnWriteArray
49 // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
Mathew Inwoode5ad5982018-08-17 15:07:52 +010050 @UnsupportedAppUsage
Romain Guyc39ed4a2012-06-12 12:06:46 -070051 private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
Mathew Inwoode5ad5982018-08-17 15:07:52 +010052 @UnsupportedAppUsage
Romain Guyc39ed4a2012-06-12 12:06:46 -070053 private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
Mathew Inwoode5ad5982018-08-17 15:07:52 +010054 @UnsupportedAppUsage
Romain Guyc39ed4a2012-06-12 12:06:46 -070055 private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
56 private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
Craig Mautner9c795042014-10-28 19:59:59 -070057 private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners;
Romain Guyc39ed4a2012-06-12 12:06:46 -070058
59 // These listeners cannot be mutated during dispatch
John Reckd94094e2016-09-08 14:12:26 -070060 private boolean mInDispatchOnDraw;
Romain Guy25eba5c2012-04-04 17:29:03 -070061 private ArrayList<OnDrawListener> mOnDrawListeners;
John Reckd94094e2016-09-08 14:12:26 -070062 private static boolean sIllegalOnDrawModificationIsFatal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
Craig Mautner9c795042014-10-28 19:59:59 -070064 /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
65 * that the listener will be immediately called. */
66 private boolean mWindowShown;
67
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 private boolean mAlive = true;
69
70 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -070071 * Interface definition for a callback to be invoked when the view hierarchy is
72 * attached to and detached from its window.
73 */
74 public interface OnWindowAttachListener {
75 /**
76 * Callback method to be invoked when the view hierarchy is attached to a window
77 */
78 public void onWindowAttached();
79
80 /**
81 * Callback method to be invoked when the view hierarchy is detached from a window
82 */
83 public void onWindowDetached();
84 }
85
86 /**
87 * Interface definition for a callback to be invoked when the view hierarchy's window
88 * focus state changes.
89 */
90 public interface OnWindowFocusChangeListener {
91 /**
92 * Callback method to be invoked when the window focus changes in the view tree.
93 *
94 * @param hasFocus Set to true if the window is gaining focus, false if it is
95 * losing focus.
96 */
97 public void onWindowFocusChanged(boolean hasFocus);
98 }
99
100 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 * Interface definition for a callback to be invoked when the focus state within
102 * the view tree changes.
103 */
104 public interface OnGlobalFocusChangeListener {
105 /**
106 * Callback method to be invoked when the focus changes in the view tree. When
107 * the view tree transitions from touch mode to non-touch mode, oldFocus is null.
108 * When the view tree transitions from non-touch mode to touch mode, newFocus is
109 * null. When focus changes in non-touch mode (without transition from or to
110 * touch mode) either oldFocus or newFocus can be null.
111 *
112 * @param oldFocus The previously focused view, if any.
113 * @param newFocus The newly focused View, if any.
114 */
115 public void onGlobalFocusChanged(View oldFocus, View newFocus);
116 }
117
118 /**
119 * Interface definition for a callback to be invoked when the global layout state
120 * or the visibility of views within the view tree changes.
121 */
122 public interface OnGlobalLayoutListener {
123 /**
124 * Callback method to be invoked when the global layout state or the visibility of views
125 * within the view tree changes
126 */
127 public void onGlobalLayout();
128 }
129
130 /**
131 * Interface definition for a callback to be invoked when the view tree is about to be drawn.
132 */
133 public interface OnPreDrawListener {
134 /**
135 * Callback method to be invoked when the view tree is about to be drawn. At this point, all
136 * views in the tree have been measured and given a frame. Clients can use this to adjust
137 * their scroll bounds or even to request a new layout before drawing occurs.
138 *
139 * @return Return true to proceed with the current drawing pass, or false to cancel.
140 *
141 * @see android.view.View#onMeasure
142 * @see android.view.View#onLayout
143 * @see android.view.View#onDraw
144 */
145 public boolean onPreDraw();
146 }
147
148 /**
Romain Guy25eba5c2012-04-04 17:29:03 -0700149 * Interface definition for a callback to be invoked when the view tree is about to be drawn.
150 */
151 public interface OnDrawListener {
152 /**
153 * <p>Callback method to be invoked when the view tree is about to be drawn. At this point,
154 * views cannot be modified in any way.</p>
155 *
156 * <p>Unlike with {@link OnPreDrawListener}, this method cannot be used to cancel the
157 * current drawing pass.</p>
158 *
159 * <p>An {@link OnDrawListener} listener <strong>cannot be added or removed</strong>
160 * from this method.</p>
161 *
162 * @see android.view.View#onMeasure
163 * @see android.view.View#onLayout
164 * @see android.view.View#onDraw
165 */
166 public void onDraw();
167 }
168
169 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 * Interface definition for a callback to be invoked when the touch mode changes.
171 */
172 public interface OnTouchModeChangeListener {
173 /**
174 * Callback method to be invoked when the touch mode changes.
175 *
176 * @param isInTouchMode True if the view hierarchy is now in touch mode, false otherwise.
177 */
178 public void onTouchModeChanged(boolean isInTouchMode);
179 }
180
181 /**
182 * Interface definition for a callback to be invoked when
183 * something in the view tree has been scrolled.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 */
185 public interface OnScrollChangedListener {
186 /**
187 * Callback method to be invoked when something in the view tree
188 * has been scrolled.
189 */
190 public void onScrollChanged();
191 }
192
193 /**
Craig Mautner9c795042014-10-28 19:59:59 -0700194 * Interface definition for a callback noting when a system window has been displayed.
195 * This is only used for non-Activity windows. Activity windows can use
196 * Activity.onEnterAnimationComplete() to get the same signal.
197 * @hide
198 */
199 public interface OnWindowShownListener {
200 /**
201 * Callback method to be invoked when a non-activity window is fully shown.
202 */
203 void onWindowShown();
204 }
205
206 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 * Parameters used with OnComputeInternalInsetsListener.
Dianne Hackborn935ae462009-04-13 16:11:55 -0700208 *
209 * We are not yet ready to commit to this API and support it, so
210 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 */
212 public final static class InternalInsetsInfo {
213 /**
214 * Offsets from the frame of the window at which the content of
215 * windows behind it should be placed.
216 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100217 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 public final Rect contentInsets = new Rect();
Romain Guyc39ed4a2012-06-12 12:06:46 -0700219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 /**
Jeff Brownfbf09772011-01-16 14:06:57 -0800221 * Offsets from the frame of the window at which windows behind it
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 * are visible.
223 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100224 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 public final Rect visibleInsets = new Rect();
Jeff Brownfbf09772011-01-16 14:06:57 -0800226
227 /**
228 * Touchable region defined relative to the origin of the frame of the window.
229 * Only used when {@link #setTouchableInsets(int)} is called with
230 * the option {@link #TOUCHABLE_INSETS_REGION}.
231 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100232 @UnsupportedAppUsage
Jeff Brownfbf09772011-01-16 14:06:57 -0800233 public final Region touchableRegion = new Region();
234
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 /**
236 * Option for {@link #setTouchableInsets(int)}: the entire window frame
237 * can be touched.
238 */
239 public static final int TOUCHABLE_INSETS_FRAME = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 /**
242 * Option for {@link #setTouchableInsets(int)}: the area inside of
243 * the content insets can be touched.
244 */
245 public static final int TOUCHABLE_INSETS_CONTENT = 1;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 /**
248 * Option for {@link #setTouchableInsets(int)}: the area inside of
249 * the visible insets can be touched.
250 */
251 public static final int TOUCHABLE_INSETS_VISIBLE = 2;
Jeff Brownfbf09772011-01-16 14:06:57 -0800252
253 /**
254 * Option for {@link #setTouchableInsets(int)}: the area inside of
255 * the provided touchable region in {@link #touchableRegion} can be touched.
256 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100257 @UnsupportedAppUsage
Jeff Brownfbf09772011-01-16 14:06:57 -0800258 public static final int TOUCHABLE_INSETS_REGION = 3;
259
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 /**
261 * Set which parts of the window can be touched: either
262 * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
Jeff Brownfbf09772011-01-16 14:06:57 -0800263 * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100265 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 public void setTouchableInsets(int val) {
267 mTouchableInsets = val;
268 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700269
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100270 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 int mTouchableInsets;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 void reset() {
Jeff Brownfbf09772011-01-16 14:06:57 -0800274 contentInsets.setEmpty();
275 visibleInsets.setEmpty();
276 touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 mTouchableInsets = TOUCHABLE_INSETS_FRAME;
278 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700279
Jeff Brown2e05ec32013-09-30 15:57:43 -0700280 boolean isEmpty() {
281 return contentInsets.isEmpty()
282 && visibleInsets.isEmpty()
283 && touchableRegion.isEmpty()
284 && mTouchableInsets == TOUCHABLE_INSETS_FRAME;
285 }
286
Romain Guy25eba5c2012-04-04 17:29:03 -0700287 @Override
288 public int hashCode() {
Romain Guy21f42302013-06-28 19:19:30 -0700289 int result = contentInsets.hashCode();
290 result = 31 * result + visibleInsets.hashCode();
291 result = 31 * result + touchableRegion.hashCode();
Romain Guy25eba5c2012-04-04 17:29:03 -0700292 result = 31 * result + mTouchableInsets;
293 return result;
294 }
295
Romain Guy1e878d22012-01-23 15:34:25 -0800296 @Override
297 public boolean equals(Object o) {
Romain Guy25eba5c2012-04-04 17:29:03 -0700298 if (this == o) return true;
299 if (o == null || getClass() != o.getClass()) return false;
300
301 InternalInsetsInfo other = (InternalInsetsInfo)o;
302 return mTouchableInsets == other.mTouchableInsets &&
303 contentInsets.equals(other.contentInsets) &&
304 visibleInsets.equals(other.visibleInsets) &&
305 touchableRegion.equals(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700307
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100308 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 void set(InternalInsetsInfo other) {
310 contentInsets.set(other.contentInsets);
311 visibleInsets.set(other.visibleInsets);
Jeff Brownfbf09772011-01-16 14:06:57 -0800312 touchableRegion.set(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 mTouchableInsets = other.mTouchableInsets;
314 }
315 }
Romain Guyc39ed4a2012-06-12 12:06:46 -0700316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 /**
318 * Interface definition for a callback to be invoked when layout has
319 * completed and the client can compute its interior insets.
Dianne Hackborn935ae462009-04-13 16:11:55 -0700320 *
321 * We are not yet ready to commit to this API and support it, so
322 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 */
324 public interface OnComputeInternalInsetsListener {
325 /**
326 * Callback method to be invoked when layout has completed and the
327 * client can compute its interior insets.
328 *
329 * @param inoutInfo Should be filled in by the implementation with
330 * the information about the insets of the window. This is called
331 * with whatever values the previous OnComputeInternalInsetsListener
332 * returned, if there are multiple such listeners in the window.
333 */
334 public void onComputeInternalInsets(InternalInsetsInfo inoutInfo);
335 }
336
337 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800338 * @hide
339 */
340 public interface OnEnterAnimationCompleteListener {
341 public void onEnterAnimationComplete();
342 }
343
344 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 * Creates a new ViewTreeObserver. This constructor should not be called
346 */
John Reckd94094e2016-09-08 14:12:26 -0700347 ViewTreeObserver(Context context) {
348 sIllegalOnDrawModificationIsFatal =
Jeff Sharkeyf383c242017-05-17 17:53:07 -0600349 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 }
351
352 /**
353 * Merges all the listeners registered on the specified observer with the listeners
354 * registered on this object. After this method is invoked, the specified observer
355 * will return false in {@link #isAlive()} and should not be used anymore.
356 *
357 * @param observer The ViewTreeObserver whose listeners must be added to this observer
358 */
359 void merge(ViewTreeObserver observer) {
Dianne Hackborn961cae92013-03-20 14:59:43 -0700360 if (observer.mOnWindowAttachListeners != null) {
361 if (mOnWindowAttachListeners != null) {
362 mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
363 } else {
364 mOnWindowAttachListeners = observer.mOnWindowAttachListeners;
365 }
366 }
367
368 if (observer.mOnWindowFocusListeners != null) {
369 if (mOnWindowFocusListeners != null) {
370 mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
371 } else {
372 mOnWindowFocusListeners = observer.mOnWindowFocusListeners;
373 }
374 }
375
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 if (observer.mOnGlobalFocusListeners != null) {
377 if (mOnGlobalFocusListeners != null) {
378 mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
379 } else {
380 mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;
381 }
382 }
383
384 if (observer.mOnGlobalLayoutListeners != null) {
385 if (mOnGlobalLayoutListeners != null) {
386 mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
387 } else {
388 mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;
389 }
390 }
391
392 if (observer.mOnPreDrawListeners != null) {
393 if (mOnPreDrawListeners != null) {
394 mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
395 } else {
396 mOnPreDrawListeners = observer.mOnPreDrawListeners;
397 }
398 }
399
John Reck9f8ec542016-12-12 11:23:05 -0800400 if (observer.mOnDrawListeners != null) {
401 if (mOnDrawListeners != null) {
402 mOnDrawListeners.addAll(observer.mOnDrawListeners);
403 } else {
404 mOnDrawListeners = observer.mOnDrawListeners;
405 }
406 }
407
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 if (observer.mOnTouchModeChangeListeners != null) {
409 if (mOnTouchModeChangeListeners != null) {
410 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
411 } else {
412 mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;
413 }
414 }
415
416 if (observer.mOnComputeInternalInsetsListeners != null) {
417 if (mOnComputeInternalInsetsListeners != null) {
418 mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
419 } else {
420 mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;
421 }
422 }
423
Mark Brophy757c6972011-10-25 17:01:28 +0100424 if (observer.mOnScrollChangedListeners != null) {
425 if (mOnScrollChangedListeners != null) {
426 mOnScrollChangedListeners.addAll(observer.mOnScrollChangedListeners);
427 } else {
428 mOnScrollChangedListeners = observer.mOnScrollChangedListeners;
429 }
430 }
431
Craig Mautner9c795042014-10-28 19:59:59 -0700432 if (observer.mOnWindowShownListeners != null) {
433 if (mOnWindowShownListeners != null) {
434 mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
435 } else {
436 mOnWindowShownListeners = observer.mOnWindowShownListeners;
437 }
438 }
439
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 observer.kill();
441 }
442
443 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700444 * Register a callback to be invoked when the view hierarchy is attached to a window.
445 *
446 * @param listener The callback to add
447 *
448 * @throws IllegalStateException If {@link #isAlive()} returns false
449 */
450 public void addOnWindowAttachListener(OnWindowAttachListener listener) {
451 checkIsAlive();
452
453 if (mOnWindowAttachListeners == null) {
454 mOnWindowAttachListeners
455 = new CopyOnWriteArrayList<OnWindowAttachListener>();
456 }
457
458 mOnWindowAttachListeners.add(listener);
459 }
460
461 /**
462 * Remove a previously installed window attach callback.
463 *
464 * @param victim The callback to remove
465 *
466 * @throws IllegalStateException If {@link #isAlive()} returns false
467 *
468 * @see #addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener)
469 */
470 public void removeOnWindowAttachListener(OnWindowAttachListener victim) {
471 checkIsAlive();
472 if (mOnWindowAttachListeners == null) {
473 return;
474 }
475 mOnWindowAttachListeners.remove(victim);
476 }
477
478 /**
479 * Register a callback to be invoked when the window focus state within the view tree changes.
480 *
481 * @param listener The callback to add
482 *
483 * @throws IllegalStateException If {@link #isAlive()} returns false
484 */
485 public void addOnWindowFocusChangeListener(OnWindowFocusChangeListener listener) {
486 checkIsAlive();
487
488 if (mOnWindowFocusListeners == null) {
489 mOnWindowFocusListeners
490 = new CopyOnWriteArrayList<OnWindowFocusChangeListener>();
491 }
492
493 mOnWindowFocusListeners.add(listener);
494 }
495
496 /**
497 * Remove a previously installed window focus change callback.
498 *
499 * @param victim The callback to remove
500 *
501 * @throws IllegalStateException If {@link #isAlive()} returns false
502 *
503 * @see #addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener)
504 */
505 public void removeOnWindowFocusChangeListener(OnWindowFocusChangeListener victim) {
506 checkIsAlive();
507 if (mOnWindowFocusListeners == null) {
508 return;
509 }
510 mOnWindowFocusListeners.remove(victim);
511 }
512
513 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 * Register a callback to be invoked when the focus state within the view tree changes.
515 *
516 * @param listener The callback to add
517 *
518 * @throws IllegalStateException If {@link #isAlive()} returns false
519 */
520 public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener) {
521 checkIsAlive();
522
523 if (mOnGlobalFocusListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700524 mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525 }
526
527 mOnGlobalFocusListeners.add(listener);
528 }
529
530 /**
531 * Remove a previously installed focus change callback.
532 *
533 * @param victim The callback to remove
534 *
535 * @throws IllegalStateException If {@link #isAlive()} returns false
536 *
537 * @see #addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener)
538 */
539 public void removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim) {
540 checkIsAlive();
541 if (mOnGlobalFocusListeners == null) {
542 return;
543 }
544 mOnGlobalFocusListeners.remove(victim);
545 }
546
547 /**
548 * Register a callback to be invoked when the global layout state or the visibility of views
549 * within the view tree changes
550 *
551 * @param listener The callback to add
552 *
553 * @throws IllegalStateException If {@link #isAlive()} returns false
554 */
555 public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
556 checkIsAlive();
557
558 if (mOnGlobalLayoutListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700559 mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 }
561
562 mOnGlobalLayoutListeners.add(listener);
563 }
564
565 /**
566 * Remove a previously installed global layout callback
567 *
568 * @param victim The callback to remove
569 *
570 * @throws IllegalStateException If {@link #isAlive()} returns false
Romain Guy1e878d22012-01-23 15:34:25 -0800571 *
572 * @deprecated Use #removeOnGlobalLayoutListener instead
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 *
574 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
575 */
Romain Guy1e878d22012-01-23 15:34:25 -0800576 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
Romain Guy1e878d22012-01-23 15:34:25 -0800578 removeOnGlobalLayoutListener(victim);
579 }
580
581 /**
582 * Remove a previously installed global layout callback
583 *
584 * @param victim The callback to remove
585 *
586 * @throws IllegalStateException If {@link #isAlive()} returns false
587 *
588 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
589 */
590 public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 checkIsAlive();
592 if (mOnGlobalLayoutListeners == null) {
593 return;
594 }
595 mOnGlobalLayoutListeners.remove(victim);
596 }
597
598 /**
599 * Register a callback to be invoked when the view tree is about to be drawn
600 *
601 * @param listener The callback to add
602 *
603 * @throws IllegalStateException If {@link #isAlive()} returns false
604 */
605 public void addOnPreDrawListener(OnPreDrawListener listener) {
606 checkIsAlive();
607
608 if (mOnPreDrawListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700609 mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 }
611
612 mOnPreDrawListeners.add(listener);
613 }
614
615 /**
616 * Remove a previously installed pre-draw callback
617 *
618 * @param victim The callback to remove
619 *
620 * @throws IllegalStateException If {@link #isAlive()} returns false
621 *
622 * @see #addOnPreDrawListener(OnPreDrawListener)
623 */
624 public void removeOnPreDrawListener(OnPreDrawListener victim) {
625 checkIsAlive();
626 if (mOnPreDrawListeners == null) {
627 return;
628 }
629 mOnPreDrawListeners.remove(victim);
630 }
631
632 /**
Craig Mautner9c795042014-10-28 19:59:59 -0700633 * Register a callback to be invoked when the view tree window has been shown
634 *
635 * @param listener The callback to add
636 *
637 * @throws IllegalStateException If {@link #isAlive()} returns false
638 * @hide
639 */
640 public void addOnWindowShownListener(OnWindowShownListener listener) {
641 checkIsAlive();
642
643 if (mOnWindowShownListeners == null) {
644 mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>();
645 }
646
647 mOnWindowShownListeners.add(listener);
648 if (mWindowShown) {
649 listener.onWindowShown();
650 }
651 }
652
653 /**
654 * Remove a previously installed window shown callback
655 *
656 * @param victim The callback to remove
657 *
658 * @throws IllegalStateException If {@link #isAlive()} returns false
659 *
660 * @see #addOnWindowShownListener(OnWindowShownListener)
661 * @hide
662 */
663 public void removeOnWindowShownListener(OnWindowShownListener victim) {
664 checkIsAlive();
665 if (mOnWindowShownListeners == null) {
666 return;
667 }
668 mOnWindowShownListeners.remove(victim);
669 }
670
671 /**
Romain Guy25eba5c2012-04-04 17:29:03 -0700672 * <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
673 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
674 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
675 *
676 * @param listener The callback to add
677 *
678 * @throws IllegalStateException If {@link #isAlive()} returns false
679 */
680 public void addOnDrawListener(OnDrawListener listener) {
681 checkIsAlive();
682
683 if (mOnDrawListeners == null) {
684 mOnDrawListeners = new ArrayList<OnDrawListener>();
685 }
686
John Reckd94094e2016-09-08 14:12:26 -0700687 if (mInDispatchOnDraw) {
688 IllegalStateException ex = new IllegalStateException(
689 "Cannot call addOnDrawListener inside of onDraw");
690 if (sIllegalOnDrawModificationIsFatal) {
691 throw ex;
692 } else {
693 Log.e("ViewTreeObserver", ex.getMessage(), ex);
694 }
695 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700696 mOnDrawListeners.add(listener);
697 }
698
699 /**
700 * <p>Remove a previously installed pre-draw callback.</p>
701 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
702 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
703 *
704 * @param victim The callback to remove
705 *
706 * @throws IllegalStateException If {@link #isAlive()} returns false
707 *
708 * @see #addOnDrawListener(OnDrawListener)
709 */
710 public void removeOnDrawListener(OnDrawListener victim) {
711 checkIsAlive();
712 if (mOnDrawListeners == null) {
713 return;
714 }
John Reckd94094e2016-09-08 14:12:26 -0700715 if (mInDispatchOnDraw) {
716 IllegalStateException ex = new IllegalStateException(
717 "Cannot call removeOnDrawListener inside of onDraw");
718 if (sIllegalOnDrawModificationIsFatal) {
719 throw ex;
720 } else {
721 Log.e("ViewTreeObserver", ex.getMessage(), ex);
722 }
723 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700724 mOnDrawListeners.remove(victim);
725 }
726
727 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 * Register a callback to be invoked when a view has been scrolled.
729 *
730 * @param listener The callback to add
731 *
732 * @throws IllegalStateException If {@link #isAlive()} returns false
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 */
734 public void addOnScrollChangedListener(OnScrollChangedListener listener) {
735 checkIsAlive();
736
737 if (mOnScrollChangedListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700738 mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800739 }
740
741 mOnScrollChangedListeners.add(listener);
742 }
743
744 /**
745 * Remove a previously installed scroll-changed callback
746 *
747 * @param victim The callback to remove
748 *
749 * @throws IllegalStateException If {@link #isAlive()} returns false
750 *
751 * @see #addOnScrollChangedListener(OnScrollChangedListener)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 */
753 public void removeOnScrollChangedListener(OnScrollChangedListener victim) {
754 checkIsAlive();
755 if (mOnScrollChangedListeners == null) {
756 return;
757 }
758 mOnScrollChangedListeners.remove(victim);
759 }
760
761 /**
762 * Register a callback to be invoked when the invoked when the touch mode changes.
763 *
764 * @param listener The callback to add
765 *
766 * @throws IllegalStateException If {@link #isAlive()} returns false
767 */
768 public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
769 checkIsAlive();
770
771 if (mOnTouchModeChangeListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700772 mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 }
774
775 mOnTouchModeChangeListeners.add(listener);
776 }
777
778 /**
779 * Remove a previously installed touch mode change callback
780 *
781 * @param victim The callback to remove
782 *
783 * @throws IllegalStateException If {@link #isAlive()} returns false
784 *
785 * @see #addOnTouchModeChangeListener(OnTouchModeChangeListener)
786 */
787 public void removeOnTouchModeChangeListener(OnTouchModeChangeListener victim) {
788 checkIsAlive();
789 if (mOnTouchModeChangeListeners == null) {
790 return;
791 }
792 mOnTouchModeChangeListeners.remove(victim);
793 }
794
795 /**
796 * Register a callback to be invoked when the invoked when it is time to
797 * compute the window's internal insets.
798 *
799 * @param listener The callback to add
800 *
801 * @throws IllegalStateException If {@link #isAlive()} returns false
Dianne Hackborn935ae462009-04-13 16:11:55 -0700802 *
803 * We are not yet ready to commit to this API and support it, so
804 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100806 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800807 public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener) {
808 checkIsAlive();
809
810 if (mOnComputeInternalInsetsListeners == null) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700811 mOnComputeInternalInsetsListeners =
Romain Guyc39ed4a2012-06-12 12:06:46 -0700812 new CopyOnWriteArray<OnComputeInternalInsetsListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 }
814
815 mOnComputeInternalInsetsListeners.add(listener);
816 }
817
818 /**
819 * Remove a previously installed internal insets computation callback
820 *
821 * @param victim The callback to remove
822 *
823 * @throws IllegalStateException If {@link #isAlive()} returns false
824 *
825 * @see #addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener)
Dianne Hackborn935ae462009-04-13 16:11:55 -0700826 *
827 * We are not yet ready to commit to this API and support it, so
828 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100830 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 public void removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim) {
832 checkIsAlive();
833 if (mOnComputeInternalInsetsListeners == null) {
834 return;
835 }
836 mOnComputeInternalInsetsListeners.remove(victim);
837 }
838
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800839 /**
840 * @hide
841 */
842 public void addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
843 checkIsAlive();
844 if (mOnEnterAnimationCompleteListeners == null) {
845 mOnEnterAnimationCompleteListeners =
846 new CopyOnWriteArrayList<OnEnterAnimationCompleteListener>();
847 }
848 mOnEnterAnimationCompleteListeners.add(listener);
849 }
850
851 /**
852 * @hide
853 */
854 public void removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
855 checkIsAlive();
856 if (mOnEnterAnimationCompleteListeners == null) {
857 return;
858 }
859 mOnEnterAnimationCompleteListeners.remove(listener);
860 }
861
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 private void checkIsAlive() {
863 if (!mAlive) {
864 throw new IllegalStateException("This ViewTreeObserver is not alive, call "
865 + "getViewTreeObserver() again");
866 }
867 }
868
869 /**
870 * Indicates whether this ViewTreeObserver is alive. When an observer is not alive,
871 * any call to a method (except this one) will throw an exception.
872 *
873 * If an application keeps a long-lived reference to this ViewTreeObserver, it should
874 * always check for the result of this method before calling any other method.
875 *
876 * @return True if this object is alive and be used, false otherwise.
877 */
878 public boolean isAlive() {
879 return mAlive;
880 }
881
882 /**
883 * Marks this ViewTreeObserver as not alive. After invoking this method, invoking
884 * any other method but {@link #isAlive()} and {@link #kill()} will throw an Exception.
885 *
886 * @hide
887 */
888 private void kill() {
889 mAlive = false;
890 }
891
892 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700893 * Notifies registered listeners that window has been attached/detached.
894 */
895 final void dispatchOnWindowAttachedChange(boolean attached) {
896 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
897 // perform the dispatching. The iterator is a safe guard against listeners that
898 // could mutate the list by calling the various add/remove methods. This prevents
899 // the array from being modified while we iterate it.
900 final CopyOnWriteArrayList<OnWindowAttachListener> listeners
901 = mOnWindowAttachListeners;
902 if (listeners != null && listeners.size() > 0) {
903 for (OnWindowAttachListener listener : listeners) {
904 if (attached) listener.onWindowAttached();
905 else listener.onWindowDetached();
906 }
907 }
908 }
909
910 /**
911 * Notifies registered listeners that window focus has changed.
912 */
913 final void dispatchOnWindowFocusChange(boolean hasFocus) {
914 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
915 // perform the dispatching. The iterator is a safe guard against listeners that
916 // could mutate the list by calling the various add/remove methods. This prevents
917 // the array from being modified while we iterate it.
918 final CopyOnWriteArrayList<OnWindowFocusChangeListener> listeners
919 = mOnWindowFocusListeners;
920 if (listeners != null && listeners.size() > 0) {
921 for (OnWindowFocusChangeListener listener : listeners) {
922 listener.onWindowFocusChanged(hasFocus);
923 }
924 }
925 }
926
927 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800928 * Notifies registered listeners that focus has changed.
929 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100930 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700932 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
933 // perform the dispatching. The iterator is a safe guard against listeners that
934 // could mutate the list by calling the various add/remove methods. This prevents
935 // the array from being modified while we iterate it.
Chet Haase0f8ffd82012-06-07 07:48:48 -0700936 final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700937 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700938 for (OnGlobalFocusChangeListener listener : listeners) {
939 listener.onGlobalFocusChanged(oldFocus, newFocus);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 }
941 }
942 }
943
944 /**
945 * Notifies registered listeners that a global layout happened. This can be called
946 * manually if you are forcing a layout on a View or a hierarchy of Views that are
947 * not attached to a Window or in the GONE state.
948 */
949 public final void dispatchOnGlobalLayout() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700950 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
951 // perform the dispatching. The iterator is a safe guard against listeners that
952 // could mutate the list by calling the various add/remove methods. This prevents
953 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -0700954 final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700955 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700956 CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
957 try {
958 int count = access.size();
959 for (int i = 0; i < count; i++) {
960 access.get(i).onGlobalLayout();
961 }
962 } finally {
963 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 }
965 }
966 }
967
968 /**
Romain Guy21f42302013-06-28 19:19:30 -0700969 * Returns whether there are listeners for on pre-draw events.
970 */
971 final boolean hasOnPreDrawListeners() {
972 return mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0;
973 }
974
975 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 * Notifies registered listeners that the drawing pass is about to start. If a
977 * listener returns true, then the drawing pass is canceled and rescheduled. This can
978 * be called manually if you are forcing the drawing on a View or a hierarchy of Views
979 * that are not attached to a Window or in the GONE state.
980 *
981 * @return True if the current draw should be canceled and resceduled, false otherwise.
982 */
Romain Guy25eba5c2012-04-04 17:29:03 -0700983 @SuppressWarnings("unchecked")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 public final boolean dispatchOnPreDraw() {
985 boolean cancelDraw = false;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700986 final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
987 if (listeners != null && listeners.size() > 0) {
988 CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();
989 try {
990 int count = access.size();
991 for (int i = 0; i < count; i++) {
992 cancelDraw |= !(access.get(i).onPreDraw());
993 }
994 } finally {
995 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 }
997 }
998 return cancelDraw;
999 }
1000
1001 /**
Craig Mautner9c795042014-10-28 19:59:59 -07001002 * Notifies registered listeners that the window is now shown
1003 * @hide
1004 */
1005 @SuppressWarnings("unchecked")
1006 public final void dispatchOnWindowShown() {
1007 mWindowShown = true;
1008 final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners;
1009 if (listeners != null && listeners.size() > 0) {
1010 CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start();
1011 try {
1012 int count = access.size();
1013 for (int i = 0; i < count; i++) {
1014 access.get(i).onWindowShown();
1015 }
1016 } finally {
1017 listeners.end();
1018 }
1019 }
1020 }
1021
1022 /**
Romain Guy25eba5c2012-04-04 17:29:03 -07001023 * Notifies registered listeners that the drawing pass is about to start.
1024 */
1025 public final void dispatchOnDraw() {
1026 if (mOnDrawListeners != null) {
John Reckd94094e2016-09-08 14:12:26 -07001027 mInDispatchOnDraw = true;
Romain Guy25eba5c2012-04-04 17:29:03 -07001028 final ArrayList<OnDrawListener> listeners = mOnDrawListeners;
1029 int numListeners = listeners.size();
1030 for (int i = 0; i < numListeners; ++i) {
1031 listeners.get(i).onDraw();
1032 }
John Reckd94094e2016-09-08 14:12:26 -07001033 mInDispatchOnDraw = false;
Romain Guy25eba5c2012-04-04 17:29:03 -07001034 }
1035 }
1036
1037 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 * Notifies registered listeners that the touch mode has changed.
1039 *
1040 * @param inTouchMode True if the touch mode is now enabled, false otherwise.
1041 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +01001042 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 final void dispatchOnTouchModeChanged(boolean inTouchMode) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001044 final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001045 mOnTouchModeChangeListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001046 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001047 for (OnTouchModeChangeListener listener : listeners) {
1048 listener.onTouchModeChanged(inTouchMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001049 }
1050 }
1051 }
1052
1053 /**
1054 * Notifies registered listeners that something has scrolled.
1055 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +01001056 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 final void dispatchOnScrollChanged() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001058 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1059 // perform the dispatching. The iterator is a safe guard against listeners that
1060 // could mutate the list by calling the various add/remove methods. This prevents
1061 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001062 final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001063 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001064 CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start();
1065 try {
1066 int count = access.size();
1067 for (int i = 0; i < count; i++) {
1068 access.get(i).onScrollChanged();
1069 }
1070 } finally {
1071 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 }
1073 }
1074 }
1075
1076 /**
1077 * Returns whether there are listeners for computing internal insets.
1078 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +01001079 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 final boolean hasComputeInternalInsetsListeners() {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001081 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001082 mOnComputeInternalInsetsListeners;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 return (listeners != null && listeners.size() > 0);
1084 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001085
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 /**
1087 * Calls all listeners to compute the current insets.
1088 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +01001089 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001091 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1092 // perform the dispatching. The iterator is a safe guard against listeners that
1093 // could mutate the list by calling the various add/remove methods. This prevents
1094 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001095 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001096 mOnComputeInternalInsetsListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001097 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001098 CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
1099 try {
1100 int count = access.size();
1101 for (int i = 0; i < count; i++) {
1102 access.get(i).onComputeInternalInsets(inoutInfo);
1103 }
1104 } finally {
1105 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 }
1107 }
1108 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001109
1110 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -08001111 * @hide
1112 */
1113 public final void dispatchOnEnterAnimationComplete() {
1114 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1115 // perform the dispatching. The iterator is a safe guard against listeners that
1116 // could mutate the list by calling the various add/remove methods. This prevents
1117 // the array from being modified while we iterate it.
1118 final CopyOnWriteArrayList<OnEnterAnimationCompleteListener> listeners =
1119 mOnEnterAnimationCompleteListeners;
1120 if (listeners != null && !listeners.isEmpty()) {
1121 for (OnEnterAnimationCompleteListener listener : listeners) {
1122 listener.onEnterAnimationComplete();
1123 }
1124 }
1125 }
1126
1127 /**
Romain Guyc39ed4a2012-06-12 12:06:46 -07001128 * Copy on write array. This array is not thread safe, and only one loop can
1129 * iterate over this array at any given time. This class avoids allocations
1130 * until a concurrent modification happens.
1131 *
1132 * Usage:
1133 *
1134 * CopyOnWriteArray.Access<MyData> access = array.start();
1135 * try {
1136 * for (int i = 0; i < access.size(); i++) {
1137 * MyData d = access.get(i);
1138 * }
1139 * } finally {
1140 * access.end();
1141 * }
1142 */
1143 static class CopyOnWriteArray<T> {
1144 private ArrayList<T> mData = new ArrayList<T>();
1145 private ArrayList<T> mDataCopy;
1146
1147 private final Access<T> mAccess = new Access<T>();
1148
1149 private boolean mStart;
1150
1151 static class Access<T> {
1152 private ArrayList<T> mData;
1153 private int mSize;
1154
1155 T get(int index) {
1156 return mData.get(index);
1157 }
1158
1159 int size() {
1160 return mSize;
1161 }
1162 }
1163
1164 CopyOnWriteArray() {
1165 }
1166
1167 private ArrayList<T> getArray() {
1168 if (mStart) {
1169 if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData);
1170 return mDataCopy;
1171 }
1172 return mData;
1173 }
1174
1175 Access<T> start() {
1176 if (mStart) throw new IllegalStateException("Iteration already started");
1177 mStart = true;
1178 mDataCopy = null;
1179 mAccess.mData = mData;
1180 mAccess.mSize = mData.size();
1181 return mAccess;
1182 }
1183
1184 void end() {
1185 if (!mStart) throw new IllegalStateException("Iteration not started");
1186 mStart = false;
1187 if (mDataCopy != null) {
1188 mData = mDataCopy;
Chet Haasefc343962013-09-18 08:44:33 -07001189 mAccess.mData.clear();
1190 mAccess.mSize = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001191 }
1192 mDataCopy = null;
1193 }
1194
1195 int size() {
1196 return getArray().size();
1197 }
1198
1199 void add(T item) {
1200 getArray().add(item);
1201 }
1202
1203 void addAll(CopyOnWriteArray<T> array) {
1204 getArray().addAll(array.mData);
1205 }
1206
1207 void remove(T item) {
1208 getArray().remove(item);
1209 }
1210
1211 void clear() {
1212 getArray().clear();
1213 }
1214 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001215}