blob: d7b0afc89eaac5d0ce59d699f25f6e66f627d61e [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;
Artur Satayevad9254c2019-12-10 17:47:54 +000021import android.compat.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 {
Artur Satayev751e5512019-11-15 19:12:49 +0000221
222 @UnsupportedAppUsage
223 public InternalInsetsInfo() {
224 }
225
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 /**
227 * Offsets from the frame of the window at which the content of
228 * windows behind it should be placed.
229 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100230 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 public final Rect contentInsets = new Rect();
Romain Guyc39ed4a2012-06-12 12:06:46 -0700232
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 /**
Jeff Brownfbf09772011-01-16 14:06:57 -0800234 * Offsets from the frame of the window at which windows behind it
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 * are visible.
236 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100237 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 public final Rect visibleInsets = new Rect();
Jeff Brownfbf09772011-01-16 14:06:57 -0800239
240 /**
241 * Touchable region defined relative to the origin of the frame of the window.
242 * Only used when {@link #setTouchableInsets(int)} is called with
243 * the option {@link #TOUCHABLE_INSETS_REGION}.
244 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100245 @UnsupportedAppUsage
Jeff Brownfbf09772011-01-16 14:06:57 -0800246 public final Region touchableRegion = new Region();
247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 /**
249 * Option for {@link #setTouchableInsets(int)}: the entire window frame
250 * can be touched.
251 */
252 public static final int TOUCHABLE_INSETS_FRAME = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700253
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 /**
255 * Option for {@link #setTouchableInsets(int)}: the area inside of
256 * the content insets can be touched.
257 */
258 public static final int TOUCHABLE_INSETS_CONTENT = 1;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700259
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 /**
261 * Option for {@link #setTouchableInsets(int)}: the area inside of
262 * the visible insets can be touched.
263 */
264 public static final int TOUCHABLE_INSETS_VISIBLE = 2;
Jeff Brownfbf09772011-01-16 14:06:57 -0800265
266 /**
267 * Option for {@link #setTouchableInsets(int)}: the area inside of
268 * the provided touchable region in {@link #touchableRegion} can be touched.
269 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100270 @UnsupportedAppUsage
Jeff Brownfbf09772011-01-16 14:06:57 -0800271 public static final int TOUCHABLE_INSETS_REGION = 3;
272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 /**
274 * Set which parts of the window can be touched: either
275 * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
Jeff Brownfbf09772011-01-16 14:06:57 -0800276 * {@link #TOUCHABLE_INSETS_VISIBLE}, or {@link #TOUCHABLE_INSETS_REGION}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100278 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 public void setTouchableInsets(int val) {
280 mTouchableInsets = val;
281 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700282
Mathew Inwooda570dee2018-08-17 14:56:00 +0100283 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 int mTouchableInsets;
Romain Guyc39ed4a2012-06-12 12:06:46 -0700285
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 void reset() {
Jeff Brownfbf09772011-01-16 14:06:57 -0800287 contentInsets.setEmpty();
288 visibleInsets.setEmpty();
289 touchableRegion.setEmpty();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 mTouchableInsets = TOUCHABLE_INSETS_FRAME;
291 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700292
Jeff Brown2e05ec32013-09-30 15:57:43 -0700293 boolean isEmpty() {
294 return contentInsets.isEmpty()
295 && visibleInsets.isEmpty()
296 && touchableRegion.isEmpty()
297 && mTouchableInsets == TOUCHABLE_INSETS_FRAME;
298 }
299
Romain Guy25eba5c2012-04-04 17:29:03 -0700300 @Override
301 public int hashCode() {
Romain Guy21f42302013-06-28 19:19:30 -0700302 int result = contentInsets.hashCode();
303 result = 31 * result + visibleInsets.hashCode();
304 result = 31 * result + touchableRegion.hashCode();
Romain Guy25eba5c2012-04-04 17:29:03 -0700305 result = 31 * result + mTouchableInsets;
306 return result;
307 }
308
Romain Guy1e878d22012-01-23 15:34:25 -0800309 @Override
310 public boolean equals(Object o) {
Romain Guy25eba5c2012-04-04 17:29:03 -0700311 if (this == o) return true;
312 if (o == null || getClass() != o.getClass()) return false;
313
314 InternalInsetsInfo other = (InternalInsetsInfo)o;
315 return mTouchableInsets == other.mTouchableInsets &&
316 contentInsets.equals(other.contentInsets) &&
317 visibleInsets.equals(other.visibleInsets) &&
318 touchableRegion.equals(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700320
Mathew Inwooda570dee2018-08-17 14:56:00 +0100321 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 void set(InternalInsetsInfo other) {
323 contentInsets.set(other.contentInsets);
324 visibleInsets.set(other.visibleInsets);
Jeff Brownfbf09772011-01-16 14:06:57 -0800325 touchableRegion.set(other.touchableRegion);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 mTouchableInsets = other.mTouchableInsets;
327 }
328 }
Romain Guyc39ed4a2012-06-12 12:06:46 -0700329
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 /**
331 * Interface definition for a callback to be invoked when layout has
332 * completed and the client can compute its interior insets.
Dianne Hackborn935ae462009-04-13 16:11:55 -0700333 *
334 * We are not yet ready to commit to this API and support it, so
335 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 */
337 public interface OnComputeInternalInsetsListener {
338 /**
339 * Callback method to be invoked when layout has completed and the
340 * client can compute its interior insets.
341 *
342 * @param inoutInfo Should be filled in by the implementation with
343 * the information about the insets of the window. This is called
344 * with whatever values the previous OnComputeInternalInsetsListener
345 * returned, if there are multiple such listeners in the window.
346 */
347 public void onComputeInternalInsets(InternalInsetsInfo inoutInfo);
348 }
349
350 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800351 * @hide
352 */
353 public interface OnEnterAnimationCompleteListener {
354 public void onEnterAnimationComplete();
355 }
356
357 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 * Creates a new ViewTreeObserver. This constructor should not be called
359 */
John Reckd94094e2016-09-08 14:12:26 -0700360 ViewTreeObserver(Context context) {
361 sIllegalOnDrawModificationIsFatal =
Jeff Sharkeyf383c242017-05-17 17:53:07 -0600362 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 }
364
365 /**
366 * Merges all the listeners registered on the specified observer with the listeners
367 * registered on this object. After this method is invoked, the specified observer
368 * will return false in {@link #isAlive()} and should not be used anymore.
369 *
370 * @param observer The ViewTreeObserver whose listeners must be added to this observer
371 */
372 void merge(ViewTreeObserver observer) {
Dianne Hackborn961cae92013-03-20 14:59:43 -0700373 if (observer.mOnWindowAttachListeners != null) {
374 if (mOnWindowAttachListeners != null) {
375 mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
376 } else {
377 mOnWindowAttachListeners = observer.mOnWindowAttachListeners;
378 }
379 }
380
381 if (observer.mOnWindowFocusListeners != null) {
382 if (mOnWindowFocusListeners != null) {
383 mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
384 } else {
385 mOnWindowFocusListeners = observer.mOnWindowFocusListeners;
386 }
387 }
388
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 if (observer.mOnGlobalFocusListeners != null) {
390 if (mOnGlobalFocusListeners != null) {
391 mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
392 } else {
393 mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;
394 }
395 }
396
397 if (observer.mOnGlobalLayoutListeners != null) {
398 if (mOnGlobalLayoutListeners != null) {
399 mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
400 } else {
401 mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;
402 }
403 }
404
405 if (observer.mOnPreDrawListeners != null) {
406 if (mOnPreDrawListeners != null) {
407 mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
408 } else {
409 mOnPreDrawListeners = observer.mOnPreDrawListeners;
410 }
411 }
412
John Reck9f8ec542016-12-12 11:23:05 -0800413 if (observer.mOnDrawListeners != null) {
414 if (mOnDrawListeners != null) {
415 mOnDrawListeners.addAll(observer.mOnDrawListeners);
416 } else {
417 mOnDrawListeners = observer.mOnDrawListeners;
418 }
419 }
420
John Reck9ab32f82018-07-26 11:02:53 -0700421 if (observer.mOnFrameCommitListeners != null) {
422 if (mOnFrameCommitListeners != null) {
423 mOnFrameCommitListeners.addAll(observer.captureFrameCommitCallbacks());
424 } else {
425 mOnFrameCommitListeners = observer.captureFrameCommitCallbacks();
426 }
427 }
428
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 if (observer.mOnTouchModeChangeListeners != null) {
430 if (mOnTouchModeChangeListeners != null) {
431 mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
432 } else {
433 mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;
434 }
435 }
436
437 if (observer.mOnComputeInternalInsetsListeners != null) {
438 if (mOnComputeInternalInsetsListeners != null) {
439 mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
440 } else {
441 mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;
442 }
443 }
444
Mark Brophy757c6972011-10-25 17:01:28 +0100445 if (observer.mOnScrollChangedListeners != null) {
446 if (mOnScrollChangedListeners != null) {
447 mOnScrollChangedListeners.addAll(observer.mOnScrollChangedListeners);
448 } else {
449 mOnScrollChangedListeners = observer.mOnScrollChangedListeners;
450 }
451 }
452
Craig Mautner9c795042014-10-28 19:59:59 -0700453 if (observer.mOnWindowShownListeners != null) {
454 if (mOnWindowShownListeners != null) {
455 mOnWindowShownListeners.addAll(observer.mOnWindowShownListeners);
456 } else {
457 mOnWindowShownListeners = observer.mOnWindowShownListeners;
458 }
459 }
460
Adam Powell769b8632019-02-04 11:28:22 -0800461 if (observer.mGestureExclusionListeners != null) {
462 if (mGestureExclusionListeners != null) {
463 mGestureExclusionListeners.addAll(observer.mGestureExclusionListeners);
464 } else {
465 mGestureExclusionListeners = observer.mGestureExclusionListeners;
466 }
467 }
468
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 observer.kill();
470 }
471
472 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700473 * Register a callback to be invoked when the view hierarchy is attached to a window.
474 *
475 * @param listener The callback to add
476 *
477 * @throws IllegalStateException If {@link #isAlive()} returns false
478 */
479 public void addOnWindowAttachListener(OnWindowAttachListener listener) {
480 checkIsAlive();
481
482 if (mOnWindowAttachListeners == null) {
483 mOnWindowAttachListeners
484 = new CopyOnWriteArrayList<OnWindowAttachListener>();
485 }
486
487 mOnWindowAttachListeners.add(listener);
488 }
489
490 /**
491 * Remove a previously installed window attach callback.
492 *
493 * @param victim The callback to remove
494 *
495 * @throws IllegalStateException If {@link #isAlive()} returns false
496 *
497 * @see #addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener)
498 */
499 public void removeOnWindowAttachListener(OnWindowAttachListener victim) {
500 checkIsAlive();
501 if (mOnWindowAttachListeners == null) {
502 return;
503 }
504 mOnWindowAttachListeners.remove(victim);
505 }
506
507 /**
508 * Register a callback to be invoked when the window focus state within the view tree changes.
509 *
510 * @param listener The callback to add
511 *
512 * @throws IllegalStateException If {@link #isAlive()} returns false
513 */
514 public void addOnWindowFocusChangeListener(OnWindowFocusChangeListener listener) {
515 checkIsAlive();
516
517 if (mOnWindowFocusListeners == null) {
518 mOnWindowFocusListeners
519 = new CopyOnWriteArrayList<OnWindowFocusChangeListener>();
520 }
521
522 mOnWindowFocusListeners.add(listener);
523 }
524
525 /**
526 * Remove a previously installed window focus change callback.
527 *
528 * @param victim The callback to remove
529 *
530 * @throws IllegalStateException If {@link #isAlive()} returns false
531 *
532 * @see #addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener)
533 */
534 public void removeOnWindowFocusChangeListener(OnWindowFocusChangeListener victim) {
535 checkIsAlive();
536 if (mOnWindowFocusListeners == null) {
537 return;
538 }
539 mOnWindowFocusListeners.remove(victim);
540 }
541
542 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 * Register a callback to be invoked when the focus state within the view tree changes.
544 *
545 * @param listener The callback to add
546 *
547 * @throws IllegalStateException If {@link #isAlive()} returns false
548 */
549 public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener) {
550 checkIsAlive();
551
552 if (mOnGlobalFocusListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700553 mOnGlobalFocusListeners = new CopyOnWriteArrayList<OnGlobalFocusChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 }
555
556 mOnGlobalFocusListeners.add(listener);
557 }
558
559 /**
560 * Remove a previously installed focus change callback.
561 *
562 * @param victim The callback to remove
563 *
564 * @throws IllegalStateException If {@link #isAlive()} returns false
565 *
566 * @see #addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener)
567 */
568 public void removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim) {
569 checkIsAlive();
570 if (mOnGlobalFocusListeners == null) {
571 return;
572 }
573 mOnGlobalFocusListeners.remove(victim);
574 }
575
576 /**
577 * Register a callback to be invoked when the global layout state or the visibility of views
578 * within the view tree changes
579 *
580 * @param listener The callback to add
581 *
582 * @throws IllegalStateException If {@link #isAlive()} returns false
583 */
584 public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
585 checkIsAlive();
586
587 if (mOnGlobalLayoutListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700588 mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 }
590
591 mOnGlobalLayoutListeners.add(listener);
592 }
593
594 /**
595 * Remove a previously installed global layout callback
596 *
597 * @param victim The callback to remove
598 *
599 * @throws IllegalStateException If {@link #isAlive()} returns false
Romain Guy1e878d22012-01-23 15:34:25 -0800600 *
601 * @deprecated Use #removeOnGlobalLayoutListener instead
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 *
603 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
604 */
Romain Guy1e878d22012-01-23 15:34:25 -0800605 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
Romain Guy1e878d22012-01-23 15:34:25 -0800607 removeOnGlobalLayoutListener(victim);
608 }
609
610 /**
611 * Remove a previously installed global layout callback
612 *
613 * @param victim The callback to remove
614 *
615 * @throws IllegalStateException If {@link #isAlive()} returns false
616 *
617 * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
618 */
619 public void removeOnGlobalLayoutListener(OnGlobalLayoutListener victim) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 checkIsAlive();
621 if (mOnGlobalLayoutListeners == null) {
622 return;
623 }
624 mOnGlobalLayoutListeners.remove(victim);
625 }
626
627 /**
628 * Register a callback to be invoked when the view tree is about to be drawn
629 *
630 * @param listener The callback to add
631 *
632 * @throws IllegalStateException If {@link #isAlive()} returns false
633 */
634 public void addOnPreDrawListener(OnPreDrawListener listener) {
635 checkIsAlive();
636
637 if (mOnPreDrawListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700638 mOnPreDrawListeners = new CopyOnWriteArray<OnPreDrawListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 }
640
641 mOnPreDrawListeners.add(listener);
642 }
643
644 /**
645 * Remove a previously installed pre-draw callback
646 *
647 * @param victim The callback to remove
648 *
649 * @throws IllegalStateException If {@link #isAlive()} returns false
650 *
651 * @see #addOnPreDrawListener(OnPreDrawListener)
652 */
653 public void removeOnPreDrawListener(OnPreDrawListener victim) {
654 checkIsAlive();
655 if (mOnPreDrawListeners == null) {
656 return;
657 }
658 mOnPreDrawListeners.remove(victim);
659 }
660
661 /**
Craig Mautner9c795042014-10-28 19:59:59 -0700662 * Register a callback to be invoked when the view tree window has been shown
663 *
664 * @param listener The callback to add
665 *
666 * @throws IllegalStateException If {@link #isAlive()} returns false
667 * @hide
668 */
669 public void addOnWindowShownListener(OnWindowShownListener listener) {
670 checkIsAlive();
671
672 if (mOnWindowShownListeners == null) {
673 mOnWindowShownListeners = new CopyOnWriteArray<OnWindowShownListener>();
674 }
675
676 mOnWindowShownListeners.add(listener);
677 if (mWindowShown) {
678 listener.onWindowShown();
679 }
680 }
681
682 /**
683 * Remove a previously installed window shown callback
684 *
685 * @param victim The callback to remove
686 *
687 * @throws IllegalStateException If {@link #isAlive()} returns false
688 *
689 * @see #addOnWindowShownListener(OnWindowShownListener)
690 * @hide
691 */
692 public void removeOnWindowShownListener(OnWindowShownListener victim) {
693 checkIsAlive();
694 if (mOnWindowShownListeners == null) {
695 return;
696 }
697 mOnWindowShownListeners.remove(victim);
698 }
699
700 /**
Romain Guy25eba5c2012-04-04 17:29:03 -0700701 * <p>Register a callback to be invoked when the view tree is about to be drawn.</p>
702 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
703 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
704 *
705 * @param listener The callback to add
706 *
707 * @throws IllegalStateException If {@link #isAlive()} returns false
708 */
709 public void addOnDrawListener(OnDrawListener listener) {
710 checkIsAlive();
711
712 if (mOnDrawListeners == null) {
713 mOnDrawListeners = new ArrayList<OnDrawListener>();
714 }
715
John Reckd94094e2016-09-08 14:12:26 -0700716 if (mInDispatchOnDraw) {
717 IllegalStateException ex = new IllegalStateException(
718 "Cannot call addOnDrawListener inside of onDraw");
719 if (sIllegalOnDrawModificationIsFatal) {
720 throw ex;
721 } else {
722 Log.e("ViewTreeObserver", ex.getMessage(), ex);
723 }
724 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700725 mOnDrawListeners.add(listener);
726 }
727
728 /**
729 * <p>Remove a previously installed pre-draw callback.</p>
730 * <p><strong>Note:</strong> this method <strong>cannot</strong> be invoked from
731 * {@link android.view.ViewTreeObserver.OnDrawListener#onDraw()}.</p>
732 *
733 * @param victim The callback to remove
734 *
735 * @throws IllegalStateException If {@link #isAlive()} returns false
736 *
737 * @see #addOnDrawListener(OnDrawListener)
738 */
739 public void removeOnDrawListener(OnDrawListener victim) {
740 checkIsAlive();
741 if (mOnDrawListeners == null) {
742 return;
743 }
John Reckd94094e2016-09-08 14:12:26 -0700744 if (mInDispatchOnDraw) {
745 IllegalStateException ex = new IllegalStateException(
746 "Cannot call removeOnDrawListener inside of onDraw");
747 if (sIllegalOnDrawModificationIsFatal) {
748 throw ex;
749 } else {
750 Log.e("ViewTreeObserver", ex.getMessage(), ex);
751 }
752 }
Romain Guy25eba5c2012-04-04 17:29:03 -0700753 mOnDrawListeners.remove(victim);
754 }
755
756 /**
John Reck9ab32f82018-07-26 11:02:53 -0700757 * Adds a frame commit callback. This callback will be invoked when the current rendering
758 * content has been rendered into a frame and submitted to the swap chain. The frame may
759 * not currently be visible on the display when this is invoked, but it has been submitted.
760 * This callback is useful in combination with {@link PixelCopy} to capture the current
761 * rendered content of the UI reliably.
762 *
763 * Note: Only works with hardware rendering. Does nothing otherwise.
764 *
765 * @param callback The callback to invoke when the frame is committed.
766 */
John Reck9ab32f82018-07-26 11:02:53 -0700767 public void registerFrameCommitCallback(@NonNull Runnable callback) {
768 checkIsAlive();
769 if (mOnFrameCommitListeners == null) {
770 mOnFrameCommitListeners = new ArrayList<>();
771 }
772 mOnFrameCommitListeners.add(callback);
773 }
774
775 @Nullable ArrayList<Runnable> captureFrameCommitCallbacks() {
776 ArrayList<Runnable> ret = mOnFrameCommitListeners;
777 mOnFrameCommitListeners = null;
778 return ret;
779 }
780
781 /**
782 * Attempts to remove the given callback from the list of pending frame complete callbacks.
783 *
784 * @param callback The callback to remove
785 * @return Whether or not the callback was removed. If this returns true the callback will
786 * not be invoked. If false is returned then the callback was either never added
787 * or may already be pending execution and was unable to be removed
788 */
John Reck9ab32f82018-07-26 11:02:53 -0700789 public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) {
790 checkIsAlive();
791 if (mOnFrameCommitListeners == null) {
792 return false;
793 }
794 return mOnFrameCommitListeners.remove(callback);
795 }
796
797 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 * Register a callback to be invoked when a view has been scrolled.
799 *
800 * @param listener The callback to add
801 *
802 * @throws IllegalStateException If {@link #isAlive()} returns false
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 */
804 public void addOnScrollChangedListener(OnScrollChangedListener listener) {
805 checkIsAlive();
806
807 if (mOnScrollChangedListeners == null) {
Romain Guyc39ed4a2012-06-12 12:06:46 -0700808 mOnScrollChangedListeners = new CopyOnWriteArray<OnScrollChangedListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 }
810
811 mOnScrollChangedListeners.add(listener);
812 }
813
814 /**
815 * Remove a previously installed scroll-changed callback
816 *
817 * @param victim The callback to remove
818 *
819 * @throws IllegalStateException If {@link #isAlive()} returns false
820 *
821 * @see #addOnScrollChangedListener(OnScrollChangedListener)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 */
823 public void removeOnScrollChangedListener(OnScrollChangedListener victim) {
824 checkIsAlive();
825 if (mOnScrollChangedListeners == null) {
826 return;
827 }
828 mOnScrollChangedListeners.remove(victim);
829 }
830
831 /**
832 * Register a callback to be invoked when the invoked when the touch mode changes.
833 *
834 * @param listener The callback to add
835 *
836 * @throws IllegalStateException If {@link #isAlive()} returns false
837 */
838 public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
839 checkIsAlive();
840
841 if (mOnTouchModeChangeListeners == null) {
Chet Haase0f8ffd82012-06-07 07:48:48 -0700842 mOnTouchModeChangeListeners = new CopyOnWriteArrayList<OnTouchModeChangeListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 }
844
845 mOnTouchModeChangeListeners.add(listener);
846 }
847
848 /**
849 * Remove a previously installed touch mode change callback
850 *
851 * @param victim The callback to remove
852 *
853 * @throws IllegalStateException If {@link #isAlive()} returns false
854 *
855 * @see #addOnTouchModeChangeListener(OnTouchModeChangeListener)
856 */
857 public void removeOnTouchModeChangeListener(OnTouchModeChangeListener victim) {
858 checkIsAlive();
859 if (mOnTouchModeChangeListeners == null) {
860 return;
861 }
862 mOnTouchModeChangeListeners.remove(victim);
863 }
864
865 /**
866 * Register a callback to be invoked when the invoked when it is time to
867 * compute the window's internal insets.
868 *
869 * @param listener The callback to add
870 *
871 * @throws IllegalStateException If {@link #isAlive()} returns false
Dianne Hackborn935ae462009-04-13 16:11:55 -0700872 *
873 * We are not yet ready to commit to this API and support it, so
874 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100876 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener) {
878 checkIsAlive();
879
880 if (mOnComputeInternalInsetsListeners == null) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700881 mOnComputeInternalInsetsListeners =
Romain Guyc39ed4a2012-06-12 12:06:46 -0700882 new CopyOnWriteArray<OnComputeInternalInsetsListener>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 }
884
885 mOnComputeInternalInsetsListeners.add(listener);
886 }
887
888 /**
889 * Remove a previously installed internal insets computation callback
890 *
891 * @param victim The callback to remove
892 *
893 * @throws IllegalStateException If {@link #isAlive()} returns false
894 *
895 * @see #addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener)
Dianne Hackborn935ae462009-04-13 16:11:55 -0700896 *
897 * We are not yet ready to commit to this API and support it, so
898 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100900 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 public void removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim) {
902 checkIsAlive();
903 if (mOnComputeInternalInsetsListeners == null) {
904 return;
905 }
906 mOnComputeInternalInsetsListeners.remove(victim);
907 }
908
Filip Gruszczynski6eafa902014-11-14 14:24:37 -0800909 /**
910 * @hide
911 */
912 public void addOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
913 checkIsAlive();
914 if (mOnEnterAnimationCompleteListeners == null) {
915 mOnEnterAnimationCompleteListeners =
916 new CopyOnWriteArrayList<OnEnterAnimationCompleteListener>();
917 }
918 mOnEnterAnimationCompleteListeners.add(listener);
919 }
920
921 /**
922 * @hide
923 */
924 public void removeOnEnterAnimationCompleteListener(OnEnterAnimationCompleteListener listener) {
925 checkIsAlive();
926 if (mOnEnterAnimationCompleteListeners == null) {
927 return;
928 }
929 mOnEnterAnimationCompleteListeners.remove(listener);
930 }
931
Adam Powell769b8632019-02-04 11:28:22 -0800932 /**
933 * Add a listener to be notified when the tree's <em>transformed</em> gesture exclusion rects
934 * change. This could be the result of an animation or other layout change, or a view calling
935 * {@link View#setSystemGestureExclusionRects(List)}.
936 *
937 * @param listener listener to add
938 * @see View#setSystemGestureExclusionRects(List)
939 */
Adam Powellaa78d792019-04-10 11:06:17 -0700940 public void addOnSystemGestureExclusionRectsChangedListener(
941 @NonNull Consumer<List<Rect>> listener) {
Adam Powell769b8632019-02-04 11:28:22 -0800942 checkIsAlive();
943 if (mGestureExclusionListeners == null) {
944 mGestureExclusionListeners = new CopyOnWriteArray<>();
945 }
946 mGestureExclusionListeners.add(listener);
947 }
948
949 /**
950 * Unsubscribe the given listener from gesture exclusion rect changes.
951 * @see #addOnSystemGestureExclusionRectsChangedListener(Consumer)
952 * @see View#setSystemGestureExclusionRects(List)
953 */
Adam Powellaa78d792019-04-10 11:06:17 -0700954 public void removeOnSystemGestureExclusionRectsChangedListener(
955 @NonNull Consumer<List<Rect>> listener) {
Adam Powell769b8632019-02-04 11:28:22 -0800956 checkIsAlive();
957 if (mGestureExclusionListeners == null) {
958 return;
959 }
960 mGestureExclusionListeners.remove(listener);
961 }
962
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 private void checkIsAlive() {
964 if (!mAlive) {
965 throw new IllegalStateException("This ViewTreeObserver is not alive, call "
966 + "getViewTreeObserver() again");
967 }
968 }
969
970 /**
971 * Indicates whether this ViewTreeObserver is alive. When an observer is not alive,
972 * any call to a method (except this one) will throw an exception.
973 *
974 * If an application keeps a long-lived reference to this ViewTreeObserver, it should
975 * always check for the result of this method before calling any other method.
976 *
977 * @return True if this object is alive and be used, false otherwise.
978 */
979 public boolean isAlive() {
980 return mAlive;
981 }
982
983 /**
984 * Marks this ViewTreeObserver as not alive. After invoking this method, invoking
985 * any other method but {@link #isAlive()} and {@link #kill()} will throw an Exception.
986 *
987 * @hide
988 */
989 private void kill() {
990 mAlive = false;
991 }
992
993 /**
Dianne Hackborn961cae92013-03-20 14:59:43 -0700994 * Notifies registered listeners that window has been attached/detached.
995 */
996 final void dispatchOnWindowAttachedChange(boolean attached) {
997 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
998 // perform the dispatching. The iterator is a safe guard against listeners that
999 // could mutate the list by calling the various add/remove methods. This prevents
1000 // the array from being modified while we iterate it.
1001 final CopyOnWriteArrayList<OnWindowAttachListener> listeners
1002 = mOnWindowAttachListeners;
1003 if (listeners != null && listeners.size() > 0) {
1004 for (OnWindowAttachListener listener : listeners) {
1005 if (attached) listener.onWindowAttached();
1006 else listener.onWindowDetached();
1007 }
1008 }
1009 }
1010
1011 /**
1012 * Notifies registered listeners that window focus has changed.
1013 */
1014 final void dispatchOnWindowFocusChange(boolean hasFocus) {
1015 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1016 // perform the dispatching. The iterator is a safe guard against listeners that
1017 // could mutate the list by calling the various add/remove methods. This prevents
1018 // the array from being modified while we iterate it.
1019 final CopyOnWriteArrayList<OnWindowFocusChangeListener> listeners
1020 = mOnWindowFocusListeners;
1021 if (listeners != null && listeners.size() > 0) {
1022 for (OnWindowFocusChangeListener listener : listeners) {
1023 listener.onWindowFocusChanged(hasFocus);
1024 }
1025 }
1026 }
1027
1028 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029 * Notifies registered listeners that focus has changed.
1030 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001031 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001033 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1034 // perform the dispatching. The iterator is a safe guard against listeners that
1035 // could mutate the list by calling the various add/remove methods. This prevents
1036 // the array from being modified while we iterate it.
Chet Haase0f8ffd82012-06-07 07:48:48 -07001037 final CopyOnWriteArrayList<OnGlobalFocusChangeListener> listeners = mOnGlobalFocusListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001038 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001039 for (OnGlobalFocusChangeListener listener : listeners) {
1040 listener.onGlobalFocusChanged(oldFocus, newFocus);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 }
1042 }
1043 }
1044
1045 /**
1046 * Notifies registered listeners that a global layout happened. This can be called
1047 * manually if you are forcing a layout on a View or a hierarchy of Views that are
1048 * not attached to a Window or in the GONE state.
1049 */
1050 public final void dispatchOnGlobalLayout() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001051 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1052 // perform the dispatching. The iterator is a safe guard against listeners that
1053 // could mutate the list by calling the various add/remove methods. This prevents
1054 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001055 final CopyOnWriteArray<OnGlobalLayoutListener> listeners = mOnGlobalLayoutListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001056 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001057 CopyOnWriteArray.Access<OnGlobalLayoutListener> access = listeners.start();
1058 try {
1059 int count = access.size();
1060 for (int i = 0; i < count; i++) {
1061 access.get(i).onGlobalLayout();
1062 }
1063 } finally {
1064 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 }
1066 }
1067 }
1068
1069 /**
Romain Guy21f42302013-06-28 19:19:30 -07001070 * Returns whether there are listeners for on pre-draw events.
1071 */
1072 final boolean hasOnPreDrawListeners() {
1073 return mOnPreDrawListeners != null && mOnPreDrawListeners.size() > 0;
1074 }
1075
1076 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 * Notifies registered listeners that the drawing pass is about to start. If a
1078 * listener returns true, then the drawing pass is canceled and rescheduled. This can
1079 * be called manually if you are forcing the drawing on a View or a hierarchy of Views
1080 * that are not attached to a Window or in the GONE state.
1081 *
Issei Suzuki60557b52019-07-03 20:04:48 +02001082 * @return True if the current draw should be canceled and rescheduled, false otherwise.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 */
Romain Guy25eba5c2012-04-04 17:29:03 -07001084 @SuppressWarnings("unchecked")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 public final boolean dispatchOnPreDraw() {
1086 boolean cancelDraw = false;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001087 final CopyOnWriteArray<OnPreDrawListener> listeners = mOnPreDrawListeners;
1088 if (listeners != null && listeners.size() > 0) {
1089 CopyOnWriteArray.Access<OnPreDrawListener> access = listeners.start();
1090 try {
1091 int count = access.size();
1092 for (int i = 0; i < count; i++) {
1093 cancelDraw |= !(access.get(i).onPreDraw());
1094 }
1095 } finally {
1096 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 }
1098 }
1099 return cancelDraw;
1100 }
1101
1102 /**
Craig Mautner9c795042014-10-28 19:59:59 -07001103 * Notifies registered listeners that the window is now shown
1104 * @hide
1105 */
1106 @SuppressWarnings("unchecked")
1107 public final void dispatchOnWindowShown() {
1108 mWindowShown = true;
1109 final CopyOnWriteArray<OnWindowShownListener> listeners = mOnWindowShownListeners;
1110 if (listeners != null && listeners.size() > 0) {
1111 CopyOnWriteArray.Access<OnWindowShownListener> access = listeners.start();
1112 try {
1113 int count = access.size();
1114 for (int i = 0; i < count; i++) {
1115 access.get(i).onWindowShown();
1116 }
1117 } finally {
1118 listeners.end();
1119 }
1120 }
1121 }
1122
1123 /**
Romain Guy25eba5c2012-04-04 17:29:03 -07001124 * Notifies registered listeners that the drawing pass is about to start.
1125 */
1126 public final void dispatchOnDraw() {
1127 if (mOnDrawListeners != null) {
John Reckd94094e2016-09-08 14:12:26 -07001128 mInDispatchOnDraw = true;
Romain Guy25eba5c2012-04-04 17:29:03 -07001129 final ArrayList<OnDrawListener> listeners = mOnDrawListeners;
1130 int numListeners = listeners.size();
1131 for (int i = 0; i < numListeners; ++i) {
1132 listeners.get(i).onDraw();
1133 }
John Reckd94094e2016-09-08 14:12:26 -07001134 mInDispatchOnDraw = false;
Romain Guy25eba5c2012-04-04 17:29:03 -07001135 }
1136 }
1137
1138 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 * Notifies registered listeners that the touch mode has changed.
1140 *
1141 * @param inTouchMode True if the touch mode is now enabled, false otherwise.
1142 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001143 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 final void dispatchOnTouchModeChanged(boolean inTouchMode) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001145 final CopyOnWriteArrayList<OnTouchModeChangeListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001146 mOnTouchModeChangeListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001147 if (listeners != null && listeners.size() > 0) {
Chet Haase0f8ffd82012-06-07 07:48:48 -07001148 for (OnTouchModeChangeListener listener : listeners) {
1149 listener.onTouchModeChanged(inTouchMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 }
1151 }
1152 }
1153
1154 /**
1155 * Notifies registered listeners that something has scrolled.
1156 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001157 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 final void dispatchOnScrollChanged() {
The Android Open Source Project10592532009-03-18 17:39:46 -07001159 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1160 // perform the dispatching. The iterator is a safe guard against listeners that
1161 // could mutate the list by calling the various add/remove methods. This prevents
1162 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001163 final CopyOnWriteArray<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001164 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001165 CopyOnWriteArray.Access<OnScrollChangedListener> access = listeners.start();
1166 try {
1167 int count = access.size();
1168 for (int i = 0; i < count; i++) {
1169 access.get(i).onScrollChanged();
1170 }
1171 } finally {
1172 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 }
1174 }
1175 }
1176
1177 /**
1178 * Returns whether there are listeners for computing internal insets.
1179 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001180 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 final boolean hasComputeInternalInsetsListeners() {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001182 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001183 mOnComputeInternalInsetsListeners;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001184 return (listeners != null && listeners.size() > 0);
1185 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001186
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 /**
1188 * Calls all listeners to compute the current insets.
1189 */
Mathew Inwooda570dee2018-08-17 14:56:00 +01001190 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001192 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1193 // perform the dispatching. The iterator is a safe guard against listeners that
1194 // could mutate the list by calling the various add/remove methods. This prevents
1195 // the array from being modified while we iterate it.
Romain Guyc39ed4a2012-06-12 12:06:46 -07001196 final CopyOnWriteArray<OnComputeInternalInsetsListener> listeners =
The Android Open Source Project10592532009-03-18 17:39:46 -07001197 mOnComputeInternalInsetsListeners;
Chet Haase6e0ecb42010-11-03 19:41:18 -07001198 if (listeners != null && listeners.size() > 0) {
Romain Guyc39ed4a2012-06-12 12:06:46 -07001199 CopyOnWriteArray.Access<OnComputeInternalInsetsListener> access = listeners.start();
1200 try {
1201 int count = access.size();
1202 for (int i = 0; i < count; i++) {
1203 access.get(i).onComputeInternalInsets(inoutInfo);
1204 }
1205 } finally {
1206 listeners.end();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 }
1208 }
1209 }
Romain Guyc39ed4a2012-06-12 12:06:46 -07001210
1211 /**
Filip Gruszczynski6eafa902014-11-14 14:24:37 -08001212 * @hide
1213 */
1214 public final void dispatchOnEnterAnimationComplete() {
1215 // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
1216 // perform the dispatching. The iterator is a safe guard against listeners that
1217 // could mutate the list by calling the various add/remove methods. This prevents
1218 // the array from being modified while we iterate it.
1219 final CopyOnWriteArrayList<OnEnterAnimationCompleteListener> listeners =
1220 mOnEnterAnimationCompleteListeners;
1221 if (listeners != null && !listeners.isEmpty()) {
1222 for (OnEnterAnimationCompleteListener listener : listeners) {
1223 listener.onEnterAnimationComplete();
1224 }
1225 }
1226 }
1227
Adam Powell769b8632019-02-04 11:28:22 -08001228 void dispatchOnSystemGestureExclusionRectsChanged(@NonNull List<Rect> rects) {
1229 final CopyOnWriteArray<Consumer<List<Rect>>> listeners = mGestureExclusionListeners;
1230 if (listeners != null && listeners.size() > 0) {
1231 CopyOnWriteArray.Access<Consumer<List<Rect>>> access = listeners.start();
1232 try {
1233 final int count = access.size();
1234 for (int i = 0; i < count; i++) {
1235 access.get(i).accept(rects);
1236 }
1237 } finally {
1238 listeners.end();
1239 }
1240 }
1241 }
1242
Filip Gruszczynski6eafa902014-11-14 14:24:37 -08001243 /**
Romain Guyc39ed4a2012-06-12 12:06:46 -07001244 * Copy on write array. This array is not thread safe, and only one loop can
1245 * iterate over this array at any given time. This class avoids allocations
1246 * until a concurrent modification happens.
1247 *
1248 * Usage:
1249 *
1250 * CopyOnWriteArray.Access<MyData> access = array.start();
1251 * try {
1252 * for (int i = 0; i < access.size(); i++) {
1253 * MyData d = access.get(i);
1254 * }
1255 * } finally {
1256 * access.end();
1257 * }
1258 */
1259 static class CopyOnWriteArray<T> {
1260 private ArrayList<T> mData = new ArrayList<T>();
1261 private ArrayList<T> mDataCopy;
1262
1263 private final Access<T> mAccess = new Access<T>();
1264
1265 private boolean mStart;
1266
1267 static class Access<T> {
1268 private ArrayList<T> mData;
1269 private int mSize;
1270
1271 T get(int index) {
1272 return mData.get(index);
1273 }
1274
1275 int size() {
1276 return mSize;
1277 }
1278 }
1279
1280 CopyOnWriteArray() {
1281 }
1282
1283 private ArrayList<T> getArray() {
1284 if (mStart) {
1285 if (mDataCopy == null) mDataCopy = new ArrayList<T>(mData);
1286 return mDataCopy;
1287 }
1288 return mData;
1289 }
1290
1291 Access<T> start() {
1292 if (mStart) throw new IllegalStateException("Iteration already started");
1293 mStart = true;
1294 mDataCopy = null;
1295 mAccess.mData = mData;
1296 mAccess.mSize = mData.size();
1297 return mAccess;
1298 }
1299
1300 void end() {
1301 if (!mStart) throw new IllegalStateException("Iteration not started");
1302 mStart = false;
1303 if (mDataCopy != null) {
1304 mData = mDataCopy;
Chet Haasefc343962013-09-18 08:44:33 -07001305 mAccess.mData.clear();
1306 mAccess.mSize = 0;
Romain Guyc39ed4a2012-06-12 12:06:46 -07001307 }
1308 mDataCopy = null;
1309 }
1310
1311 int size() {
1312 return getArray().size();
1313 }
1314
1315 void add(T item) {
1316 getArray().add(item);
1317 }
1318
1319 void addAll(CopyOnWriteArray<T> array) {
1320 getArray().addAll(array.mData);
1321 }
1322
1323 void remove(T item) {
1324 getArray().remove(item);
1325 }
1326
1327 void clear() {
1328 getArray().clear();
1329 }
1330 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001331}