blob: 2896bd049e7cd6e2b328a2f5f9a2d67126d7b915 [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 */
935 public void addOnSystemGestureExclusionRectsChangedListener(Consumer<List<Rect>> listener) {
936 checkIsAlive();
937 if (mGestureExclusionListeners == null) {
938 mGestureExclusionListeners = new CopyOnWriteArray<>();
939 }
940 mGestureExclusionListeners.add(listener);
941 }
942
943 /**
944 * Unsubscribe the given listener from gesture exclusion rect changes.
945 * @see #addOnSystemGestureExclusionRectsChangedListener(Consumer)
946 * @see View#setSystemGestureExclusionRects(List)
947 */
948 public void removeOnSystemGestureExclusionRectsChangedListener(Consumer<List<Rect>> listener) {
949 checkIsAlive();
950 if (mGestureExclusionListeners == null) {
951 return;
952 }
953 mGestureExclusionListeners.remove(listener);
954 }
955
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 private void checkIsAlive() {
957 if (!mAlive) {
958 throw new IllegalStateException("This ViewTreeObserver is not alive, call "
959 + "getViewTreeObserver() again");
960 }
961 }
962
963 /**
964 * Indicates whether this ViewTreeObserver is alive. When an observer is not alive,
965 * any call to a method (except this one) will throw an exception.
966 *
967 * If an application keeps a long-lived reference to this ViewTreeObserver, it should
968 * always check for the result of this method before calling any other method.
969 *
970 * @return True if this object is alive and be used, false otherwise.
971 */
972 public boolean isAlive() {
973 return mAlive;
974 }
975
976 /**
977 * Marks this ViewTreeObserver as not alive. After invoking this method, invoking
978 * any other method but {@link #isAlive()} and {@link #kill()} will throw an Exception.
979 *
980 * @hide
981 */
982 private void kill() {
983 mAlive = false;
984 }
985
986 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700987 * Notifies registered listeners that window has been attached/detached.
988 */
989 final void dispatchOnWindowAttachedChange(boolean attached) {
990 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
991 // perform the dispatching. The iterator is a safe guard against listeners that
992 // could mutate the list by calling the various add/remove methods. This prevents
993 // the array from being modified while we iterate it.
994 final CopyOnWriteArrayList<OnWindowAttachListener> listeners
995 = mOnWindowAttachListeners;
996 if (listeners != null && listeners.size() > 0) {
997 for (OnWindowAttachListener listener : listeners) {
998 if (attached) listener.onWindowAttached();
999 else listener.onWindowDetached();
1000 }
1001 }
1002 }
1003
1004 /**
1005 * Notifies registered listeners that window focus has changed.
1006 */
1007 final void dispatchOnWindowFocusChange(boolean hasFocus) {
1008 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1009 // perform the dispatching. The iterator is a safe guard against listeners that
1010 // could mutate the list by calling the various add/remove methods. This prevents
1011 // the array from being modified while we iterate it.
1012 final CopyOnWriteArrayList<OnWindowFocusChangeListener> listeners
1013 = mOnWindowFocusListeners;
1014 if (listeners != null && listeners.size() > 0) {
1015 for (OnWindowFocusChangeListener listener : listeners) {
1016 listener.onWindowFocusChanged(hasFocus);
1017 }
1018 }
1019 }
1020
1021 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 * Notifies registered listeners that focus has changed.
1023 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001024 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001026 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1027 // perform the dispatching. The iterator is a safe guard against listeners that
1028 // could mutate the list by calling the various add/remove methods. This prevents
1029 // the array from being modified while we iterate it.
Chet Haase0f8ffd82012-06-07 07:48:48 -07001030 final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001031 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001032 for (OnGlobalFocusChangeListener listener : listeners) {
1033 listener.onGlobalFocusChanged(oldFocus, newFocus);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 }
1035 }
1036 }
1037
1038 /**
1039 * Notifies registered listeners that a global layout happened. This can be called
1040 * manually if you are forcing a layout on a View or a hierarchy of Views that are
1041 * not attached to a Window or in the GONE state.
1042 */
1043 public final void dispatchOnGlobalLayout() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001044 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1045 // perform the dispatching. The iterator is a safe guard against listeners that
1046 // could mutate the list by calling the various add/remove methods. This prevents
1047 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001048 final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001049 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001050 CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
1051 try {
1052 int count = access.size();
1053 for (int i = 0; i < count; i++) {
1054 access.get(i).onGlobalLayout();
1055 }
1056 } finally {
1057 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 }
1059 }
1060 }
1061
1062 /**
Romain Guy21f42302013-06-28 19:19:30 -07001063 * Returns whether there are listeners for on pre-draw events.
1064 */
1065 final boolean hasOnPreDrawListeners() {
1066 return mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0;
1067 }
1068
1069 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 * Notifies registered listeners that the drawing pass is about to start. If a
1071 * listener returns true, then the drawing pass is canceled and rescheduled. This can
1072 * be called manually if you are forcing the drawing on a View or a hierarchy of Views
1073 * that are not attached to a Window or in the GONE state.
1074 *
1075 * @return True if the current draw should be canceled and resceduled, false otherwise.
1076 */
Romain Guy25eba5c2012-04-04 17:29:03 -07001077 @SuppressWarnings("unchecked")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 public final boolean dispatchOnPreDraw() {
1079 boolean cancelDraw = false;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001080 final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
1081 if (listeners != null && listeners.size() > 0) {
1082 CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();
1083 try {
1084 int count = access.size();
1085 for (int i = 0; i < count; i++) {
1086 cancelDraw |= !(access.get(i).onPreDraw());
1087 }
1088 } finally {
1089 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 }
1091 }
1092 return cancelDraw;
1093 }
1094
1095 /**
Craig Mautner9c795042014-10-28 19:59:59 -07001096 * Notifies registered listeners that the window is now shown
1097 * @hide
1098 */
1099 @SuppressWarnings("unchecked")
1100 public final void dispatchOnWindowShown() {
1101 mWindowShown = true;
1102 final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners;
1103 if (listeners != null && listeners.size() > 0) {
1104 CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start();
1105 try {
1106 int count = access.size();
1107 for (int i = 0; i < count; i++) {
1108 access.get(i).onWindowShown();
1109 }
1110 } finally {
1111 listeners.end();
1112 }
1113 }
1114 }
1115
1116 /**
Romain Guy25eba5c2012-04-04 17:29:03 -07001117 * Notifies registered listeners that the drawing pass is about to start.
1118 */
1119 public final void dispatchOnDraw() {
1120 if (mOnDrawListeners != null) {
John Reckd94094e2016-09-08 14:12:26 -07001121 mInDispatchOnDraw = true;
Romain Guy25eba5c2012-04-04 17:29:03 -07001122 final ArrayList<OnDrawListener> listeners = mOnDrawListeners;
1123 int numListeners = listeners.size();
1124 for (int i = 0; i < numListeners; ++i) {
1125 listeners.get(i).onDraw();
1126 }
John Reckd94094e2016-09-08 14:12:26 -07001127 mInDispatchOnDraw = false;
Romain Guy25eba5c2012-04-04 17:29:03 -07001128 }
1129 }
1130
1131 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 * Notifies registered listeners that the touch mode has changed.
1133 *
1134 * @param inTouchMode True if the touch mode is now enabled, false otherwise.
1135 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001136 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 final void dispatchOnTouchModeChanged(boolean inTouchMode) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001138 final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001139 mOnTouchModeChangeListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001140 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001141 for (OnTouchModeChangeListener listener : listeners) {
1142 listener.onTouchModeChanged(inTouchMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 }
1144 }
1145 }
1146
1147 /**
1148 * Notifies registered listeners that something has scrolled.
1149 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001150 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 final void dispatchOnScrollChanged() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001152 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1153 // perform the dispatching. The iterator is a safe guard against listeners that
1154 // could mutate the list by calling the various add/remove methods. This prevents
1155 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001156 final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001157 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001158 CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start();
1159 try {
1160 int count = access.size();
1161 for (int i = 0; i < count; i++) {
1162 access.get(i).onScrollChanged();
1163 }
1164 } finally {
1165 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 }
1167 }
1168 }
1169
1170 /**
1171 * Returns whether there are listeners for computing internal insets.
1172 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001173 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 final boolean hasComputeInternalInsetsListeners() {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001175 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001176 mOnComputeInternalInsetsListeners;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 return (listeners != null && listeners.size() > 0);
1178 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 /**
1181 * Calls all listeners to compute the current insets.
1182 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001183 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001184 final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001185 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1186 // perform the dispatching. The iterator is a safe guard against listeners that
1187 // could mutate the list by calling the various add/remove methods. This prevents
1188 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001189 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001190 mOnComputeInternalInsetsListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001191 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001192 CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
1193 try {
1194 int count = access.size();
1195 for (int i = 0; i < count; i++) {
1196 access.get(i).onComputeInternalInsets(inoutInfo);
1197 }
1198 } finally {
1199 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 }
1201 }
1202 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001203
1204 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -08001205 * @hide
1206 */
1207 public final void dispatchOnEnterAnimationComplete() {
1208 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1209 // perform the dispatching. The iterator is a safe guard against listeners that
1210 // could mutate the list by calling the various add/remove methods. This prevents
1211 // the array from being modified while we iterate it.
1212 final CopyOnWriteArrayList<OnEnterAnimationCompleteListener> listeners =
1213 mOnEnterAnimationCompleteListeners;
1214 if (listeners != null && !listeners.isEmpty()) {
1215 for (OnEnterAnimationCompleteListener listener : listeners) {
1216 listener.onEnterAnimationComplete();
1217 }
1218 }
1219 }
1220
Adam Powell769b8632019-02-04 11:28:22 -08001221 void dispatchOnSystemGestureExclusionRectsChanged(@NonNull List<Rect> rects) {
1222 final CopyOnWriteArray<Consumer<List<Rect>>> listeners = mGestureExclusionListeners;
1223 if (listeners != null && listeners.size() > 0) {
1224 CopyOnWriteArray.Access<Consumer<List<Rect>>> access = listeners.start();
1225 try {
1226 final int count = access.size();
1227 for (int i = 0; i < count; i++) {
1228 access.get(i).accept(rects);
1229 }
1230 } finally {
1231 listeners.end();
1232 }
1233 }
1234 }
1235
Filip Gruszczynski6eafa902014-11-14 14:24:37 -08001236 /**
Romain Guyc39ed4a2012-06-12 12:06:46 -07001237 * Copy on write array. This array is not thread safe, and only one loop can
1238 * iterate over this array at any given time. This class avoids allocations
1239 * until a concurrent modification happens.
1240 *
1241 * Usage:
1242 *
1243 * CopyOnWriteArray.Access<MyData> access = array.start();
1244 * try {
1245 * for (int i = 0; i < access.size(); i++) {
1246 * MyData d = access.get(i);
1247 * }
1248 * } finally {
1249 * access.end();
1250 * }
1251 */
1252 static class CopyOnWriteArray<T> {
1253 private ArrayList<T> mData = new ArrayList<T>();
1254 private ArrayList<T> mDataCopy;
1255
1256 private final Access<T> mAccess = new Access<T>();
1257
1258 private boolean mStart;
1259
1260 static class Access<T> {
1261 private ArrayList<T> mData;
1262 private int mSize;
1263
1264 T get(int index) {
1265 return mData.get(index);
1266 }
1267
1268 int size() {
1269 return mSize;
1270 }
1271 }
1272
1273 CopyOnWriteArray() {
1274 }
1275
1276 private ArrayList<T> getArray() {
1277 if (mStart) {
1278 if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData);
1279 return mDataCopy;
1280 }
1281 return mData;
1282 }
1283
1284 Access<T> start() {
1285 if (mStart) throw new IllegalStateException("Iteration already started");
1286 mStart = true;
1287 mDataCopy = null;
1288 mAccess.mData = mData;
1289 mAccess.mSize = mData.size();
1290 return mAccess;
1291 }
1292
1293 void end() {
1294 if (!mStart) throw new IllegalStateException("Iteration not started");
1295 mStart = false;
1296 if (mDataCopy != null) {
1297 mData = mDataCopy;
Chet Haasefc343962013-09-18 08:44:33 -07001298 mAccess.mData.clear();
1299 mAccess.mSize = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001300 }
1301 mDataCopy = null;
1302 }
1303
1304 int size() {
1305 return getArray().size();
1306 }
1307
1308 void add(T item) {
1309 getArray().add(item);
1310 }
1311
1312 void addAll(CopyOnWriteArray<T> array) {
1313 getArray().addAll(array.mData);
1314 }
1315
1316 void remove(T item) {
1317 getArray().remove(item);
1318 }
1319
1320 void clear() {
1321 getArray().clear();
1322 }
1323 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324}