blob: c50a3aa8ac7cfe335e8748e17f22399dc50ce88c [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;
Adam Powell769b8632019-02-04 11:28:22 -080029import java.util.List;
Chet Haase0f8ffd82012-06-07 07:48:48 -070030import java.util.concurrent.CopyOnWriteArrayList;
Adam Powell769b8632019-02-04 11:28:22 -080031import java.util.function.Consumer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032
33/**
34 * A view tree observer is used to register listeners that can be notified of global
35 * changes in the view tree. Such global events include, but are not limited to,
36 * layout of the whole tree, beginning of the drawing pass, touch mode change....
37 *
38 * A ViewTreeObserver should never be instantiated by applications as it is provided
39 * by the views hierarchy. Refer to {@link android.view.View#getViewTreeObserver()}
40 * for more information.
41 */
42public final class ViewTreeObserver {
Romain Guyc39ed4a2012-06-12 12:06:46 -070043 // Recursive listeners use CopyOnWriteArrayList
Dianne Hackborn961cae92013-03-20 14:59:43 -070044 private CopyOnWriteArrayList<OnWindowFocusChangeListener> mOnWindowFocusListeners;
45 private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners;
Chet Haase0f8ffd82012-06-07 07:48:48 -070046 private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
Mathew Inwooda570dee2018-08-17 14:56:00 +010047 @UnsupportedAppUsage
Chet Haase0f8ffd82012-06-07 07:48:48 -070048 private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
Filip Gruszczynski6eafa902014-11-14 14:24:37 -080049 private CopyOnWriteArrayList<OnEnterAnimationCompleteListener>
50 mOnEnterAnimationCompleteListeners;
Romain Guyc39ed4a2012-06-12 12:06:46 -070051
52 // Non-recursive listeners use CopyOnWriteArray
53 // Any listener invoked from ViewRootImpl.performTraversals() should not be recursive
Mathew Inwooda570dee2018-08-17 14:56:00 +010054 @UnsupportedAppUsage
Romain Guyc39ed4a2012-06-12 12:06:46 -070055 private CopyOnWriteArray<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
Mathew Inwooda570dee2018-08-17 14:56:00 +010056 @UnsupportedAppUsage
Romain Guyc39ed4a2012-06-12 12:06:46 -070057 private CopyOnWriteArray<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
Mathew Inwooda570dee2018-08-17 14:56:00 +010058 @UnsupportedAppUsage
Romain Guyc39ed4a2012-06-12 12:06:46 -070059 private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners;
60 private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners;
Craig Mautner9c795042014-10-28 19:59:59 -070061 private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners;
Adam Powell769b8632019-02-04 11:28:22 -080062 private CopyOnWriteArray<Consumer<List<Rect>>> mGestureExclusionListeners;
Romain Guyc39ed4a2012-06-12 12:06:46 -070063
64 // These listeners cannot be mutated during dispatch
John Reckd94094e2016-09-08 14:12:26 -070065 private boolean mInDispatchOnDraw;
Romain Guy25eba5c2012-04-04 17:29:03 -070066 private ArrayList<OnDrawListener> mOnDrawListeners;
John Reckd94094e2016-09-08 14:12:26 -070067 private static boolean sIllegalOnDrawModificationIsFatal;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068
John Reck9ab32f82018-07-26 11:02:53 -070069 // These listeners are one-shot
70 private ArrayList<Runnable> mOnFrameCommitListeners;
71
Craig Mautner9c795042014-10-28 19:59:59 -070072 /** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
73 * that the listener will be immediately called. */
74 private boolean mWindowShown;
75
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 private boolean mAlive = true;
77
78 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -070079 * Interface definition for a callback to be invoked when the view hierarchy is
80 * attached to and detached from its window.
81 */
82 public interface OnWindowAttachListener {
83 /**
84 * Callback method to be invoked when the view hierarchy is attached to a window
85 */
86 public void onWindowAttached();
87
88 /**
89 * Callback method to be invoked when the view hierarchy is detached from a window
90 */
91 public void onWindowDetached();
92 }
93
94 /**
95 * Interface definition for a callback to be invoked when the view hierarchy's window
96 * focus state changes.
97 */
98 public interface OnWindowFocusChangeListener {
99 /**
100 * Callback method to be invoked when the window focus changes in the view tree.
101 *
102 * @param hasFocus Set to true if the window is gaining focus, false if it is
103 * losing focus.
104 */
105 public void onWindowFocusChanged(boolean hasFocus);
106 }
107
108 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 * Interface definition for a callback to be invoked when the focus state within
110 * the view tree changes.
111 */
112 public interface OnGlobalFocusChangeListener {
113 /**
114 * Callback method to be invoked when the focus changes in the view tree. When
115 * the view tree transitions from touch mode to non-touch mode, oldFocus is null.
116 * When the view tree transitions from non-touch mode to touch mode, newFocus is
117 * null. When focus changes in non-touch mode (without transition from or to
118 * touch mode) either oldFocus or newFocus can be null.
119 *
120 * @param oldFocus The previously focused view, if any.
121 * @param newFocus The newly focused View, if any.
122 */
123 public void onGlobalFocusChanged(View oldFocus, View newFocus);
124 }
125
126 /**
127 * Interface definition for a callback to be invoked when the global layout state
128 * or the visibility of views within the view tree changes.
129 */
130 public interface OnGlobalLayoutListener {
131 /**
132 * Callback method to be invoked when the global layout state or the visibility of views
133 * within the view tree changes
134 */
135 public void onGlobalLayout();
136 }
137
138 /**
139 * Interface definition for a callback to be invoked when the view tree is about to be drawn.
140 */
141 public interface OnPreDrawListener {
142 /**
143 * Callback method to be invoked when the view tree is about to be drawn. At this point, all
144 * views in the tree have been measured and given a frame. Clients can use this to adjust
145 * their scroll bounds or even to request a new layout before drawing occurs.
146 *
147 * @return Return true to proceed with the current drawing pass, or false to cancel.
148 *
149 * @see android.view.View#onMeasure
150 * @see android.view.View#onLayout
151 * @see android.view.View#onDraw
152 */
153 public boolean onPreDraw();
154 }
155
156 /**
Romain Guy25eba5c2012-04-04 17:29:03 -0700157 * Interface definition for a callback to be invoked when the view tree is about to be drawn.
158 */
159 public interface OnDrawListener {
160 /**
161 * <p>Callback method to be invoked when the view tree is about to be drawn. At this point,
162 * views cannot be modified in any way.</p>
163 *
164 * <p>Unlike with {@link OnPreDrawListener}, this method cannot be used to cancel the
165 * current drawing pass.</p>
166 *
167 * <p>An {@link OnDrawListener} listener <strong>cannot be added or removed</strong>
168 * from this method.</p>
169 *
170 * @see android.view.View#onMeasure
171 * @see android.view.View#onLayout
172 * @see android.view.View#onDraw
173 */
174 public void onDraw();
175 }
176
177 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 * Interface definition for a callback to be invoked when the touch mode changes.
179 */
180 public interface OnTouchModeChangeListener {
181 /**
182 * Callback method to be invoked when the touch mode changes.
183 *
184 * @param isInTouchMode True if the view hierarchy is now in touch mode, false otherwise.
185 */
186 public void onTouchModeChanged(boolean isInTouchMode);
187 }
188
189 /**
190 * Interface definition for a callback to be invoked when
191 * something in the view tree has been scrolled.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 */
193 public interface OnScrollChangedListener {
194 /**
195 * Callback method to be invoked when something in the view tree
196 * has been scrolled.
197 */
198 public void onScrollChanged();
199 }
200
201 /**
Craig Mautner9c795042014-10-28 19:59:59 -0700202 * Interface definition for a callback noting when a system window has been displayed.
203 * This is only used for non-Activity windows. Activity windows can use
204 * Activity.onEnterAnimationComplete() to get the same signal.
205 * @hide
206 */
207 public interface OnWindowShownListener {
208 /**
209 * Callback method to be invoked when a non-activity window is fully shown.
210 */
211 void onWindowShown();
212 }
213
214 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 * Parameters used with OnComputeInternalInsetsListener.
Dianne Hackborn935ae462009-04-13 16:11:55 -0700216 *
217 * We are not yet ready to commit to this API and support it, so
218 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 */
220 public final static class InternalInsetsInfo {
221 /**
222 * Offsets from the frame of the window at which the content of
223 * windows behind it should be placed.
224 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100225 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 public final Rect contentInsets = new Rect();
Romain Guyc39ed4a2012-06-12 12:06:46 -0700227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 /**
Jeff Brownfbf09772011-01-16 14:06:57 -0800229 * Offsets from the frame of the window at which windows behind it
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 * are visible.
231 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100232 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 public final Rect visibleInsets = new Rect();
Jeff Brownfbf09772011-01-16 14:06:57 -0800234
235 /**
236 * Touchable region defined relative to the origin of the frame of the window.
237 * Only used when {@link #setTouchableInsets(int)} is called with
238 * the option {@link #TOUCHABLE_INSETS_REGION}.
239 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100240 @UnsupportedAppUsage
Jeff Brownfbf09772011-01-16 14:06:57 -0800241 public final Region touchableRegion = new Region();
242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 /**
244 * Option for {@link #setTouchableInsets(int)}: the entire window frame
245 * can be touched.
246 */
247 public static final int TOUCHABLE_INSETS_FRAME = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700248
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 /**
250 * Option for {@link #setTouchableInsets(int)}: the area inside of
251 * the content insets can be touched.
252 */
253 public static final int TOUCHABLE_INSETS_CONTENT = 1;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 /**
256 * Option for {@link #setTouchableInsets(int)}: the area inside of
257 * the visible insets can be touched.
258 */
259 public static final int TOUCHABLE_INSETS_VISIBLE = 2;
Jeff Brownfbf09772011-01-16 14:06:57 -0800260
261 /**
262 * Option for {@link #setTouchableInsets(int)}: the area inside of
263 * the provided touchable region in {@link #touchableRegion} can be touched.
264 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100265 @UnsupportedAppUsage
Jeff Brownfbf09772011-01-16 14:06:57 -0800266 public static final int TOUCHABLE_INSETS_REGION = 3;
267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 /**
269 * Set which parts of the window can be touched: either
270 * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
Jeff Brownfbf09772011-01-16 14:06:57 -0800271 * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100273 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 public void setTouchableInsets(int val) {
275 mTouchableInsets = val;
276 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700277
Mathew Inwooda570dee2018-08-17 14:56:00 +0100278 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 int mTouchableInsets;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700280
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 void reset() {
Jeff Brownfbf09772011-01-16 14:06:57 -0800282 contentInsets.setEmpty();
283 visibleInsets.setEmpty();
284 touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 mTouchableInsets = TOUCHABLE_INSETS_FRAME;
286 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700287
Jeff Brown2e05ec32013-09-30 15:57:43 -0700288 boolean isEmpty() {
289 return contentInsets.isEmpty()
290 && visibleInsets.isEmpty()
291 && touchableRegion.isEmpty()
292 && mTouchableInsets == TOUCHABLE_INSETS_FRAME;
293 }
294
Romain Guy25eba5c2012-04-04 17:29:03 -0700295 @Override
296 public int hashCode() {
Romain Guy21f42302013-06-28 19:19:30 -0700297 int result = contentInsets.hashCode();
298 result = 31 * result + visibleInsets.hashCode();
299 result = 31 * result + touchableRegion.hashCode();
Romain Guy25eba5c2012-04-04 17:29:03 -0700300 result = 31 * result + mTouchableInsets;
301 return result;
302 }
303
Romain Guy1e878d22012-01-23 15:34:25 -0800304 @Override
305 public boolean equals(Object o) {
Romain Guy25eba5c2012-04-04 17:29:03 -0700306 if (this == o) return true;
307 if (o == null || getClass() != o.getClass()) return false;
308
309 InternalInsetsInfo other = (InternalInsetsInfo)o;
310 return mTouchableInsets == other.mTouchableInsets &&
311 contentInsets.equals(other.contentInsets) &&
312 visibleInsets.equals(other.visibleInsets) &&
313 touchableRegion.equals(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700315
Mathew Inwooda570dee2018-08-17 14:56:00 +0100316 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 void set(InternalInsetsInfo other) {
318 contentInsets.set(other.contentInsets);
319 visibleInsets.set(other.visibleInsets);
Jeff Brownfbf09772011-01-16 14:06:57 -0800320 touchableRegion.set(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 mTouchableInsets = other.mTouchableInsets;
322 }
323 }
Romain Guyc39ed4a2012-06-12 12:06:46 -0700324
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 /**
326 * Interface definition for a callback to be invoked when layout has
327 * completed and the client can compute its interior insets.
Dianne Hackborn935ae462009-04-13 16:11:55 -0700328 *
329 * We are not yet ready to commit to this API and support it, so
330 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 */
332 public interface OnComputeInternalInsetsListener {
333 /**
334 * Callback method to be invoked when layout has completed and the
335 * client can compute its interior insets.
336 *
337 * @param inoutInfo Should be filled in by the implementation with
338 * the information about the insets of the window. This is called
339 * with whatever values the previous OnComputeInternalInsetsListener
340 * returned, if there are multiple such listeners in the window.
341 */
342 public void onComputeInternalInsets(InternalInsetsInfo inoutInfo);
343 }
344
345 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800346 * @hide
347 */
348 public interface OnEnterAnimationCompleteListener {
349 public void onEnterAnimationComplete();
350 }
351
352 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 * Creates a new ViewTreeObserver. This constructor should not be called
354 */
John Reckd94094e2016-09-08 14:12:26 -0700355 ViewTreeObserver(Context context) {
356 sIllegalOnDrawModificationIsFatal =
Jeff Sharkeyf383c242017-05-17 17:53:07 -0600357 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 }
359
360 /**
361 * Merges all the listeners registered on the specified observer with the listeners
362 * registered on this object. After this method is invoked, the specified observer
363 * will return false in {@link #isAlive()} and should not be used anymore.
364 *
365 * @param observer The ViewTreeObserver whose listeners must be added to this observer
366 */
367 void merge(ViewTreeObserver observer) {
Dianne Hackborn961cae92013-03-20 14:59:43 -0700368 if (observer.mOnWindowAttachListeners != null) {
369 if (mOnWindowAttachListeners != null) {
370 mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
371 } else {
372 mOnWindowAttachListeners = observer.mOnWindowAttachListeners;
373 }
374 }
375
376 if (observer.mOnWindowFocusListeners != null) {
377 if (mOnWindowFocusListeners != null) {
378 mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
379 } else {
380 mOnWindowFocusListeners = observer.mOnWindowFocusListeners;
381 }
382 }
383
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 if (observer.mOnGlobalFocusListeners != null) {
385 if (mOnGlobalFocusListeners != null) {
386 mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
387 } else {
388 mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;
389 }
390 }
391
392 if (observer.mOnGlobalLayoutListeners != null) {
393 if (mOnGlobalLayoutListeners != null) {
394 mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
395 } else {
396 mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;
397 }
398 }
399
400 if (observer.mOnPreDrawListeners != null) {
401 if (mOnPreDrawListeners != null) {
402 mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
403 } else {
404 mOnPreDrawListeners = observer.mOnPreDrawListeners;
405 }
406 }
407
John Reck9f8ec542016-12-12 11:23:05 -0800408 if (observer.mOnDrawListeners != null) {
409 if (mOnDrawListeners != null) {
410 mOnDrawListeners.addAll(observer.mOnDrawListeners);
411 } else {
412 mOnDrawListeners = observer.mOnDrawListeners;
413 }
414 }
415
John Reck9ab32f82018-07-26 11:02:53 -0700416 if (observer.mOnFrameCommitListeners != null) {
417 if (mOnFrameCommitListeners != null) {
418 mOnFrameCommitListeners.addAll(observer.captureFrameCommitCallbacks());
419 } else {
420 mOnFrameCommitListeners = observer.captureFrameCommitCallbacks();
421 }
422 }
423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 if (observer.mOnTouchModeChangeListeners != null) {
425 if (mOnTouchModeChangeListeners != null) {
426 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
427 } else {
428 mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;
429 }
430 }
431
432 if (observer.mOnComputeInternalInsetsListeners != null) {
433 if (mOnComputeInternalInsetsListeners != null) {
434 mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
435 } else {
436 mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;
437 }
438 }
439
Mark Brophy757c6972011-10-25 17:01:28 +0100440 if (observer.mOnScrollChangedListeners != null) {
441 if (mOnScrollChangedListeners != null) {
442 mOnScrollChangedListeners.addAll(observer.mOnScrollChangedListeners);
443 } else {
444 mOnScrollChangedListeners = observer.mOnScrollChangedListeners;
445 }
446 }
447
Craig Mautner9c795042014-10-28 19:59:59 -0700448 if (observer.mOnWindowShownListeners != null) {
449 if (mOnWindowShownListeners != null) {
450 mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
451 } else {
452 mOnWindowShownListeners = observer.mOnWindowShownListeners;
453 }
454 }
455
Adam Powell769b8632019-02-04 11:28:22 -0800456 if (observer.mGestureExclusionListeners != null) {
457 if (mGestureExclusionListeners != null) {
458 mGestureExclusionListeners.addAll(observer.mGestureExclusionListeners);
459 } else {
460 mGestureExclusionListeners = observer.mGestureExclusionListeners;
461 }
462 }
463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 observer.kill();
465 }
466
467 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700468 * Register a callback to be invoked when the view hierarchy is attached to a window.
469 *
470 * @param listener The callback to add
471 *
472 * @throws IllegalStateException If {@link #isAlive()} returns false
473 */
474 public void addOnWindowAttachListener(OnWindowAttachListener listener) {
475 checkIsAlive();
476
477 if (mOnWindowAttachListeners == null) {
478 mOnWindowAttachListeners
479 = new CopyOnWriteArrayList<OnWindowAttachListener>();
480 }
481
482 mOnWindowAttachListeners.add(listener);
483 }
484
485 /**
486 * Remove a previously installed window attach callback.
487 *
488 * @param victim The callback to remove
489 *
490 * @throws IllegalStateException If {@link #isAlive()} returns false
491 *
492 * @see #addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener)
493 */
494 public void removeOnWindowAttachListener(OnWindowAttachListener victim) {
495 checkIsAlive();
496 if (mOnWindowAttachListeners == null) {
497 return;
498 }
499 mOnWindowAttachListeners.remove(victim);
500 }
501
502 /**
503 * Register a callback to be invoked when the window focus state within the view tree changes.
504 *
505 * @param listener The callback to add
506 *
507 * @throws IllegalStateException If {@link #isAlive()} returns false
508 */
509 public void addOnWindowFocusChangeListener(OnWindowFocusChangeListener listener) {
510 checkIsAlive();
511
512 if (mOnWindowFocusListeners == null) {
513 mOnWindowFocusListeners
514 = new CopyOnWriteArrayList<OnWindowFocusChangeListener>();
515 }
516
517 mOnWindowFocusListeners.add(listener);
518 }
519
520 /**
521 * Remove a previously installed window focus change callback.
522 *
523 * @param victim The callback to remove
524 *
525 * @throws IllegalStateException If {@link #isAlive()} returns false
526 *
527 * @see #addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener)
528 */
529 public void removeOnWindowFocusChangeListener(OnWindowFocusChangeListener victim) {
530 checkIsAlive();
531 if (mOnWindowFocusListeners == null) {
532 return;
533 }
534 mOnWindowFocusListeners.remove(victim);
535 }
536
537 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 * Register a callback to be invoked when the focus state within the view tree changes.
539 *
540 * @param listener The callback to add
541 *
542 * @throws IllegalStateException If {@link #isAlive()} returns false
543 */
544 public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener) {
545 checkIsAlive();
546
547 if (mOnGlobalFocusListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700548 mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549 }
550
551 mOnGlobalFocusListeners.add(listener);
552 }
553
554 /**
555 * Remove a previously installed focus change callback.
556 *
557 * @param victim The callback to remove
558 *
559 * @throws IllegalStateException If {@link #isAlive()} returns false
560 *
561 * @see #addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener)
562 */
563 public void removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim) {
564 checkIsAlive();
565 if (mOnGlobalFocusListeners == null) {
566 return;
567 }
568 mOnGlobalFocusListeners.remove(victim);
569 }
570
571 /**
572 * Register a callback to be invoked when the global layout state or the visibility of views
573 * within the view tree changes
574 *
575 * @param listener The callback to add
576 *
577 * @throws IllegalStateException If {@link #isAlive()} returns false
578 */
579 public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
580 checkIsAlive();
581
582 if (mOnGlobalLayoutListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700583 mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 }
585
586 mOnGlobalLayoutListeners.add(listener);
587 }
588
589 /**
590 * Remove a previously installed global layout callback
591 *
592 * @param victim The callback to remove
593 *
594 * @throws IllegalStateException If {@link #isAlive()} returns false
Romain Guy1e878d22012-01-23 15:34:25 -0800595 *
596 * @deprecated Use #removeOnGlobalLayoutListener instead
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 *
598 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
599 */
Romain Guy1e878d22012-01-23 15:34:25 -0800600 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
Romain Guy1e878d22012-01-23 15:34:25 -0800602 removeOnGlobalLayoutListener(victim);
603 }
604
605 /**
606 * Remove a previously installed global layout callback
607 *
608 * @param victim The callback to remove
609 *
610 * @throws IllegalStateException If {@link #isAlive()} returns false
611 *
612 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
613 */
614 public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 checkIsAlive();
616 if (mOnGlobalLayoutListeners == null) {
617 return;
618 }
619 mOnGlobalLayoutListeners.remove(victim);
620 }
621
622 /**
623 * Register a callback to be invoked when the view tree is about to be drawn
624 *
625 * @param listener The callback to add
626 *
627 * @throws IllegalStateException If {@link #isAlive()} returns false
628 */
629 public void addOnPreDrawListener(OnPreDrawListener listener) {
630 checkIsAlive();
631
632 if (mOnPreDrawListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700633 mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 }
635
636 mOnPreDrawListeners.add(listener);
637 }
638
639 /**
640 * Remove a previously installed pre-draw callback
641 *
642 * @param victim The callback to remove
643 *
644 * @throws IllegalStateException If {@link #isAlive()} returns false
645 *
646 * @see #addOnPreDrawListener(OnPreDrawListener)
647 */
648 public void removeOnPreDrawListener(OnPreDrawListener victim) {
649 checkIsAlive();
650 if (mOnPreDrawListeners == null) {
651 return;
652 }
653 mOnPreDrawListeners.remove(victim);
654 }
655
656 /**
Craig Mautner9c795042014-10-28 19:59:59 -0700657 * Register a callback to be invoked when the view tree window has been shown
658 *
659 * @param listener The callback to add
660 *
661 * @throws IllegalStateException If {@link #isAlive()} returns false
662 * @hide
663 */
664 public void addOnWindowShownListener(OnWindowShownListener listener) {
665 checkIsAlive();
666
667 if (mOnWindowShownListeners == null) {
668 mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>();
669 }
670
671 mOnWindowShownListeners.add(listener);
672 if (mWindowShown) {
673 listener.onWindowShown();
674 }
675 }
676
677 /**
678 * Remove a previously installed window shown callback
679 *
680 * @param victim The callback to remove
681 *
682 * @throws IllegalStateException If {@link #isAlive()} returns false
683 *
684 * @see #addOnWindowShownListener(OnWindowShownListener)
685 * @hide
686 */
687 public void removeOnWindowShownListener(OnWindowShownListener victim) {
688 checkIsAlive();
689 if (mOnWindowShownListeners == null) {
690 return;
691 }
692 mOnWindowShownListeners.remove(victim);
693 }
694
695 /**
Romain Guy25eba5c2012-04-04 17:29:03 -0700696 * <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
697 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
698 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
699 *
700 * @param listener The callback to add
701 *
702 * @throws IllegalStateException If {@link #isAlive()} returns false
703 */
704 public void addOnDrawListener(OnDrawListener listener) {
705 checkIsAlive();
706
707 if (mOnDrawListeners == null) {
708 mOnDrawListeners = new ArrayList<OnDrawListener>();
709 }
710
John Reckd94094e2016-09-08 14:12:26 -0700711 if (mInDispatchOnDraw) {
712 IllegalStateException ex = new IllegalStateException(
713 "Cannot call addOnDrawListener inside of onDraw");
714 if (sIllegalOnDrawModificationIsFatal) {
715 throw ex;
716 } else {
717 Log.e("ViewTreeObserver", ex.getMessage(), ex);
718 }
719 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700720 mOnDrawListeners.add(listener);
721 }
722
723 /**
724 * <p>Remove a previously installed pre-draw callback.</p>
725 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
726 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
727 *
728 * @param victim The callback to remove
729 *
730 * @throws IllegalStateException If {@link #isAlive()} returns false
731 *
732 * @see #addOnDrawListener(OnDrawListener)
733 */
734 public void removeOnDrawListener(OnDrawListener victim) {
735 checkIsAlive();
736 if (mOnDrawListeners == null) {
737 return;
738 }
John Reckd94094e2016-09-08 14:12:26 -0700739 if (mInDispatchOnDraw) {
740 IllegalStateException ex = new IllegalStateException(
741 "Cannot call removeOnDrawListener inside of onDraw");
742 if (sIllegalOnDrawModificationIsFatal) {
743 throw ex;
744 } else {
745 Log.e("ViewTreeObserver", ex.getMessage(), ex);
746 }
747 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700748 mOnDrawListeners.remove(victim);
749 }
750
751 /**
John Reck9ab32f82018-07-26 11:02:53 -0700752 * Adds a frame commit callback. This callback will be invoked when the current rendering
753 * content has been rendered into a frame and submitted to the swap chain. The frame may
754 * not currently be visible on the display when this is invoked, but it has been submitted.
755 * This callback is useful in combination with {@link PixelCopy} to capture the current
756 * rendered content of the UI reliably.
757 *
758 * Note: Only works with hardware rendering. Does nothing otherwise.
759 *
760 * @param callback The callback to invoke when the frame is committed.
761 */
John Reck9ab32f82018-07-26 11:02:53 -0700762 public void registerFrameCommitCallback(@NonNull Runnable callback) {
763 checkIsAlive();
764 if (mOnFrameCommitListeners == null) {
765 mOnFrameCommitListeners = new ArrayList<>();
766 }
767 mOnFrameCommitListeners.add(callback);
768 }
769
770 @Nullable ArrayList<Runnable> captureFrameCommitCallbacks() {
771 ArrayList<Runnable> ret = mOnFrameCommitListeners;
772 mOnFrameCommitListeners = null;
773 return ret;
774 }
775
776 /**
777 * Attempts to remove the given callback from the list of pending frame complete callbacks.
778 *
779 * @param callback The callback to remove
780 * @return Whether or not the callback was removed. If this returns true the callback will
781 * not be invoked. If false is returned then the callback was either never added
782 * or may already be pending execution and was unable to be removed
783 */
John Reck9ab32f82018-07-26 11:02:53 -0700784 public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) {
785 checkIsAlive();
786 if (mOnFrameCommitListeners == null) {
787 return false;
788 }
789 return mOnFrameCommitListeners.remove(callback);
790 }
791
792 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 * Register a callback to be invoked when a view has been scrolled.
794 *
795 * @param listener The callback to add
796 *
797 * @throws IllegalStateException If {@link #isAlive()} returns false
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 */
799 public void addOnScrollChangedListener(OnScrollChangedListener listener) {
800 checkIsAlive();
801
802 if (mOnScrollChangedListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700803 mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
805
806 mOnScrollChangedListeners.add(listener);
807 }
808
809 /**
810 * Remove a previously installed scroll-changed callback
811 *
812 * @param victim The callback to remove
813 *
814 * @throws IllegalStateException If {@link #isAlive()} returns false
815 *
816 * @see #addOnScrollChangedListener(OnScrollChangedListener)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 */
818 public void removeOnScrollChangedListener(OnScrollChangedListener victim) {
819 checkIsAlive();
820 if (mOnScrollChangedListeners == null) {
821 return;
822 }
823 mOnScrollChangedListeners.remove(victim);
824 }
825
826 /**
827 * Register a callback to be invoked when the invoked when the touch mode changes.
828 *
829 * @param listener The callback to add
830 *
831 * @throws IllegalStateException If {@link #isAlive()} returns false
832 */
833 public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
834 checkIsAlive();
835
836 if (mOnTouchModeChangeListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700837 mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 }
839
840 mOnTouchModeChangeListeners.add(listener);
841 }
842
843 /**
844 * Remove a previously installed touch mode change callback
845 *
846 * @param victim The callback to remove
847 *
848 * @throws IllegalStateException If {@link #isAlive()} returns false
849 *
850 * @see #addOnTouchModeChangeListener(OnTouchModeChangeListener)
851 */
852 public void removeOnTouchModeChangeListener(OnTouchModeChangeListener victim) {
853 checkIsAlive();
854 if (mOnTouchModeChangeListeners == null) {
855 return;
856 }
857 mOnTouchModeChangeListeners.remove(victim);
858 }
859
860 /**
861 * Register a callback to be invoked when the invoked when it is time to
862 * compute the window's internal insets.
863 *
864 * @param listener The callback to add
865 *
866 * @throws IllegalStateException If {@link #isAlive()} returns false
Dianne Hackborn935ae462009-04-13 16:11:55 -0700867 *
868 * We are not yet ready to commit to this API and support it, so
869 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100871 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener) {
873 checkIsAlive();
874
875 if (mOnComputeInternalInsetsListeners == null) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700876 mOnComputeInternalInsetsListeners =
Romain Guyc39ed4a2012-06-12 12:06:46 -0700877 new CopyOnWriteArray<OnComputeInternalInsetsListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 }
879
880 mOnComputeInternalInsetsListeners.add(listener);
881 }
882
883 /**
884 * Remove a previously installed internal insets computation callback
885 *
886 * @param victim The callback to remove
887 *
888 * @throws IllegalStateException If {@link #isAlive()} returns false
889 *
890 * @see #addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener)
Dianne Hackborn935ae462009-04-13 16:11:55 -0700891 *
892 * We are not yet ready to commit to this API and support it, so
893 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100895 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 public void removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim) {
897 checkIsAlive();
898 if (mOnComputeInternalInsetsListeners == null) {
899 return;
900 }
901 mOnComputeInternalInsetsListeners.remove(victim);
902 }
903
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800904 /**
905 * @hide
906 */
907 public void addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
908 checkIsAlive();
909 if (mOnEnterAnimationCompleteListeners == null) {
910 mOnEnterAnimationCompleteListeners =
911 new CopyOnWriteArrayList<OnEnterAnimationCompleteListener>();
912 }
913 mOnEnterAnimationCompleteListeners.add(listener);
914 }
915
916 /**
917 * @hide
918 */
919 public void removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
920 checkIsAlive();
921 if (mOnEnterAnimationCompleteListeners == null) {
922 return;
923 }
924 mOnEnterAnimationCompleteListeners.remove(listener);
925 }
926
Adam Powell769b8632019-02-04 11:28:22 -0800927 /**
928 * Add a listener to be notified when the tree's <em>transformed</em> gesture exclusion rects
929 * change. This could be the result of an animation or other layout change, or a view calling
930 * {@link View#setSystemGestureExclusionRects(List)}.
931 *
932 * @param listener listener to add
933 * @see View#setSystemGestureExclusionRects(List)
934 */
Adam Powellaa78d792019-04-10 11:06:17 -0700935 public void addOnSystemGestureExclusionRectsChangedListener(
936 @NonNull Consumer<List<Rect>> listener) {
Adam Powell769b8632019-02-04 11:28:22 -0800937 checkIsAlive();
938 if (mGestureExclusionListeners == null) {
939 mGestureExclusionListeners = new CopyOnWriteArray<>();
940 }
941 mGestureExclusionListeners.add(listener);
942 }
943
944 /**
945 * Unsubscribe the given listener from gesture exclusion rect changes.
946 * @see #addOnSystemGestureExclusionRectsChangedListener(Consumer)
947 * @see View#setSystemGestureExclusionRects(List)
948 */
Adam Powellaa78d792019-04-10 11:06:17 -0700949 public void removeOnSystemGestureExclusionRectsChangedListener(
950 @NonNull Consumer<List<Rect>> listener) {
Adam Powell769b8632019-02-04 11:28:22 -0800951 checkIsAlive();
952 if (mGestureExclusionListeners == null) {
953 return;
954 }
955 mGestureExclusionListeners.remove(listener);
956 }
957
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 private void checkIsAlive() {
959 if (!mAlive) {
960 throw new IllegalStateException("This ViewTreeObserver is not alive, call "
961 + "getViewTreeObserver() again");
962 }
963 }
964
965 /**
966 * Indicates whether this ViewTreeObserver is alive. When an observer is not alive,
967 * any call to a method (except this one) will throw an exception.
968 *
969 * If an application keeps a long-lived reference to this ViewTreeObserver, it should
970 * always check for the result of this method before calling any other method.
971 *
972 * @return True if this object is alive and be used, false otherwise.
973 */
974 public boolean isAlive() {
975 return mAlive;
976 }
977
978 /**
979 * Marks this ViewTreeObserver as not alive. After invoking this method, invoking
980 * any other method but {@link #isAlive()} and {@link #kill()} will throw an Exception.
981 *
982 * @hide
983 */
984 private void kill() {
985 mAlive = false;
986 }
987
988 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700989 * Notifies registered listeners that window has been attached/detached.
990 */
991 final void dispatchOnWindowAttachedChange(boolean attached) {
992 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
993 // perform the dispatching. The iterator is a safe guard against listeners that
994 // could mutate the list by calling the various add/remove methods. This prevents
995 // the array from being modified while we iterate it.
996 final CopyOnWriteArrayList<OnWindowAttachListener> listeners
997 = mOnWindowAttachListeners;
998 if (listeners != null && listeners.size() > 0) {
999 for (OnWindowAttachListener listener : listeners) {
1000 if (attached) listener.onWindowAttached();
1001 else listener.onWindowDetached();
1002 }
1003 }
1004 }
1005
1006 /**
1007 * Notifies registered listeners that window focus has changed.
1008 */
1009 final void dispatchOnWindowFocusChange(boolean hasFocus) {
1010 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1011 // perform the dispatching. The iterator is a safe guard against listeners that
1012 // could mutate the list by calling the various add/remove methods. This prevents
1013 // the array from being modified while we iterate it.
1014 final CopyOnWriteArrayList<OnWindowFocusChangeListener> listeners
1015 = mOnWindowFocusListeners;
1016 if (listeners != null && listeners.size() > 0) {
1017 for (OnWindowFocusChangeListener listener : listeners) {
1018 listener.onWindowFocusChanged(hasFocus);
1019 }
1020 }
1021 }
1022
1023 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 * Notifies registered listeners that focus has changed.
1025 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001026 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001028 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1029 // perform the dispatching. The iterator is a safe guard against listeners that
1030 // could mutate the list by calling the various add/remove methods. This prevents
1031 // the array from being modified while we iterate it.
Chet Haase0f8ffd82012-06-07 07:48:48 -07001032 final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001033 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001034 for (OnGlobalFocusChangeListener listener : listeners) {
1035 listener.onGlobalFocusChanged(oldFocus, newFocus);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 }
1037 }
1038 }
1039
1040 /**
1041 * Notifies registered listeners that a global layout happened. This can be called
1042 * manually if you are forcing a layout on a View or a hierarchy of Views that are
1043 * not attached to a Window or in the GONE state.
1044 */
1045 public final void dispatchOnGlobalLayout() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001046 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1047 // perform the dispatching. The iterator is a safe guard against listeners that
1048 // could mutate the list by calling the various add/remove methods. This prevents
1049 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001050 final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001051 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001052 CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
1053 try {
1054 int count = access.size();
1055 for (int i = 0; i < count; i++) {
1056 access.get(i).onGlobalLayout();
1057 }
1058 } finally {
1059 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 }
1061 }
1062 }
1063
1064 /**
Romain Guy21f42302013-06-28 19:19:30 -07001065 * Returns whether there are listeners for on pre-draw events.
1066 */
1067 final boolean hasOnPreDrawListeners() {
1068 return mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0;
1069 }
1070
1071 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 * Notifies registered listeners that the drawing pass is about to start. If a
1073 * listener returns true, then the drawing pass is canceled and rescheduled. This can
1074 * be called manually if you are forcing the drawing on a View or a hierarchy of Views
1075 * that are not attached to a Window or in the GONE state.
1076 *
1077 * @return True if the current draw should be canceled and resceduled, false otherwise.
1078 */
Romain Guy25eba5c2012-04-04 17:29:03 -07001079 @SuppressWarnings("unchecked")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 public final boolean dispatchOnPreDraw() {
1081 boolean cancelDraw = false;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001082 final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
1083 if (listeners != null && listeners.size() > 0) {
1084 CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();
1085 try {
1086 int count = access.size();
1087 for (int i = 0; i < count; i++) {
1088 cancelDraw |= !(access.get(i).onPreDraw());
1089 }
1090 } finally {
1091 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 }
1093 }
1094 return cancelDraw;
1095 }
1096
1097 /**
Craig Mautner9c795042014-10-28 19:59:59 -07001098 * Notifies registered listeners that the window is now shown
1099 * @hide
1100 */
1101 @SuppressWarnings("unchecked")
1102 public final void dispatchOnWindowShown() {
1103 mWindowShown = true;
1104 final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners;
1105 if (listeners != null && listeners.size() > 0) {
1106 CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start();
1107 try {
1108 int count = access.size();
1109 for (int i = 0; i < count; i++) {
1110 access.get(i).onWindowShown();
1111 }
1112 } finally {
1113 listeners.end();
1114 }
1115 }
1116 }
1117
1118 /**
Romain Guy25eba5c2012-04-04 17:29:03 -07001119 * Notifies registered listeners that the drawing pass is about to start.
1120 */
1121 public final void dispatchOnDraw() {
1122 if (mOnDrawListeners != null) {
John Reckd94094e2016-09-08 14:12:26 -07001123 mInDispatchOnDraw = true;
Romain Guy25eba5c2012-04-04 17:29:03 -07001124 final ArrayList<OnDrawListener> listeners = mOnDrawListeners;
1125 int numListeners = listeners.size();
1126 for (int i = 0; i < numListeners; ++i) {
1127 listeners.get(i).onDraw();
1128 }
John Reckd94094e2016-09-08 14:12:26 -07001129 mInDispatchOnDraw = false;
Romain Guy25eba5c2012-04-04 17:29:03 -07001130 }
1131 }
1132
1133 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 * Notifies registered listeners that the touch mode has changed.
1135 *
1136 * @param inTouchMode True if the touch mode is now enabled, false otherwise.
1137 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001138 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 final void dispatchOnTouchModeChanged(boolean inTouchMode) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001140 final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001141 mOnTouchModeChangeListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001142 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001143 for (OnTouchModeChangeListener listener : listeners) {
1144 listener.onTouchModeChanged(inTouchMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 }
1146 }
1147 }
1148
1149 /**
1150 * Notifies registered listeners that something has scrolled.
1151 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001152 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 final void dispatchOnScrollChanged() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001154 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1155 // perform the dispatching. The iterator is a safe guard against listeners that
1156 // could mutate the list by calling the various add/remove methods. This prevents
1157 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001158 final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001159 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001160 CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start();
1161 try {
1162 int count = access.size();
1163 for (int i = 0; i < count; i++) {
1164 access.get(i).onScrollChanged();
1165 }
1166 } finally {
1167 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 }
1169 }
1170 }
1171
1172 /**
1173 * Returns whether there are listeners for computing internal insets.
1174 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001175 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 final boolean hasComputeInternalInsetsListeners() {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001177 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001178 mOnComputeInternalInsetsListeners;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 return (listeners != null && listeners.size() > 0);
1180 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001181
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 /**
1183 * Calls all listeners to compute the current insets.
1184 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001185 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001187 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1188 // perform the dispatching. The iterator is a safe guard against listeners that
1189 // could mutate the list by calling the various add/remove methods. This prevents
1190 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001191 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001192 mOnComputeInternalInsetsListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001193 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001194 CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
1195 try {
1196 int count = access.size();
1197 for (int i = 0; i < count; i++) {
1198 access.get(i).onComputeInternalInsets(inoutInfo);
1199 }
1200 } finally {
1201 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001202 }
1203 }
1204 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001205
1206 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -08001207 * @hide
1208 */
1209 public final void dispatchOnEnterAnimationComplete() {
1210 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1211 // perform the dispatching. The iterator is a safe guard against listeners that
1212 // could mutate the list by calling the various add/remove methods. This prevents
1213 // the array from being modified while we iterate it.
1214 final CopyOnWriteArrayList<OnEnterAnimationCompleteListener> listeners =
1215 mOnEnterAnimationCompleteListeners;
1216 if (listeners != null && !listeners.isEmpty()) {
1217 for (OnEnterAnimationCompleteListener listener : listeners) {
1218 listener.onEnterAnimationComplete();
1219 }
1220 }
1221 }
1222
Adam Powell769b8632019-02-04 11:28:22 -08001223 void dispatchOnSystemGestureExclusionRectsChanged(@NonNull List<Rect> rects) {
1224 final CopyOnWriteArray<Consumer<List<Rect>>> listeners = mGestureExclusionListeners;
1225 if (listeners != null && listeners.size() > 0) {
1226 CopyOnWriteArray.Access<Consumer<List<Rect>>> access = listeners.start();
1227 try {
1228 final int count = access.size();
1229 for (int i = 0; i < count; i++) {
1230 access.get(i).accept(rects);
1231 }
1232 } finally {
1233 listeners.end();
1234 }
1235 }
1236 }
1237
Filip Gruszczynski6eafa902014-11-14 14:24:37 -08001238 /**
Romain Guyc39ed4a2012-06-12 12:06:46 -07001239 * Copy on write array. This array is not thread safe, and only one loop can
1240 * iterate over this array at any given time. This class avoids allocations
1241 * until a concurrent modification happens.
1242 *
1243 * Usage:
1244 *
1245 * CopyOnWriteArray.Access<MyData> access = array.start();
1246 * try {
1247 * for (int i = 0; i < access.size(); i++) {
1248 * MyData d = access.get(i);
1249 * }
1250 * } finally {
1251 * access.end();
1252 * }
1253 */
1254 static class CopyOnWriteArray<T> {
1255 private ArrayList<T> mData = new ArrayList<T>();
1256 private ArrayList<T> mDataCopy;
1257
1258 private final Access<T> mAccess = new Access<T>();
1259
1260 private boolean mStart;
1261
1262 static class Access<T> {
1263 private ArrayList<T> mData;
1264 private int mSize;
1265
1266 T get(int index) {
1267 return mData.get(index);
1268 }
1269
1270 int size() {
1271 return mSize;
1272 }
1273 }
1274
1275 CopyOnWriteArray() {
1276 }
1277
1278 private ArrayList<T> getArray() {
1279 if (mStart) {
1280 if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData);
1281 return mDataCopy;
1282 }
1283 return mData;
1284 }
1285
1286 Access<T> start() {
1287 if (mStart) throw new IllegalStateException("Iteration already started");
1288 mStart = true;
1289 mDataCopy = null;
1290 mAccess.mData = mData;
1291 mAccess.mSize = mData.size();
1292 return mAccess;
1293 }
1294
1295 void end() {
1296 if (!mStart) throw new IllegalStateException("Iteration not started");
1297 mStart = false;
1298 if (mDataCopy != null) {
1299 mData = mDataCopy;
Chet Haasefc343962013-09-18 08:44:33 -07001300 mAccess.mData.clear();
1301 mAccess.mSize = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001302 }
1303 mDataCopy = null;
1304 }
1305
1306 int size() {
1307 return getArray().size();
1308 }
1309
1310 void add(T item) {
1311 getArray().add(item);
1312 }
1313
1314 void addAll(CopyOnWriteArray<T> array) {
1315 getArray().addAll(array.mData);
1316 }
1317
1318 void remove(T item) {
1319 getArray().remove(item);
1320 }
1321
1322 void clear() {
1323 getArray().clear();
1324 }
1325 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001326}