blob: efc18072e547cfdb13ffd54a51197e8a66934245 [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
John Reck9ab32f82018-07-26 11:02:53 -070019import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.annotation.TestApi;
John Reckd94094e2016-09-08 14:12:26 -070022import android.content.Context;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.graphics.Rect;
Jeff Brownfbf09772011-01-16 14:06:57 -080024import android.graphics.Region;
John Reckd94094e2016-09-08 14:12:26 -070025import android.os.Build;
26import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027
Chet Haase6e0ecb42010-11-03 19:41:18 -070028import java.util.ArrayList;
Chet Haase0f8ffd82012-06-07 07:48:48 -070029import java.util.concurrent.CopyOnWriteArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030
31/**
32 * A view tree observer is used to register listeners that can be notified of global
33 * changes in the view tree. Such global events include, but are not limited to,
34 * layout of the whole tree, beginning of the drawing pass, touch mode change....
35 *
36 * A ViewTreeObserver should never be instantiated by applications as it is provided
37 * by the views hierarchy. Refer to {@link android.view.View#getViewTreeObserver()}
38 * for more information.
39 */
40public final class ViewTreeObserver {
Romain Guyc39ed4a2012-06-12 12:06:46 -070041 // Recursive listeners use CopyOnWriteArrayList
Dianne Hackborn961cae92013-03-20 14:59:43 -070042 private CopyOnWriteArrayList<OnWindowFocusChangeListener> mOnWindowFocusListeners;
43 private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners;
Chet Haase0f8ffd82012-06-07 07:48:48 -070044 private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
Chet Haase0f8ffd82012-06-07 07:48:48 -070045 private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
Filip Gruszczynski6eafa902014-11-14 14:24:37 -080046 private CopyOnWriteArrayList<OnEnterAnimationCompleteListener>
47 mOnEnterAnimationCompleteListeners;
Romain Guyc39ed4a2012-06-12 12:06:46 -070048
49 // Non-recursive listeners use CopyOnWriteArray
50 // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
51 private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
52 private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
53 private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
54 private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
Craig Mautner9c795042014-10-28 19:59:59 -070055 private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners;
Romain Guyc39ed4a2012-06-12 12:06:46 -070056
57 // These listeners cannot be mutated during dispatch
John Reckd94094e2016-09-08 14:12:26 -070058 private boolean mInDispatchOnDraw;
Romain Guy25eba5c2012-04-04 17:29:03 -070059 private ArrayList<OnDrawListener> mOnDrawListeners;
John Reckd94094e2016-09-08 14:12:26 -070060 private static boolean sIllegalOnDrawModificationIsFatal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
John Reck9ab32f82018-07-26 11:02:53 -070062 // These listeners are one-shot
63 private ArrayList<Runnable> mOnFrameCommitListeners;
64
Craig Mautner9c795042014-10-28 19:59:59 -070065 /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
66 * that the listener will be immediately called. */
67 private boolean mWindowShown;
68
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 private boolean mAlive = true;
70
71 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -070072 * Interface definition for a callback to be invoked when the view hierarchy is
73 * attached to and detached from its window.
74 */
75 public interface OnWindowAttachListener {
76 /**
77 * Callback method to be invoked when the view hierarchy is attached to a window
78 */
79 public void onWindowAttached();
80
81 /**
82 * Callback method to be invoked when the view hierarchy is detached from a window
83 */
84 public void onWindowDetached();
85 }
86
87 /**
88 * Interface definition for a callback to be invoked when the view hierarchy's window
89 * focus state changes.
90 */
91 public interface OnWindowFocusChangeListener {
92 /**
93 * Callback method to be invoked when the window focus changes in the view tree.
94 *
95 * @param hasFocus Set to true if the window is gaining focus, false if it is
96 * losing focus.
97 */
98 public void onWindowFocusChanged(boolean hasFocus);
99 }
100
101 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 * Interface definition for a callback to be invoked when the focus state within
103 * the view tree changes.
104 */
105 public interface OnGlobalFocusChangeListener {
106 /**
107 * Callback method to be invoked when the focus changes in the view tree. When
108 * the view tree transitions from touch mode to non-touch mode, oldFocus is null.
109 * When the view tree transitions from non-touch mode to touch mode, newFocus is
110 * null. When focus changes in non-touch mode (without transition from or to
111 * touch mode) either oldFocus or newFocus can be null.
112 *
113 * @param oldFocus The previously focused view, if any.
114 * @param newFocus The newly focused View, if any.
115 */
116 public void onGlobalFocusChanged(View oldFocus, View newFocus);
117 }
118
119 /**
120 * Interface definition for a callback to be invoked when the global layout state
121 * or the visibility of views within the view tree changes.
122 */
123 public interface OnGlobalLayoutListener {
124 /**
125 * Callback method to be invoked when the global layout state or the visibility of views
126 * within the view tree changes
127 */
128 public void onGlobalLayout();
129 }
130
131 /**
132 * Interface definition for a callback to be invoked when the view tree is about to be drawn.
133 */
134 public interface OnPreDrawListener {
135 /**
136 * Callback method to be invoked when the view tree is about to be drawn. At this point, all
137 * views in the tree have been measured and given a frame. Clients can use this to adjust
138 * their scroll bounds or even to request a new layout before drawing occurs.
139 *
140 * @return Return true to proceed with the current drawing pass, or false to cancel.
141 *
142 * @see android.view.View#onMeasure
143 * @see android.view.View#onLayout
144 * @see android.view.View#onDraw
145 */
146 public boolean onPreDraw();
147 }
148
149 /**
Romain Guy25eba5c2012-04-04 17:29:03 -0700150 * Interface definition for a callback to be invoked when the view tree is about to be drawn.
151 */
152 public interface OnDrawListener {
153 /**
154 * <p>Callback method to be invoked when the view tree is about to be drawn. At this point,
155 * views cannot be modified in any way.</p>
156 *
157 * <p>Unlike with {@link OnPreDrawListener}, this method cannot be used to cancel the
158 * current drawing pass.</p>
159 *
160 * <p>An {@link OnDrawListener} listener <strong>cannot be added or removed</strong>
161 * from this method.</p>
162 *
163 * @see android.view.View#onMeasure
164 * @see android.view.View#onLayout
165 * @see android.view.View#onDraw
166 */
167 public void onDraw();
168 }
169
170 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 * Interface definition for a callback to be invoked when the touch mode changes.
172 */
173 public interface OnTouchModeChangeListener {
174 /**
175 * Callback method to be invoked when the touch mode changes.
176 *
177 * @param isInTouchMode True if the view hierarchy is now in touch mode, false otherwise.
178 */
179 public void onTouchModeChanged(boolean isInTouchMode);
180 }
181
182 /**
183 * Interface definition for a callback to be invoked when
184 * something in the view tree has been scrolled.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 */
186 public interface OnScrollChangedListener {
187 /**
188 * Callback method to be invoked when something in the view tree
189 * has been scrolled.
190 */
191 public void onScrollChanged();
192 }
193
194 /**
Craig Mautner9c795042014-10-28 19:59:59 -0700195 * Interface definition for a callback noting when a system window has been displayed.
196 * This is only used for non-Activity windows. Activity windows can use
197 * Activity.onEnterAnimationComplete() to get the same signal.
198 * @hide
199 */
200 public interface OnWindowShownListener {
201 /**
202 * Callback method to be invoked when a non-activity window is fully shown.
203 */
204 void onWindowShown();
205 }
206
207 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 * Parameters used with OnComputeInternalInsetsListener.
Dianne Hackborn935ae462009-04-13 16:11:55 -0700209 *
210 * We are not yet ready to commit to this API and support it, so
211 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 */
213 public final static class InternalInsetsInfo {
214 /**
215 * Offsets from the frame of the window at which the content of
216 * windows behind it should be placed.
217 */
218 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 */
224 public final Rect visibleInsets = new Rect();
Jeff Brownfbf09772011-01-16 14:06:57 -0800225
226 /**
227 * Touchable region defined relative to the origin of the frame of the window.
228 * Only used when {@link #setTouchableInsets(int)} is called with
229 * the option {@link #TOUCHABLE_INSETS_REGION}.
230 */
231 public final Region touchableRegion = new Region();
232
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 /**
234 * Option for {@link #setTouchableInsets(int)}: the entire window frame
235 * can be touched.
236 */
237 public static final int TOUCHABLE_INSETS_FRAME = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700238
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 /**
240 * Option for {@link #setTouchableInsets(int)}: the area inside of
241 * the content insets can be touched.
242 */
243 public static final int TOUCHABLE_INSETS_CONTENT = 1;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700244
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 /**
246 * Option for {@link #setTouchableInsets(int)}: the area inside of
247 * the visible insets can be touched.
248 */
249 public static final int TOUCHABLE_INSETS_VISIBLE = 2;
Jeff Brownfbf09772011-01-16 14:06:57 -0800250
251 /**
252 * Option for {@link #setTouchableInsets(int)}: the area inside of
253 * the provided touchable region in {@link #touchableRegion} can be touched.
254 */
255 public static final int TOUCHABLE_INSETS_REGION = 3;
256
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 /**
258 * Set which parts of the window can be touched: either
259 * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
Jeff Brownfbf09772011-01-16 14:06:57 -0800260 * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 */
262 public void setTouchableInsets(int val) {
263 mTouchableInsets = val;
264 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700265
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800266 int mTouchableInsets;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 void reset() {
Jeff Brownfbf09772011-01-16 14:06:57 -0800269 contentInsets.setEmpty();
270 visibleInsets.setEmpty();
271 touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 mTouchableInsets = TOUCHABLE_INSETS_FRAME;
273 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700274
Jeff Brown2e05ec32013-09-30 15:57:43 -0700275 boolean isEmpty() {
276 return contentInsets.isEmpty()
277 && visibleInsets.isEmpty()
278 && touchableRegion.isEmpty()
279 && mTouchableInsets == TOUCHABLE_INSETS_FRAME;
280 }
281
Romain Guy25eba5c2012-04-04 17:29:03 -0700282 @Override
283 public int hashCode() {
Romain Guy21f42302013-06-28 19:19:30 -0700284 int result = contentInsets.hashCode();
285 result = 31 * result + visibleInsets.hashCode();
286 result = 31 * result + touchableRegion.hashCode();
Romain Guy25eba5c2012-04-04 17:29:03 -0700287 result = 31 * result + mTouchableInsets;
288 return result;
289 }
290
Romain Guy1e878d22012-01-23 15:34:25 -0800291 @Override
292 public boolean equals(Object o) {
Romain Guy25eba5c2012-04-04 17:29:03 -0700293 if (this == o) return true;
294 if (o == null || getClass() != o.getClass()) return false;
295
296 InternalInsetsInfo other = (InternalInsetsInfo)o;
297 return mTouchableInsets == other.mTouchableInsets &&
298 contentInsets.equals(other.contentInsets) &&
299 visibleInsets.equals(other.visibleInsets) &&
300 touchableRegion.equals(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700302
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 void set(InternalInsetsInfo other) {
304 contentInsets.set(other.contentInsets);
305 visibleInsets.set(other.visibleInsets);
Jeff Brownfbf09772011-01-16 14:06:57 -0800306 touchableRegion.set(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 mTouchableInsets = other.mTouchableInsets;
308 }
309 }
Romain Guyc39ed4a2012-06-12 12:06:46 -0700310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 /**
312 * Interface definition for a callback to be invoked when layout has
313 * completed and the client can compute its interior insets.
Dianne Hackborn935ae462009-04-13 16:11:55 -0700314 *
315 * We are not yet ready to commit to this API and support it, so
316 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 */
318 public interface OnComputeInternalInsetsListener {
319 /**
320 * Callback method to be invoked when layout has completed and the
321 * client can compute its interior insets.
322 *
323 * @param inoutInfo Should be filled in by the implementation with
324 * the information about the insets of the window. This is called
325 * with whatever values the previous OnComputeInternalInsetsListener
326 * returned, if there are multiple such listeners in the window.
327 */
328 public void onComputeInternalInsets(InternalInsetsInfo inoutInfo);
329 }
330
331 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800332 * @hide
333 */
334 public interface OnEnterAnimationCompleteListener {
335 public void onEnterAnimationComplete();
336 }
337
338 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 * Creates a new ViewTreeObserver. This constructor should not be called
340 */
John Reckd94094e2016-09-08 14:12:26 -0700341 ViewTreeObserver(Context context) {
342 sIllegalOnDrawModificationIsFatal =
Jeff Sharkeyf383c242017-05-17 17:53:07 -0600343 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 }
345
346 /**
347 * Merges all the listeners registered on the specified observer with the listeners
348 * registered on this object. After this method is invoked, the specified observer
349 * will return false in {@link #isAlive()} and should not be used anymore.
350 *
351 * @param observer The ViewTreeObserver whose listeners must be added to this observer
352 */
353 void merge(ViewTreeObserver observer) {
Dianne Hackborn961cae92013-03-20 14:59:43 -0700354 if (observer.mOnWindowAttachListeners != null) {
355 if (mOnWindowAttachListeners != null) {
356 mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
357 } else {
358 mOnWindowAttachListeners = observer.mOnWindowAttachListeners;
359 }
360 }
361
362 if (observer.mOnWindowFocusListeners != null) {
363 if (mOnWindowFocusListeners != null) {
364 mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
365 } else {
366 mOnWindowFocusListeners = observer.mOnWindowFocusListeners;
367 }
368 }
369
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 if (observer.mOnGlobalFocusListeners != null) {
371 if (mOnGlobalFocusListeners != null) {
372 mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
373 } else {
374 mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;
375 }
376 }
377
378 if (observer.mOnGlobalLayoutListeners != null) {
379 if (mOnGlobalLayoutListeners != null) {
380 mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
381 } else {
382 mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;
383 }
384 }
385
386 if (observer.mOnPreDrawListeners != null) {
387 if (mOnPreDrawListeners != null) {
388 mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
389 } else {
390 mOnPreDrawListeners = observer.mOnPreDrawListeners;
391 }
392 }
393
John Reck9f8ec542016-12-12 11:23:05 -0800394 if (observer.mOnDrawListeners != null) {
395 if (mOnDrawListeners != null) {
396 mOnDrawListeners.addAll(observer.mOnDrawListeners);
397 } else {
398 mOnDrawListeners = observer.mOnDrawListeners;
399 }
400 }
401
John Reck9ab32f82018-07-26 11:02:53 -0700402 if (observer.mOnFrameCommitListeners != null) {
403 if (mOnFrameCommitListeners != null) {
404 mOnFrameCommitListeners.addAll(observer.captureFrameCommitCallbacks());
405 } else {
406 mOnFrameCommitListeners = observer.captureFrameCommitCallbacks();
407 }
408 }
409
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 if (observer.mOnTouchModeChangeListeners != null) {
411 if (mOnTouchModeChangeListeners != null) {
412 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
413 } else {
414 mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;
415 }
416 }
417
418 if (observer.mOnComputeInternalInsetsListeners != null) {
419 if (mOnComputeInternalInsetsListeners != null) {
420 mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
421 } else {
422 mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;
423 }
424 }
425
Mark Brophy757c6972011-10-25 17:01:28 +0100426 if (observer.mOnScrollChangedListeners != null) {
427 if (mOnScrollChangedListeners != null) {
428 mOnScrollChangedListeners.addAll(observer.mOnScrollChangedListeners);
429 } else {
430 mOnScrollChangedListeners = observer.mOnScrollChangedListeners;
431 }
432 }
433
Craig Mautner9c795042014-10-28 19:59:59 -0700434 if (observer.mOnWindowShownListeners != null) {
435 if (mOnWindowShownListeners != null) {
436 mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
437 } else {
438 mOnWindowShownListeners = observer.mOnWindowShownListeners;
439 }
440 }
441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 observer.kill();
443 }
444
445 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700446 * Register a callback to be invoked when the view hierarchy is attached to a window.
447 *
448 * @param listener The callback to add
449 *
450 * @throws IllegalStateException If {@link #isAlive()} returns false
451 */
452 public void addOnWindowAttachListener(OnWindowAttachListener listener) {
453 checkIsAlive();
454
455 if (mOnWindowAttachListeners == null) {
456 mOnWindowAttachListeners
457 = new CopyOnWriteArrayList<OnWindowAttachListener>();
458 }
459
460 mOnWindowAttachListeners.add(listener);
461 }
462
463 /**
464 * Remove a previously installed window attach callback.
465 *
466 * @param victim The callback to remove
467 *
468 * @throws IllegalStateException If {@link #isAlive()} returns false
469 *
470 * @see #addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener)
471 */
472 public void removeOnWindowAttachListener(OnWindowAttachListener victim) {
473 checkIsAlive();
474 if (mOnWindowAttachListeners == null) {
475 return;
476 }
477 mOnWindowAttachListeners.remove(victim);
478 }
479
480 /**
481 * Register a callback to be invoked when the window focus state within the view tree changes.
482 *
483 * @param listener The callback to add
484 *
485 * @throws IllegalStateException If {@link #isAlive()} returns false
486 */
487 public void addOnWindowFocusChangeListener(OnWindowFocusChangeListener listener) {
488 checkIsAlive();
489
490 if (mOnWindowFocusListeners == null) {
491 mOnWindowFocusListeners
492 = new CopyOnWriteArrayList<OnWindowFocusChangeListener>();
493 }
494
495 mOnWindowFocusListeners.add(listener);
496 }
497
498 /**
499 * Remove a previously installed window focus change callback.
500 *
501 * @param victim The callback to remove
502 *
503 * @throws IllegalStateException If {@link #isAlive()} returns false
504 *
505 * @see #addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener)
506 */
507 public void removeOnWindowFocusChangeListener(OnWindowFocusChangeListener victim) {
508 checkIsAlive();
509 if (mOnWindowFocusListeners == null) {
510 return;
511 }
512 mOnWindowFocusListeners.remove(victim);
513 }
514
515 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 * Register a callback to be invoked when the focus state within the view tree changes.
517 *
518 * @param listener The callback to add
519 *
520 * @throws IllegalStateException If {@link #isAlive()} returns false
521 */
522 public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener) {
523 checkIsAlive();
524
525 if (mOnGlobalFocusListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700526 mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 }
528
529 mOnGlobalFocusListeners.add(listener);
530 }
531
532 /**
533 * Remove a previously installed focus change callback.
534 *
535 * @param victim The callback to remove
536 *
537 * @throws IllegalStateException If {@link #isAlive()} returns false
538 *
539 * @see #addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener)
540 */
541 public void removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim) {
542 checkIsAlive();
543 if (mOnGlobalFocusListeners == null) {
544 return;
545 }
546 mOnGlobalFocusListeners.remove(victim);
547 }
548
549 /**
550 * Register a callback to be invoked when the global layout state or the visibility of views
551 * within the view tree changes
552 *
553 * @param listener The callback to add
554 *
555 * @throws IllegalStateException If {@link #isAlive()} returns false
556 */
557 public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
558 checkIsAlive();
559
560 if (mOnGlobalLayoutListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700561 mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 }
563
564 mOnGlobalLayoutListeners.add(listener);
565 }
566
567 /**
568 * Remove a previously installed global layout callback
569 *
570 * @param victim The callback to remove
571 *
572 * @throws IllegalStateException If {@link #isAlive()} returns false
Romain Guy1e878d22012-01-23 15:34:25 -0800573 *
574 * @deprecated Use #removeOnGlobalLayoutListener instead
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 *
576 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
577 */
Romain Guy1e878d22012-01-23 15:34:25 -0800578 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
Romain Guy1e878d22012-01-23 15:34:25 -0800580 removeOnGlobalLayoutListener(victim);
581 }
582
583 /**
584 * Remove a previously installed global layout callback
585 *
586 * @param victim The callback to remove
587 *
588 * @throws IllegalStateException If {@link #isAlive()} returns false
589 *
590 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
591 */
592 public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 checkIsAlive();
594 if (mOnGlobalLayoutListeners == null) {
595 return;
596 }
597 mOnGlobalLayoutListeners.remove(victim);
598 }
599
600 /**
601 * Register a callback to be invoked when the view tree is about to be drawn
602 *
603 * @param listener The callback to add
604 *
605 * @throws IllegalStateException If {@link #isAlive()} returns false
606 */
607 public void addOnPreDrawListener(OnPreDrawListener listener) {
608 checkIsAlive();
609
610 if (mOnPreDrawListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700611 mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 }
613
614 mOnPreDrawListeners.add(listener);
615 }
616
617 /**
618 * Remove a previously installed pre-draw callback
619 *
620 * @param victim The callback to remove
621 *
622 * @throws IllegalStateException If {@link #isAlive()} returns false
623 *
624 * @see #addOnPreDrawListener(OnPreDrawListener)
625 */
626 public void removeOnPreDrawListener(OnPreDrawListener victim) {
627 checkIsAlive();
628 if (mOnPreDrawListeners == null) {
629 return;
630 }
631 mOnPreDrawListeners.remove(victim);
632 }
633
634 /**
Craig Mautner9c795042014-10-28 19:59:59 -0700635 * Register a callback to be invoked when the view tree window has been shown
636 *
637 * @param listener The callback to add
638 *
639 * @throws IllegalStateException If {@link #isAlive()} returns false
640 * @hide
641 */
642 public void addOnWindowShownListener(OnWindowShownListener listener) {
643 checkIsAlive();
644
645 if (mOnWindowShownListeners == null) {
646 mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>();
647 }
648
649 mOnWindowShownListeners.add(listener);
650 if (mWindowShown) {
651 listener.onWindowShown();
652 }
653 }
654
655 /**
656 * Remove a previously installed window shown callback
657 *
658 * @param victim The callback to remove
659 *
660 * @throws IllegalStateException If {@link #isAlive()} returns false
661 *
662 * @see #addOnWindowShownListener(OnWindowShownListener)
663 * @hide
664 */
665 public void removeOnWindowShownListener(OnWindowShownListener victim) {
666 checkIsAlive();
667 if (mOnWindowShownListeners == null) {
668 return;
669 }
670 mOnWindowShownListeners.remove(victim);
671 }
672
673 /**
Romain Guy25eba5c2012-04-04 17:29:03 -0700674 * <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
675 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
676 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
677 *
678 * @param listener The callback to add
679 *
680 * @throws IllegalStateException If {@link #isAlive()} returns false
681 */
682 public void addOnDrawListener(OnDrawListener listener) {
683 checkIsAlive();
684
685 if (mOnDrawListeners == null) {
686 mOnDrawListeners = new ArrayList<OnDrawListener>();
687 }
688
John Reckd94094e2016-09-08 14:12:26 -0700689 if (mInDispatchOnDraw) {
690 IllegalStateException ex = new IllegalStateException(
691 "Cannot call addOnDrawListener inside of onDraw");
692 if (sIllegalOnDrawModificationIsFatal) {
693 throw ex;
694 } else {
695 Log.e("ViewTreeObserver", ex.getMessage(), ex);
696 }
697 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700698 mOnDrawListeners.add(listener);
699 }
700
701 /**
702 * <p>Remove a previously installed pre-draw callback.</p>
703 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
704 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
705 *
706 * @param victim The callback to remove
707 *
708 * @throws IllegalStateException If {@link #isAlive()} returns false
709 *
710 * @see #addOnDrawListener(OnDrawListener)
711 */
712 public void removeOnDrawListener(OnDrawListener victim) {
713 checkIsAlive();
714 if (mOnDrawListeners == null) {
715 return;
716 }
John Reckd94094e2016-09-08 14:12:26 -0700717 if (mInDispatchOnDraw) {
718 IllegalStateException ex = new IllegalStateException(
719 "Cannot call removeOnDrawListener inside of onDraw");
720 if (sIllegalOnDrawModificationIsFatal) {
721 throw ex;
722 } else {
723 Log.e("ViewTreeObserver", ex.getMessage(), ex);
724 }
725 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700726 mOnDrawListeners.remove(victim);
727 }
728
729 /**
John Reck9ab32f82018-07-26 11:02:53 -0700730 * Adds a frame commit callback. This callback will be invoked when the current rendering
731 * content has been rendered into a frame and submitted to the swap chain. The frame may
732 * not currently be visible on the display when this is invoked, but it has been submitted.
733 * This callback is useful in combination with {@link PixelCopy} to capture the current
734 * rendered content of the UI reliably.
735 *
736 * Note: Only works with hardware rendering. Does nothing otherwise.
737 *
738 * @param callback The callback to invoke when the frame is committed.
739 */
740 @TestApi
741 public void registerFrameCommitCallback(@NonNull Runnable callback) {
742 checkIsAlive();
743 if (mOnFrameCommitListeners == null) {
744 mOnFrameCommitListeners = new ArrayList<>();
745 }
746 mOnFrameCommitListeners.add(callback);
747 }
748
749 @Nullable ArrayList<Runnable> captureFrameCommitCallbacks() {
750 ArrayList<Runnable> ret = mOnFrameCommitListeners;
751 mOnFrameCommitListeners = null;
752 return ret;
753 }
754
755 /**
756 * Attempts to remove the given callback from the list of pending frame complete callbacks.
757 *
758 * @param callback The callback to remove
759 * @return Whether or not the callback was removed. If this returns true the callback will
760 * not be invoked. If false is returned then the callback was either never added
761 * or may already be pending execution and was unable to be removed
762 */
763 @TestApi
764 public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) {
765 checkIsAlive();
766 if (mOnFrameCommitListeners == null) {
767 return false;
768 }
769 return mOnFrameCommitListeners.remove(callback);
770 }
771
772 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 * Register a callback to be invoked when a view has been scrolled.
774 *
775 * @param listener The callback to add
776 *
777 * @throws IllegalStateException If {@link #isAlive()} returns false
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 */
779 public void addOnScrollChangedListener(OnScrollChangedListener listener) {
780 checkIsAlive();
781
782 if (mOnScrollChangedListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700783 mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 }
785
786 mOnScrollChangedListeners.add(listener);
787 }
788
789 /**
790 * Remove a previously installed scroll-changed callback
791 *
792 * @param victim The callback to remove
793 *
794 * @throws IllegalStateException If {@link #isAlive()} returns false
795 *
796 * @see #addOnScrollChangedListener(OnScrollChangedListener)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 */
798 public void removeOnScrollChangedListener(OnScrollChangedListener victim) {
799 checkIsAlive();
800 if (mOnScrollChangedListeners == null) {
801 return;
802 }
803 mOnScrollChangedListeners.remove(victim);
804 }
805
806 /**
807 * Register a callback to be invoked when the invoked when the touch mode changes.
808 *
809 * @param listener The callback to add
810 *
811 * @throws IllegalStateException If {@link #isAlive()} returns false
812 */
813 public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
814 checkIsAlive();
815
816 if (mOnTouchModeChangeListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700817 mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 }
819
820 mOnTouchModeChangeListeners.add(listener);
821 }
822
823 /**
824 * Remove a previously installed touch mode change callback
825 *
826 * @param victim The callback to remove
827 *
828 * @throws IllegalStateException If {@link #isAlive()} returns false
829 *
830 * @see #addOnTouchModeChangeListener(OnTouchModeChangeListener)
831 */
832 public void removeOnTouchModeChangeListener(OnTouchModeChangeListener victim) {
833 checkIsAlive();
834 if (mOnTouchModeChangeListeners == null) {
835 return;
836 }
837 mOnTouchModeChangeListeners.remove(victim);
838 }
839
840 /**
841 * Register a callback to be invoked when the invoked when it is time to
842 * compute the window's internal insets.
843 *
844 * @param listener The callback to add
845 *
846 * @throws IllegalStateException If {@link #isAlive()} returns false
Dianne Hackborn935ae462009-04-13 16:11:55 -0700847 *
848 * We are not yet ready to commit to this API and support it, so
849 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 */
851 public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener) {
852 checkIsAlive();
853
854 if (mOnComputeInternalInsetsListeners == null) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700855 mOnComputeInternalInsetsListeners =
Romain Guyc39ed4a2012-06-12 12:06:46 -0700856 new CopyOnWriteArray<OnComputeInternalInsetsListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 }
858
859 mOnComputeInternalInsetsListeners.add(listener);
860 }
861
862 /**
863 * Remove a previously installed internal insets computation callback
864 *
865 * @param victim The callback to remove
866 *
867 * @throws IllegalStateException If {@link #isAlive()} returns false
868 *
869 * @see #addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener)
Dianne Hackborn935ae462009-04-13 16:11:55 -0700870 *
871 * We are not yet ready to commit to this API and support it, so
872 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 */
874 public void removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim) {
875 checkIsAlive();
876 if (mOnComputeInternalInsetsListeners == null) {
877 return;
878 }
879 mOnComputeInternalInsetsListeners.remove(victim);
880 }
881
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800882 /**
883 * @hide
884 */
885 public void addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
886 checkIsAlive();
887 if (mOnEnterAnimationCompleteListeners == null) {
888 mOnEnterAnimationCompleteListeners =
889 new CopyOnWriteArrayList<OnEnterAnimationCompleteListener>();
890 }
891 mOnEnterAnimationCompleteListeners.add(listener);
892 }
893
894 /**
895 * @hide
896 */
897 public void removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
898 checkIsAlive();
899 if (mOnEnterAnimationCompleteListeners == null) {
900 return;
901 }
902 mOnEnterAnimationCompleteListeners.remove(listener);
903 }
904
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 private void checkIsAlive() {
906 if (!mAlive) {
907 throw new IllegalStateException("This ViewTreeObserver is not alive, call "
908 + "getViewTreeObserver() again");
909 }
910 }
911
912 /**
913 * Indicates whether this ViewTreeObserver is alive. When an observer is not alive,
914 * any call to a method (except this one) will throw an exception.
915 *
916 * If an application keeps a long-lived reference to this ViewTreeObserver, it should
917 * always check for the result of this method before calling any other method.
918 *
919 * @return True if this object is alive and be used, false otherwise.
920 */
921 public boolean isAlive() {
922 return mAlive;
923 }
924
925 /**
926 * Marks this ViewTreeObserver as not alive. After invoking this method, invoking
927 * any other method but {@link #isAlive()} and {@link #kill()} will throw an Exception.
928 *
929 * @hide
930 */
931 private void kill() {
932 mAlive = false;
933 }
934
935 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700936 * Notifies registered listeners that window has been attached/detached.
937 */
938 final void dispatchOnWindowAttachedChange(boolean attached) {
939 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
940 // perform the dispatching. The iterator is a safe guard against listeners that
941 // could mutate the list by calling the various add/remove methods. This prevents
942 // the array from being modified while we iterate it.
943 final CopyOnWriteArrayList<OnWindowAttachListener> listeners
944 = mOnWindowAttachListeners;
945 if (listeners != null && listeners.size() > 0) {
946 for (OnWindowAttachListener listener : listeners) {
947 if (attached) listener.onWindowAttached();
948 else listener.onWindowDetached();
949 }
950 }
951 }
952
953 /**
954 * Notifies registered listeners that window focus has changed.
955 */
956 final void dispatchOnWindowFocusChange(boolean hasFocus) {
957 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
958 // perform the dispatching. The iterator is a safe guard against listeners that
959 // could mutate the list by calling the various add/remove methods. This prevents
960 // the array from being modified while we iterate it.
961 final CopyOnWriteArrayList<OnWindowFocusChangeListener> listeners
962 = mOnWindowFocusListeners;
963 if (listeners != null && listeners.size() > 0) {
964 for (OnWindowFocusChangeListener listener : listeners) {
965 listener.onWindowFocusChanged(hasFocus);
966 }
967 }
968 }
969
970 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971 * Notifies registered listeners that focus has changed.
972 */
973 final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700974 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
975 // perform the dispatching. The iterator is a safe guard against listeners that
976 // could mutate the list by calling the various add/remove methods. This prevents
977 // the array from being modified while we iterate it.
Chet Haase0f8ffd82012-06-07 07:48:48 -0700978 final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700979 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700980 for (OnGlobalFocusChangeListener listener : listeners) {
981 listener.onGlobalFocusChanged(oldFocus, newFocus);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 }
983 }
984 }
985
986 /**
987 * Notifies registered listeners that a global layout happened. This can be called
988 * manually if you are forcing a layout on a View or a hierarchy of Views that are
989 * not attached to a Window or in the GONE state.
990 */
991 public final void dispatchOnGlobalLayout() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700992 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
993 // perform the dispatching. The iterator is a safe guard against listeners that
994 // could mutate the list by calling the various add/remove methods. This prevents
995 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -0700996 final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700997 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700998 CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
999 try {
1000 int count = access.size();
1001 for (int i = 0; i < count; i++) {
1002 access.get(i).onGlobalLayout();
1003 }
1004 } finally {
1005 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006 }
1007 }
1008 }
1009
1010 /**
Romain Guy21f42302013-06-28 19:19:30 -07001011 * Returns whether there are listeners for on pre-draw events.
1012 */
1013 final boolean hasOnPreDrawListeners() {
1014 return mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0;
1015 }
1016
1017 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 * Notifies registered listeners that the drawing pass is about to start. If a
1019 * listener returns true, then the drawing pass is canceled and rescheduled. This can
1020 * be called manually if you are forcing the drawing on a View or a hierarchy of Views
1021 * that are not attached to a Window or in the GONE state.
1022 *
1023 * @return True if the current draw should be canceled and resceduled, false otherwise.
1024 */
Romain Guy25eba5c2012-04-04 17:29:03 -07001025 @SuppressWarnings("unchecked")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 public final boolean dispatchOnPreDraw() {
1027 boolean cancelDraw = false;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001028 final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
1029 if (listeners != null && listeners.size() > 0) {
1030 CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();
1031 try {
1032 int count = access.size();
1033 for (int i = 0; i < count; i++) {
1034 cancelDraw |= !(access.get(i).onPreDraw());
1035 }
1036 } finally {
1037 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 }
1039 }
1040 return cancelDraw;
1041 }
1042
1043 /**
Craig Mautner9c795042014-10-28 19:59:59 -07001044 * Notifies registered listeners that the window is now shown
1045 * @hide
1046 */
1047 @SuppressWarnings("unchecked")
1048 public final void dispatchOnWindowShown() {
1049 mWindowShown = true;
1050 final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners;
1051 if (listeners != null && listeners.size() > 0) {
1052 CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start();
1053 try {
1054 int count = access.size();
1055 for (int i = 0; i < count; i++) {
1056 access.get(i).onWindowShown();
1057 }
1058 } finally {
1059 listeners.end();
1060 }
1061 }
1062 }
1063
1064 /**
Romain Guy25eba5c2012-04-04 17:29:03 -07001065 * Notifies registered listeners that the drawing pass is about to start.
1066 */
1067 public final void dispatchOnDraw() {
1068 if (mOnDrawListeners != null) {
John Reckd94094e2016-09-08 14:12:26 -07001069 mInDispatchOnDraw = true;
Romain Guy25eba5c2012-04-04 17:29:03 -07001070 final ArrayList<OnDrawListener> listeners = mOnDrawListeners;
1071 int numListeners = listeners.size();
1072 for (int i = 0; i < numListeners; ++i) {
1073 listeners.get(i).onDraw();
1074 }
John Reckd94094e2016-09-08 14:12:26 -07001075 mInDispatchOnDraw = false;
Romain Guy25eba5c2012-04-04 17:29:03 -07001076 }
1077 }
1078
1079 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 * Notifies registered listeners that the touch mode has changed.
1081 *
1082 * @param inTouchMode True if the touch mode is now enabled, false otherwise.
1083 */
1084 final void dispatchOnTouchModeChanged(boolean inTouchMode) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001085 final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001086 mOnTouchModeChangeListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001087 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001088 for (OnTouchModeChangeListener listener : listeners) {
1089 listener.onTouchModeChanged(inTouchMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 }
1091 }
1092 }
1093
1094 /**
1095 * Notifies registered listeners that something has scrolled.
1096 */
1097 final void dispatchOnScrollChanged() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001098 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1099 // perform the dispatching. The iterator is a safe guard against listeners that
1100 // could mutate the list by calling the various add/remove methods. This prevents
1101 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001102 final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001103 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001104 CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start();
1105 try {
1106 int count = access.size();
1107 for (int i = 0; i < count; i++) {
1108 access.get(i).onScrollChanged();
1109 }
1110 } finally {
1111 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112 }
1113 }
1114 }
1115
1116 /**
1117 * Returns whether there are listeners for computing internal insets.
1118 */
1119 final boolean hasComputeInternalInsetsListeners() {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001120 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001121 mOnComputeInternalInsetsListeners;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 return (listeners != null && listeners.size() > 0);
1123 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 /**
1126 * Calls all listeners to compute the current insets.
1127 */
1128 final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001129 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1130 // perform the dispatching. The iterator is a safe guard against listeners that
1131 // could mutate the list by calling the various add/remove methods. This prevents
1132 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001133 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001134 mOnComputeInternalInsetsListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001135 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001136 CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
1137 try {
1138 int count = access.size();
1139 for (int i = 0; i < count; i++) {
1140 access.get(i).onComputeInternalInsets(inoutInfo);
1141 }
1142 } finally {
1143 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 }
1145 }
1146 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001147
1148 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -08001149 * @hide
1150 */
1151 public final void dispatchOnEnterAnimationComplete() {
1152 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1153 // perform the dispatching. The iterator is a safe guard against listeners that
1154 // could mutate the list by calling the various add/remove methods. This prevents
1155 // the array from being modified while we iterate it.
1156 final CopyOnWriteArrayList<OnEnterAnimationCompleteListener> listeners =
1157 mOnEnterAnimationCompleteListeners;
1158 if (listeners != null && !listeners.isEmpty()) {
1159 for (OnEnterAnimationCompleteListener listener : listeners) {
1160 listener.onEnterAnimationComplete();
1161 }
1162 }
1163 }
1164
1165 /**
Romain Guyc39ed4a2012-06-12 12:06:46 -07001166 * Copy on write array. This array is not thread safe, and only one loop can
1167 * iterate over this array at any given time. This class avoids allocations
1168 * until a concurrent modification happens.
1169 *
1170 * Usage:
1171 *
1172 * CopyOnWriteArray.Access<MyData> access = array.start();
1173 * try {
1174 * for (int i = 0; i < access.size(); i++) {
1175 * MyData d = access.get(i);
1176 * }
1177 * } finally {
1178 * access.end();
1179 * }
1180 */
1181 static class CopyOnWriteArray<T> {
1182 private ArrayList<T> mData = new ArrayList<T>();
1183 private ArrayList<T> mDataCopy;
1184
1185 private final Access<T> mAccess = new Access<T>();
1186
1187 private boolean mStart;
1188
1189 static class Access<T> {
1190 private ArrayList<T> mData;
1191 private int mSize;
1192
1193 T get(int index) {
1194 return mData.get(index);
1195 }
1196
1197 int size() {
1198 return mSize;
1199 }
1200 }
1201
1202 CopyOnWriteArray() {
1203 }
1204
1205 private ArrayList<T> getArray() {
1206 if (mStart) {
1207 if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData);
1208 return mDataCopy;
1209 }
1210 return mData;
1211 }
1212
1213 Access<T> start() {
1214 if (mStart) throw new IllegalStateException("Iteration already started");
1215 mStart = true;
1216 mDataCopy = null;
1217 mAccess.mData = mData;
1218 mAccess.mSize = mData.size();
1219 return mAccess;
1220 }
1221
1222 void end() {
1223 if (!mStart) throw new IllegalStateException("Iteration not started");
1224 mStart = false;
1225 if (mDataCopy != null) {
1226 mData = mDataCopy;
Chet Haasefc343962013-09-18 08:44:33 -07001227 mAccess.mData.clear();
1228 mAccess.mSize = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001229 }
1230 mDataCopy = null;
1231 }
1232
1233 int size() {
1234 return getArray().size();
1235 }
1236
1237 void add(T item) {
1238 getArray().add(item);
1239 }
1240
1241 void addAll(CopyOnWriteArray<T> array) {
1242 getArray().addAll(array.mData);
1243 }
1244
1245 void remove(T item) {
1246 getArray().remove(item);
1247 }
1248
1249 void clear() {
1250 getArray().clear();
1251 }
1252 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253}