blob: 763ce4f45b01afd7a16f178c507be20a21b9a214 [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;
Mathew Inwooda570dee2018-08-17 14:56:00 +010021import android.annotation.UnsupportedAppUsage;
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;
Mathew Inwooda570dee2018-08-17 14:56:00 +010045 @UnsupportedAppUsage
Chet Haase0f8ffd82012-06-07 07:48:48 -070046 private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
Filip Gruszczynski6eafa902014-11-14 14:24:37 -080047 private CopyOnWriteArrayList<OnEnterAnimationCompleteListener>
48 mOnEnterAnimationCompleteListeners;
Romain Guyc39ed4a2012-06-12 12:06:46 -070049
50 // Non-recursive listeners use CopyOnWriteArray
51 // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
Mathew Inwooda570dee2018-08-17 14:56:00 +010052 @UnsupportedAppUsage
Romain Guyc39ed4a2012-06-12 12:06:46 -070053 private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
Mathew Inwooda570dee2018-08-17 14:56:00 +010054 @UnsupportedAppUsage
Romain Guyc39ed4a2012-06-12 12:06:46 -070055 private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
Mathew Inwooda570dee2018-08-17 14:56:00 +010056 @UnsupportedAppUsage
Romain Guyc39ed4a2012-06-12 12:06:46 -070057 private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
58 private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
Craig Mautner9c795042014-10-28 19:59:59 -070059 private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners;
Romain Guyc39ed4a2012-06-12 12:06:46 -070060
61 // These listeners cannot be mutated during dispatch
John Reckd94094e2016-09-08 14:12:26 -070062 private boolean mInDispatchOnDraw;
Romain Guy25eba5c2012-04-04 17:29:03 -070063 private ArrayList<OnDrawListener> mOnDrawListeners;
John Reckd94094e2016-09-08 14:12:26 -070064 private static boolean sIllegalOnDrawModificationIsFatal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
John Reck9ab32f82018-07-26 11:02:53 -070066 // These listeners are one-shot
67 private ArrayList<Runnable> mOnFrameCommitListeners;
68
Craig Mautner9c795042014-10-28 19:59:59 -070069 /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
70 * that the listener will be immediately called. */
71 private boolean mWindowShown;
72
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 private boolean mAlive = true;
74
75 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -070076 * Interface definition for a callback to be invoked when the view hierarchy is
77 * attached to and detached from its window.
78 */
79 public interface OnWindowAttachListener {
80 /**
81 * Callback method to be invoked when the view hierarchy is attached to a window
82 */
83 public void onWindowAttached();
84
85 /**
86 * Callback method to be invoked when the view hierarchy is detached from a window
87 */
88 public void onWindowDetached();
89 }
90
91 /**
92 * Interface definition for a callback to be invoked when the view hierarchy's window
93 * focus state changes.
94 */
95 public interface OnWindowFocusChangeListener {
96 /**
97 * Callback method to be invoked when the window focus changes in the view tree.
98 *
99 * @param hasFocus Set to true if the window is gaining focus, false if it is
100 * losing focus.
101 */
102 public void onWindowFocusChanged(boolean hasFocus);
103 }
104
105 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 * Interface definition for a callback to be invoked when the focus state within
107 * the view tree changes.
108 */
109 public interface OnGlobalFocusChangeListener {
110 /**
111 * Callback method to be invoked when the focus changes in the view tree. When
112 * the view tree transitions from touch mode to non-touch mode, oldFocus is null.
113 * When the view tree transitions from non-touch mode to touch mode, newFocus is
114 * null. When focus changes in non-touch mode (without transition from or to
115 * touch mode) either oldFocus or newFocus can be null.
116 *
117 * @param oldFocus The previously focused view, if any.
118 * @param newFocus The newly focused View, if any.
119 */
120 public void onGlobalFocusChanged(View oldFocus, View newFocus);
121 }
122
123 /**
124 * Interface definition for a callback to be invoked when the global layout state
125 * or the visibility of views within the view tree changes.
126 */
127 public interface OnGlobalLayoutListener {
128 /**
129 * Callback method to be invoked when the global layout state or the visibility of views
130 * within the view tree changes
131 */
132 public void onGlobalLayout();
133 }
134
135 /**
136 * Interface definition for a callback to be invoked when the view tree is about to be drawn.
137 */
138 public interface OnPreDrawListener {
139 /**
140 * Callback method to be invoked when the view tree is about to be drawn. At this point, all
141 * views in the tree have been measured and given a frame. Clients can use this to adjust
142 * their scroll bounds or even to request a new layout before drawing occurs.
143 *
144 * @return Return true to proceed with the current drawing pass, or false to cancel.
145 *
146 * @see android.view.View#onMeasure
147 * @see android.view.View#onLayout
148 * @see android.view.View#onDraw
149 */
150 public boolean onPreDraw();
151 }
152
153 /**
Romain Guy25eba5c2012-04-04 17:29:03 -0700154 * Interface definition for a callback to be invoked when the view tree is about to be drawn.
155 */
156 public interface OnDrawListener {
157 /**
158 * <p>Callback method to be invoked when the view tree is about to be drawn. At this point,
159 * views cannot be modified in any way.</p>
160 *
161 * <p>Unlike with {@link OnPreDrawListener}, this method cannot be used to cancel the
162 * current drawing pass.</p>
163 *
164 * <p>An {@link OnDrawListener} listener <strong>cannot be added or removed</strong>
165 * from this method.</p>
166 *
167 * @see android.view.View#onMeasure
168 * @see android.view.View#onLayout
169 * @see android.view.View#onDraw
170 */
171 public void onDraw();
172 }
173
174 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 * Interface definition for a callback to be invoked when the touch mode changes.
176 */
177 public interface OnTouchModeChangeListener {
178 /**
179 * Callback method to be invoked when the touch mode changes.
180 *
181 * @param isInTouchMode True if the view hierarchy is now in touch mode, false otherwise.
182 */
183 public void onTouchModeChanged(boolean isInTouchMode);
184 }
185
186 /**
187 * Interface definition for a callback to be invoked when
188 * something in the view tree has been scrolled.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 */
190 public interface OnScrollChangedListener {
191 /**
192 * Callback method to be invoked when something in the view tree
193 * has been scrolled.
194 */
195 public void onScrollChanged();
196 }
197
198 /**
Craig Mautner9c795042014-10-28 19:59:59 -0700199 * Interface definition for a callback noting when a system window has been displayed.
200 * This is only used for non-Activity windows. Activity windows can use
201 * Activity.onEnterAnimationComplete() to get the same signal.
202 * @hide
203 */
204 public interface OnWindowShownListener {
205 /**
206 * Callback method to be invoked when a non-activity window is fully shown.
207 */
208 void onWindowShown();
209 }
210
211 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 * Parameters used with OnComputeInternalInsetsListener.
Dianne Hackborn935ae462009-04-13 16:11:55 -0700213 *
214 * We are not yet ready to commit to this API and support it, so
215 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 */
217 public final static class InternalInsetsInfo {
218 /**
219 * Offsets from the frame of the window at which the content of
220 * windows behind it should be placed.
221 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100222 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 public final Rect contentInsets = new Rect();
Romain Guyc39ed4a2012-06-12 12:06:46 -0700224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 /**
Jeff Brownfbf09772011-01-16 14:06:57 -0800226 * Offsets from the frame of the window at which windows behind it
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 * are visible.
228 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100229 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 public final Rect visibleInsets = new Rect();
Jeff Brownfbf09772011-01-16 14:06:57 -0800231
232 /**
233 * Touchable region defined relative to the origin of the frame of the window.
234 * Only used when {@link #setTouchableInsets(int)} is called with
235 * the option {@link #TOUCHABLE_INSETS_REGION}.
236 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100237 @UnsupportedAppUsage
Jeff Brownfbf09772011-01-16 14:06:57 -0800238 public final Region touchableRegion = new Region();
239
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 /**
241 * Option for {@link #setTouchableInsets(int)}: the entire window frame
242 * can be touched.
243 */
244 public static final int TOUCHABLE_INSETS_FRAME = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700245
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 /**
247 * Option for {@link #setTouchableInsets(int)}: the area inside of
248 * the content insets can be touched.
249 */
250 public static final int TOUCHABLE_INSETS_CONTENT = 1;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700251
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 /**
253 * Option for {@link #setTouchableInsets(int)}: the area inside of
254 * the visible insets can be touched.
255 */
256 public static final int TOUCHABLE_INSETS_VISIBLE = 2;
Jeff Brownfbf09772011-01-16 14:06:57 -0800257
258 /**
259 * Option for {@link #setTouchableInsets(int)}: the area inside of
260 * the provided touchable region in {@link #touchableRegion} can be touched.
261 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100262 @UnsupportedAppUsage
Jeff Brownfbf09772011-01-16 14:06:57 -0800263 public static final int TOUCHABLE_INSETS_REGION = 3;
264
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 /**
266 * Set which parts of the window can be touched: either
267 * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
Jeff Brownfbf09772011-01-16 14:06:57 -0800268 * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100270 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 public void setTouchableInsets(int val) {
272 mTouchableInsets = val;
273 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700274
Mathew Inwooda570dee2018-08-17 14:56:00 +0100275 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276 int mTouchableInsets;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 void reset() {
Jeff Brownfbf09772011-01-16 14:06:57 -0800279 contentInsets.setEmpty();
280 visibleInsets.setEmpty();
281 touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 mTouchableInsets = TOUCHABLE_INSETS_FRAME;
283 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700284
Jeff Brown2e05ec32013-09-30 15:57:43 -0700285 boolean isEmpty() {
286 return contentInsets.isEmpty()
287 && visibleInsets.isEmpty()
288 && touchableRegion.isEmpty()
289 && mTouchableInsets == TOUCHABLE_INSETS_FRAME;
290 }
291
Romain Guy25eba5c2012-04-04 17:29:03 -0700292 @Override
293 public int hashCode() {
Romain Guy21f42302013-06-28 19:19:30 -0700294 int result = contentInsets.hashCode();
295 result = 31 * result + visibleInsets.hashCode();
296 result = 31 * result + touchableRegion.hashCode();
Romain Guy25eba5c2012-04-04 17:29:03 -0700297 result = 31 * result + mTouchableInsets;
298 return result;
299 }
300
Romain Guy1e878d22012-01-23 15:34:25 -0800301 @Override
302 public boolean equals(Object o) {
Romain Guy25eba5c2012-04-04 17:29:03 -0700303 if (this == o) return true;
304 if (o == null || getClass() != o.getClass()) return false;
305
306 InternalInsetsInfo other = (InternalInsetsInfo)o;
307 return mTouchableInsets == other.mTouchableInsets &&
308 contentInsets.equals(other.contentInsets) &&
309 visibleInsets.equals(other.visibleInsets) &&
310 touchableRegion.equals(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700312
Mathew Inwooda570dee2018-08-17 14:56:00 +0100313 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 void set(InternalInsetsInfo other) {
315 contentInsets.set(other.contentInsets);
316 visibleInsets.set(other.visibleInsets);
Jeff Brownfbf09772011-01-16 14:06:57 -0800317 touchableRegion.set(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 mTouchableInsets = other.mTouchableInsets;
319 }
320 }
Romain Guyc39ed4a2012-06-12 12:06:46 -0700321
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 /**
323 * Interface definition for a callback to be invoked when layout has
324 * completed and the client can compute its interior insets.
Dianne Hackborn935ae462009-04-13 16:11:55 -0700325 *
326 * We are not yet ready to commit to this API and support it, so
327 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 */
329 public interface OnComputeInternalInsetsListener {
330 /**
331 * Callback method to be invoked when layout has completed and the
332 * client can compute its interior insets.
333 *
334 * @param inoutInfo Should be filled in by the implementation with
335 * the information about the insets of the window. This is called
336 * with whatever values the previous OnComputeInternalInsetsListener
337 * returned, if there are multiple such listeners in the window.
338 */
339 public void onComputeInternalInsets(InternalInsetsInfo inoutInfo);
340 }
341
342 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800343 * @hide
344 */
345 public interface OnEnterAnimationCompleteListener {
346 public void onEnterAnimationComplete();
347 }
348
349 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 * Creates a new ViewTreeObserver. This constructor should not be called
351 */
John Reckd94094e2016-09-08 14:12:26 -0700352 ViewTreeObserver(Context context) {
353 sIllegalOnDrawModificationIsFatal =
Jeff Sharkeyf383c242017-05-17 17:53:07 -0600354 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 }
356
357 /**
358 * Merges all the listeners registered on the specified observer with the listeners
359 * registered on this object. After this method is invoked, the specified observer
360 * will return false in {@link #isAlive()} and should not be used anymore.
361 *
362 * @param observer The ViewTreeObserver whose listeners must be added to this observer
363 */
364 void merge(ViewTreeObserver observer) {
Dianne Hackborn961cae92013-03-20 14:59:43 -0700365 if (observer.mOnWindowAttachListeners != null) {
366 if (mOnWindowAttachListeners != null) {
367 mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
368 } else {
369 mOnWindowAttachListeners = observer.mOnWindowAttachListeners;
370 }
371 }
372
373 if (observer.mOnWindowFocusListeners != null) {
374 if (mOnWindowFocusListeners != null) {
375 mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
376 } else {
377 mOnWindowFocusListeners = observer.mOnWindowFocusListeners;
378 }
379 }
380
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 if (observer.mOnGlobalFocusListeners != null) {
382 if (mOnGlobalFocusListeners != null) {
383 mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
384 } else {
385 mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;
386 }
387 }
388
389 if (observer.mOnGlobalLayoutListeners != null) {
390 if (mOnGlobalLayoutListeners != null) {
391 mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
392 } else {
393 mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;
394 }
395 }
396
397 if (observer.mOnPreDrawListeners != null) {
398 if (mOnPreDrawListeners != null) {
399 mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
400 } else {
401 mOnPreDrawListeners = observer.mOnPreDrawListeners;
402 }
403 }
404
John Reck9f8ec542016-12-12 11:23:05 -0800405 if (observer.mOnDrawListeners != null) {
406 if (mOnDrawListeners != null) {
407 mOnDrawListeners.addAll(observer.mOnDrawListeners);
408 } else {
409 mOnDrawListeners = observer.mOnDrawListeners;
410 }
411 }
412
John Reck9ab32f82018-07-26 11:02:53 -0700413 if (observer.mOnFrameCommitListeners != null) {
414 if (mOnFrameCommitListeners != null) {
415 mOnFrameCommitListeners.addAll(observer.captureFrameCommitCallbacks());
416 } else {
417 mOnFrameCommitListeners = observer.captureFrameCommitCallbacks();
418 }
419 }
420
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 if (observer.mOnTouchModeChangeListeners != null) {
422 if (mOnTouchModeChangeListeners != null) {
423 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
424 } else {
425 mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;
426 }
427 }
428
429 if (observer.mOnComputeInternalInsetsListeners != null) {
430 if (mOnComputeInternalInsetsListeners != null) {
431 mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
432 } else {
433 mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;
434 }
435 }
436
Mark Brophy757c6972011-10-25 17:01:28 +0100437 if (observer.mOnScrollChangedListeners != null) {
438 if (mOnScrollChangedListeners != null) {
439 mOnScrollChangedListeners.addAll(observer.mOnScrollChangedListeners);
440 } else {
441 mOnScrollChangedListeners = observer.mOnScrollChangedListeners;
442 }
443 }
444
Craig Mautner9c795042014-10-28 19:59:59 -0700445 if (observer.mOnWindowShownListeners != null) {
446 if (mOnWindowShownListeners != null) {
447 mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
448 } else {
449 mOnWindowShownListeners = observer.mOnWindowShownListeners;
450 }
451 }
452
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 observer.kill();
454 }
455
456 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700457 * Register a callback to be invoked when the view hierarchy is attached to a window.
458 *
459 * @param listener The callback to add
460 *
461 * @throws IllegalStateException If {@link #isAlive()} returns false
462 */
463 public void addOnWindowAttachListener(OnWindowAttachListener listener) {
464 checkIsAlive();
465
466 if (mOnWindowAttachListeners == null) {
467 mOnWindowAttachListeners
468 = new CopyOnWriteArrayList<OnWindowAttachListener>();
469 }
470
471 mOnWindowAttachListeners.add(listener);
472 }
473
474 /**
475 * Remove a previously installed window attach callback.
476 *
477 * @param victim The callback to remove
478 *
479 * @throws IllegalStateException If {@link #isAlive()} returns false
480 *
481 * @see #addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener)
482 */
483 public void removeOnWindowAttachListener(OnWindowAttachListener victim) {
484 checkIsAlive();
485 if (mOnWindowAttachListeners == null) {
486 return;
487 }
488 mOnWindowAttachListeners.remove(victim);
489 }
490
491 /**
492 * Register a callback to be invoked when the window focus state within the view tree changes.
493 *
494 * @param listener The callback to add
495 *
496 * @throws IllegalStateException If {@link #isAlive()} returns false
497 */
498 public void addOnWindowFocusChangeListener(OnWindowFocusChangeListener listener) {
499 checkIsAlive();
500
501 if (mOnWindowFocusListeners == null) {
502 mOnWindowFocusListeners
503 = new CopyOnWriteArrayList<OnWindowFocusChangeListener>();
504 }
505
506 mOnWindowFocusListeners.add(listener);
507 }
508
509 /**
510 * Remove a previously installed window focus change callback.
511 *
512 * @param victim The callback to remove
513 *
514 * @throws IllegalStateException If {@link #isAlive()} returns false
515 *
516 * @see #addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener)
517 */
518 public void removeOnWindowFocusChangeListener(OnWindowFocusChangeListener victim) {
519 checkIsAlive();
520 if (mOnWindowFocusListeners == null) {
521 return;
522 }
523 mOnWindowFocusListeners.remove(victim);
524 }
525
526 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 * Register a callback to be invoked when the focus state within the view tree changes.
528 *
529 * @param listener The callback to add
530 *
531 * @throws IllegalStateException If {@link #isAlive()} returns false
532 */
533 public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener) {
534 checkIsAlive();
535
536 if (mOnGlobalFocusListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700537 mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 }
539
540 mOnGlobalFocusListeners.add(listener);
541 }
542
543 /**
544 * Remove a previously installed focus change callback.
545 *
546 * @param victim The callback to remove
547 *
548 * @throws IllegalStateException If {@link #isAlive()} returns false
549 *
550 * @see #addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener)
551 */
552 public void removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim) {
553 checkIsAlive();
554 if (mOnGlobalFocusListeners == null) {
555 return;
556 }
557 mOnGlobalFocusListeners.remove(victim);
558 }
559
560 /**
561 * Register a callback to be invoked when the global layout state or the visibility of views
562 * within the view tree changes
563 *
564 * @param listener The callback to add
565 *
566 * @throws IllegalStateException If {@link #isAlive()} returns false
567 */
568 public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
569 checkIsAlive();
570
571 if (mOnGlobalLayoutListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700572 mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 }
574
575 mOnGlobalLayoutListeners.add(listener);
576 }
577
578 /**
579 * Remove a previously installed global layout callback
580 *
581 * @param victim The callback to remove
582 *
583 * @throws IllegalStateException If {@link #isAlive()} returns false
Romain Guy1e878d22012-01-23 15:34:25 -0800584 *
585 * @deprecated Use #removeOnGlobalLayoutListener instead
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 *
587 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
588 */
Romain Guy1e878d22012-01-23 15:34:25 -0800589 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
Romain Guy1e878d22012-01-23 15:34:25 -0800591 removeOnGlobalLayoutListener(victim);
592 }
593
594 /**
595 * Remove a previously installed global layout callback
596 *
597 * @param victim The callback to remove
598 *
599 * @throws IllegalStateException If {@link #isAlive()} returns false
600 *
601 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
602 */
603 public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 checkIsAlive();
605 if (mOnGlobalLayoutListeners == null) {
606 return;
607 }
608 mOnGlobalLayoutListeners.remove(victim);
609 }
610
611 /**
612 * Register a callback to be invoked when the view tree is about to be drawn
613 *
614 * @param listener The callback to add
615 *
616 * @throws IllegalStateException If {@link #isAlive()} returns false
617 */
618 public void addOnPreDrawListener(OnPreDrawListener listener) {
619 checkIsAlive();
620
621 if (mOnPreDrawListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700622 mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 }
624
625 mOnPreDrawListeners.add(listener);
626 }
627
628 /**
629 * Remove a previously installed pre-draw callback
630 *
631 * @param victim The callback to remove
632 *
633 * @throws IllegalStateException If {@link #isAlive()} returns false
634 *
635 * @see #addOnPreDrawListener(OnPreDrawListener)
636 */
637 public void removeOnPreDrawListener(OnPreDrawListener victim) {
638 checkIsAlive();
639 if (mOnPreDrawListeners == null) {
640 return;
641 }
642 mOnPreDrawListeners.remove(victim);
643 }
644
645 /**
Craig Mautner9c795042014-10-28 19:59:59 -0700646 * Register a callback to be invoked when the view tree window has been shown
647 *
648 * @param listener The callback to add
649 *
650 * @throws IllegalStateException If {@link #isAlive()} returns false
651 * @hide
652 */
653 public void addOnWindowShownListener(OnWindowShownListener listener) {
654 checkIsAlive();
655
656 if (mOnWindowShownListeners == null) {
657 mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>();
658 }
659
660 mOnWindowShownListeners.add(listener);
661 if (mWindowShown) {
662 listener.onWindowShown();
663 }
664 }
665
666 /**
667 * Remove a previously installed window shown callback
668 *
669 * @param victim The callback to remove
670 *
671 * @throws IllegalStateException If {@link #isAlive()} returns false
672 *
673 * @see #addOnWindowShownListener(OnWindowShownListener)
674 * @hide
675 */
676 public void removeOnWindowShownListener(OnWindowShownListener victim) {
677 checkIsAlive();
678 if (mOnWindowShownListeners == null) {
679 return;
680 }
681 mOnWindowShownListeners.remove(victim);
682 }
683
684 /**
Romain Guy25eba5c2012-04-04 17:29:03 -0700685 * <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
686 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
687 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
688 *
689 * @param listener The callback to add
690 *
691 * @throws IllegalStateException If {@link #isAlive()} returns false
692 */
693 public void addOnDrawListener(OnDrawListener listener) {
694 checkIsAlive();
695
696 if (mOnDrawListeners == null) {
697 mOnDrawListeners = new ArrayList<OnDrawListener>();
698 }
699
John Reckd94094e2016-09-08 14:12:26 -0700700 if (mInDispatchOnDraw) {
701 IllegalStateException ex = new IllegalStateException(
702 "Cannot call addOnDrawListener inside of onDraw");
703 if (sIllegalOnDrawModificationIsFatal) {
704 throw ex;
705 } else {
706 Log.e("ViewTreeObserver", ex.getMessage(), ex);
707 }
708 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700709 mOnDrawListeners.add(listener);
710 }
711
712 /**
713 * <p>Remove a previously installed pre-draw callback.</p>
714 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
715 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
716 *
717 * @param victim The callback to remove
718 *
719 * @throws IllegalStateException If {@link #isAlive()} returns false
720 *
721 * @see #addOnDrawListener(OnDrawListener)
722 */
723 public void removeOnDrawListener(OnDrawListener victim) {
724 checkIsAlive();
725 if (mOnDrawListeners == null) {
726 return;
727 }
John Reckd94094e2016-09-08 14:12:26 -0700728 if (mInDispatchOnDraw) {
729 IllegalStateException ex = new IllegalStateException(
730 "Cannot call removeOnDrawListener inside of onDraw");
731 if (sIllegalOnDrawModificationIsFatal) {
732 throw ex;
733 } else {
734 Log.e("ViewTreeObserver", ex.getMessage(), ex);
735 }
736 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700737 mOnDrawListeners.remove(victim);
738 }
739
740 /**
John Reck9ab32f82018-07-26 11:02:53 -0700741 * Adds a frame commit callback. This callback will be invoked when the current rendering
742 * content has been rendered into a frame and submitted to the swap chain. The frame may
743 * not currently be visible on the display when this is invoked, but it has been submitted.
744 * This callback is useful in combination with {@link PixelCopy} to capture the current
745 * rendered content of the UI reliably.
746 *
747 * Note: Only works with hardware rendering. Does nothing otherwise.
748 *
749 * @param callback The callback to invoke when the frame is committed.
750 */
John Reck9ab32f82018-07-26 11:02:53 -0700751 public void registerFrameCommitCallback(@NonNull Runnable callback) {
752 checkIsAlive();
753 if (mOnFrameCommitListeners == null) {
754 mOnFrameCommitListeners = new ArrayList<>();
755 }
756 mOnFrameCommitListeners.add(callback);
757 }
758
759 @Nullable ArrayList<Runnable> captureFrameCommitCallbacks() {
760 ArrayList<Runnable> ret = mOnFrameCommitListeners;
761 mOnFrameCommitListeners = null;
762 return ret;
763 }
764
765 /**
766 * Attempts to remove the given callback from the list of pending frame complete callbacks.
767 *
768 * @param callback The callback to remove
769 * @return Whether or not the callback was removed. If this returns true the callback will
770 * not be invoked. If false is returned then the callback was either never added
771 * or may already be pending execution and was unable to be removed
772 */
John Reck9ab32f82018-07-26 11:02:53 -0700773 public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) {
774 checkIsAlive();
775 if (mOnFrameCommitListeners == null) {
776 return false;
777 }
778 return mOnFrameCommitListeners.remove(callback);
779 }
780
781 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 * Register a callback to be invoked when a view has been scrolled.
783 *
784 * @param listener The callback to add
785 *
786 * @throws IllegalStateException If {@link #isAlive()} returns false
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 */
788 public void addOnScrollChangedListener(OnScrollChangedListener listener) {
789 checkIsAlive();
790
791 if (mOnScrollChangedListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700792 mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
794
795 mOnScrollChangedListeners.add(listener);
796 }
797
798 /**
799 * Remove a previously installed scroll-changed callback
800 *
801 * @param victim The callback to remove
802 *
803 * @throws IllegalStateException If {@link #isAlive()} returns false
804 *
805 * @see #addOnScrollChangedListener(OnScrollChangedListener)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 */
807 public void removeOnScrollChangedListener(OnScrollChangedListener victim) {
808 checkIsAlive();
809 if (mOnScrollChangedListeners == null) {
810 return;
811 }
812 mOnScrollChangedListeners.remove(victim);
813 }
814
815 /**
816 * Register a callback to be invoked when the invoked when the touch mode changes.
817 *
818 * @param listener The callback to add
819 *
820 * @throws IllegalStateException If {@link #isAlive()} returns false
821 */
822 public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
823 checkIsAlive();
824
825 if (mOnTouchModeChangeListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700826 mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 }
828
829 mOnTouchModeChangeListeners.add(listener);
830 }
831
832 /**
833 * Remove a previously installed touch mode change callback
834 *
835 * @param victim The callback to remove
836 *
837 * @throws IllegalStateException If {@link #isAlive()} returns false
838 *
839 * @see #addOnTouchModeChangeListener(OnTouchModeChangeListener)
840 */
841 public void removeOnTouchModeChangeListener(OnTouchModeChangeListener victim) {
842 checkIsAlive();
843 if (mOnTouchModeChangeListeners == null) {
844 return;
845 }
846 mOnTouchModeChangeListeners.remove(victim);
847 }
848
849 /**
850 * Register a callback to be invoked when the invoked when it is time to
851 * compute the window's internal insets.
852 *
853 * @param listener The callback to add
854 *
855 * @throws IllegalStateException If {@link #isAlive()} returns false
Dianne Hackborn935ae462009-04-13 16:11:55 -0700856 *
857 * We are not yet ready to commit to this API and support it, so
858 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100860 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener) {
862 checkIsAlive();
863
864 if (mOnComputeInternalInsetsListeners == null) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700865 mOnComputeInternalInsetsListeners =
Romain Guyc39ed4a2012-06-12 12:06:46 -0700866 new CopyOnWriteArray<OnComputeInternalInsetsListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 }
868
869 mOnComputeInternalInsetsListeners.add(listener);
870 }
871
872 /**
873 * Remove a previously installed internal insets computation callback
874 *
875 * @param victim The callback to remove
876 *
877 * @throws IllegalStateException If {@link #isAlive()} returns false
878 *
879 * @see #addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener)
Dianne Hackborn935ae462009-04-13 16:11:55 -0700880 *
881 * We are not yet ready to commit to this API and support it, so
882 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100884 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 public void removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim) {
886 checkIsAlive();
887 if (mOnComputeInternalInsetsListeners == null) {
888 return;
889 }
890 mOnComputeInternalInsetsListeners.remove(victim);
891 }
892
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800893 /**
894 * @hide
895 */
896 public void addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
897 checkIsAlive();
898 if (mOnEnterAnimationCompleteListeners == null) {
899 mOnEnterAnimationCompleteListeners =
900 new CopyOnWriteArrayList<OnEnterAnimationCompleteListener>();
901 }
902 mOnEnterAnimationCompleteListeners.add(listener);
903 }
904
905 /**
906 * @hide
907 */
908 public void removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
909 checkIsAlive();
910 if (mOnEnterAnimationCompleteListeners == null) {
911 return;
912 }
913 mOnEnterAnimationCompleteListeners.remove(listener);
914 }
915
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 private void checkIsAlive() {
917 if (!mAlive) {
918 throw new IllegalStateException("This ViewTreeObserver is not alive, call "
919 + "getViewTreeObserver() again");
920 }
921 }
922
923 /**
924 * Indicates whether this ViewTreeObserver is alive. When an observer is not alive,
925 * any call to a method (except this one) will throw an exception.
926 *
927 * If an application keeps a long-lived reference to this ViewTreeObserver, it should
928 * always check for the result of this method before calling any other method.
929 *
930 * @return True if this object is alive and be used, false otherwise.
931 */
932 public boolean isAlive() {
933 return mAlive;
934 }
935
936 /**
937 * Marks this ViewTreeObserver as not alive. After invoking this method, invoking
938 * any other method but {@link #isAlive()} and {@link #kill()} will throw an Exception.
939 *
940 * @hide
941 */
942 private void kill() {
943 mAlive = false;
944 }
945
946 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700947 * Notifies registered listeners that window has been attached/detached.
948 */
949 final void dispatchOnWindowAttachedChange(boolean attached) {
950 // 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.
954 final CopyOnWriteArrayList<OnWindowAttachListener> listeners
955 = mOnWindowAttachListeners;
956 if (listeners != null && listeners.size() > 0) {
957 for (OnWindowAttachListener listener : listeners) {
958 if (attached) listener.onWindowAttached();
959 else listener.onWindowDetached();
960 }
961 }
962 }
963
964 /**
965 * Notifies registered listeners that window focus has changed.
966 */
967 final void dispatchOnWindowFocusChange(boolean hasFocus) {
968 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
969 // perform the dispatching. The iterator is a safe guard against listeners that
970 // could mutate the list by calling the various add/remove methods. This prevents
971 // the array from being modified while we iterate it.
972 final CopyOnWriteArrayList<OnWindowFocusChangeListener> listeners
973 = mOnWindowFocusListeners;
974 if (listeners != null && listeners.size() > 0) {
975 for (OnWindowFocusChangeListener listener : listeners) {
976 listener.onWindowFocusChanged(hasFocus);
977 }
978 }
979 }
980
981 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 * Notifies registered listeners that focus has changed.
983 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100984 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700986 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
987 // perform the dispatching. The iterator is a safe guard against listeners that
988 // could mutate the list by calling the various add/remove methods. This prevents
989 // the array from being modified while we iterate it.
Chet Haase0f8ffd82012-06-07 07:48:48 -0700990 final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -0700991 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700992 for (OnGlobalFocusChangeListener listener : listeners) {
993 listener.onGlobalFocusChanged(oldFocus, newFocus);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 }
995 }
996 }
997
998 /**
999 * Notifies registered listeners that a global layout happened. This can be called
1000 * manually if you are forcing a layout on a View or a hierarchy of Views that are
1001 * not attached to a Window or in the GONE state.
1002 */
1003 public final void dispatchOnGlobalLayout() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001004 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1005 // perform the dispatching. The iterator is a safe guard against listeners that
1006 // could mutate the list by calling the various add/remove methods. This prevents
1007 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001008 final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001009 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001010 CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
1011 try {
1012 int count = access.size();
1013 for (int i = 0; i < count; i++) {
1014 access.get(i).onGlobalLayout();
1015 }
1016 } finally {
1017 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 }
1019 }
1020 }
1021
1022 /**
Romain Guy21f42302013-06-28 19:19:30 -07001023 * Returns whether there are listeners for on pre-draw events.
1024 */
1025 final boolean hasOnPreDrawListeners() {
1026 return mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0;
1027 }
1028
1029 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 * Notifies registered listeners that the drawing pass is about to start. If a
1031 * listener returns true, then the drawing pass is canceled and rescheduled. This can
1032 * be called manually if you are forcing the drawing on a View or a hierarchy of Views
1033 * that are not attached to a Window or in the GONE state.
1034 *
1035 * @return True if the current draw should be canceled and resceduled, false otherwise.
1036 */
Romain Guy25eba5c2012-04-04 17:29:03 -07001037 @SuppressWarnings("unchecked")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 public final boolean dispatchOnPreDraw() {
1039 boolean cancelDraw = false;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001040 final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
1041 if (listeners != null && listeners.size() > 0) {
1042 CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();
1043 try {
1044 int count = access.size();
1045 for (int i = 0; i < count; i++) {
1046 cancelDraw |= !(access.get(i).onPreDraw());
1047 }
1048 } finally {
1049 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 }
1051 }
1052 return cancelDraw;
1053 }
1054
1055 /**
Craig Mautner9c795042014-10-28 19:59:59 -07001056 * Notifies registered listeners that the window is now shown
1057 * @hide
1058 */
1059 @SuppressWarnings("unchecked")
1060 public final void dispatchOnWindowShown() {
1061 mWindowShown = true;
1062 final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners;
1063 if (listeners != null && listeners.size() > 0) {
1064 CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start();
1065 try {
1066 int count = access.size();
1067 for (int i = 0; i < count; i++) {
1068 access.get(i).onWindowShown();
1069 }
1070 } finally {
1071 listeners.end();
1072 }
1073 }
1074 }
1075
1076 /**
Romain Guy25eba5c2012-04-04 17:29:03 -07001077 * Notifies registered listeners that the drawing pass is about to start.
1078 */
1079 public final void dispatchOnDraw() {
1080 if (mOnDrawListeners != null) {
John Reckd94094e2016-09-08 14:12:26 -07001081 mInDispatchOnDraw = true;
Romain Guy25eba5c2012-04-04 17:29:03 -07001082 final ArrayList<OnDrawListener> listeners = mOnDrawListeners;
1083 int numListeners = listeners.size();
1084 for (int i = 0; i < numListeners; ++i) {
1085 listeners.get(i).onDraw();
1086 }
John Reckd94094e2016-09-08 14:12:26 -07001087 mInDispatchOnDraw = false;
Romain Guy25eba5c2012-04-04 17:29:03 -07001088 }
1089 }
1090
1091 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 * Notifies registered listeners that the touch mode has changed.
1093 *
1094 * @param inTouchMode True if the touch mode is now enabled, false otherwise.
1095 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001096 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 final void dispatchOnTouchModeChanged(boolean inTouchMode) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001098 final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001099 mOnTouchModeChangeListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001100 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001101 for (OnTouchModeChangeListener listener : listeners) {
1102 listener.onTouchModeChanged(inTouchMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 }
1104 }
1105 }
1106
1107 /**
1108 * Notifies registered listeners that something has scrolled.
1109 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001110 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 final void dispatchOnScrollChanged() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001112 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1113 // perform the dispatching. The iterator is a safe guard against listeners that
1114 // could mutate the list by calling the various add/remove methods. This prevents
1115 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001116 final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001117 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001118 CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start();
1119 try {
1120 int count = access.size();
1121 for (int i = 0; i < count; i++) {
1122 access.get(i).onScrollChanged();
1123 }
1124 } finally {
1125 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 }
1127 }
1128 }
1129
1130 /**
1131 * Returns whether there are listeners for computing internal insets.
1132 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001133 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 final boolean hasComputeInternalInsetsListeners() {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001135 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001136 mOnComputeInternalInsetsListeners;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 return (listeners != null && listeners.size() > 0);
1138 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 /**
1141 * Calls all listeners to compute the current insets.
1142 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001143 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001145 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1146 // perform the dispatching. The iterator is a safe guard against listeners that
1147 // could mutate the list by calling the various add/remove methods. This prevents
1148 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001149 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001150 mOnComputeInternalInsetsListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001151 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001152 CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
1153 try {
1154 int count = access.size();
1155 for (int i = 0; i < count; i++) {
1156 access.get(i).onComputeInternalInsets(inoutInfo);
1157 }
1158 } finally {
1159 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 }
1161 }
1162 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001163
1164 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -08001165 * @hide
1166 */
1167 public final void dispatchOnEnterAnimationComplete() {
1168 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1169 // perform the dispatching. The iterator is a safe guard against listeners that
1170 // could mutate the list by calling the various add/remove methods. This prevents
1171 // the array from being modified while we iterate it.
1172 final CopyOnWriteArrayList<OnEnterAnimationCompleteListener> listeners =
1173 mOnEnterAnimationCompleteListeners;
1174 if (listeners != null && !listeners.isEmpty()) {
1175 for (OnEnterAnimationCompleteListener listener : listeners) {
1176 listener.onEnterAnimationComplete();
1177 }
1178 }
1179 }
1180
1181 /**
Romain Guyc39ed4a2012-06-12 12:06:46 -07001182 * Copy on write array. This array is not thread safe, and only one loop can
1183 * iterate over this array at any given time. This class avoids allocations
1184 * until a concurrent modification happens.
1185 *
1186 * Usage:
1187 *
1188 * CopyOnWriteArray.Access<MyData> access = array.start();
1189 * try {
1190 * for (int i = 0; i < access.size(); i++) {
1191 * MyData d = access.get(i);
1192 * }
1193 * } finally {
1194 * access.end();
1195 * }
1196 */
1197 static class CopyOnWriteArray<T> {
1198 private ArrayList<T> mData = new ArrayList<T>();
1199 private ArrayList<T> mDataCopy;
1200
1201 private final Access<T> mAccess = new Access<T>();
1202
1203 private boolean mStart;
1204
1205 static class Access<T> {
1206 private ArrayList<T> mData;
1207 private int mSize;
1208
1209 T get(int index) {
1210 return mData.get(index);
1211 }
1212
1213 int size() {
1214 return mSize;
1215 }
1216 }
1217
1218 CopyOnWriteArray() {
1219 }
1220
1221 private ArrayList<T> getArray() {
1222 if (mStart) {
1223 if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData);
1224 return mDataCopy;
1225 }
1226 return mData;
1227 }
1228
1229 Access<T> start() {
1230 if (mStart) throw new IllegalStateException("Iteration already started");
1231 mStart = true;
1232 mDataCopy = null;
1233 mAccess.mData = mData;
1234 mAccess.mSize = mData.size();
1235 return mAccess;
1236 }
1237
1238 void end() {
1239 if (!mStart) throw new IllegalStateException("Iteration not started");
1240 mStart = false;
1241 if (mDataCopy != null) {
1242 mData = mDataCopy;
Chet Haasefc343962013-09-18 08:44:33 -07001243 mAccess.mData.clear();
1244 mAccess.mSize = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001245 }
1246 mDataCopy = null;
1247 }
1248
1249 int size() {
1250 return getArray().size();
1251 }
1252
1253 void add(T item) {
1254 getArray().add(item);
1255 }
1256
1257 void addAll(CopyOnWriteArray<T> array) {
1258 getArray().addAll(array.mData);
1259 }
1260
1261 void remove(T item) {
1262 getArray().remove(item);
1263 }
1264
1265 void clear() {
1266 getArray().clear();
1267 }
1268 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269}