1 /*
2  * Copyright (C) 2006 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 
17 package android.view;
18 
19 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
20 
21 import android.animation.LayoutTransition;
22 import android.annotation.CallSuper;
23 import android.annotation.IdRes;
24 import android.annotation.NonNull;
25 import android.annotation.TestApi;
26 import android.annotation.UiThread;
27 import android.content.ClipData;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.pm.PackageManager;
31 import android.content.res.Configuration;
32 import android.content.res.TypedArray;
33 import android.graphics.Bitmap;
34 import android.graphics.Canvas;
35 import android.graphics.Color;
36 import android.graphics.Insets;
37 import android.graphics.Matrix;
38 import android.graphics.Paint;
39 import android.graphics.PointF;
40 import android.graphics.Rect;
41 import android.graphics.RectF;
42 import android.graphics.Region;
43 import android.os.Build;
44 import android.os.Bundle;
45 import android.os.Parcelable;
46 import android.os.SystemClock;
47 import android.util.AttributeSet;
48 import android.util.Log;
49 import android.util.Pools;
50 import android.util.Pools.SynchronizedPool;
51 import android.util.SparseArray;
52 import android.util.SparseBooleanArray;
53 import android.view.accessibility.AccessibilityEvent;
54 import android.view.accessibility.AccessibilityManager;
55 import android.view.accessibility.AccessibilityNodeInfo;
56 import android.view.animation.Animation;
57 import android.view.animation.AnimationUtils;
58 import android.view.animation.LayoutAnimationController;
59 import android.view.animation.Transformation;
60 
61 import com.android.internal.R;
62 
63 import java.util.ArrayList;
64 import java.util.Collection;
65 import java.util.Collections;
66 import java.util.HashSet;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.function.Predicate;
70 
71 /**
72  * <p>
73  * A <code>ViewGroup</code> is a special view that can contain other views
74  * (called children.) The view group is the base class for layouts and views
75  * containers. This class also defines the
76  * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
77  * class for layouts parameters.
78  * </p>
79  *
80  * <p>
81  * Also see {@link LayoutParams} for layout attributes.
82  * </p>
83  *
84  * <div class="special reference">
85  * <h3>Developer Guides</h3>
86  * <p>For more information about creating user interface layouts, read the
87  * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
88  * guide.</p></div>
89  *
90  * <p>Here is a complete implementation of a custom ViewGroup that implements
91  * a simple {@link android.widget.FrameLayout} along with the ability to stack
92  * children in left and right gutters.</p>
93  *
94  * {@sample development/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java
95  *      Complete}
96  *
97  * <p>If you are implementing XML layout attributes as shown in the example, this is the
98  * corresponding definition for them that would go in <code>res/values/attrs.xml</code>:</p>
99  *
100  * {@sample development/samples/ApiDemos/res/values/attrs.xml CustomLayout}
101  *
102  * <p>Finally the layout manager can be used in an XML layout like so:</p>
103  *
104  * {@sample development/samples/ApiDemos/res/layout/custom_layout.xml Complete}
105  *
106  * @attr ref android.R.styleable#ViewGroup_clipChildren
107  * @attr ref android.R.styleable#ViewGroup_clipToPadding
108  * @attr ref android.R.styleable#ViewGroup_layoutAnimation
109  * @attr ref android.R.styleable#ViewGroup_animationCache
110  * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
111  * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
112  * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
113  * @attr ref android.R.styleable#ViewGroup_descendantFocusability
114  * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
115  * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
116  * @attr ref android.R.styleable#ViewGroup_layoutMode
117  */
118 @UiThread
119 public abstract class ViewGroup extends View implements ViewParent, ViewManager {
120     private static final String TAG = "ViewGroup";
121 
122     private static final boolean DBG = false;
123 
124     /**
125      * Views which have been hidden or removed which need to be animated on
126      * their way out.
127      * This field should be made private, so it is hidden from the SDK.
128      * {@hide}
129      */
130     protected ArrayList<View> mDisappearingChildren;
131 
132     /**
133      * Listener used to propagate events indicating when children are added
134      * and/or removed from a view group.
135      * This field should be made private, so it is hidden from the SDK.
136      * {@hide}
137      */
138     protected OnHierarchyChangeListener mOnHierarchyChangeListener;
139 
140     // The view contained within this ViewGroup that has or contains focus.
141     private View mFocused;
142     // The view contained within this ViewGroup (excluding nested keyboard navigation clusters)
143     // that is or contains a default-focus view.
144     private View mDefaultFocus;
145     // The last child of this ViewGroup which held focus within the current cluster
146     View mFocusedInCluster;
147 
148     /**
149      * A Transformation used when drawing children, to
150      * apply on the child being drawn.
151      */
152     private Transformation mChildTransformation;
153 
154     /**
155      * Used to track the current invalidation region.
156      */
157     RectF mInvalidateRegion;
158 
159     /**
160      * A Transformation used to calculate a correct
161      * invalidation area when the application is autoscaled.
162      */
163     Transformation mInvalidationTransformation;
164 
165     // Current frontmost child that can accept drag and lies under the drag location.
166     // Used only to generate ENTER/EXIT events for pre-Nougat aps.
167     private View mCurrentDragChild;
168 
169     // Metadata about the ongoing drag
170     private DragEvent mCurrentDragStartEvent;
171     private boolean mIsInterestedInDrag;
172     private HashSet<View> mChildrenInterestedInDrag;
173 
174     // Used during drag dispatch
175     private PointF mLocalPoint;
176 
177     // Lazily-created holder for point computations.
178     private float[] mTempPoint;
179 
180     // Layout animation
181     private LayoutAnimationController mLayoutAnimationController;
182     private Animation.AnimationListener mAnimationListener;
183 
184     // First touch target in the linked list of touch targets.
185     private TouchTarget mFirstTouchTarget;
186 
187     // For debugging only.  You can see these in hierarchyviewer.
188     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
189     @ViewDebug.ExportedProperty(category = "events")
190     private long mLastTouchDownTime;
191     @ViewDebug.ExportedProperty(category = "events")
192     private int mLastTouchDownIndex = -1;
193     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
194     @ViewDebug.ExportedProperty(category = "events")
195     private float mLastTouchDownX;
196     @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
197     @ViewDebug.ExportedProperty(category = "events")
198     private float mLastTouchDownY;
199 
200     // First hover target in the linked list of hover targets.
201     // The hover targets are children which have received ACTION_HOVER_ENTER.
202     // They might not have actually handled the hover event, but we will
203     // continue sending hover events to them as long as the pointer remains over
204     // their bounds and the view group does not intercept hover.
205     private HoverTarget mFirstHoverTarget;
206 
207     // True if the view group itself received a hover event.
208     // It might not have actually handled the hover event.
209     private boolean mHoveredSelf;
210 
211     // The child capable of showing a tooltip and currently under the pointer.
212     private View mTooltipHoverTarget;
213 
214     // True if the view group is capable of showing a tooltip and the pointer is directly
215     // over the view group but not one of its child views.
216     private boolean mTooltipHoveredSelf;
217 
218     /**
219      * Internal flags.
220      *
221      * This field should be made private, so it is hidden from the SDK.
222      * {@hide}
223      */
224     @ViewDebug.ExportedProperty(flagMapping = {
225             @ViewDebug.FlagToString(mask = FLAG_CLIP_CHILDREN, equals = FLAG_CLIP_CHILDREN,
226                     name = "CLIP_CHILDREN"),
227             @ViewDebug.FlagToString(mask = FLAG_CLIP_TO_PADDING, equals = FLAG_CLIP_TO_PADDING,
228                     name = "CLIP_TO_PADDING"),
229             @ViewDebug.FlagToString(mask = FLAG_PADDING_NOT_NULL, equals = FLAG_PADDING_NOT_NULL,
230                     name = "PADDING_NOT_NULL")
231     }, formatToHexString = true)
232     protected int mGroupFlags;
233 
234     /**
235      * Either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
236      */
237     private int mLayoutMode = LAYOUT_MODE_UNDEFINED;
238 
239     /**
240      * NOTE: If you change the flags below make sure to reflect the changes
241      *       the DisplayList class
242      */
243 
244     // When set, ViewGroup invalidates only the child's rectangle
245     // Set by default
246     static final int FLAG_CLIP_CHILDREN = 0x1;
247 
248     // When set, ViewGroup excludes the padding area from the invalidate rectangle
249     // Set by default
250     private static final int FLAG_CLIP_TO_PADDING = 0x2;
251 
252     // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
253     // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
254     static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
255 
256     // When set, dispatchDraw() will run the layout animation and unset the flag
257     private static final int FLAG_RUN_ANIMATION = 0x8;
258 
259     // When set, there is either no layout animation on the ViewGroup or the layout
260     // animation is over
261     // Set by default
262     static final int FLAG_ANIMATION_DONE = 0x10;
263 
264     // If set, this ViewGroup has padding; if unset there is no padding and we don't need
265     // to clip it, even if FLAG_CLIP_TO_PADDING is set
266     private static final int FLAG_PADDING_NOT_NULL = 0x20;
267 
268     /** @deprecated - functionality removed */
269     @Deprecated
270     private static final int FLAG_ANIMATION_CACHE = 0x40;
271 
272     // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
273     // layout animation; this avoid clobbering the hierarchy
274     // Automatically set when the layout animation starts, depending on the animation's
275     // characteristics
276     static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
277 
278     // When set, the next call to drawChild() will clear mChildTransformation's matrix
279     static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
280 
281     // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
282     // the children's Bitmap caches if necessary
283     // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
284     private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
285 
286     /**
287      * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
288      * to get the index of the child to draw for that iteration.
289      *
290      * @hide
291      */
292     protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
293 
294     /**
295      * When set, this ViewGroup supports static transformations on children; this causes
296      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
297      * invoked when a child is drawn.
298      *
299      * Any subclass overriding
300      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
301      * set this flags in {@link #mGroupFlags}.
302      *
303      * {@hide}
304      */
305     protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
306 
307     // UNUSED FLAG VALUE: 0x1000;
308 
309     /**
310      * When set, this ViewGroup's drawable states also include those
311      * of its children.
312      */
313     private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
314 
315     /** @deprecated functionality removed */
316     @Deprecated
317     private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
318 
319     /** @deprecated functionality removed */
320     @Deprecated
321     private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
322 
323     /**
324      * When set, this group will go through its list of children to notify them of
325      * any drawable state change.
326      */
327     private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
328 
329     private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
330 
331     /**
332      * This view will get focus before any of its descendants.
333      */
334     public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
335 
336     /**
337      * This view will get focus only if none of its descendants want it.
338      */
339     public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
340 
341     /**
342      * This view will block any of its descendants from getting focus, even
343      * if they are focusable.
344      */
345     public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
346 
347     /**
348      * Used to map between enum in attrubutes and flag values.
349      */
350     private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
351             {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
352                     FOCUS_BLOCK_DESCENDANTS};
353 
354     /**
355      * When set, this ViewGroup should not intercept touch events.
356      * {@hide}
357      */
358     protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
359 
360     /**
361      * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
362      */
363     private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
364 
365     /**
366      * When set, this ViewGroup will not dispatch onAttachedToWindow calls
367      * to children when adding new views. This is used to prevent multiple
368      * onAttached calls when a ViewGroup adds children in its own onAttached method.
369      */
370     private static final int FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW = 0x400000;
371 
372     /**
373      * When true, indicates that a layoutMode has been explicitly set, either with
374      * an explicit call to {@link #setLayoutMode(int)} in code or from an XML resource.
375      * This distinguishes the situation in which a layout mode was inherited from
376      * one of the ViewGroup's ancestors and cached locally.
377      */
378     private static final int FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET = 0x800000;
379 
380     static final int FLAG_IS_TRANSITION_GROUP = 0x1000000;
381 
382     static final int FLAG_IS_TRANSITION_GROUP_SET = 0x2000000;
383 
384     /**
385      * When set, focus will not be permitted to enter this group if a touchscreen is present.
386      */
387     static final int FLAG_TOUCHSCREEN_BLOCKS_FOCUS = 0x4000000;
388 
389     /**
390      * When true, indicates that a call to startActionModeForChild was made with the type parameter
391      * and should not be ignored. This helps in backwards compatibility with the existing method
392      * without a type.
393      *
394      * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
395      * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
396      */
397     private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED = 0x8000000;
398 
399     /**
400      * When true, indicates that a call to startActionModeForChild was made without the type
401      * parameter. This helps in backwards compatibility with the existing method
402      * without a type.
403      *
404      * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
405      * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
406      */
407     private static final int FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED = 0x10000000;
408 
409     /**
410      * When set, indicates that a call to showContextMenuForChild was made with explicit
411      * coordinates within the initiating child view.
412      */
413     private static final int FLAG_SHOW_CONTEXT_MENU_WITH_COORDS = 0x20000000;
414 
415     /**
416      * Indicates which types of drawing caches are to be kept in memory.
417      * This field should be made private, so it is hidden from the SDK.
418      * {@hide}
419      */
420     protected int mPersistentDrawingCache;
421 
422     /**
423      * Used to indicate that no drawing cache should be kept in memory.
424      */
425     public static final int PERSISTENT_NO_CACHE = 0x0;
426 
427     /**
428      * Used to indicate that the animation drawing cache should be kept in memory.
429      */
430     public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
431 
432     /**
433      * Used to indicate that the scrolling drawing cache should be kept in memory.
434      */
435     public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
436 
437     /**
438      * Used to indicate that all drawing caches should be kept in memory.
439      */
440     public static final int PERSISTENT_ALL_CACHES = 0x3;
441 
442     // Layout Modes
443 
444     private static final int LAYOUT_MODE_UNDEFINED = -1;
445 
446     /**
447      * This constant is a {@link #setLayoutMode(int) layoutMode}.
448      * Clip bounds are the raw values of {@link #getLeft() left}, {@link #getTop() top},
449      * {@link #getRight() right} and {@link #getBottom() bottom}.
450      */
451     public static final int LAYOUT_MODE_CLIP_BOUNDS = 0;
452 
453     /**
454      * This constant is a {@link #setLayoutMode(int) layoutMode}.
455      * Optical bounds describe where a widget appears to be. They sit inside the clip
456      * bounds which need to cover a larger area to allow other effects,
457      * such as shadows and glows, to be drawn.
458      */
459     public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1;
460 
461     /** @hide */
462     public static int LAYOUT_MODE_DEFAULT = LAYOUT_MODE_CLIP_BOUNDS;
463 
464     /**
465      * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
466      * are set at the same time.
467      */
468     protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
469 
470     // Index of the child's left position in the mLocation array
471     private static final int CHILD_LEFT_INDEX = 0;
472     // Index of the child's top position in the mLocation array
473     private static final int CHILD_TOP_INDEX = 1;
474 
475     // Child views of this ViewGroup
476     private View[] mChildren;
477     // Number of valid children in the mChildren array, the rest should be null or not
478     // considered as children
479     private int mChildrenCount;
480 
481     // Whether layout calls are currently being suppressed, controlled by calls to
482     // suppressLayout()
483     boolean mSuppressLayout = false;
484 
485     // Whether any layout calls have actually been suppressed while mSuppressLayout
486     // has been true. This tracks whether we need to issue a requestLayout() when
487     // layout is later re-enabled.
488     private boolean mLayoutCalledWhileSuppressed = false;
489 
490     private static final int ARRAY_INITIAL_CAPACITY = 12;
491     private static final int ARRAY_CAPACITY_INCREMENT = 12;
492 
493     private static float[] sDebugLines;
494 
495     // Used to draw cached views
496     Paint mCachePaint;
497 
498     // Used to animate add/remove changes in layout
499     private LayoutTransition mTransition;
500 
501     // The set of views that are currently being transitioned. This list is used to track views
502     // being removed that should not actually be removed from the parent yet because they are
503     // being animated.
504     private ArrayList<View> mTransitioningViews;
505 
506     // List of children changing visibility. This is used to potentially keep rendering
507     // views during a transition when they otherwise would have become gone/invisible
508     private ArrayList<View> mVisibilityChangingChildren;
509 
510     // Temporary holder of presorted children, only used for
511     // input/software draw dispatch for correctly Z ordering.
512     private ArrayList<View> mPreSortedChildren;
513 
514     // Indicates how many of this container's child subtrees contain transient state
515     @ViewDebug.ExportedProperty(category = "layout")
516     private int mChildCountWithTransientState = 0;
517 
518     /**
519      * Currently registered axes for nested scrolling. Flag set consisting of
520      * {@link #SCROLL_AXIS_HORIZONTAL} {@link #SCROLL_AXIS_VERTICAL} or {@link #SCROLL_AXIS_NONE}
521      * for null.
522      */
523     private int mNestedScrollAxes;
524 
525     // Used to manage the list of transient views, added by addTransientView()
526     private List<Integer> mTransientIndices = null;
527     private List<View> mTransientViews = null;
528 
529 
530     /**
531      * Empty ActionMode used as a sentinel in recursive entries to startActionModeForChild.
532      *
533      * @see #startActionModeForChild(View, android.view.ActionMode.Callback)
534      * @see #startActionModeForChild(View, android.view.ActionMode.Callback, int)
535      */
536     private static final ActionMode SENTINEL_ACTION_MODE = new ActionMode() {
537         @Override
538         public void setTitle(CharSequence title) {}
539 
540         @Override
541         public void setTitle(int resId) {}
542 
543         @Override
544         public void setSubtitle(CharSequence subtitle) {}
545 
546         @Override
547         public void setSubtitle(int resId) {}
548 
549         @Override
550         public void setCustomView(View view) {}
551 
552         @Override
553         public void invalidate() {}
554 
555         @Override
556         public void finish() {}
557 
558         @Override
559         public Menu getMenu() {
560             return null;
561         }
562 
563         @Override
564         public CharSequence getTitle() {
565             return null;
566         }
567 
568         @Override
569         public CharSequence getSubtitle() {
570             return null;
571         }
572 
573         @Override
574         public View getCustomView() {
575             return null;
576         }
577 
578         @Override
579         public MenuInflater getMenuInflater() {
580             return null;
581         }
582     };
583 
ViewGroup(Context context)584     public ViewGroup(Context context) {
585         this(context, null);
586     }
587 
ViewGroup(Context context, AttributeSet attrs)588     public ViewGroup(Context context, AttributeSet attrs) {
589         this(context, attrs, 0);
590     }
591 
ViewGroup(Context context, AttributeSet attrs, int defStyleAttr)592     public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
593         this(context, attrs, defStyleAttr, 0);
594     }
595 
ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)596     public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
597         super(context, attrs, defStyleAttr, defStyleRes);
598 
599         initViewGroup();
600         initFromAttributes(context, attrs, defStyleAttr, defStyleRes);
601     }
602 
initViewGroup()603     private void initViewGroup() {
604         // ViewGroup doesn't draw by default
605         if (!debugDraw()) {
606             setFlags(WILL_NOT_DRAW, DRAW_MASK);
607         }
608         mGroupFlags |= FLAG_CLIP_CHILDREN;
609         mGroupFlags |= FLAG_CLIP_TO_PADDING;
610         mGroupFlags |= FLAG_ANIMATION_DONE;
611         mGroupFlags |= FLAG_ANIMATION_CACHE;
612         mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
613 
614         if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
615             mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
616         }
617 
618         setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
619 
620         mChildren = new View[ARRAY_INITIAL_CAPACITY];
621         mChildrenCount = 0;
622 
623         mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
624     }
625 
initFromAttributes( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)626     private void initFromAttributes(
627             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
628         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewGroup, defStyleAttr,
629                 defStyleRes);
630 
631         final int N = a.getIndexCount();
632         for (int i = 0; i < N; i++) {
633             int attr = a.getIndex(i);
634             switch (attr) {
635                 case R.styleable.ViewGroup_clipChildren:
636                     setClipChildren(a.getBoolean(attr, true));
637                     break;
638                 case R.styleable.ViewGroup_clipToPadding:
639                     setClipToPadding(a.getBoolean(attr, true));
640                     break;
641                 case R.styleable.ViewGroup_animationCache:
642                     setAnimationCacheEnabled(a.getBoolean(attr, true));
643                     break;
644                 case R.styleable.ViewGroup_persistentDrawingCache:
645                     setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
646                     break;
647                 case R.styleable.ViewGroup_addStatesFromChildren:
648                     setAddStatesFromChildren(a.getBoolean(attr, false));
649                     break;
650                 case R.styleable.ViewGroup_alwaysDrawnWithCache:
651                     setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
652                     break;
653                 case R.styleable.ViewGroup_layoutAnimation:
654                     int id = a.getResourceId(attr, -1);
655                     if (id > 0) {
656                         setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
657                     }
658                     break;
659                 case R.styleable.ViewGroup_descendantFocusability:
660                     setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
661                     break;
662                 case R.styleable.ViewGroup_splitMotionEvents:
663                     setMotionEventSplittingEnabled(a.getBoolean(attr, false));
664                     break;
665                 case R.styleable.ViewGroup_animateLayoutChanges:
666                     boolean animateLayoutChanges = a.getBoolean(attr, false);
667                     if (animateLayoutChanges) {
668                         setLayoutTransition(new LayoutTransition());
669                     }
670                     break;
671                 case R.styleable.ViewGroup_layoutMode:
672                     setLayoutMode(a.getInt(attr, LAYOUT_MODE_UNDEFINED));
673                     break;
674                 case R.styleable.ViewGroup_transitionGroup:
675                     setTransitionGroup(a.getBoolean(attr, false));
676                     break;
677                 case R.styleable.ViewGroup_touchscreenBlocksFocus:
678                     setTouchscreenBlocksFocus(a.getBoolean(attr, false));
679                     break;
680             }
681         }
682 
683         a.recycle();
684     }
685 
686     /**
687      * Gets the descendant focusability of this view group.  The descendant
688      * focusability defines the relationship between this view group and its
689      * descendants when looking for a view to take focus in
690      * {@link #requestFocus(int, android.graphics.Rect)}.
691      *
692      * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
693      *   {@link #FOCUS_BLOCK_DESCENDANTS}.
694      */
695     @ViewDebug.ExportedProperty(category = "focus", mapping = {
696         @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
697         @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
698         @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
699     })
getDescendantFocusability()700     public int getDescendantFocusability() {
701         return mGroupFlags & FLAG_MASK_FOCUSABILITY;
702     }
703 
704     /**
705      * Set the descendant focusability of this view group. This defines the relationship
706      * between this view group and its descendants when looking for a view to
707      * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
708      *
709      * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
710      *   {@link #FOCUS_BLOCK_DESCENDANTS}.
711      */
setDescendantFocusability(int focusability)712     public void setDescendantFocusability(int focusability) {
713         switch (focusability) {
714             case FOCUS_BEFORE_DESCENDANTS:
715             case FOCUS_AFTER_DESCENDANTS:
716             case FOCUS_BLOCK_DESCENDANTS:
717                 break;
718             default:
719                 throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
720                         + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
721         }
722         mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
723         mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
724     }
725 
726     @Override
handleFocusGainInternal(int direction, Rect previouslyFocusedRect)727     void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
728         if (mFocused != null) {
729             mFocused.unFocus(this);
730             mFocused = null;
731             mFocusedInCluster = null;
732         }
733         super.handleFocusGainInternal(direction, previouslyFocusedRect);
734     }
735 
736     @Override
requestChildFocus(View child, View focused)737     public void requestChildFocus(View child, View focused) {
738         if (DBG) {
739             System.out.println(this + " requestChildFocus()");
740         }
741         if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
742             return;
743         }
744 
745         // Unfocus us, if necessary
746         super.unFocus(focused);
747 
748         // We had a previous notion of who had focus. Clear it.
749         if (mFocused != child) {
750             if (mFocused != null) {
751                 mFocused.unFocus(focused);
752             }
753 
754             mFocused = child;
755         }
756         if (mParent != null) {
757             mParent.requestChildFocus(this, focused);
758         }
759     }
760 
setDefaultFocus(View child)761     void setDefaultFocus(View child) {
762         // Stop at any higher view which is explicitly focused-by-default
763         if (mDefaultFocus != null && mDefaultFocus.isFocusedByDefault()) {
764             return;
765         }
766 
767         mDefaultFocus = child;
768 
769         if (mParent instanceof ViewGroup) {
770             ((ViewGroup) mParent).setDefaultFocus(this);
771         }
772     }
773 
774     /**
775      * Clears the default-focus chain from {@param child} up to the first parent which has another
776      * default-focusable branch below it or until there is no default-focus chain.
777      *
778      * @param child
779      */
clearDefaultFocus(View child)780     void clearDefaultFocus(View child) {
781         // Stop at any higher view which is explicitly focused-by-default
782         if (mDefaultFocus != child && mDefaultFocus != null
783                 && mDefaultFocus.isFocusedByDefault()) {
784             return;
785         }
786 
787         mDefaultFocus = null;
788 
789         // Search child siblings for default focusables.
790         for (int i = 0; i < mChildrenCount; ++i) {
791             View sibling = mChildren[i];
792             if (sibling.isFocusedByDefault()) {
793                 mDefaultFocus = sibling;
794                 return;
795             } else if (mDefaultFocus == null && sibling.hasDefaultFocus()) {
796                 mDefaultFocus = sibling;
797             }
798         }
799 
800         if (mParent instanceof ViewGroup) {
801             ((ViewGroup) mParent).clearDefaultFocus(this);
802         }
803     }
804 
805     @Override
hasDefaultFocus()806     boolean hasDefaultFocus() {
807         return mDefaultFocus != null || super.hasDefaultFocus();
808     }
809 
810     /**
811      * Removes {@code child} (and associated focusedInCluster chain) from the cluster containing
812      * it.
813      * <br>
814      * This is intended to be run on {@code child}'s immediate parent. This is necessary because
815      * the chain is sometimes cleared after {@code child} has been detached.
816      */
clearFocusedInCluster(View child)817     void clearFocusedInCluster(View child) {
818         if (mFocusedInCluster != child) {
819             return;
820         }
821         View top = findKeyboardNavigationCluster();
822         ViewParent parent = this;
823         do {
824             ((ViewGroup) parent).mFocusedInCluster = null;
825             if (parent == top) {
826                 break;
827             }
828             parent = parent.getParent();
829         } while (parent instanceof ViewGroup);
830     }
831 
832     @Override
focusableViewAvailable(View v)833     public void focusableViewAvailable(View v) {
834         if (mParent != null
835                 // shortcut: don't report a new focusable view if we block our descendants from
836                 // getting focus or if we're not visible
837                 && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
838                 && ((mViewFlags & VISIBILITY_MASK) == VISIBLE)
839                 && (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen())
840                 // shortcut: don't report a new focusable view if we already are focused
841                 // (and we don't prefer our descendants)
842                 //
843                 // note: knowing that mFocused is non-null is not a good enough reason
844                 // to break the traversal since in that case we'd actually have to find
845                 // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
846                 // an ancestor of v; this will get checked for at ViewAncestor
847                 && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
848             mParent.focusableViewAvailable(v);
849         }
850     }
851 
852     @Override
showContextMenuForChild(View originalView)853     public boolean showContextMenuForChild(View originalView) {
854         if (isShowingContextMenuWithCoords()) {
855             // We're being called for compatibility. Return false and let the version
856             // with coordinates recurse up.
857             return false;
858         }
859         return mParent != null && mParent.showContextMenuForChild(originalView);
860     }
861 
862     /**
863      * @hide used internally for compatibility with existing app code only
864      */
isShowingContextMenuWithCoords()865     public final boolean isShowingContextMenuWithCoords() {
866         return (mGroupFlags & FLAG_SHOW_CONTEXT_MENU_WITH_COORDS) != 0;
867     }
868 
869     @Override
showContextMenuForChild(View originalView, float x, float y)870     public boolean showContextMenuForChild(View originalView, float x, float y) {
871         try {
872             mGroupFlags |= FLAG_SHOW_CONTEXT_MENU_WITH_COORDS;
873             if (showContextMenuForChild(originalView)) {
874                 return true;
875             }
876         } finally {
877             mGroupFlags &= ~FLAG_SHOW_CONTEXT_MENU_WITH_COORDS;
878         }
879         return mParent != null && mParent.showContextMenuForChild(originalView, x, y);
880     }
881 
882     @Override
startActionModeForChild(View originalView, ActionMode.Callback callback)883     public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
884         if ((mGroupFlags & FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED) == 0) {
885             // This is the original call.
886             try {
887                 mGroupFlags |= FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED;
888                 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
889             } finally {
890                 mGroupFlags &= ~FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED;
891             }
892         } else {
893             // We are being called from the new method with type.
894             return SENTINEL_ACTION_MODE;
895         }
896     }
897 
898     @Override
startActionModeForChild( View originalView, ActionMode.Callback callback, int type)899     public ActionMode startActionModeForChild(
900             View originalView, ActionMode.Callback callback, int type) {
901         if ((mGroupFlags & FLAG_START_ACTION_MODE_FOR_CHILD_IS_NOT_TYPED) == 0
902                 && type == ActionMode.TYPE_PRIMARY) {
903             ActionMode mode;
904             try {
905                 mGroupFlags |= FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED;
906                 mode = startActionModeForChild(originalView, callback);
907             } finally {
908                 mGroupFlags &= ~FLAG_START_ACTION_MODE_FOR_CHILD_IS_TYPED;
909             }
910             if (mode != SENTINEL_ACTION_MODE) {
911                 return mode;
912             }
913         }
914         if (mParent != null) {
915             try {
916                 return mParent.startActionModeForChild(originalView, callback, type);
917             } catch (AbstractMethodError ame) {
918                 // Custom view parents might not implement this method.
919                 return mParent.startActionModeForChild(originalView, callback);
920             }
921         }
922         return null;
923     }
924 
925     /**
926      * @hide
927      */
928     @Override
dispatchActivityResult( String who, int requestCode, int resultCode, Intent data)929     public boolean dispatchActivityResult(
930             String who, int requestCode, int resultCode, Intent data) {
931         if (super.dispatchActivityResult(who, requestCode, resultCode, data)) {
932             return true;
933         }
934         int childCount = getChildCount();
935         for (int i = 0; i < childCount; i++) {
936             View child = getChildAt(i);
937             if (child.dispatchActivityResult(who, requestCode, resultCode, data)) {
938                 return true;
939             }
940         }
941         return false;
942     }
943 
944     /**
945      * Find the nearest view in the specified direction that wants to take
946      * focus.
947      *
948      * @param focused The view that currently has focus
949      * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
950      *        FOCUS_RIGHT, or 0 for not applicable.
951      */
952     @Override
focusSearch(View focused, int direction)953     public View focusSearch(View focused, int direction) {
954         if (isRootNamespace()) {
955             // root namespace means we should consider ourselves the top of the
956             // tree for focus searching; otherwise we could be focus searching
957             // into other tabs.  see LocalActivityManager and TabHost for more info.
958             return FocusFinder.getInstance().findNextFocus(this, focused, direction);
959         } else if (mParent != null) {
960             return mParent.focusSearch(focused, direction);
961         }
962         return null;
963     }
964 
965     @Override
requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate)966     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
967         return false;
968     }
969 
970     @Override
requestSendAccessibilityEvent(View child, AccessibilityEvent event)971     public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
972         ViewParent parent = mParent;
973         if (parent == null) {
974             return false;
975         }
976         final boolean propagate = onRequestSendAccessibilityEvent(child, event);
977         if (!propagate) {
978             return false;
979         }
980         return parent.requestSendAccessibilityEvent(this, event);
981     }
982 
983     /**
984      * Called when a child has requested sending an {@link AccessibilityEvent} and
985      * gives an opportunity to its parent to augment the event.
986      * <p>
987      * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling
988      * {@link android.view.View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its
989      * {@link android.view.View.AccessibilityDelegate#onRequestSendAccessibilityEvent(ViewGroup, View, AccessibilityEvent)}
990      * is responsible for handling this call.
991      * </p>
992      *
993      * @param child The child which requests sending the event.
994      * @param event The event to be sent.
995      * @return True if the event should be sent.
996      *
997      * @see #requestSendAccessibilityEvent(View, AccessibilityEvent)
998      */
onRequestSendAccessibilityEvent(View child, AccessibilityEvent event)999     public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
1000         if (mAccessibilityDelegate != null) {
1001             return mAccessibilityDelegate.onRequestSendAccessibilityEvent(this, child, event);
1002         } else {
1003             return onRequestSendAccessibilityEventInternal(child, event);
1004         }
1005     }
1006 
1007     /**
1008      * @see #onRequestSendAccessibilityEvent(View, AccessibilityEvent)
1009      *
1010      * Note: Called from the default {@link View.AccessibilityDelegate}.
1011      *
1012      * @hide
1013      */
onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event)1014     public boolean onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event) {
1015         return true;
1016     }
1017 
1018     /**
1019      * Called when a child view has changed whether or not it is tracking transient state.
1020      */
1021     @Override
childHasTransientStateChanged(View child, boolean childHasTransientState)1022     public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
1023         final boolean oldHasTransientState = hasTransientState();
1024         if (childHasTransientState) {
1025             mChildCountWithTransientState++;
1026         } else {
1027             mChildCountWithTransientState--;
1028         }
1029 
1030         final boolean newHasTransientState = hasTransientState();
1031         if (mParent != null && oldHasTransientState != newHasTransientState) {
1032             try {
1033                 mParent.childHasTransientStateChanged(this, newHasTransientState);
1034             } catch (AbstractMethodError e) {
1035                 Log.e(TAG, mParent.getClass().getSimpleName() +
1036                         " does not fully implement ViewParent", e);
1037             }
1038         }
1039     }
1040 
1041     @Override
hasTransientState()1042     public boolean hasTransientState() {
1043         return mChildCountWithTransientState > 0 || super.hasTransientState();
1044     }
1045 
1046     @Override
dispatchUnhandledMove(View focused, int direction)1047     public boolean dispatchUnhandledMove(View focused, int direction) {
1048         return mFocused != null &&
1049                 mFocused.dispatchUnhandledMove(focused, direction);
1050     }
1051 
1052     @Override
clearChildFocus(View child)1053     public void clearChildFocus(View child) {
1054         if (DBG) {
1055             System.out.println(this + " clearChildFocus()");
1056         }
1057 
1058         mFocused = null;
1059         if (mParent != null) {
1060             mParent.clearChildFocus(this);
1061         }
1062     }
1063 
1064     @Override
clearFocus()1065     public void clearFocus() {
1066         if (DBG) {
1067             System.out.println(this + " clearFocus()");
1068         }
1069         if (mFocused == null) {
1070             super.clearFocus();
1071         } else {
1072             View focused = mFocused;
1073             mFocused = null;
1074             focused.clearFocus();
1075         }
1076     }
1077 
1078     @Override
unFocus(View focused)1079     void unFocus(View focused) {
1080         if (DBG) {
1081             System.out.println(this + " unFocus()");
1082         }
1083         if (mFocused == null) {
1084             super.unFocus(focused);
1085         } else {
1086             mFocused.unFocus(focused);
1087             mFocused = null;
1088         }
1089     }
1090 
1091     /**
1092      * Returns the focused child of this view, if any. The child may have focus
1093      * or contain focus.
1094      *
1095      * @return the focused child or null.
1096      */
getFocusedChild()1097     public View getFocusedChild() {
1098         return mFocused;
1099     }
1100 
getDeepestFocusedChild()1101     View getDeepestFocusedChild() {
1102         View v = this;
1103         while (v != null) {
1104             if (v.isFocused()) {
1105                 return v;
1106             }
1107             v = v instanceof ViewGroup ? ((ViewGroup) v).getFocusedChild() : null;
1108         }
1109         return null;
1110     }
1111 
1112     /**
1113      * Returns true if this view has or contains focus
1114      *
1115      * @return true if this view has or contains focus
1116      */
1117     @Override
hasFocus()1118     public boolean hasFocus() {
1119         return (mPrivateFlags & PFLAG_FOCUSED) != 0 || mFocused != null;
1120     }
1121 
1122     /*
1123      * (non-Javadoc)
1124      *
1125      * @see android.view.View#findFocus()
1126      */
1127     @Override
findFocus()1128     public View findFocus() {
1129         if (DBG) {
1130             System.out.println("Find focus in " + this + ": flags="
1131                     + isFocused() + ", child=" + mFocused);
1132         }
1133 
1134         if (isFocused()) {
1135             return this;
1136         }
1137 
1138         if (mFocused != null) {
1139             return mFocused.findFocus();
1140         }
1141         return null;
1142     }
1143 
1144     @Override
hasFocusable(boolean allowAutoFocus, boolean dispatchExplicit)1145     boolean hasFocusable(boolean allowAutoFocus, boolean dispatchExplicit) {
1146         // This should probably be super.hasFocusable, but that would change
1147         // behavior. Historically, we have not checked the ancestor views for
1148         // shouldBlockFocusForTouchscreen() in ViewGroup.hasFocusable.
1149 
1150         // Invisible and gone views are never focusable.
1151         if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
1152             return false;
1153         }
1154 
1155         // Only use effective focusable value when allowed.
1156         if ((allowAutoFocus || getFocusable() != FOCUSABLE_AUTO) && isFocusable()) {
1157             return true;
1158         }
1159 
1160         // Determine whether we have a focused descendant.
1161         final int descendantFocusability = getDescendantFocusability();
1162         if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
1163             return hasFocusableChild(dispatchExplicit);
1164         }
1165 
1166         return false;
1167     }
1168 
hasFocusableChild(boolean dispatchExplicit)1169     boolean hasFocusableChild(boolean dispatchExplicit) {
1170         // Determine whether we have a focusable descendant.
1171         final int count = mChildrenCount;
1172         final View[] children = mChildren;
1173 
1174         for (int i = 0; i < count; i++) {
1175             final View child = children[i];
1176 
1177             // In case the subclass has overridden has[Explicit]Focusable, dispatch
1178             // to the expected one for each child even though we share logic here.
1179             if ((dispatchExplicit && child.hasExplicitFocusable())
1180                     || (!dispatchExplicit && child.hasFocusable())) {
1181                 return true;
1182             }
1183         }
1184 
1185         return false;
1186     }
1187 
1188     @Override
addFocusables(ArrayList<View> views, int direction, int focusableMode)1189     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
1190         final int focusableCount = views.size();
1191 
1192         final int descendantFocusability = getDescendantFocusability();
1193         final boolean blockFocusForTouchscreen = shouldBlockFocusForTouchscreen();
1194         final boolean focusSelf = (isFocusableInTouchMode() || !blockFocusForTouchscreen);
1195 
1196         if (descendantFocusability == FOCUS_BLOCK_DESCENDANTS) {
1197             if (focusSelf) {
1198                 super.addFocusables(views, direction, focusableMode);
1199             }
1200             return;
1201         }
1202 
1203         if (blockFocusForTouchscreen) {
1204             focusableMode |= FOCUSABLES_TOUCH_MODE;
1205         }
1206 
1207         if ((descendantFocusability == FOCUS_BEFORE_DESCENDANTS) && focusSelf) {
1208             super.addFocusables(views, direction, focusableMode);
1209         }
1210 
1211         int count = 0;
1212         final View[] children = new View[mChildrenCount];
1213         for (int i = 0; i < mChildrenCount; ++i) {
1214             View child = mChildren[i];
1215             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1216                 children[count++] = child;
1217             }
1218         }
1219         FocusFinder.sort(children, 0, count, this, isLayoutRtl());
1220         for (int i = 0; i < count; ++i) {
1221             children[i].addFocusables(views, direction, focusableMode);
1222         }
1223 
1224         // When set to FOCUS_AFTER_DESCENDANTS, we only add ourselves if
1225         // there aren't any focusable descendants.  this is
1226         // to avoid the focus search finding layouts when a more precise search
1227         // among the focusable children would be more interesting.
1228         if ((descendantFocusability == FOCUS_AFTER_DESCENDANTS) && focusSelf
1229                 && focusableCount == views.size()) {
1230             super.addFocusables(views, direction, focusableMode);
1231         }
1232     }
1233 
1234     @Override
addKeyboardNavigationClusters(Collection<View> views, int direction)1235     public void addKeyboardNavigationClusters(Collection<View> views, int direction) {
1236         final int focusableCount = views.size();
1237 
1238         if (isKeyboardNavigationCluster()) {
1239             // Cluster-navigation can enter a touchscreenBlocksFocus cluster, so temporarily
1240             // disable touchscreenBlocksFocus to evaluate whether it contains focusables.
1241             final boolean blockedFocus = getTouchscreenBlocksFocus();
1242             try {
1243                 setTouchscreenBlocksFocusNoRefocus(false);
1244                 super.addKeyboardNavigationClusters(views, direction);
1245             } finally {
1246                 setTouchscreenBlocksFocusNoRefocus(blockedFocus);
1247             }
1248         } else {
1249             super.addKeyboardNavigationClusters(views, direction);
1250         }
1251 
1252         if (focusableCount != views.size()) {
1253             // No need to look for groups inside a group.
1254             return;
1255         }
1256 
1257         if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
1258             return;
1259         }
1260 
1261         int count = 0;
1262         final View[] visibleChildren = new View[mChildrenCount];
1263         for (int i = 0; i < mChildrenCount; ++i) {
1264             final View child = mChildren[i];
1265             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1266                 visibleChildren[count++] = child;
1267             }
1268         }
1269         FocusFinder.sort(visibleChildren, 0, count, this, isLayoutRtl());
1270         for (int i = 0; i < count; ++i) {
1271             visibleChildren[i].addKeyboardNavigationClusters(views, direction);
1272         }
1273     }
1274 
1275     /**
1276      * Set whether this ViewGroup should ignore focus requests for itself and its children.
1277      * If this option is enabled and the ViewGroup or a descendant currently has focus, focus
1278      * will proceed forward.
1279      *
1280      * @param touchscreenBlocksFocus true to enable blocking focus in the presence of a touchscreen
1281      */
setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus)1282     public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) {
1283         if (touchscreenBlocksFocus) {
1284             mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1285             if (hasFocus() && !isKeyboardNavigationCluster()) {
1286                 final View focusedChild = getDeepestFocusedChild();
1287                 if (!focusedChild.isFocusableInTouchMode()) {
1288                     final View newFocus = focusSearch(FOCUS_FORWARD);
1289                     if (newFocus != null) {
1290                         newFocus.requestFocus();
1291                     }
1292                 }
1293             }
1294         } else {
1295             mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1296         }
1297     }
1298 
setTouchscreenBlocksFocusNoRefocus(boolean touchscreenBlocksFocus)1299     private void setTouchscreenBlocksFocusNoRefocus(boolean touchscreenBlocksFocus) {
1300         if (touchscreenBlocksFocus) {
1301             mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1302         } else {
1303             mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
1304         }
1305     }
1306 
1307     /**
1308      * Check whether this ViewGroup should ignore focus requests for itself and its children.
1309      */
1310     @ViewDebug.ExportedProperty(category = "focus")
getTouchscreenBlocksFocus()1311     public boolean getTouchscreenBlocksFocus() {
1312         return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0;
1313     }
1314 
shouldBlockFocusForTouchscreen()1315     boolean shouldBlockFocusForTouchscreen() {
1316         // There is a special case for keyboard-navigation clusters. We allow cluster navigation
1317         // to jump into blockFocusForTouchscreen ViewGroups which are clusters. Once in the
1318         // cluster, focus is free to move around within it.
1319         return getTouchscreenBlocksFocus() &&
1320                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
1321                 && !(isKeyboardNavigationCluster()
1322                         && (hasFocus() || (findKeyboardNavigationCluster() != this)));
1323     }
1324 
1325     @Override
findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags)1326     public void findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags) {
1327         super.findViewsWithText(outViews, text, flags);
1328         final int childrenCount = mChildrenCount;
1329         final View[] children = mChildren;
1330         for (int i = 0; i < childrenCount; i++) {
1331             View child = children[i];
1332             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
1333                     && (child.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
1334                 child.findViewsWithText(outViews, text, flags);
1335             }
1336         }
1337     }
1338 
1339     /** @hide */
1340     @Override
findViewByAccessibilityIdTraversal(int accessibilityId)1341     public View findViewByAccessibilityIdTraversal(int accessibilityId) {
1342         View foundView = super.findViewByAccessibilityIdTraversal(accessibilityId);
1343         if (foundView != null) {
1344             return foundView;
1345         }
1346 
1347         if (getAccessibilityNodeProvider() != null) {
1348             return null;
1349         }
1350 
1351         final int childrenCount = mChildrenCount;
1352         final View[] children = mChildren;
1353         for (int i = 0; i < childrenCount; i++) {
1354             View child = children[i];
1355             foundView = child.findViewByAccessibilityIdTraversal(accessibilityId);
1356             if (foundView != null) {
1357                 return foundView;
1358             }
1359         }
1360 
1361         return null;
1362     }
1363 
1364     /** @hide */
1365     @Override
findViewByAutofillIdTraversal(int autofillId)1366     public View findViewByAutofillIdTraversal(int autofillId) {
1367         View foundView = super.findViewByAutofillIdTraversal(autofillId);
1368         if (foundView != null) {
1369             return foundView;
1370         }
1371 
1372         final int childrenCount = mChildrenCount;
1373         final View[] children = mChildren;
1374         for (int i = 0; i < childrenCount; i++) {
1375             View child = children[i];
1376             foundView = child.findViewByAutofillIdTraversal(autofillId);
1377             if (foundView != null) {
1378                 return foundView;
1379             }
1380         }
1381 
1382         return null;
1383     }
1384 
1385     @Override
dispatchWindowFocusChanged(boolean hasFocus)1386     public void dispatchWindowFocusChanged(boolean hasFocus) {
1387         super.dispatchWindowFocusChanged(hasFocus);
1388         final int count = mChildrenCount;
1389         final View[] children = mChildren;
1390         for (int i = 0; i < count; i++) {
1391             children[i].dispatchWindowFocusChanged(hasFocus);
1392         }
1393     }
1394 
1395     @Override
addTouchables(ArrayList<View> views)1396     public void addTouchables(ArrayList<View> views) {
1397         super.addTouchables(views);
1398 
1399         final int count = mChildrenCount;
1400         final View[] children = mChildren;
1401 
1402         for (int i = 0; i < count; i++) {
1403             final View child = children[i];
1404             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1405                 child.addTouchables(views);
1406             }
1407         }
1408     }
1409 
1410     /**
1411      * @hide
1412      */
1413     @Override
makeOptionalFitsSystemWindows()1414     public void makeOptionalFitsSystemWindows() {
1415         super.makeOptionalFitsSystemWindows();
1416         final int count = mChildrenCount;
1417         final View[] children = mChildren;
1418         for (int i = 0; i < count; i++) {
1419             children[i].makeOptionalFitsSystemWindows();
1420         }
1421     }
1422 
1423     @Override
dispatchDisplayHint(int hint)1424     public void dispatchDisplayHint(int hint) {
1425         super.dispatchDisplayHint(hint);
1426         final int count = mChildrenCount;
1427         final View[] children = mChildren;
1428         for (int i = 0; i < count; i++) {
1429             children[i].dispatchDisplayHint(hint);
1430         }
1431     }
1432 
1433     /**
1434      * Called when a view's visibility has changed. Notify the parent to take any appropriate
1435      * action.
1436      *
1437      * @param child The view whose visibility has changed
1438      * @param oldVisibility The previous visibility value (GONE, INVISIBLE, or VISIBLE).
1439      * @param newVisibility The new visibility value (GONE, INVISIBLE, or VISIBLE).
1440      * @hide
1441      */
onChildVisibilityChanged(View child, int oldVisibility, int newVisibility)1442     protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
1443         if (mTransition != null) {
1444             if (newVisibility == VISIBLE) {
1445                 mTransition.showChild(this, child, oldVisibility);
1446             } else {
1447                 mTransition.hideChild(this, child, newVisibility);
1448                 if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
1449                     // Only track this on disappearing views - appearing views are already visible
1450                     // and don't need special handling during drawChild()
1451                     if (mVisibilityChangingChildren == null) {
1452                         mVisibilityChangingChildren = new ArrayList<View>();
1453                     }
1454                     mVisibilityChangingChildren.add(child);
1455                     addDisappearingView(child);
1456                 }
1457             }
1458         }
1459 
1460         // in all cases, for drags
1461         if (newVisibility == VISIBLE && mCurrentDragStartEvent != null) {
1462             if (!mChildrenInterestedInDrag.contains(child)) {
1463                 notifyChildOfDragStart(child);
1464             }
1465         }
1466     }
1467 
1468     @Override
dispatchVisibilityChanged(View changedView, int visibility)1469     protected void dispatchVisibilityChanged(View changedView, int visibility) {
1470         super.dispatchVisibilityChanged(changedView, visibility);
1471         final int count = mChildrenCount;
1472         final View[] children = mChildren;
1473         for (int i = 0; i < count; i++) {
1474             children[i].dispatchVisibilityChanged(changedView, visibility);
1475         }
1476     }
1477 
1478     @Override
dispatchWindowVisibilityChanged(int visibility)1479     public void dispatchWindowVisibilityChanged(int visibility) {
1480         super.dispatchWindowVisibilityChanged(visibility);
1481         final int count = mChildrenCount;
1482         final View[] children = mChildren;
1483         for (int i = 0; i < count; i++) {
1484             children[i].dispatchWindowVisibilityChanged(visibility);
1485         }
1486     }
1487 
1488     @Override
dispatchVisibilityAggregated(boolean isVisible)1489     boolean dispatchVisibilityAggregated(boolean isVisible) {
1490         isVisible = super.dispatchVisibilityAggregated(isVisible);
1491         final int count = mChildrenCount;
1492         final View[] children = mChildren;
1493         for (int i = 0; i < count; i++) {
1494             // Only dispatch to visible children. Not visible children and their subtrees already
1495             // know that they aren't visible and that's not going to change as a result of
1496             // whatever triggered this dispatch.
1497             if (children[i].getVisibility() == VISIBLE) {
1498                 children[i].dispatchVisibilityAggregated(isVisible);
1499             }
1500         }
1501         return isVisible;
1502     }
1503 
1504     @Override
dispatchConfigurationChanged(Configuration newConfig)1505     public void dispatchConfigurationChanged(Configuration newConfig) {
1506         super.dispatchConfigurationChanged(newConfig);
1507         final int count = mChildrenCount;
1508         final View[] children = mChildren;
1509         for (int i = 0; i < count; i++) {
1510             children[i].dispatchConfigurationChanged(newConfig);
1511         }
1512     }
1513 
1514     @Override
recomputeViewAttributes(View child)1515     public void recomputeViewAttributes(View child) {
1516         if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
1517             ViewParent parent = mParent;
1518             if (parent != null) parent.recomputeViewAttributes(this);
1519         }
1520     }
1521 
1522     @Override
dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility)1523     void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
1524         if ((visibility & VISIBILITY_MASK) == VISIBLE) {
1525             super.dispatchCollectViewAttributes(attachInfo, visibility);
1526             final int count = mChildrenCount;
1527             final View[] children = mChildren;
1528             for (int i = 0; i < count; i++) {
1529                 final View child = children[i];
1530                 child.dispatchCollectViewAttributes(attachInfo,
1531                         visibility | (child.mViewFlags&VISIBILITY_MASK));
1532             }
1533         }
1534     }
1535 
1536     @Override
bringChildToFront(View child)1537     public void bringChildToFront(View child) {
1538         final int index = indexOfChild(child);
1539         if (index >= 0) {
1540             removeFromArray(index);
1541             addInArray(child, mChildrenCount);
1542             child.mParent = this;
1543             requestLayout();
1544             invalidate();
1545         }
1546     }
1547 
getLocalPoint()1548     private PointF getLocalPoint() {
1549         if (mLocalPoint == null) mLocalPoint = new PointF();
1550         return mLocalPoint;
1551     }
1552 
1553     @Override
dispatchDragEnterExitInPreN(DragEvent event)1554     boolean dispatchDragEnterExitInPreN(DragEvent event) {
1555         if (event.mAction == DragEvent.ACTION_DRAG_EXITED && mCurrentDragChild != null) {
1556             // The drag exited a sub-tree of views; notify of the exit all descendants that are in
1557             // entered state.
1558             // We don't need this recursive delivery for ENTERED events because they get generated
1559             // from the recursive delivery of LOCATION/DROP events, and hence, don't need their own
1560             // recursion.
1561             mCurrentDragChild.dispatchDragEnterExitInPreN(event);
1562             mCurrentDragChild = null;
1563         }
1564         return mIsInterestedInDrag && super.dispatchDragEnterExitInPreN(event);
1565     }
1566 
1567     // TODO: Write real docs
1568     @Override
dispatchDragEvent(DragEvent event)1569     public boolean dispatchDragEvent(DragEvent event) {
1570         boolean retval = false;
1571         final float tx = event.mX;
1572         final float ty = event.mY;
1573         final ClipData td = event.mClipData;
1574 
1575         // Dispatch down the view hierarchy
1576         final PointF localPoint = getLocalPoint();
1577 
1578         switch (event.mAction) {
1579         case DragEvent.ACTION_DRAG_STARTED: {
1580             // Clear the state to recalculate which views we drag over.
1581             mCurrentDragChild = null;
1582 
1583             // Set up our tracking of drag-started notifications
1584             mCurrentDragStartEvent = DragEvent.obtain(event);
1585             if (mChildrenInterestedInDrag == null) {
1586                 mChildrenInterestedInDrag = new HashSet<View>();
1587             } else {
1588                 mChildrenInterestedInDrag.clear();
1589             }
1590 
1591             // Now dispatch down to our children, caching the responses
1592             final int count = mChildrenCount;
1593             final View[] children = mChildren;
1594             for (int i = 0; i < count; i++) {
1595                 final View child = children[i];
1596                 child.mPrivateFlags2 &= ~View.DRAG_MASK;
1597                 if (child.getVisibility() == VISIBLE) {
1598                     if (notifyChildOfDragStart(children[i])) {
1599                         retval = true;
1600                     }
1601                 }
1602             }
1603 
1604             // Notify itself of the drag start.
1605             mIsInterestedInDrag = super.dispatchDragEvent(event);
1606             if (mIsInterestedInDrag) {
1607                 retval = true;
1608             }
1609 
1610             if (!retval) {
1611                 // Neither us nor any of our children are interested in this drag, so stop tracking
1612                 // the current drag event.
1613                 mCurrentDragStartEvent.recycle();
1614                 mCurrentDragStartEvent = null;
1615             }
1616         } break;
1617 
1618         case DragEvent.ACTION_DRAG_ENDED: {
1619             // Release the bookkeeping now that the drag lifecycle has ended
1620             final HashSet<View> childrenInterestedInDrag = mChildrenInterestedInDrag;
1621             if (childrenInterestedInDrag != null) {
1622                 for (View child : childrenInterestedInDrag) {
1623                     // If a child was interested in the ongoing drag, it's told that it's over
1624                     if (child.dispatchDragEvent(event)) {
1625                         retval = true;
1626                     }
1627                 }
1628                 childrenInterestedInDrag.clear();
1629             }
1630             if (mCurrentDragStartEvent != null) {
1631                 mCurrentDragStartEvent.recycle();
1632                 mCurrentDragStartEvent = null;
1633             }
1634 
1635             if (mIsInterestedInDrag) {
1636                 if (super.dispatchDragEvent(event)) {
1637                     retval = true;
1638                 }
1639                 mIsInterestedInDrag = false;
1640             }
1641         } break;
1642 
1643         case DragEvent.ACTION_DRAG_LOCATION:
1644         case DragEvent.ACTION_DROP: {
1645             // Find the [possibly new] drag target
1646             View target = findFrontmostDroppableChildAt(event.mX, event.mY, localPoint);
1647 
1648             if (target != mCurrentDragChild) {
1649                 if (sCascadedDragDrop) {
1650                     // For pre-Nougat apps, make sure that the whole hierarchy of views that contain
1651                     // the drag location is kept in the state between ENTERED and EXITED events.
1652                     // (Starting with N, only the innermost view will be in that state).
1653 
1654                     final int action = event.mAction;
1655                     // Position should not be available for ACTION_DRAG_ENTERED and
1656                     // ACTION_DRAG_EXITED.
1657                     event.mX = 0;
1658                     event.mY = 0;
1659                     event.mClipData = null;
1660 
1661                     if (mCurrentDragChild != null) {
1662                         event.mAction = DragEvent.ACTION_DRAG_EXITED;
1663                         mCurrentDragChild.dispatchDragEnterExitInPreN(event);
1664                     }
1665 
1666                     if (target != null) {
1667                         event.mAction = DragEvent.ACTION_DRAG_ENTERED;
1668                         target.dispatchDragEnterExitInPreN(event);
1669                     }
1670 
1671                     event.mAction = action;
1672                     event.mX = tx;
1673                     event.mY = ty;
1674                     event.mClipData = td;
1675                 }
1676                 mCurrentDragChild = target;
1677             }
1678 
1679             if (target == null && mIsInterestedInDrag) {
1680                 target = this;
1681             }
1682 
1683             // Dispatch the actual drag notice, localized into the target coordinates.
1684             if (target != null) {
1685                 if (target != this) {
1686                     event.mX = localPoint.x;
1687                     event.mY = localPoint.y;
1688 
1689                     retval = target.dispatchDragEvent(event);
1690 
1691                     event.mX = tx;
1692                     event.mY = ty;
1693 
1694                     if (mIsInterestedInDrag) {
1695                         final boolean eventWasConsumed;
1696                         if (sCascadedDragDrop) {
1697                             eventWasConsumed = retval;
1698                         } else {
1699                             eventWasConsumed = event.mEventHandlerWasCalled;
1700                         }
1701 
1702                         if (!eventWasConsumed) {
1703                             retval = super.dispatchDragEvent(event);
1704                         }
1705                     }
1706                 } else {
1707                     retval = super.dispatchDragEvent(event);
1708                 }
1709             }
1710         } break;
1711         }
1712 
1713         return retval;
1714     }
1715 
1716     // Find the frontmost child view that lies under the given point, and calculate
1717     // the position within its own local coordinate system.
findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint)1718     View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
1719         final int count = mChildrenCount;
1720         final View[] children = mChildren;
1721         for (int i = count - 1; i >= 0; i--) {
1722             final View child = children[i];
1723             if (!child.canAcceptDrag()) {
1724                 continue;
1725             }
1726 
1727             if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
1728                 return child;
1729             }
1730         }
1731         return null;
1732     }
1733 
notifyChildOfDragStart(View child)1734     boolean notifyChildOfDragStart(View child) {
1735         // The caller guarantees that the child is not in mChildrenInterestedInDrag yet.
1736 
1737         if (ViewDebug.DEBUG_DRAG) {
1738             Log.d(View.VIEW_LOG_TAG, "Sending drag-started to view: " + child);
1739         }
1740 
1741         final float tx = mCurrentDragStartEvent.mX;
1742         final float ty = mCurrentDragStartEvent.mY;
1743 
1744         final float[] point = getTempPoint();
1745         point[0] = tx;
1746         point[1] = ty;
1747         transformPointToViewLocal(point, child);
1748 
1749         mCurrentDragStartEvent.mX = point[0];
1750         mCurrentDragStartEvent.mY = point[1];
1751         final boolean canAccept = child.dispatchDragEvent(mCurrentDragStartEvent);
1752         mCurrentDragStartEvent.mX = tx;
1753         mCurrentDragStartEvent.mY = ty;
1754         mCurrentDragStartEvent.mEventHandlerWasCalled = false;
1755         if (canAccept) {
1756             mChildrenInterestedInDrag.add(child);
1757             if (!child.canAcceptDrag()) {
1758                 child.mPrivateFlags2 |= View.PFLAG2_DRAG_CAN_ACCEPT;
1759                 child.refreshDrawableState();
1760             }
1761         }
1762         return canAccept;
1763     }
1764 
1765     @Override
dispatchWindowSystemUiVisiblityChanged(int visible)1766     public void dispatchWindowSystemUiVisiblityChanged(int visible) {
1767         super.dispatchWindowSystemUiVisiblityChanged(visible);
1768 
1769         final int count = mChildrenCount;
1770         final View[] children = mChildren;
1771         for (int i=0; i <count; i++) {
1772             final View child = children[i];
1773             child.dispatchWindowSystemUiVisiblityChanged(visible);
1774         }
1775     }
1776 
1777     @Override
dispatchSystemUiVisibilityChanged(int visible)1778     public void dispatchSystemUiVisibilityChanged(int visible) {
1779         super.dispatchSystemUiVisibilityChanged(visible);
1780 
1781         final int count = mChildrenCount;
1782         final View[] children = mChildren;
1783         for (int i=0; i <count; i++) {
1784             final View child = children[i];
1785             child.dispatchSystemUiVisibilityChanged(visible);
1786         }
1787     }
1788 
1789     @Override
updateLocalSystemUiVisibility(int localValue, int localChanges)1790     boolean updateLocalSystemUiVisibility(int localValue, int localChanges) {
1791         boolean changed = super.updateLocalSystemUiVisibility(localValue, localChanges);
1792 
1793         final int count = mChildrenCount;
1794         final View[] children = mChildren;
1795         for (int i=0; i <count; i++) {
1796             final View child = children[i];
1797             changed |= child.updateLocalSystemUiVisibility(localValue, localChanges);
1798         }
1799         return changed;
1800     }
1801 
1802     @Override
dispatchKeyEventPreIme(KeyEvent event)1803     public boolean dispatchKeyEventPreIme(KeyEvent event) {
1804         if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1805                 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1806             return super.dispatchKeyEventPreIme(event);
1807         } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1808                 == PFLAG_HAS_BOUNDS) {
1809             return mFocused.dispatchKeyEventPreIme(event);
1810         }
1811         return false;
1812     }
1813 
1814     @Override
dispatchKeyEvent(KeyEvent event)1815     public boolean dispatchKeyEvent(KeyEvent event) {
1816         if (mInputEventConsistencyVerifier != null) {
1817             mInputEventConsistencyVerifier.onKeyEvent(event, 1);
1818         }
1819 
1820         if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1821                 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1822             if (super.dispatchKeyEvent(event)) {
1823                 return true;
1824             }
1825         } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1826                 == PFLAG_HAS_BOUNDS) {
1827             if (mFocused.dispatchKeyEvent(event)) {
1828                 return true;
1829             }
1830         }
1831 
1832         if (mInputEventConsistencyVerifier != null) {
1833             mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1834         }
1835         return false;
1836     }
1837 
1838     @Override
dispatchKeyShortcutEvent(KeyEvent event)1839     public boolean dispatchKeyShortcutEvent(KeyEvent event) {
1840         if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1841                 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1842             return super.dispatchKeyShortcutEvent(event);
1843         } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1844                 == PFLAG_HAS_BOUNDS) {
1845             return mFocused.dispatchKeyShortcutEvent(event);
1846         }
1847         return false;
1848     }
1849 
1850     @Override
dispatchTrackballEvent(MotionEvent event)1851     public boolean dispatchTrackballEvent(MotionEvent event) {
1852         if (mInputEventConsistencyVerifier != null) {
1853             mInputEventConsistencyVerifier.onTrackballEvent(event, 1);
1854         }
1855 
1856         if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1857                 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1858             if (super.dispatchTrackballEvent(event)) {
1859                 return true;
1860             }
1861         } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1862                 == PFLAG_HAS_BOUNDS) {
1863             if (mFocused.dispatchTrackballEvent(event)) {
1864                 return true;
1865             }
1866         }
1867 
1868         if (mInputEventConsistencyVerifier != null) {
1869             mInputEventConsistencyVerifier.onUnhandledEvent(event, 1);
1870         }
1871         return false;
1872     }
1873 
1874     @Override
dispatchCapturedPointerEvent(MotionEvent event)1875     public boolean dispatchCapturedPointerEvent(MotionEvent event) {
1876         if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
1877                 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
1878             if (super.dispatchCapturedPointerEvent(event)) {
1879                 return true;
1880             }
1881         } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
1882                 == PFLAG_HAS_BOUNDS) {
1883             if (mFocused.dispatchCapturedPointerEvent(event)) {
1884                 return true;
1885             }
1886         }
1887         return false;
1888     }
1889 
1890     @Override
dispatchPointerCaptureChanged(boolean hasCapture)1891     public void dispatchPointerCaptureChanged(boolean hasCapture) {
1892         exitHoverTargets();
1893 
1894         super.dispatchPointerCaptureChanged(hasCapture);
1895         final int count = mChildrenCount;
1896         final View[] children = mChildren;
1897         for (int i = 0; i < count; i++) {
1898             children[i].dispatchPointerCaptureChanged(hasCapture);
1899         }
1900     }
1901 
1902     @Override
onResolvePointerIcon(MotionEvent event, int pointerIndex)1903     public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
1904         final float x = event.getX(pointerIndex);
1905         final float y = event.getY(pointerIndex);
1906         if (isOnScrollbarThumb(x, y) || isDraggingScrollBar()) {
1907             return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW);
1908         }
1909         // Check what the child under the pointer says about the pointer.
1910         final int childrenCount = mChildrenCount;
1911         if (childrenCount != 0) {
1912             final ArrayList<View> preorderedList = buildOrderedChildList();
1913             final boolean customOrder = preorderedList == null
1914                     && isChildrenDrawingOrderEnabled();
1915             final View[] children = mChildren;
1916             for (int i = childrenCount - 1; i >= 0; i--) {
1917                 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
1918                 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
1919                 if (!canViewReceivePointerEvents(child)
1920                         || !isTransformedTouchPointInView(x, y, child, null)) {
1921                     continue;
1922                 }
1923                 final PointerIcon pointerIcon =
1924                         dispatchResolvePointerIcon(event, pointerIndex, child);
1925                 if (pointerIcon != null) {
1926                     if (preorderedList != null) preorderedList.clear();
1927                     return pointerIcon;
1928                 }
1929             }
1930             if (preorderedList != null) preorderedList.clear();
1931         }
1932 
1933         // The pointer is not a child or the child has no preferences, returning the default
1934         // implementation.
1935         return super.onResolvePointerIcon(event, pointerIndex);
1936     }
1937 
dispatchResolvePointerIcon(MotionEvent event, int pointerIndex, View child)1938     private PointerIcon dispatchResolvePointerIcon(MotionEvent event, int pointerIndex,
1939             View child) {
1940         final PointerIcon pointerIcon;
1941         if (!child.hasIdentityMatrix()) {
1942             MotionEvent transformedEvent = getTransformedMotionEvent(event, child);
1943             pointerIcon = child.onResolvePointerIcon(transformedEvent, pointerIndex);
1944             transformedEvent.recycle();
1945         } else {
1946             final float offsetX = mScrollX - child.mLeft;
1947             final float offsetY = mScrollY - child.mTop;
1948             event.offsetLocation(offsetX, offsetY);
1949             pointerIcon = child.onResolvePointerIcon(event, pointerIndex);
1950             event.offsetLocation(-offsetX, -offsetY);
1951         }
1952         return pointerIcon;
1953     }
1954 
getAndVerifyPreorderedIndex(int childrenCount, int i, boolean customOrder)1955     private int getAndVerifyPreorderedIndex(int childrenCount, int i, boolean customOrder) {
1956         final int childIndex;
1957         if (customOrder) {
1958             final int childIndex1 = getChildDrawingOrder(childrenCount, i);
1959             if (childIndex1 >= childrenCount) {
1960                 throw new IndexOutOfBoundsException("getChildDrawingOrder() "
1961                         + "returned invalid index " + childIndex1
1962                         + " (child count is " + childrenCount + ")");
1963             }
1964             childIndex = childIndex1;
1965         } else {
1966             childIndex = i;
1967         }
1968         return childIndex;
1969     }
1970 
1971     @SuppressWarnings({"ConstantConditions"})
1972     @Override
dispatchHoverEvent(MotionEvent event)1973     protected boolean dispatchHoverEvent(MotionEvent event) {
1974         final int action = event.getAction();
1975 
1976         // First check whether the view group wants to intercept the hover event.
1977         final boolean interceptHover = onInterceptHoverEvent(event);
1978         event.setAction(action); // restore action in case it was changed
1979 
1980         MotionEvent eventNoHistory = event;
1981         boolean handled = false;
1982 
1983         // Send events to the hovered children and build a new list of hover targets until
1984         // one is found that handles the event.
1985         HoverTarget firstOldHoverTarget = mFirstHoverTarget;
1986         mFirstHoverTarget = null;
1987         if (!interceptHover && action != MotionEvent.ACTION_HOVER_EXIT) {
1988             final float x = event.getX();
1989             final float y = event.getY();
1990             final int childrenCount = mChildrenCount;
1991             if (childrenCount != 0) {
1992                 final ArrayList<View> preorderedList = buildOrderedChildList();
1993                 final boolean customOrder = preorderedList == null
1994                         && isChildrenDrawingOrderEnabled();
1995                 final View[] children = mChildren;
1996                 HoverTarget lastHoverTarget = null;
1997                 for (int i = childrenCount - 1; i >= 0; i--) {
1998                     final int childIndex = getAndVerifyPreorderedIndex(
1999                             childrenCount, i, customOrder);
2000                     final View child = getAndVerifyPreorderedView(
2001                             preorderedList, children, childIndex);
2002                     if (!canViewReceivePointerEvents(child)
2003                             || !isTransformedTouchPointInView(x, y, child, null)) {
2004                         continue;
2005                     }
2006 
2007                     // Obtain a hover target for this child.  Dequeue it from the
2008                     // old hover target list if the child was previously hovered.
2009                     HoverTarget hoverTarget = firstOldHoverTarget;
2010                     final boolean wasHovered;
2011                     for (HoverTarget predecessor = null; ;) {
2012                         if (hoverTarget == null) {
2013                             hoverTarget = HoverTarget.obtain(child);
2014                             wasHovered = false;
2015                             break;
2016                         }
2017 
2018                         if (hoverTarget.child == child) {
2019                             if (predecessor != null) {
2020                                 predecessor.next = hoverTarget.next;
2021                             } else {
2022                                 firstOldHoverTarget = hoverTarget.next;
2023                             }
2024                             hoverTarget.next = null;
2025                             wasHovered = true;
2026                             break;
2027                         }
2028 
2029                         predecessor = hoverTarget;
2030                         hoverTarget = hoverTarget.next;
2031                     }
2032 
2033                     // Enqueue the hover target onto the new hover target list.
2034                     if (lastHoverTarget != null) {
2035                         lastHoverTarget.next = hoverTarget;
2036                     } else {
2037                         mFirstHoverTarget = hoverTarget;
2038                     }
2039                     lastHoverTarget = hoverTarget;
2040 
2041                     // Dispatch the event to the child.
2042                     if (action == MotionEvent.ACTION_HOVER_ENTER) {
2043                         if (!wasHovered) {
2044                             // Send the enter as is.
2045                             handled |= dispatchTransformedGenericPointerEvent(
2046                                     event, child); // enter
2047                         }
2048                     } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
2049                         if (!wasHovered) {
2050                             // Synthesize an enter from a move.
2051                             eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
2052                             eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
2053                             handled |= dispatchTransformedGenericPointerEvent(
2054                                     eventNoHistory, child); // enter
2055                             eventNoHistory.setAction(action);
2056 
2057                             handled |= dispatchTransformedGenericPointerEvent(
2058                                     eventNoHistory, child); // move
2059                         } else {
2060                             // Send the move as is.
2061                             handled |= dispatchTransformedGenericPointerEvent(event, child);
2062                         }
2063                     }
2064                     if (handled) {
2065                         break;
2066                     }
2067                 }
2068                 if (preorderedList != null) preorderedList.clear();
2069             }
2070         }
2071 
2072         // Send exit events to all previously hovered children that are no longer hovered.
2073         while (firstOldHoverTarget != null) {
2074             final View child = firstOldHoverTarget.child;
2075 
2076             // Exit the old hovered child.
2077             if (action == MotionEvent.ACTION_HOVER_EXIT) {
2078                 // Send the exit as is.
2079                 handled |= dispatchTransformedGenericPointerEvent(
2080                         event, child); // exit
2081             } else {
2082                 // Synthesize an exit from a move or enter.
2083                 // Ignore the result because hover focus has moved to a different view.
2084                 if (action == MotionEvent.ACTION_HOVER_MOVE) {
2085                     final boolean hoverExitPending = event.isHoverExitPending();
2086                     event.setHoverExitPending(true);
2087                     dispatchTransformedGenericPointerEvent(
2088                             event, child); // move
2089                     event.setHoverExitPending(hoverExitPending);
2090                 }
2091                 eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
2092                 eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
2093                 dispatchTransformedGenericPointerEvent(
2094                         eventNoHistory, child); // exit
2095                 eventNoHistory.setAction(action);
2096             }
2097 
2098             final HoverTarget nextOldHoverTarget = firstOldHoverTarget.next;
2099             firstOldHoverTarget.recycle();
2100             firstOldHoverTarget = nextOldHoverTarget;
2101         }
2102 
2103         // Send events to the view group itself if no children have handled it and the view group
2104         // itself is not currently being hover-exited.
2105         boolean newHoveredSelf = !handled &&
2106                 (action != MotionEvent.ACTION_HOVER_EXIT) && !event.isHoverExitPending();
2107         if (newHoveredSelf == mHoveredSelf) {
2108             if (newHoveredSelf) {
2109                 // Send event to the view group as before.
2110                 handled |= super.dispatchHoverEvent(event);
2111             }
2112         } else {
2113             if (mHoveredSelf) {
2114                 // Exit the view group.
2115                 if (action == MotionEvent.ACTION_HOVER_EXIT) {
2116                     // Send the exit as is.
2117                     handled |= super.dispatchHoverEvent(event); // exit
2118                 } else {
2119                     // Synthesize an exit from a move or enter.
2120                     // Ignore the result because hover focus is moving to a different view.
2121                     if (action == MotionEvent.ACTION_HOVER_MOVE) {
2122                         super.dispatchHoverEvent(event); // move
2123                     }
2124                     eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
2125                     eventNoHistory.setAction(MotionEvent.ACTION_HOVER_EXIT);
2126                     super.dispatchHoverEvent(eventNoHistory); // exit
2127                     eventNoHistory.setAction(action);
2128                 }
2129                 mHoveredSelf = false;
2130             }
2131 
2132             if (newHoveredSelf) {
2133                 // Enter the view group.
2134                 if (action == MotionEvent.ACTION_HOVER_ENTER) {
2135                     // Send the enter as is.
2136                     handled |= super.dispatchHoverEvent(event); // enter
2137                     mHoveredSelf = true;
2138                 } else if (action == MotionEvent.ACTION_HOVER_MOVE) {
2139                     // Synthesize an enter from a move.
2140                     eventNoHistory = obtainMotionEventNoHistoryOrSelf(eventNoHistory);
2141                     eventNoHistory.setAction(MotionEvent.ACTION_HOVER_ENTER);
2142                     handled |= super.dispatchHoverEvent(eventNoHistory); // enter
2143                     eventNoHistory.setAction(action);
2144 
2145                     handled |= super.dispatchHoverEvent(eventNoHistory); // move
2146                     mHoveredSelf = true;
2147                 }
2148             }
2149         }
2150 
2151         // Recycle the copy of the event that we made.
2152         if (eventNoHistory != event) {
2153             eventNoHistory.recycle();
2154         }
2155 
2156         // Done.
2157         return handled;
2158     }
2159 
exitHoverTargets()2160     private void exitHoverTargets() {
2161         if (mHoveredSelf || mFirstHoverTarget != null) {
2162             final long now = SystemClock.uptimeMillis();
2163             MotionEvent event = MotionEvent.obtain(now, now,
2164                     MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
2165             event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2166             dispatchHoverEvent(event);
2167             event.recycle();
2168         }
2169     }
2170 
cancelHoverTarget(View view)2171     private void cancelHoverTarget(View view) {
2172         HoverTarget predecessor = null;
2173         HoverTarget target = mFirstHoverTarget;
2174         while (target != null) {
2175             final HoverTarget next = target.next;
2176             if (target.child == view) {
2177                 if (predecessor == null) {
2178                     mFirstHoverTarget = next;
2179                 } else {
2180                     predecessor.next = next;
2181                 }
2182                 target.recycle();
2183 
2184                 final long now = SystemClock.uptimeMillis();
2185                 MotionEvent event = MotionEvent.obtain(now, now,
2186                         MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
2187                 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2188                 view.dispatchHoverEvent(event);
2189                 event.recycle();
2190                 return;
2191             }
2192             predecessor = target;
2193             target = next;
2194         }
2195     }
2196 
2197     @Override
dispatchTooltipHoverEvent(MotionEvent event)2198     boolean dispatchTooltipHoverEvent(MotionEvent event) {
2199         final int action = event.getAction();
2200         switch (action) {
2201             case MotionEvent.ACTION_HOVER_ENTER:
2202                 break;
2203 
2204             case MotionEvent.ACTION_HOVER_MOVE:
2205                 View newTarget = null;
2206 
2207                 // Check what the child under the pointer says about the tooltip.
2208                 final int childrenCount = mChildrenCount;
2209                 if (childrenCount != 0) {
2210                     final float x = event.getX();
2211                     final float y = event.getY();
2212 
2213                     final ArrayList<View> preorderedList = buildOrderedChildList();
2214                     final boolean customOrder = preorderedList == null
2215                             && isChildrenDrawingOrderEnabled();
2216                     final View[] children = mChildren;
2217                     for (int i = childrenCount - 1; i >= 0; i--) {
2218                         final int childIndex =
2219                                 getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
2220                         final View child =
2221                                 getAndVerifyPreorderedView(preorderedList, children, childIndex);
2222                         if (!canViewReceivePointerEvents(child)
2223                                 || !isTransformedTouchPointInView(x, y, child, null)) {
2224                             continue;
2225                         }
2226                         if (dispatchTooltipHoverEvent(event, child)) {
2227                             newTarget = child;
2228                             break;
2229                         }
2230                     }
2231                     if (preorderedList != null) preorderedList.clear();
2232                 }
2233 
2234                 if (mTooltipHoverTarget != newTarget) {
2235                     if (mTooltipHoverTarget != null) {
2236                         event.setAction(MotionEvent.ACTION_HOVER_EXIT);
2237                         mTooltipHoverTarget.dispatchTooltipHoverEvent(event);
2238                         event.setAction(action);
2239                     }
2240                     mTooltipHoverTarget = newTarget;
2241                 }
2242 
2243                 if (mTooltipHoverTarget != null) {
2244                     if (mTooltipHoveredSelf) {
2245                         mTooltipHoveredSelf = false;
2246                         event.setAction(MotionEvent.ACTION_HOVER_EXIT);
2247                         super.dispatchTooltipHoverEvent(event);
2248                         event.setAction(action);
2249                     }
2250                     return true;
2251                 }
2252 
2253                 mTooltipHoveredSelf = super.dispatchTooltipHoverEvent(event);
2254                 return mTooltipHoveredSelf;
2255 
2256             case MotionEvent.ACTION_HOVER_EXIT:
2257                 if (mTooltipHoverTarget != null) {
2258                     mTooltipHoverTarget.dispatchTooltipHoverEvent(event);
2259                     mTooltipHoverTarget = null;
2260                 } else if (mTooltipHoveredSelf) {
2261                     super.dispatchTooltipHoverEvent(event);
2262                     mTooltipHoveredSelf = false;
2263                 }
2264                 break;
2265         }
2266         return false;
2267     }
2268 
dispatchTooltipHoverEvent(MotionEvent event, View child)2269     private boolean dispatchTooltipHoverEvent(MotionEvent event, View child) {
2270         final boolean result;
2271         if (!child.hasIdentityMatrix()) {
2272             MotionEvent transformedEvent = getTransformedMotionEvent(event, child);
2273             result = child.dispatchTooltipHoverEvent(transformedEvent);
2274             transformedEvent.recycle();
2275         } else {
2276             final float offsetX = mScrollX - child.mLeft;
2277             final float offsetY = mScrollY - child.mTop;
2278             event.offsetLocation(offsetX, offsetY);
2279             result = child.dispatchTooltipHoverEvent(event);
2280             event.offsetLocation(-offsetX, -offsetY);
2281         }
2282         return result;
2283     }
2284 
exitTooltipHoverTargets()2285     private void exitTooltipHoverTargets() {
2286         if (mTooltipHoveredSelf || mTooltipHoverTarget != null) {
2287             final long now = SystemClock.uptimeMillis();
2288             MotionEvent event = MotionEvent.obtain(now, now,
2289                     MotionEvent.ACTION_HOVER_EXIT, 0.0f, 0.0f, 0);
2290             event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2291             dispatchTooltipHoverEvent(event);
2292             event.recycle();
2293         }
2294     }
2295 
2296     /** @hide */
2297     @Override
hasHoveredChild()2298     protected boolean hasHoveredChild() {
2299         return mFirstHoverTarget != null;
2300     }
2301 
2302     @Override
addChildrenForAccessibility(ArrayList<View> outChildren)2303     public void addChildrenForAccessibility(ArrayList<View> outChildren) {
2304         if (getAccessibilityNodeProvider() != null) {
2305             return;
2306         }
2307         ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
2308         try {
2309             final int childrenCount = children.getChildCount();
2310             for (int i = 0; i < childrenCount; i++) {
2311                 View child = children.getChildAt(i);
2312                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2313                     if (child.includeForAccessibility()) {
2314                         outChildren.add(child);
2315                     } else {
2316                         child.addChildrenForAccessibility(outChildren);
2317                     }
2318                 }
2319             }
2320         } finally {
2321             children.recycle();
2322         }
2323     }
2324 
2325     /**
2326      * Implement this method to intercept hover events before they are handled
2327      * by child views.
2328      * <p>
2329      * This method is called before dispatching a hover event to a child of
2330      * the view group or to the view group's own {@link #onHoverEvent} to allow
2331      * the view group a chance to intercept the hover event.
2332      * This method can also be used to watch all pointer motions that occur within
2333      * the bounds of the view group even when the pointer is hovering over
2334      * a child of the view group rather than over the view group itself.
2335      * </p><p>
2336      * The view group can prevent its children from receiving hover events by
2337      * implementing this method and returning <code>true</code> to indicate
2338      * that it would like to intercept hover events.  The view group must
2339      * continuously return <code>true</code> from {@link #onInterceptHoverEvent}
2340      * for as long as it wishes to continue intercepting hover events from
2341      * its children.
2342      * </p><p>
2343      * Interception preserves the invariant that at most one view can be
2344      * hovered at a time by transferring hover focus from the currently hovered
2345      * child to the view group or vice-versa as needed.
2346      * </p><p>
2347      * If this method returns <code>true</code> and a child is already hovered, then the
2348      * child view will first receive a hover exit event and then the view group
2349      * itself will receive a hover enter event in {@link #onHoverEvent}.
2350      * Likewise, if this method had previously returned <code>true</code> to intercept hover
2351      * events and instead returns <code>false</code> while the pointer is hovering
2352      * within the bounds of one of a child, then the view group will first receive a
2353      * hover exit event in {@link #onHoverEvent} and then the hovered child will
2354      * receive a hover enter event.
2355      * </p><p>
2356      * The default implementation handles mouse hover on the scroll bars.
2357      * </p>
2358      *
2359      * @param event The motion event that describes the hover.
2360      * @return True if the view group would like to intercept the hover event
2361      * and prevent its children from receiving it.
2362      */
onInterceptHoverEvent(MotionEvent event)2363     public boolean onInterceptHoverEvent(MotionEvent event) {
2364         if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
2365             final int action = event.getAction();
2366             final float x = event.getX();
2367             final float y = event.getY();
2368             if ((action == MotionEvent.ACTION_HOVER_MOVE
2369                     || action == MotionEvent.ACTION_HOVER_ENTER) && isOnScrollbar(x, y)) {
2370                 return true;
2371             }
2372         }
2373         return false;
2374     }
2375 
obtainMotionEventNoHistoryOrSelf(MotionEvent event)2376     private static MotionEvent obtainMotionEventNoHistoryOrSelf(MotionEvent event) {
2377         if (event.getHistorySize() == 0) {
2378             return event;
2379         }
2380         return MotionEvent.obtainNoHistory(event);
2381     }
2382 
2383     @Override
dispatchGenericPointerEvent(MotionEvent event)2384     protected boolean dispatchGenericPointerEvent(MotionEvent event) {
2385         // Send the event to the child under the pointer.
2386         final int childrenCount = mChildrenCount;
2387         if (childrenCount != 0) {
2388             final float x = event.getX();
2389             final float y = event.getY();
2390 
2391             final ArrayList<View> preorderedList = buildOrderedChildList();
2392             final boolean customOrder = preorderedList == null
2393                     && isChildrenDrawingOrderEnabled();
2394             final View[] children = mChildren;
2395             for (int i = childrenCount - 1; i >= 0; i--) {
2396                 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
2397                 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
2398                 if (!canViewReceivePointerEvents(child)
2399                         || !isTransformedTouchPointInView(x, y, child, null)) {
2400                     continue;
2401                 }
2402 
2403                 if (dispatchTransformedGenericPointerEvent(event, child)) {
2404                     if (preorderedList != null) preorderedList.clear();
2405                     return true;
2406                 }
2407             }
2408             if (preorderedList != null) preorderedList.clear();
2409         }
2410 
2411         // No child handled the event.  Send it to this view group.
2412         return super.dispatchGenericPointerEvent(event);
2413     }
2414 
2415     @Override
dispatchGenericFocusedEvent(MotionEvent event)2416     protected boolean dispatchGenericFocusedEvent(MotionEvent event) {
2417         // Send the event to the focused child or to this view group if it has focus.
2418         if ((mPrivateFlags & (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS))
2419                 == (PFLAG_FOCUSED | PFLAG_HAS_BOUNDS)) {
2420             return super.dispatchGenericFocusedEvent(event);
2421         } else if (mFocused != null && (mFocused.mPrivateFlags & PFLAG_HAS_BOUNDS)
2422                 == PFLAG_HAS_BOUNDS) {
2423             return mFocused.dispatchGenericMotionEvent(event);
2424         }
2425         return false;
2426     }
2427 
2428     /**
2429      * Dispatches a generic pointer event to a child, taking into account
2430      * transformations that apply to the child.
2431      *
2432      * @param event The event to send.
2433      * @param child The view to send the event to.
2434      * @return {@code true} if the child handled the event.
2435      */
dispatchTransformedGenericPointerEvent(MotionEvent event, View child)2436     private boolean dispatchTransformedGenericPointerEvent(MotionEvent event, View child) {
2437         boolean handled;
2438         if (!child.hasIdentityMatrix()) {
2439             MotionEvent transformedEvent = getTransformedMotionEvent(event, child);
2440             handled = child.dispatchGenericMotionEvent(transformedEvent);
2441             transformedEvent.recycle();
2442         } else {
2443             final float offsetX = mScrollX - child.mLeft;
2444             final float offsetY = mScrollY - child.mTop;
2445             event.offsetLocation(offsetX, offsetY);
2446             handled = child.dispatchGenericMotionEvent(event);
2447             event.offsetLocation(-offsetX, -offsetY);
2448         }
2449         return handled;
2450     }
2451 
2452     /**
2453      * Returns a MotionEvent that's been transformed into the child's local coordinates.
2454      *
2455      * It's the responsibility of the caller to recycle it once they're finished with it.
2456      * @param event The event to transform.
2457      * @param child The view whose coordinate space is to be used.
2458      * @return A copy of the the given MotionEvent, transformed into the given View's coordinate
2459      *         space.
2460      */
getTransformedMotionEvent(MotionEvent event, View child)2461     private MotionEvent getTransformedMotionEvent(MotionEvent event, View child) {
2462         final float offsetX = mScrollX - child.mLeft;
2463         final float offsetY = mScrollY - child.mTop;
2464         final MotionEvent transformedEvent = MotionEvent.obtain(event);
2465         transformedEvent.offsetLocation(offsetX, offsetY);
2466         if (!child.hasIdentityMatrix()) {
2467             transformedEvent.transform(child.getInverseMatrix());
2468         }
2469         return transformedEvent;
2470     }
2471 
2472     @Override
dispatchTouchEvent(MotionEvent ev)2473     public boolean dispatchTouchEvent(MotionEvent ev) {
2474         if (mInputEventConsistencyVerifier != null) {
2475             mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
2476         }
2477 
2478         // If the event targets the accessibility focused view and this is it, start
2479         // normal event dispatch. Maybe a descendant is what will handle the click.
2480         if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
2481             ev.setTargetAccessibilityFocus(false);
2482         }
2483 
2484         boolean handled = false;
2485         if (onFilterTouchEventForSecurity(ev)) {
2486             final int action = ev.getAction();
2487             final int actionMasked = action & MotionEvent.ACTION_MASK;
2488 
2489             // Handle an initial down.
2490             if (actionMasked == MotionEvent.ACTION_DOWN) {
2491                 // Throw away all previous state when starting a new touch gesture.
2492                 // The framework may have dropped the up or cancel event for the previous gesture
2493                 // due to an app switch, ANR, or some other state change.
2494                 cancelAndClearTouchTargets(ev);
2495                 resetTouchState();
2496             }
2497 
2498             // Check for interception.
2499             final boolean intercepted;
2500             if (actionMasked == MotionEvent.ACTION_DOWN
2501                     || mFirstTouchTarget != null) {
2502                 final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
2503                 if (!disallowIntercept) {
2504                     intercepted = onInterceptTouchEvent(ev);
2505                     ev.setAction(action); // restore action in case it was changed
2506                 } else {
2507                     intercepted = false;
2508                 }
2509             } else {
2510                 // There are no touch targets and this action is not an initial down
2511                 // so this view group continues to intercept touches.
2512                 intercepted = true;
2513             }
2514 
2515             // If intercepted, start normal event dispatch. Also if there is already
2516             // a view that is handling the gesture, do normal event dispatch.
2517             if (intercepted || mFirstTouchTarget != null) {
2518                 ev.setTargetAccessibilityFocus(false);
2519             }
2520 
2521             // Check for cancelation.
2522             final boolean canceled = resetCancelNextUpFlag(this)
2523                     || actionMasked == MotionEvent.ACTION_CANCEL;
2524 
2525             // Update list of touch targets for pointer down, if needed.
2526             final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
2527             TouchTarget newTouchTarget = null;
2528             boolean alreadyDispatchedToNewTouchTarget = false;
2529             if (!canceled && !intercepted) {
2530 
2531                 // If the event is targeting accessiiblity focus we give it to the
2532                 // view that has accessibility focus and if it does not handle it
2533                 // we clear the flag and dispatch the event to all children as usual.
2534                 // We are looking up the accessibility focused host to avoid keeping
2535                 // state since these events are very rare.
2536                 View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
2537                         ? findChildWithAccessibilityFocus() : null;
2538 
2539                 if (actionMasked == MotionEvent.ACTION_DOWN
2540                         || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
2541                         || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
2542                     final int actionIndex = ev.getActionIndex(); // always 0 for down
2543                     final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
2544                             : TouchTarget.ALL_POINTER_IDS;
2545 
2546                     // Clean up earlier touch targets for this pointer id in case they
2547                     // have become out of sync.
2548                     removePointersFromTouchTargets(idBitsToAssign);
2549 
2550                     final int childrenCount = mChildrenCount;
2551                     if (newTouchTarget == null && childrenCount != 0) {
2552                         final float x = ev.getX(actionIndex);
2553                         final float y = ev.getY(actionIndex);
2554                         // Find a child that can receive the event.
2555                         // Scan children from front to back.
2556                         final ArrayList<View> preorderedList = buildTouchDispatchChildList();
2557                         final boolean customOrder = preorderedList == null
2558                                 && isChildrenDrawingOrderEnabled();
2559                         final View[] children = mChildren;
2560                         for (int i = childrenCount - 1; i >= 0; i--) {
2561                             final int childIndex = getAndVerifyPreorderedIndex(
2562                                     childrenCount, i, customOrder);
2563                             final View child = getAndVerifyPreorderedView(
2564                                     preorderedList, children, childIndex);
2565 
2566                             // If there is a view that has accessibility focus we want it
2567                             // to get the event first and if not handled we will perform a
2568                             // normal dispatch. We may do a double iteration but this is
2569                             // safer given the timeframe.
2570                             if (childWithAccessibilityFocus != null) {
2571                                 if (childWithAccessibilityFocus != child) {
2572                                     continue;
2573                                 }
2574                                 childWithAccessibilityFocus = null;
2575                                 i = childrenCount - 1;
2576                             }
2577 
2578                             if (!canViewReceivePointerEvents(child)
2579                                     || !isTransformedTouchPointInView(x, y, child, null)) {
2580                                 ev.setTargetAccessibilityFocus(false);
2581                                 continue;
2582                             }
2583 
2584                             newTouchTarget = getTouchTarget(child);
2585                             if (newTouchTarget != null) {
2586                                 // Child is already receiving touch within its bounds.
2587                                 // Give it the new pointer in addition to the ones it is handling.
2588                                 newTouchTarget.pointerIdBits |= idBitsToAssign;
2589                                 break;
2590                             }
2591 
2592                             resetCancelNextUpFlag(child);
2593                             if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
2594                                 // Child wants to receive touch within its bounds.
2595                                 mLastTouchDownTime = ev.getDownTime();
2596                                 if (preorderedList != null) {
2597                                     // childIndex points into presorted list, find original index
2598                                     for (int j = 0; j < childrenCount; j++) {
2599                                         if (children[childIndex] == mChildren[j]) {
2600                                             mLastTouchDownIndex = j;
2601                                             break;
2602                                         }
2603                                     }
2604                                 } else {
2605                                     mLastTouchDownIndex = childIndex;
2606                                 }
2607                                 mLastTouchDownX = ev.getX();
2608                                 mLastTouchDownY = ev.getY();
2609                                 newTouchTarget = addTouchTarget(child, idBitsToAssign);
2610                                 alreadyDispatchedToNewTouchTarget = true;
2611                                 break;
2612                             }
2613 
2614                             // The accessibility focus didn't handle the event, so clear
2615                             // the flag and do a normal dispatch to all children.
2616                             ev.setTargetAccessibilityFocus(false);
2617                         }
2618                         if (preorderedList != null) preorderedList.clear();
2619                     }
2620 
2621                     if (newTouchTarget == null && mFirstTouchTarget != null) {
2622                         // Did not find a child to receive the event.
2623                         // Assign the pointer to the least recently added target.
2624                         newTouchTarget = mFirstTouchTarget;
2625                         while (newTouchTarget.next != null) {
2626                             newTouchTarget = newTouchTarget.next;
2627                         }
2628                         newTouchTarget.pointerIdBits |= idBitsToAssign;
2629                     }
2630                 }
2631             }
2632 
2633             // Dispatch to touch targets.
2634             if (mFirstTouchTarget == null) {
2635                 // No touch targets so treat this as an ordinary view.
2636                 handled = dispatchTransformedTouchEvent(ev, canceled, null,
2637                         TouchTarget.ALL_POINTER_IDS);
2638             } else {
2639                 // Dispatch to touch targets, excluding the new touch target if we already
2640                 // dispatched to it.  Cancel touch targets if necessary.
2641                 TouchTarget predecessor = null;
2642                 TouchTarget target = mFirstTouchTarget;
2643                 while (target != null) {
2644                     final TouchTarget next = target.next;
2645                     if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
2646                         handled = true;
2647                     } else {
2648                         final boolean cancelChild = resetCancelNextUpFlag(target.child)
2649                                 || intercepted;
2650                         if (dispatchTransformedTouchEvent(ev, cancelChild,
2651                                 target.child, target.pointerIdBits)) {
2652                             handled = true;
2653                         }
2654                         if (cancelChild) {
2655                             if (predecessor == null) {
2656                                 mFirstTouchTarget = next;
2657                             } else {
2658                                 predecessor.next = next;
2659                             }
2660                             target.recycle();
2661                             target = next;
2662                             continue;
2663                         }
2664                     }
2665                     predecessor = target;
2666                     target = next;
2667                 }
2668             }
2669 
2670             // Update list of touch targets for pointer up or cancel, if needed.
2671             if (canceled
2672                     || actionMasked == MotionEvent.ACTION_UP
2673                     || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
2674                 resetTouchState();
2675             } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
2676                 final int actionIndex = ev.getActionIndex();
2677                 final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
2678                 removePointersFromTouchTargets(idBitsToRemove);
2679             }
2680         }
2681 
2682         if (!handled && mInputEventConsistencyVerifier != null) {
2683             mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
2684         }
2685         return handled;
2686     }
2687 
2688     /**
2689      * Provide custom ordering of views in which the touch will be dispatched.
2690      *
2691      * This is called within a tight loop, so you are not allowed to allocate objects, including
2692      * the return array. Instead, you should return a pre-allocated list that will be cleared
2693      * after the dispatch is finished.
2694      * @hide
2695      */
buildTouchDispatchChildList()2696     public ArrayList<View> buildTouchDispatchChildList() {
2697         return buildOrderedChildList();
2698     }
2699 
2700     /**
2701      * Finds the child which has accessibility focus.
2702      *
2703      * @return The child that has focus.
2704      */
findChildWithAccessibilityFocus()2705     private View findChildWithAccessibilityFocus() {
2706         ViewRootImpl viewRoot = getViewRootImpl();
2707         if (viewRoot == null) {
2708             return null;
2709         }
2710 
2711         View current = viewRoot.getAccessibilityFocusedHost();
2712         if (current == null) {
2713             return null;
2714         }
2715 
2716         ViewParent parent = current.getParent();
2717         while (parent instanceof View) {
2718             if (parent == this) {
2719                 return current;
2720             }
2721             current = (View) parent;
2722             parent = current.getParent();
2723         }
2724 
2725         return null;
2726     }
2727 
2728     /**
2729      * Resets all touch state in preparation for a new cycle.
2730      */
resetTouchState()2731     private void resetTouchState() {
2732         clearTouchTargets();
2733         resetCancelNextUpFlag(this);
2734         mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2735         mNestedScrollAxes = SCROLL_AXIS_NONE;
2736     }
2737 
2738     /**
2739      * Resets the cancel next up flag.
2740      * Returns true if the flag was previously set.
2741      */
resetCancelNextUpFlag(@onNull View view)2742     private static boolean resetCancelNextUpFlag(@NonNull View view) {
2743         if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
2744             view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
2745             return true;
2746         }
2747         return false;
2748     }
2749 
2750     /**
2751      * Clears all touch targets.
2752      */
clearTouchTargets()2753     private void clearTouchTargets() {
2754         TouchTarget target = mFirstTouchTarget;
2755         if (target != null) {
2756             do {
2757                 TouchTarget next = target.next;
2758                 target.recycle();
2759                 target = next;
2760             } while (target != null);
2761             mFirstTouchTarget = null;
2762         }
2763     }
2764 
2765     /**
2766      * Cancels and clears all touch targets.
2767      */
cancelAndClearTouchTargets(MotionEvent event)2768     private void cancelAndClearTouchTargets(MotionEvent event) {
2769         if (mFirstTouchTarget != null) {
2770             boolean syntheticEvent = false;
2771             if (event == null) {
2772                 final long now = SystemClock.uptimeMillis();
2773                 event = MotionEvent.obtain(now, now,
2774                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2775                 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2776                 syntheticEvent = true;
2777             }
2778 
2779             for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2780                 resetCancelNextUpFlag(target.child);
2781                 dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
2782             }
2783             clearTouchTargets();
2784 
2785             if (syntheticEvent) {
2786                 event.recycle();
2787             }
2788         }
2789     }
2790 
2791     /**
2792      * Gets the touch target for specified child view.
2793      * Returns null if not found.
2794      */
getTouchTarget(@onNull View child)2795     private TouchTarget getTouchTarget(@NonNull View child) {
2796         for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2797             if (target.child == child) {
2798                 return target;
2799             }
2800         }
2801         return null;
2802     }
2803 
2804     /**
2805      * Adds a touch target for specified child to the beginning of the list.
2806      * Assumes the target child is not already present.
2807      */
addTouchTarget(@onNull View child, int pointerIdBits)2808     private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
2809         final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
2810         target.next = mFirstTouchTarget;
2811         mFirstTouchTarget = target;
2812         return target;
2813     }
2814 
2815     /**
2816      * Removes the pointer ids from consideration.
2817      */
removePointersFromTouchTargets(int pointerIdBits)2818     private void removePointersFromTouchTargets(int pointerIdBits) {
2819         TouchTarget predecessor = null;
2820         TouchTarget target = mFirstTouchTarget;
2821         while (target != null) {
2822             final TouchTarget next = target.next;
2823             if ((target.pointerIdBits & pointerIdBits) != 0) {
2824                 target.pointerIdBits &= ~pointerIdBits;
2825                 if (target.pointerIdBits == 0) {
2826                     if (predecessor == null) {
2827                         mFirstTouchTarget = next;
2828                     } else {
2829                         predecessor.next = next;
2830                     }
2831                     target.recycle();
2832                     target = next;
2833                     continue;
2834                 }
2835             }
2836             predecessor = target;
2837             target = next;
2838         }
2839     }
2840 
cancelTouchTarget(View view)2841     private void cancelTouchTarget(View view) {
2842         TouchTarget predecessor = null;
2843         TouchTarget target = mFirstTouchTarget;
2844         while (target != null) {
2845             final TouchTarget next = target.next;
2846             if (target.child == view) {
2847                 if (predecessor == null) {
2848                     mFirstTouchTarget = next;
2849                 } else {
2850                     predecessor.next = next;
2851                 }
2852                 target.recycle();
2853 
2854                 final long now = SystemClock.uptimeMillis();
2855                 MotionEvent event = MotionEvent.obtain(now, now,
2856                         MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2857                 event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2858                 view.dispatchTouchEvent(event);
2859                 event.recycle();
2860                 return;
2861             }
2862             predecessor = target;
2863             target = next;
2864         }
2865     }
2866 
2867     /**
2868      * Returns true if a child view can receive pointer events.
2869      * @hide
2870      */
canViewReceivePointerEvents(@onNull View child)2871     private static boolean canViewReceivePointerEvents(@NonNull View child) {
2872         return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
2873                 || child.getAnimation() != null;
2874     }
2875 
getTempPoint()2876     private float[] getTempPoint() {
2877         if (mTempPoint == null) {
2878             mTempPoint = new float[2];
2879         }
2880         return mTempPoint;
2881     }
2882 
2883     /**
2884      * Returns true if a child view contains the specified point when transformed
2885      * into its coordinate space.
2886      * Child must not be null.
2887      * @hide
2888      */
isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint)2889     protected boolean isTransformedTouchPointInView(float x, float y, View child,
2890             PointF outLocalPoint) {
2891         final float[] point = getTempPoint();
2892         point[0] = x;
2893         point[1] = y;
2894         transformPointToViewLocal(point, child);
2895         final boolean isInView = child.pointInView(point[0], point[1]);
2896         if (isInView && outLocalPoint != null) {
2897             outLocalPoint.set(point[0], point[1]);
2898         }
2899         return isInView;
2900     }
2901 
2902     /**
2903      * @hide
2904      */
transformPointToViewLocal(float[] point, View child)2905     public void transformPointToViewLocal(float[] point, View child) {
2906         point[0] += mScrollX - child.mLeft;
2907         point[1] += mScrollY - child.mTop;
2908 
2909         if (!child.hasIdentityMatrix()) {
2910             child.getInverseMatrix().mapPoints(point);
2911         }
2912     }
2913 
2914     /**
2915      * Transforms a motion event into the coordinate space of a particular child view,
2916      * filters out irrelevant pointer ids, and overrides its action if necessary.
2917      * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
2918      */
dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits)2919     private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
2920             View child, int desiredPointerIdBits) {
2921         final boolean handled;
2922 
2923         // Canceling motions is a special case.  We don't need to perform any transformations
2924         // or filtering.  The important part is the action, not the contents.
2925         final int oldAction = event.getAction();
2926         if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
2927             event.setAction(MotionEvent.ACTION_CANCEL);
2928             if (child == null) {
2929                 handled = super.dispatchTouchEvent(event);
2930             } else {
2931                 handled = child.dispatchTouchEvent(event);
2932             }
2933             event.setAction(oldAction);
2934             return handled;
2935         }
2936 
2937         // Calculate the number of pointers to deliver.
2938         final int oldPointerIdBits = event.getPointerIdBits();
2939         final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
2940 
2941         // If for some reason we ended up in an inconsistent state where it looks like we
2942         // might produce a motion event with no pointers in it, then drop the event.
2943         if (newPointerIdBits == 0) {
2944             return false;
2945         }
2946 
2947         // If the number of pointers is the same and we don't need to perform any fancy
2948         // irreversible transformations, then we can reuse the motion event for this
2949         // dispatch as long as we are careful to revert any changes we make.
2950         // Otherwise we need to make a copy.
2951         final MotionEvent transformedEvent;
2952         if (newPointerIdBits == oldPointerIdBits) {
2953             if (child == null || child.hasIdentityMatrix()) {
2954                 if (child == null) {
2955                     handled = super.dispatchTouchEvent(event);
2956                 } else {
2957                     final float offsetX = mScrollX - child.mLeft;
2958                     final float offsetY = mScrollY - child.mTop;
2959                     event.offsetLocation(offsetX, offsetY);
2960 
2961                     handled = child.dispatchTouchEvent(event);
2962 
2963                     event.offsetLocation(-offsetX, -offsetY);
2964                 }
2965                 return handled;
2966             }
2967             transformedEvent = MotionEvent.obtain(event);
2968         } else {
2969             transformedEvent = event.split(newPointerIdBits);
2970         }
2971 
2972         // Perform any necessary transformations and dispatch.
2973         if (child == null) {
2974             handled = super.dispatchTouchEvent(transformedEvent);
2975         } else {
2976             final float offsetX = mScrollX - child.mLeft;
2977             final float offsetY = mScrollY - child.mTop;
2978             transformedEvent.offsetLocation(offsetX, offsetY);
2979             if (! child.hasIdentityMatrix()) {
2980                 transformedEvent.transform(child.getInverseMatrix());
2981             }
2982 
2983             handled = child.dispatchTouchEvent(transformedEvent);
2984         }
2985 
2986         // Done.
2987         transformedEvent.recycle();
2988         return handled;
2989     }
2990 
2991     /**
2992      * Enable or disable the splitting of MotionEvents to multiple children during touch event
2993      * dispatch. This behavior is enabled by default for applications that target an
2994      * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
2995      *
2996      * <p>When this option is enabled MotionEvents may be split and dispatched to different child
2997      * views depending on where each pointer initially went down. This allows for user interactions
2998      * such as scrolling two panes of content independently, chording of buttons, and performing
2999      * independent gestures on different pieces of content.
3000      *
3001      * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
3002      *              child views. <code>false</code> to only allow one child view to be the target of
3003      *              any MotionEvent received by this ViewGroup.
3004      * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
3005      */
setMotionEventSplittingEnabled(boolean split)3006     public void setMotionEventSplittingEnabled(boolean split) {
3007         // TODO Applications really shouldn't change this setting mid-touch event,
3008         // but perhaps this should handle that case and send ACTION_CANCELs to any child views
3009         // with gestures in progress when this is changed.
3010         if (split) {
3011             mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
3012         } else {
3013             mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
3014         }
3015     }
3016 
3017     /**
3018      * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
3019      * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
3020      */
isMotionEventSplittingEnabled()3021     public boolean isMotionEventSplittingEnabled() {
3022         return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
3023     }
3024 
3025     /**
3026      * Returns true if this ViewGroup should be considered as a single entity for removal
3027      * when executing an Activity transition. If this is false, child elements will move
3028      * individually during the transition.
3029      *
3030      * @return True if the ViewGroup should be acted on together during an Activity transition.
3031      * The default value is true when there is a non-null background or if
3032      * {@link #getTransitionName()} is not null or if a
3033      * non-null {@link android.view.ViewOutlineProvider} other than
3034      * {@link android.view.ViewOutlineProvider#BACKGROUND} was given to
3035      * {@link #setOutlineProvider(ViewOutlineProvider)} and false otherwise.
3036      */
isTransitionGroup()3037     public boolean isTransitionGroup() {
3038         if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) {
3039             return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0);
3040         } else {
3041             final ViewOutlineProvider outlineProvider = getOutlineProvider();
3042             return getBackground() != null || getTransitionName() != null ||
3043                     (outlineProvider != null && outlineProvider != ViewOutlineProvider.BACKGROUND);
3044         }
3045     }
3046 
3047     /**
3048      * Changes whether or not this ViewGroup should be treated as a single entity during
3049      * Activity Transitions.
3050      * @param isTransitionGroup Whether or not the ViewGroup should be treated as a unit
3051      *                          in Activity transitions. If false, the ViewGroup won't transition,
3052      *                          only its children. If true, the entire ViewGroup will transition
3053      *                          together.
3054      * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
3055      * android.util.Pair[])
3056      */
setTransitionGroup(boolean isTransitionGroup)3057     public void setTransitionGroup(boolean isTransitionGroup) {
3058         mGroupFlags |= FLAG_IS_TRANSITION_GROUP_SET;
3059         if (isTransitionGroup) {
3060             mGroupFlags |= FLAG_IS_TRANSITION_GROUP;
3061         } else {
3062             mGroupFlags &= ~FLAG_IS_TRANSITION_GROUP;
3063         }
3064     }
3065 
3066     @Override
requestDisallowInterceptTouchEvent(boolean disallowIntercept)3067     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
3068 
3069         if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
3070             // We're already in this state, assume our ancestors are too
3071             return;
3072         }
3073 
3074         if (disallowIntercept) {
3075             mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
3076         } else {
3077             mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
3078         }
3079 
3080         // Pass it up to our parent
3081         if (mParent != null) {
3082             mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
3083         }
3084     }
3085 
3086     /**
3087      * Implement this method to intercept all touch screen motion events.  This
3088      * allows you to watch events as they are dispatched to your children, and
3089      * take ownership of the current gesture at any point.
3090      *
3091      * <p>Using this function takes some care, as it has a fairly complicated
3092      * interaction with {@link View#onTouchEvent(MotionEvent)
3093      * View.onTouchEvent(MotionEvent)}, and using it requires implementing
3094      * that method as well as this one in the correct way.  Events will be
3095      * received in the following order:
3096      *
3097      * <ol>
3098      * <li> You will receive the down event here.
3099      * <li> The down event will be handled either by a child of this view
3100      * group, or given to your own onTouchEvent() method to handle; this means
3101      * you should implement onTouchEvent() to return true, so you will
3102      * continue to see the rest of the gesture (instead of looking for
3103      * a parent view to handle it).  Also, by returning true from
3104      * onTouchEvent(), you will not receive any following
3105      * events in onInterceptTouchEvent() and all touch processing must
3106      * happen in onTouchEvent() like normal.
3107      * <li> For as long as you return false from this function, each following
3108      * event (up to and including the final up) will be delivered first here
3109      * and then to the target's onTouchEvent().
3110      * <li> If you return true from here, you will not receive any
3111      * following events: the target view will receive the same event but
3112      * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
3113      * events will be delivered to your onTouchEvent() method and no longer
3114      * appear here.
3115      * </ol>
3116      *
3117      * @param ev The motion event being dispatched down the hierarchy.
3118      * @return Return true to steal motion events from the children and have
3119      * them dispatched to this ViewGroup through onTouchEvent().
3120      * The current target will receive an ACTION_CANCEL event, and no further
3121      * messages will be delivered here.
3122      */
onInterceptTouchEvent(MotionEvent ev)3123     public boolean onInterceptTouchEvent(MotionEvent ev) {
3124         if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
3125                 && ev.getAction() == MotionEvent.ACTION_DOWN
3126                 && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
3127                 && isOnScrollbarThumb(ev.getX(), ev.getY())) {
3128             return true;
3129         }
3130         return false;
3131     }
3132 
3133     /**
3134      * {@inheritDoc}
3135      *
3136      * Looks for a view to give focus to respecting the setting specified by
3137      * {@link #getDescendantFocusability()}.
3138      *
3139      * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
3140      * find focus within the children of this group when appropriate.
3141      *
3142      * @see #FOCUS_BEFORE_DESCENDANTS
3143      * @see #FOCUS_AFTER_DESCENDANTS
3144      * @see #FOCUS_BLOCK_DESCENDANTS
3145      * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
3146      */
3147     @Override
requestFocus(int direction, Rect previouslyFocusedRect)3148     public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
3149         if (DBG) {
3150             System.out.println(this + " ViewGroup.requestFocus direction="
3151                     + direction);
3152         }
3153         int descendantFocusability = getDescendantFocusability();
3154 
3155         switch (descendantFocusability) {
3156             case FOCUS_BLOCK_DESCENDANTS:
3157                 return super.requestFocus(direction, previouslyFocusedRect);
3158             case FOCUS_BEFORE_DESCENDANTS: {
3159                 final boolean took = super.requestFocus(direction, previouslyFocusedRect);
3160                 return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
3161             }
3162             case FOCUS_AFTER_DESCENDANTS: {
3163                 final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
3164                 return took ? took : super.requestFocus(direction, previouslyFocusedRect);
3165             }
3166             default:
3167                 throw new IllegalStateException("descendant focusability must be "
3168                         + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
3169                         + "but is " + descendantFocusability);
3170         }
3171     }
3172 
3173     /**
3174      * Look for a descendant to call {@link View#requestFocus} on.
3175      * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
3176      * when it wants to request focus within its children.  Override this to
3177      * customize how your {@link ViewGroup} requests focus within its children.
3178      * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
3179      * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
3180      *        to give a finer grained hint about where focus is coming from.  May be null
3181      *        if there is no hint.
3182      * @return Whether focus was taken.
3183      */
3184     @SuppressWarnings({"ConstantConditions"})
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)3185     protected boolean onRequestFocusInDescendants(int direction,
3186             Rect previouslyFocusedRect) {
3187         int index;
3188         int increment;
3189         int end;
3190         int count = mChildrenCount;
3191         if ((direction & FOCUS_FORWARD) != 0) {
3192             index = 0;
3193             increment = 1;
3194             end = count;
3195         } else {
3196             index = count - 1;
3197             increment = -1;
3198             end = -1;
3199         }
3200         final View[] children = mChildren;
3201         for (int i = index; i != end; i += increment) {
3202             View child = children[i];
3203             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
3204                 if (child.requestFocus(direction, previouslyFocusedRect)) {
3205                     return true;
3206                 }
3207             }
3208         }
3209         return false;
3210     }
3211 
3212     @Override
restoreDefaultFocus()3213     public boolean restoreDefaultFocus() {
3214         if (mDefaultFocus != null
3215                 && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
3216                 && (mDefaultFocus.mViewFlags & VISIBILITY_MASK) == VISIBLE
3217                 && mDefaultFocus.restoreDefaultFocus()) {
3218             return true;
3219         }
3220         return super.restoreDefaultFocus();
3221     }
3222 
3223     /**
3224      * @hide
3225      */
3226     @TestApi
3227     @Override
restoreFocusInCluster(@ocusRealDirection int direction)3228     public boolean restoreFocusInCluster(@FocusRealDirection int direction) {
3229         // Allow cluster-navigation to enter touchscreenBlocksFocus ViewGroups.
3230         if (isKeyboardNavigationCluster()) {
3231             final boolean blockedFocus = getTouchscreenBlocksFocus();
3232             try {
3233                 setTouchscreenBlocksFocusNoRefocus(false);
3234                 return restoreFocusInClusterInternal(direction);
3235             } finally {
3236                 setTouchscreenBlocksFocusNoRefocus(blockedFocus);
3237             }
3238         } else {
3239             return restoreFocusInClusterInternal(direction);
3240         }
3241     }
3242 
restoreFocusInClusterInternal(@ocusRealDirection int direction)3243     private boolean restoreFocusInClusterInternal(@FocusRealDirection int direction) {
3244         if (mFocusedInCluster != null && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
3245                 && (mFocusedInCluster.mViewFlags & VISIBILITY_MASK) == VISIBLE
3246                 && mFocusedInCluster.restoreFocusInCluster(direction)) {
3247             return true;
3248         }
3249         return super.restoreFocusInCluster(direction);
3250     }
3251 
3252     /**
3253      * @hide
3254      */
3255     @Override
restoreFocusNotInCluster()3256     public boolean restoreFocusNotInCluster() {
3257         if (mFocusedInCluster != null) {
3258             // since clusters don't nest; we can assume that a non-null mFocusedInCluster
3259             // will refer to a view not-in a cluster.
3260             return restoreFocusInCluster(View.FOCUS_DOWN);
3261         }
3262         if (isKeyboardNavigationCluster() || (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
3263             return false;
3264         }
3265         int descendentFocusability = getDescendantFocusability();
3266         if (descendentFocusability == FOCUS_BLOCK_DESCENDANTS) {
3267             return super.requestFocus(FOCUS_DOWN, null);
3268         }
3269         if (descendentFocusability == FOCUS_BEFORE_DESCENDANTS
3270                 && super.requestFocus(FOCUS_DOWN, null)) {
3271             return true;
3272         }
3273         for (int i = 0; i < mChildrenCount; ++i) {
3274             View child = mChildren[i];
3275             if (!child.isKeyboardNavigationCluster()
3276                     && child.restoreFocusNotInCluster()) {
3277                 return true;
3278             }
3279         }
3280         if (descendentFocusability == FOCUS_AFTER_DESCENDANTS && !hasFocusableChild(false)) {
3281             return super.requestFocus(FOCUS_DOWN, null);
3282         }
3283         return false;
3284     }
3285 
3286     /**
3287      * {@inheritDoc}
3288      *
3289      * @hide
3290      */
3291     @Override
dispatchStartTemporaryDetach()3292     public void dispatchStartTemporaryDetach() {
3293         super.dispatchStartTemporaryDetach();
3294         final int count = mChildrenCount;
3295         final View[] children = mChildren;
3296         for (int i = 0; i < count; i++) {
3297             children[i].dispatchStartTemporaryDetach();
3298         }
3299     }
3300 
3301     /**
3302      * {@inheritDoc}
3303      *
3304      * @hide
3305      */
3306     @Override
dispatchFinishTemporaryDetach()3307     public void dispatchFinishTemporaryDetach() {
3308         super.dispatchFinishTemporaryDetach();
3309         final int count = mChildrenCount;
3310         final View[] children = mChildren;
3311         for (int i = 0; i < count; i++) {
3312             children[i].dispatchFinishTemporaryDetach();
3313         }
3314     }
3315 
3316     @Override
dispatchAttachedToWindow(AttachInfo info, int visibility)3317     void dispatchAttachedToWindow(AttachInfo info, int visibility) {
3318         mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
3319         super.dispatchAttachedToWindow(info, visibility);
3320         mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
3321 
3322         final int count = mChildrenCount;
3323         final View[] children = mChildren;
3324         for (int i = 0; i < count; i++) {
3325             final View child = children[i];
3326             child.dispatchAttachedToWindow(info,
3327                     combineVisibility(visibility, child.getVisibility()));
3328         }
3329         final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
3330         for (int i = 0; i < transientCount; ++i) {
3331             View view = mTransientViews.get(i);
3332             view.dispatchAttachedToWindow(info,
3333                     combineVisibility(visibility, view.getVisibility()));
3334         }
3335     }
3336 
3337     @Override
dispatchScreenStateChanged(int screenState)3338     void dispatchScreenStateChanged(int screenState) {
3339         super.dispatchScreenStateChanged(screenState);
3340 
3341         final int count = mChildrenCount;
3342         final View[] children = mChildren;
3343         for (int i = 0; i < count; i++) {
3344             children[i].dispatchScreenStateChanged(screenState);
3345         }
3346     }
3347 
3348     @Override
dispatchMovedToDisplay(Display display, Configuration config)3349     void dispatchMovedToDisplay(Display display, Configuration config) {
3350         super.dispatchMovedToDisplay(display, config);
3351 
3352         final int count = mChildrenCount;
3353         final View[] children = mChildren;
3354         for (int i = 0; i < count; i++) {
3355             children[i].dispatchMovedToDisplay(display, config);
3356         }
3357     }
3358 
3359     /** @hide */
3360     @Override
dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)3361     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
3362         boolean handled = false;
3363         if (includeForAccessibility()) {
3364             handled = super.dispatchPopulateAccessibilityEventInternal(event);
3365             if (handled) {
3366                 return handled;
3367             }
3368         }
3369         // Let our children have a shot in populating the event.
3370         ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
3371         try {
3372             final int childCount = children.getChildCount();
3373             for (int i = 0; i < childCount; i++) {
3374                 View child = children.getChildAt(i);
3375                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
3376                     handled = child.dispatchPopulateAccessibilityEvent(event);
3377                     if (handled) {
3378                         return handled;
3379                     }
3380                 }
3381             }
3382         } finally {
3383             children.recycle();
3384         }
3385         return false;
3386     }
3387 
3388     /**
3389      * Dispatch creation of {@link ViewStructure} down the hierarchy.  This implementation
3390      * adds in all child views of the view group, in addition to calling the default View
3391      * implementation.
3392      */
3393     @Override
dispatchProvideStructure(ViewStructure structure)3394     public void dispatchProvideStructure(ViewStructure structure) {
3395         super.dispatchProvideStructure(structure);
3396         if (isAssistBlocked() || structure.getChildCount() != 0) {
3397             return;
3398         }
3399         final int childrenCount = mChildrenCount;
3400         if (childrenCount <= 0) {
3401             return;
3402         }
3403         structure.setChildCount(childrenCount);
3404         ArrayList<View> preorderedList = buildOrderedChildList();
3405         boolean customOrder = preorderedList == null
3406                 && isChildrenDrawingOrderEnabled();
3407         for (int i = 0; i < childrenCount; i++) {
3408             int childIndex;
3409             try {
3410                 childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
3411             } catch (IndexOutOfBoundsException e) {
3412                 childIndex = i;
3413                 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
3414                     Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
3415                             + i + " of " + childrenCount, e);
3416                     // At least one app is failing when we call getChildDrawingOrder
3417                     // at this point, so deal semi-gracefully with it by falling back
3418                     // on the basic order.
3419                     customOrder = false;
3420                     if (i > 0) {
3421                         // If we failed at the first index, there really isn't
3422                         // anything to do -- we will just proceed with the simple
3423                         // sequence order.
3424                         // Otherwise, we failed in the middle, so need to come up
3425                         // with an order for the remaining indices and use that.
3426                         // Failed at the first one, easy peasy.
3427                         int[] permutation = new int[childrenCount];
3428                         SparseBooleanArray usedIndices = new SparseBooleanArray();
3429                         // Go back and collected the indices we have done so far.
3430                         for (int j = 0; j < i; j++) {
3431                             permutation[j] = getChildDrawingOrder(childrenCount, j);
3432                             usedIndices.put(permutation[j], true);
3433                         }
3434                         // Fill in the remaining indices with indices that have not
3435                         // yet been used.
3436                         int nextIndex = 0;
3437                         for (int j = i; j < childrenCount; j++) {
3438                             while (usedIndices.get(nextIndex, false)) {
3439                                 nextIndex++;
3440                             }
3441                             permutation[j] = nextIndex;
3442                             nextIndex++;
3443                         }
3444                         // Build the final view list.
3445                         preorderedList = new ArrayList<>(childrenCount);
3446                         for (int j = 0; j < childrenCount; j++) {
3447                             final int index = permutation[j];
3448                             final View child = mChildren[index];
3449                             preorderedList.add(child);
3450                         }
3451                     }
3452                 } else {
3453                     throw e;
3454                 }
3455             }
3456             final View child = getAndVerifyPreorderedView(preorderedList, mChildren,
3457                     childIndex);
3458             final ViewStructure cstructure = structure.newChild(i);
3459             child.dispatchProvideStructure(cstructure);
3460         }
3461         if (preorderedList != null) {
3462             preorderedList.clear();
3463         }
3464     }
3465 
3466     /**
3467      * {@inheritDoc}
3468      *
3469      * <p>This implementation adds in all child views of the view group, in addition to calling the
3470      * default {@link View} implementation.
3471      */
3472     @Override
dispatchProvideAutofillStructure(ViewStructure structure, @AutofillFlags int flags)3473     public void dispatchProvideAutofillStructure(ViewStructure structure,
3474             @AutofillFlags int flags) {
3475         super.dispatchProvideAutofillStructure(structure, flags);
3476         if (structure.getChildCount() != 0) {
3477             return;
3478         }
3479         final ChildListForAutoFill children = getChildrenForAutofill(flags);
3480         final int childrenCount = children.size();
3481         structure.setChildCount(childrenCount);
3482         for (int i = 0; i < childrenCount; i++) {
3483             final View child = children.get(i);
3484             final ViewStructure cstructure = structure.newChild(i);
3485             child.dispatchProvideAutofillStructure(cstructure, flags);
3486         }
3487         children.recycle();
3488     }
3489 
3490     /**
3491      * Gets the children for autofill. Children for autofill are the first
3492      * level descendants that are important for autofill. The returned
3493      * child list object is pooled and the caller must recycle it once done.
3494      * @hide */
getChildrenForAutofill(@utofillFlags int flags)3495     private @NonNull ChildListForAutoFill getChildrenForAutofill(@AutofillFlags int flags) {
3496         final ChildListForAutoFill children = ChildListForAutoFill.obtain();
3497         populateChildrenForAutofill(children, flags);
3498         return children;
3499     }
3500 
3501     /** @hide */
populateChildrenForAutofill(ArrayList<View> list, @AutofillFlags int flags)3502     private void populateChildrenForAutofill(ArrayList<View> list, @AutofillFlags int flags) {
3503         final int childrenCount = mChildrenCount;
3504         if (childrenCount <= 0) {
3505             return;
3506         }
3507         final ArrayList<View> preorderedList = buildOrderedChildList();
3508         final boolean customOrder = preorderedList == null
3509                 && isChildrenDrawingOrderEnabled();
3510         for (int i = 0; i < childrenCount; i++) {
3511             final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
3512             final View child = (preorderedList == null)
3513                     ? mChildren[childIndex] : preorderedList.get(childIndex);
3514             if ((flags & AUTOFILL_FLAG_INCLUDE_NOT_IMPORTANT_VIEWS) != 0
3515                     || child.isImportantForAutofill()) {
3516                 list.add(child);
3517             } else if (child instanceof ViewGroup) {
3518                 ((ViewGroup) child).populateChildrenForAutofill(list, flags);
3519             }
3520         }
3521     }
3522 
getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children, int childIndex)3523     private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
3524             int childIndex) {
3525         final View child;
3526         if (preorderedList != null) {
3527             child = preorderedList.get(childIndex);
3528             if (child == null) {
3529                 throw new RuntimeException("Invalid preorderedList contained null child at index "
3530                         + childIndex);
3531             }
3532         } else {
3533             child = children[childIndex];
3534         }
3535         return child;
3536     }
3537 
3538     /** @hide */
3539     @Override
onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info)3540     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
3541         super.onInitializeAccessibilityNodeInfoInternal(info);
3542         if (getAccessibilityNodeProvider() != null) {
3543             return;
3544         }
3545         if (mAttachInfo != null) {
3546             final ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
3547             childrenForAccessibility.clear();
3548             addChildrenForAccessibility(childrenForAccessibility);
3549             final int childrenForAccessibilityCount = childrenForAccessibility.size();
3550             for (int i = 0; i < childrenForAccessibilityCount; i++) {
3551                 final View child = childrenForAccessibility.get(i);
3552                 info.addChildUnchecked(child);
3553             }
3554             childrenForAccessibility.clear();
3555         }
3556     }
3557 
3558     @Override
getAccessibilityClassName()3559     public CharSequence getAccessibilityClassName() {
3560         return ViewGroup.class.getName();
3561     }
3562 
3563     @Override
notifySubtreeAccessibilityStateChanged(View child, View source, int changeType)3564     public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
3565         // If this is a live region, we should send a subtree change event
3566         // from this view. Otherwise, we can let it propagate up.
3567         if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
3568             notifyViewAccessibilityStateChangedIfNeeded(changeType);
3569         } else if (mParent != null) {
3570             try {
3571                 mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
3572             } catch (AbstractMethodError e) {
3573                 Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
3574                         " does not fully implement ViewParent", e);
3575             }
3576         }
3577     }
3578 
3579     /** @hide */
3580     @Override
notifySubtreeAccessibilityStateChangedIfNeeded()3581     public void notifySubtreeAccessibilityStateChangedIfNeeded() {
3582         if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
3583             return;
3584         }
3585         // If something important for a11y is happening in this subtree, make sure it's dispatched
3586         // from a view that is important for a11y so it doesn't get lost.
3587         if ((getImportantForAccessibility() != IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
3588                 && !isImportantForAccessibility() && (getChildCount() > 0)) {
3589             ViewParent a11yParent = getParentForAccessibility();
3590             if (a11yParent instanceof View) {
3591                 ((View) a11yParent).notifySubtreeAccessibilityStateChangedIfNeeded();
3592                 return;
3593             }
3594         }
3595         super.notifySubtreeAccessibilityStateChangedIfNeeded();
3596     }
3597 
3598     @Override
resetSubtreeAccessibilityStateChanged()3599     void resetSubtreeAccessibilityStateChanged() {
3600         super.resetSubtreeAccessibilityStateChanged();
3601         View[] children = mChildren;
3602         final int childCount = mChildrenCount;
3603         for (int i = 0; i < childCount; i++) {
3604             children[i].resetSubtreeAccessibilityStateChanged();
3605         }
3606     }
3607 
3608     /**
3609      * Counts the number of children of this View that will be sent to an accessibility service.
3610      *
3611      * @return The number of children an {@code AccessibilityNodeInfo} rooted at this View
3612      * would have.
3613      */
getNumChildrenForAccessibility()3614     int getNumChildrenForAccessibility() {
3615         int numChildrenForAccessibility = 0;
3616         for (int i = 0; i < getChildCount(); i++) {
3617             View child = getChildAt(i);
3618             if (child.includeForAccessibility()) {
3619                 numChildrenForAccessibility++;
3620             } else if (child instanceof ViewGroup) {
3621                 numChildrenForAccessibility += ((ViewGroup) child)
3622                         .getNumChildrenForAccessibility();
3623             }
3624         }
3625         return numChildrenForAccessibility;
3626     }
3627 
3628     /**
3629      * {@inheritDoc}
3630      *
3631      * <p>Subclasses should always call <code>super.onNestedPrePerformAccessibilityAction</code></p>
3632      *
3633      * @param target The target view dispatching this action
3634      * @param action Action being performed; see
3635      *               {@link android.view.accessibility.AccessibilityNodeInfo}
3636      * @param args Optional action arguments
3637      * @return false by default. Subclasses should return true if they handle the event.
3638      */
3639     @Override
onNestedPrePerformAccessibilityAction(View target, int action, Bundle args)3640     public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
3641         return false;
3642     }
3643 
3644     @Override
dispatchDetachedFromWindow()3645     void dispatchDetachedFromWindow() {
3646         // If we still have a touch target, we are still in the process of
3647         // dispatching motion events to a child; we need to get rid of that
3648         // child to avoid dispatching events to it after the window is torn
3649         // down. To make sure we keep the child in a consistent state, we
3650         // first send it an ACTION_CANCEL motion event.
3651         cancelAndClearTouchTargets(null);
3652 
3653         // Similarly, set ACTION_EXIT to all hover targets and clear them.
3654         exitHoverTargets();
3655         exitTooltipHoverTargets();
3656 
3657         // In case view is detached while transition is running
3658         mLayoutCalledWhileSuppressed = false;
3659 
3660         // Tear down our drag tracking
3661         mChildrenInterestedInDrag = null;
3662         mIsInterestedInDrag = false;
3663         if (mCurrentDragStartEvent != null) {
3664             mCurrentDragStartEvent.recycle();
3665             mCurrentDragStartEvent = null;
3666         }
3667 
3668         final int count = mChildrenCount;
3669         final View[] children = mChildren;
3670         for (int i = 0; i < count; i++) {
3671             children[i].dispatchDetachedFromWindow();
3672         }
3673         clearDisappearingChildren();
3674         final int transientCount = mTransientViews == null ? 0 : mTransientIndices.size();
3675         for (int i = 0; i < transientCount; ++i) {
3676             View view = mTransientViews.get(i);
3677             view.dispatchDetachedFromWindow();
3678         }
3679         super.dispatchDetachedFromWindow();
3680     }
3681 
3682     /**
3683      * @hide
3684      */
3685     @Override
internalSetPadding(int left, int top, int right, int bottom)3686     protected void internalSetPadding(int left, int top, int right, int bottom) {
3687         super.internalSetPadding(left, top, right, bottom);
3688 
3689         if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
3690             mGroupFlags |= FLAG_PADDING_NOT_NULL;
3691         } else {
3692             mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
3693         }
3694     }
3695 
3696     @Override
dispatchSaveInstanceState(SparseArray<Parcelable> container)3697     protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
3698         super.dispatchSaveInstanceState(container);
3699         final int count = mChildrenCount;
3700         final View[] children = mChildren;
3701         for (int i = 0; i < count; i++) {
3702             View c = children[i];
3703             if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
3704                 c.dispatchSaveInstanceState(container);
3705             }
3706         }
3707     }
3708 
3709     /**
3710      * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)}  freeze()}
3711      * to only this view, not to its children.  For use when overriding
3712      * {@link #dispatchSaveInstanceState(android.util.SparseArray)}  dispatchFreeze()} to allow
3713      * subclasses to freeze their own state but not the state of their children.
3714      *
3715      * @param container the container
3716      */
dispatchFreezeSelfOnly(SparseArray<Parcelable> container)3717     protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
3718         super.dispatchSaveInstanceState(container);
3719     }
3720 
3721     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)3722     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
3723         super.dispatchRestoreInstanceState(container);
3724         final int count = mChildrenCount;
3725         final View[] children = mChildren;
3726         for (int i = 0; i < count; i++) {
3727             View c = children[i];
3728             if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
3729                 c.dispatchRestoreInstanceState(container);
3730             }
3731         }
3732     }
3733 
3734     /**
3735      * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
3736      * to only this view, not to its children.  For use when overriding
3737      * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
3738      * subclasses to thaw their own state but not the state of their children.
3739      *
3740      * @param container the container
3741      */
dispatchThawSelfOnly(SparseArray<Parcelable> container)3742     protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
3743         super.dispatchRestoreInstanceState(container);
3744     }
3745 
3746     /**
3747      * Enables or disables the drawing cache for each child of this view group.
3748      *
3749      * @param enabled true to enable the cache, false to dispose of it
3750      */
setChildrenDrawingCacheEnabled(boolean enabled)3751     protected void setChildrenDrawingCacheEnabled(boolean enabled) {
3752         if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
3753             final View[] children = mChildren;
3754             final int count = mChildrenCount;
3755             for (int i = 0; i < count; i++) {
3756                 children[i].setDrawingCacheEnabled(enabled);
3757             }
3758         }
3759     }
3760 
3761     /**
3762      * @hide
3763      */
3764     @Override
createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren)3765     public Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
3766         int count = mChildrenCount;
3767         int[] visibilities = null;
3768 
3769         if (skipChildren) {
3770             visibilities = new int[count];
3771             for (int i = 0; i < count; i++) {
3772                 View child = getChildAt(i);
3773                 visibilities[i] = child.getVisibility();
3774                 if (visibilities[i] == View.VISIBLE) {
3775                     child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK)
3776                             | (View.INVISIBLE & View.VISIBILITY_MASK);
3777                 }
3778             }
3779         }
3780 
3781         Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
3782 
3783         if (skipChildren) {
3784             for (int i = 0; i < count; i++) {
3785                 View child = getChildAt(i);
3786                 child.mViewFlags = (child.mViewFlags & ~View.VISIBILITY_MASK)
3787                         | (visibilities[i] & View.VISIBILITY_MASK);
3788             }
3789         }
3790 
3791         return b;
3792     }
3793 
3794     /** Return true if this ViewGroup is laying out using optical bounds. */
isLayoutModeOptical()3795     boolean isLayoutModeOptical() {
3796         return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
3797     }
3798 
3799     @Override
computeOpticalInsets()3800     Insets computeOpticalInsets() {
3801         if (isLayoutModeOptical()) {
3802             int left = 0;
3803             int top = 0;
3804             int right = 0;
3805             int bottom = 0;
3806             for (int i = 0; i < mChildrenCount; i++) {
3807                 View child = getChildAt(i);
3808                 if (child.getVisibility() == VISIBLE) {
3809                     Insets insets = child.getOpticalInsets();
3810                     left =   Math.max(left,   insets.left);
3811                     top =    Math.max(top,    insets.top);
3812                     right =  Math.max(right,  insets.right);
3813                     bottom = Math.max(bottom, insets.bottom);
3814                 }
3815             }
3816             return Insets.of(left, top, right, bottom);
3817         } else {
3818             return Insets.NONE;
3819         }
3820     }
3821 
fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2)3822     private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
3823         if (x1 != x2 && y1 != y2) {
3824             if (x1 > x2) {
3825                 int tmp = x1; x1 = x2; x2 = tmp;
3826             }
3827             if (y1 > y2) {
3828                 int tmp = y1; y1 = y2; y2 = tmp;
3829             }
3830             canvas.drawRect(x1, y1, x2, y2, paint);
3831         }
3832     }
3833 
sign(int x)3834     private static int sign(int x) {
3835         return (x >= 0) ? 1 : -1;
3836     }
3837 
drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw)3838     private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) {
3839         fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy));
3840         fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy);
3841     }
3842 
drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint, int lineLength, int lineWidth)3843     private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
3844             int lineLength, int lineWidth) {
3845         drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
3846         drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth);
3847         drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth);
3848         drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth);
3849     }
3850 
fillDifference(Canvas canvas, int x2, int y2, int x3, int y3, int dx1, int dy1, int dx2, int dy2, Paint paint)3851     private static void fillDifference(Canvas canvas,
3852             int x2, int y2, int x3, int y3,
3853             int dx1, int dy1, int dx2, int dy2, Paint paint) {
3854         int x1 = x2 - dx1;
3855         int y1 = y2 - dy1;
3856 
3857         int x4 = x3 + dx2;
3858         int y4 = y3 + dy2;
3859 
3860         fillRect(canvas, paint, x1, y1, x4, y2);
3861         fillRect(canvas, paint, x1, y2, x2, y3);
3862         fillRect(canvas, paint, x3, y2, x4, y3);
3863         fillRect(canvas, paint, x1, y3, x4, y4);
3864     }
3865 
3866     /**
3867      * @hide
3868      */
onDebugDrawMargins(Canvas canvas, Paint paint)3869     protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
3870         for (int i = 0; i < getChildCount(); i++) {
3871             View c = getChildAt(i);
3872             c.getLayoutParams().onDebugDraw(c, canvas, paint);
3873         }
3874     }
3875 
3876     /**
3877      * @hide
3878      */
onDebugDraw(Canvas canvas)3879     protected void onDebugDraw(Canvas canvas) {
3880         Paint paint = getDebugPaint();
3881 
3882         // Draw optical bounds
3883         {
3884             paint.setColor(Color.RED);
3885             paint.setStyle(Paint.Style.STROKE);
3886 
3887             for (int i = 0; i < getChildCount(); i++) {
3888                 View c = getChildAt(i);
3889                 if (c.getVisibility() != View.GONE) {
3890                     Insets insets = c.getOpticalInsets();
3891 
3892                     drawRect(canvas, paint,
3893                             c.getLeft() + insets.left,
3894                             c.getTop() + insets.top,
3895                             c.getRight() - insets.right - 1,
3896                             c.getBottom() - insets.bottom - 1);
3897                 }
3898             }
3899         }
3900 
3901         // Draw margins
3902         {
3903             paint.setColor(Color.argb(63, 255, 0, 255));
3904             paint.setStyle(Paint.Style.FILL);
3905 
3906             onDebugDrawMargins(canvas, paint);
3907         }
3908 
3909         // Draw clip bounds
3910         {
3911             paint.setColor(DEBUG_CORNERS_COLOR);
3912             paint.setStyle(Paint.Style.FILL);
3913 
3914             int lineLength = dipsToPixels(DEBUG_CORNERS_SIZE_DIP);
3915             int lineWidth = dipsToPixels(1);
3916             for (int i = 0; i < getChildCount(); i++) {
3917                 View c = getChildAt(i);
3918                 if (c.getVisibility() != View.GONE) {
3919                     drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(),
3920                             paint, lineLength, lineWidth);
3921                 }
3922             }
3923         }
3924     }
3925 
3926     @Override
dispatchDraw(Canvas canvas)3927     protected void dispatchDraw(Canvas canvas) {
3928         boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
3929         final int childrenCount = mChildrenCount;
3930         final View[] children = mChildren;
3931         int flags = mGroupFlags;
3932 
3933         if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
3934             final boolean buildCache = !isHardwareAccelerated();
3935             for (int i = 0; i < childrenCount; i++) {
3936                 final View child = children[i];
3937                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
3938                     final LayoutParams params = child.getLayoutParams();
3939                     attachLayoutAnimationParameters(child, params, i, childrenCount);
3940                     bindLayoutAnimation(child);
3941                 }
3942             }
3943 
3944             final LayoutAnimationController controller = mLayoutAnimationController;
3945             if (controller.willOverlap()) {
3946                 mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
3947             }
3948 
3949             controller.start();
3950 
3951             mGroupFlags &= ~FLAG_RUN_ANIMATION;
3952             mGroupFlags &= ~FLAG_ANIMATION_DONE;
3953 
3954             if (mAnimationListener != null) {
3955                 mAnimationListener.onAnimationStart(controller.getAnimation());
3956             }
3957         }
3958 
3959         int clipSaveCount = 0;
3960         final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
3961         if (clipToPadding) {
3962             clipSaveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
3963             canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
3964                     mScrollX + mRight - mLeft - mPaddingRight,
3965                     mScrollY + mBottom - mTop - mPaddingBottom);
3966         }
3967 
3968         // We will draw our child's animation, let's reset the flag
3969         mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
3970         mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
3971 
3972         boolean more = false;
3973         final long drawingTime = getDrawingTime();
3974 
3975         if (usingRenderNodeProperties) canvas.insertReorderBarrier();
3976         final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
3977         int transientIndex = transientCount != 0 ? 0 : -1;
3978         // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
3979         // draw reordering internally
3980         final ArrayList<View> preorderedList = usingRenderNodeProperties
3981                 ? null : buildOrderedChildList();
3982         final boolean customOrder = preorderedList == null
3983                 && isChildrenDrawingOrderEnabled();
3984         for (int i = 0; i < childrenCount; i++) {
3985             while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
3986                 final View transientChild = mTransientViews.get(transientIndex);
3987                 if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
3988                         transientChild.getAnimation() != null) {
3989                     more |= drawChild(canvas, transientChild, drawingTime);
3990                 }
3991                 transientIndex++;
3992                 if (transientIndex >= transientCount) {
3993                     transientIndex = -1;
3994                 }
3995             }
3996 
3997             final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
3998             final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
3999             if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
4000                 more |= drawChild(canvas, child, drawingTime);
4001             }
4002         }
4003         while (transientIndex >= 0) {
4004             // there may be additional transient views after the normal views
4005             final View transientChild = mTransientViews.get(transientIndex);
4006             if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
4007                     transientChild.getAnimation() != null) {
4008                 more |= drawChild(canvas, transientChild, drawingTime);
4009             }
4010             transientIndex++;
4011             if (transientIndex >= transientCount) {
4012                 break;
4013             }
4014         }
4015         if (preorderedList != null) preorderedList.clear();
4016 
4017         // Draw any disappearing views that have animations
4018         if (mDisappearingChildren != null) {
4019             final ArrayList<View> disappearingChildren = mDisappearingChildren;
4020             final int disappearingCount = disappearingChildren.size() - 1;
4021             // Go backwards -- we may delete as animations finish
4022             for (int i = disappearingCount; i >= 0; i--) {
4023                 final View child = disappearingChildren.get(i);
4024                 more |= drawChild(canvas, child, drawingTime);
4025             }
4026         }
4027         if (usingRenderNodeProperties) canvas.insertInorderBarrier();
4028 
4029         if (debugDraw()) {
4030             onDebugDraw(canvas);
4031         }
4032 
4033         if (clipToPadding) {
4034             canvas.restoreToCount(clipSaveCount);
4035         }
4036 
4037         // mGroupFlags might have been updated by drawChild()
4038         flags = mGroupFlags;
4039 
4040         if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
4041             invalidate(true);
4042         }
4043 
4044         if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
4045                 mLayoutAnimationController.isDone() && !more) {
4046             // We want to erase the drawing cache and notify the listener after the
4047             // next frame is drawn because one extra invalidate() is caused by
4048             // drawChild() after the animation is over
4049             mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
4050             final Runnable end = new Runnable() {
4051                @Override
4052                public void run() {
4053                    notifyAnimationListener();
4054                }
4055             };
4056             post(end);
4057         }
4058     }
4059 
4060     /**
4061      * Returns the ViewGroupOverlay for this view group, creating it if it does
4062      * not yet exist. In addition to {@link ViewOverlay}'s support for drawables,
4063      * {@link ViewGroupOverlay} allows views to be added to the overlay. These
4064      * views, like overlay drawables, are visual-only; they do not receive input
4065      * events and should not be used as anything other than a temporary
4066      * representation of a view in a parent container, such as might be used
4067      * by an animation effect.
4068      *
4069      * <p>Note: Overlays do not currently work correctly with {@link
4070      * SurfaceView} or {@link TextureView}; contents in overlays for these
4071      * types of views may not display correctly.</p>
4072      *
4073      * @return The ViewGroupOverlay object for this view.
4074      * @see ViewGroupOverlay
4075      */
4076     @Override
getOverlay()4077     public ViewGroupOverlay getOverlay() {
4078         if (mOverlay == null) {
4079             mOverlay = new ViewGroupOverlay(mContext, this);
4080         }
4081         return (ViewGroupOverlay) mOverlay;
4082     }
4083 
4084     /**
4085      * Returns the index of the child to draw for this iteration. Override this
4086      * if you want to change the drawing order of children. By default, it
4087      * returns i.
4088      * <p>
4089      * NOTE: In order for this method to be called, you must enable child ordering
4090      * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
4091      *
4092      * @param i The current iteration.
4093      * @return The index of the child to draw this iteration.
4094      *
4095      * @see #setChildrenDrawingOrderEnabled(boolean)
4096      * @see #isChildrenDrawingOrderEnabled()
4097      */
getChildDrawingOrder(int childCount, int i)4098     protected int getChildDrawingOrder(int childCount, int i) {
4099         return i;
4100     }
4101 
hasChildWithZ()4102     private boolean hasChildWithZ() {
4103         for (int i = 0; i < mChildrenCount; i++) {
4104             if (mChildren[i].getZ() != 0) return true;
4105         }
4106         return false;
4107     }
4108 
4109     /**
4110      * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children,
4111      * sorted first by Z, then by child drawing order (if applicable). This list must be cleared
4112      * after use to avoid leaking child Views.
4113      *
4114      * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
4115      * children.
4116      */
buildOrderedChildList()4117     ArrayList<View> buildOrderedChildList() {
4118         final int childrenCount = mChildrenCount;
4119         if (childrenCount <= 1 || !hasChildWithZ()) return null;
4120 
4121         if (mPreSortedChildren == null) {
4122             mPreSortedChildren = new ArrayList<>(childrenCount);
4123         } else {
4124             // callers should clear, so clear shouldn't be necessary, but for safety...
4125             mPreSortedChildren.clear();
4126             mPreSortedChildren.ensureCapacity(childrenCount);
4127         }
4128 
4129         final boolean customOrder = isChildrenDrawingOrderEnabled();
4130         for (int i = 0; i < childrenCount; i++) {
4131             // add next child (in child order) to end of list
4132             final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
4133             final View nextChild = mChildren[childIndex];
4134             final float currentZ = nextChild.getZ();
4135 
4136             // insert ahead of any Views with greater Z
4137             int insertIndex = i;
4138             while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
4139                 insertIndex--;
4140             }
4141             mPreSortedChildren.add(insertIndex, nextChild);
4142         }
4143         return mPreSortedChildren;
4144     }
4145 
notifyAnimationListener()4146     private void notifyAnimationListener() {
4147         mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
4148         mGroupFlags |= FLAG_ANIMATION_DONE;
4149 
4150         if (mAnimationListener != null) {
4151            final Runnable end = new Runnable() {
4152                @Override
4153                public void run() {
4154                    mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
4155                }
4156            };
4157            post(end);
4158         }
4159 
4160         invalidate(true);
4161     }
4162 
4163     /**
4164      * This method is used to cause children of this ViewGroup to restore or recreate their
4165      * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
4166      * to recreate its own display list, which would happen if it went through the normal
4167      * draw/dispatchDraw mechanisms.
4168      *
4169      * @hide
4170      */
4171     @Override
dispatchGetDisplayList()4172     protected void dispatchGetDisplayList() {
4173         final int count = mChildrenCount;
4174         final View[] children = mChildren;
4175         for (int i = 0; i < count; i++) {
4176             final View child = children[i];
4177             if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
4178                 recreateChildDisplayList(child);
4179             }
4180         }
4181         if (mOverlay != null) {
4182             View overlayView = mOverlay.getOverlayView();
4183             recreateChildDisplayList(overlayView);
4184         }
4185         if (mDisappearingChildren != null) {
4186             final ArrayList<View> disappearingChildren = mDisappearingChildren;
4187             final int disappearingCount = disappearingChildren.size();
4188             for (int i = 0; i < disappearingCount; ++i) {
4189                 final View child = disappearingChildren.get(i);
4190                 recreateChildDisplayList(child);
4191             }
4192         }
4193     }
4194 
recreateChildDisplayList(View child)4195     private void recreateChildDisplayList(View child) {
4196         child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
4197         child.mPrivateFlags &= ~PFLAG_INVALIDATED;
4198         child.updateDisplayListIfDirty();
4199         child.mRecreateDisplayList = false;
4200     }
4201 
4202     /**
4203      * Draw one child of this View Group. This method is responsible for getting
4204      * the canvas in the right state. This includes clipping, translating so
4205      * that the child's scrolled origin is at 0, 0, and applying any animation
4206      * transformations.
4207      *
4208      * @param canvas The canvas on which to draw the child
4209      * @param child Who to draw
4210      * @param drawingTime The time at which draw is occurring
4211      * @return True if an invalidate() was issued
4212      */
drawChild(Canvas canvas, View child, long drawingTime)4213     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
4214         return child.draw(canvas, this, drawingTime);
4215     }
4216 
4217     @Override
getScrollIndicatorBounds(@onNull Rect out)4218     void getScrollIndicatorBounds(@NonNull Rect out) {
4219         super.getScrollIndicatorBounds(out);
4220 
4221         // If we have padding and we're supposed to clip children to that
4222         // padding, offset the scroll indicators to match our clip bounds.
4223         final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
4224         if (clipToPadding) {
4225             out.left += mPaddingLeft;
4226             out.right -= mPaddingRight;
4227             out.top += mPaddingTop;
4228             out.bottom -= mPaddingBottom;
4229         }
4230     }
4231 
4232     /**
4233      * Returns whether this group's children are clipped to their bounds before drawing.
4234      * The default value is true.
4235      * @see #setClipChildren(boolean)
4236      *
4237      * @return True if the group's children will be clipped to their bounds,
4238      * false otherwise.
4239      */
4240     @ViewDebug.ExportedProperty(category = "drawing")
getClipChildren()4241     public boolean getClipChildren() {
4242         return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
4243     }
4244 
4245     /**
4246      * By default, children are clipped to their bounds before drawing. This
4247      * allows view groups to override this behavior for animations, etc.
4248      *
4249      * @param clipChildren true to clip children to their bounds,
4250      *        false otherwise
4251      * @attr ref android.R.styleable#ViewGroup_clipChildren
4252      */
setClipChildren(boolean clipChildren)4253     public void setClipChildren(boolean clipChildren) {
4254         boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN;
4255         if (clipChildren != previousValue) {
4256             setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
4257             for (int i = 0; i < mChildrenCount; ++i) {
4258                 View child = getChildAt(i);
4259                 if (child.mRenderNode != null) {
4260                     child.mRenderNode.setClipToBounds(clipChildren);
4261                 }
4262             }
4263             invalidate(true);
4264         }
4265     }
4266 
4267     /**
4268      * Sets whether this ViewGroup will clip its children to its padding and resize (but not
4269      * clip) any EdgeEffect to the padded region, if padding is present.
4270      * <p>
4271      * By default, children are clipped to the padding of their parent
4272      * ViewGroup. This clipping behavior is only enabled if padding is non-zero.
4273      *
4274      * @param clipToPadding true to clip children to the padding of the group, and resize (but
4275      *        not clip) any EdgeEffect to the padded region. False otherwise.
4276      * @attr ref android.R.styleable#ViewGroup_clipToPadding
4277      */
setClipToPadding(boolean clipToPadding)4278     public void setClipToPadding(boolean clipToPadding) {
4279         if (hasBooleanFlag(FLAG_CLIP_TO_PADDING) != clipToPadding) {
4280             setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
4281             invalidate(true);
4282         }
4283     }
4284 
4285     /**
4286      * Returns whether this ViewGroup will clip its children to its padding, and resize (but
4287      * not clip) any EdgeEffect to the padded region, if padding is present.
4288      * <p>
4289      * By default, children are clipped to the padding of their parent
4290      * Viewgroup. This clipping behavior is only enabled if padding is non-zero.
4291      *
4292      * @return true if this ViewGroup clips children to its padding and resizes (but doesn't
4293      *         clip) any EdgeEffect to the padded region, false otherwise.
4294      *
4295      * @attr ref android.R.styleable#ViewGroup_clipToPadding
4296      */
4297     @ViewDebug.ExportedProperty(category = "drawing")
getClipToPadding()4298     public boolean getClipToPadding() {
4299         return hasBooleanFlag(FLAG_CLIP_TO_PADDING);
4300     }
4301 
4302     @Override
dispatchSetSelected(boolean selected)4303     public void dispatchSetSelected(boolean selected) {
4304         final View[] children = mChildren;
4305         final int count = mChildrenCount;
4306         for (int i = 0; i < count; i++) {
4307             children[i].setSelected(selected);
4308         }
4309     }
4310 
4311     @Override
dispatchSetActivated(boolean activated)4312     public void dispatchSetActivated(boolean activated) {
4313         final View[] children = mChildren;
4314         final int count = mChildrenCount;
4315         for (int i = 0; i < count; i++) {
4316             children[i].setActivated(activated);
4317         }
4318     }
4319 
4320     @Override
dispatchSetPressed(boolean pressed)4321     protected void dispatchSetPressed(boolean pressed) {
4322         final View[] children = mChildren;
4323         final int count = mChildrenCount;
4324         for (int i = 0; i < count; i++) {
4325             final View child = children[i];
4326             // Children that are clickable on their own should not
4327             // show a pressed state when their parent view does.
4328             // Clearing a pressed state always propagates.
4329             if (!pressed || (!child.isClickable() && !child.isLongClickable())) {
4330                 child.setPressed(pressed);
4331             }
4332         }
4333     }
4334 
4335     /**
4336      * Dispatches drawable hotspot changes to child views that meet at least
4337      * one of the following criteria:
4338      * <ul>
4339      *     <li>Returns {@code false} from both {@link View#isClickable()} and
4340      *     {@link View#isLongClickable()}</li>
4341      *     <li>Requests duplication of parent state via
4342      *     {@link View#setDuplicateParentStateEnabled(boolean)}</li>
4343      * </ul>
4344      *
4345      * @param x hotspot x coordinate
4346      * @param y hotspot y coordinate
4347      * @see #drawableHotspotChanged(float, float)
4348      */
4349     @Override
dispatchDrawableHotspotChanged(float x, float y)4350     public void dispatchDrawableHotspotChanged(float x, float y) {
4351         final int count = mChildrenCount;
4352         if (count == 0) {
4353             return;
4354         }
4355 
4356         final View[] children = mChildren;
4357         for (int i = 0; i < count; i++) {
4358             final View child = children[i];
4359             // Children that are clickable on their own should not
4360             // receive hotspots when their parent view does.
4361             final boolean nonActionable = !child.isClickable() && !child.isLongClickable();
4362             final boolean duplicatesState = (child.mViewFlags & DUPLICATE_PARENT_STATE) != 0;
4363             if (nonActionable || duplicatesState) {
4364                 final float[] point = getTempPoint();
4365                 point[0] = x;
4366                 point[1] = y;
4367                 transformPointToViewLocal(point, child);
4368                 child.drawableHotspotChanged(point[0], point[1]);
4369             }
4370         }
4371     }
4372 
4373     @Override
dispatchCancelPendingInputEvents()4374     void dispatchCancelPendingInputEvents() {
4375         super.dispatchCancelPendingInputEvents();
4376 
4377         final View[] children = mChildren;
4378         final int count = mChildrenCount;
4379         for (int i = 0; i < count; i++) {
4380             children[i].dispatchCancelPendingInputEvents();
4381         }
4382     }
4383 
4384     /**
4385      * When this property is set to true, this ViewGroup supports static transformations on
4386      * children; this causes
4387      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
4388      * invoked when a child is drawn.
4389      *
4390      * Any subclass overriding
4391      * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
4392      * set this property to true.
4393      *
4394      * @param enabled True to enable static transformations on children, false otherwise.
4395      *
4396      * @see #getChildStaticTransformation(View, android.view.animation.Transformation)
4397      */
setStaticTransformationsEnabled(boolean enabled)4398     protected void setStaticTransformationsEnabled(boolean enabled) {
4399         setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
4400     }
4401 
4402     /**
4403      * Sets  <code>t</code> to be the static transformation of the child, if set, returning a
4404      * boolean to indicate whether a static transform was set. The default implementation
4405      * simply returns <code>false</code>; subclasses may override this method for different
4406      * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true
4407      * for this method to be called.
4408      *
4409      * @param child The child view whose static transform is being requested
4410      * @param t The Transformation which will hold the result
4411      * @return true if the transformation was set, false otherwise
4412      * @see #setStaticTransformationsEnabled(boolean)
4413      */
getChildStaticTransformation(View child, Transformation t)4414     protected boolean getChildStaticTransformation(View child, Transformation t) {
4415         return false;
4416     }
4417 
getChildTransformation()4418     Transformation getChildTransformation() {
4419         if (mChildTransformation == null) {
4420             mChildTransformation = new Transformation();
4421         }
4422         return mChildTransformation;
4423     }
4424 
4425     /**
4426      * {@hide}
4427      */
4428     @Override
findViewTraversal(@dRes int id)4429     protected <T extends View> T findViewTraversal(@IdRes int id) {
4430         if (id == mID) {
4431             return (T) this;
4432         }
4433 
4434         final View[] where = mChildren;
4435         final int len = mChildrenCount;
4436 
4437         for (int i = 0; i < len; i++) {
4438             View v = where[i];
4439 
4440             if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
4441                 v = v.findViewById(id);
4442 
4443                 if (v != null) {
4444                     return (T) v;
4445                 }
4446             }
4447         }
4448 
4449         return null;
4450     }
4451 
4452     /**
4453      * {@hide}
4454      */
4455     @Override
findViewWithTagTraversal(Object tag)4456     protected <T extends View> T findViewWithTagTraversal(Object tag) {
4457         if (tag != null && tag.equals(mTag)) {
4458             return (T) this;
4459         }
4460 
4461         final View[] where = mChildren;
4462         final int len = mChildrenCount;
4463 
4464         for (int i = 0; i < len; i++) {
4465             View v = where[i];
4466 
4467             if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
4468                 v = v.findViewWithTag(tag);
4469 
4470                 if (v != null) {
4471                     return (T) v;
4472                 }
4473             }
4474         }
4475 
4476         return null;
4477     }
4478 
4479     /**
4480      * {@hide}
4481      */
4482     @Override
findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip)4483     protected <T extends View> T findViewByPredicateTraversal(Predicate<View> predicate,
4484             View childToSkip) {
4485         if (predicate.test(this)) {
4486             return (T) this;
4487         }
4488 
4489         final View[] where = mChildren;
4490         final int len = mChildrenCount;
4491 
4492         for (int i = 0; i < len; i++) {
4493             View v = where[i];
4494 
4495             if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
4496                 v = v.findViewByPredicate(predicate);
4497 
4498                 if (v != null) {
4499                     return (T) v;
4500                 }
4501             }
4502         }
4503 
4504         return null;
4505     }
4506 
4507     /**
4508      * This method adds a view to this container at the specified index purely for the
4509      * purposes of allowing that view to draw even though it is not a normal child of
4510      * the container. That is, the view does not participate in layout, focus, accessibility,
4511      * input, or other normal view operations; it is purely an item to be drawn during the normal
4512      * rendering operation of this container. The index that it is added at is the order
4513      * in which it will be drawn, with respect to the other views in the container.
4514      * For example, a transient view added at index 0 will be drawn before all other views
4515      * in the container because it will be drawn first (including before any real view
4516      * at index 0). There can be more than one transient view at any particular index;
4517      * these views will be drawn in the order in which they were added to the list of
4518      * transient views. The index of transient views can also be greater than the number
4519      * of normal views in the container; that just means that they will be drawn after all
4520      * other views are drawn.
4521      *
4522      * <p>Note that since transient views do not participate in layout, they must be sized
4523      * manually or, more typically, they should just use the size that they had before they
4524      * were removed from their container.</p>
4525      *
4526      * <p>Transient views are useful for handling animations of views that have been removed
4527      * from the container, but which should be animated out after the removal. Adding these
4528      * views as transient views allows them to participate in drawing without side-effecting
4529      * the layout of the container.</p>
4530      *
4531      * <p>Transient views must always be explicitly {@link #removeTransientView(View) removed}
4532      * from the container when they are no longer needed. For example, a transient view
4533      * which is added in order to fade it out in its old location should be removed
4534      * once the animation is complete.</p>
4535      *
4536      * @param view The view to be added
4537      * @param index The index at which this view should be drawn, must be >= 0.
4538      * This value is relative to the {@link #getChildAt(int) index} values in the normal
4539      * child list of this container, where any transient view at a particular index will
4540      * be drawn before any normal child at that same index.
4541      *
4542      * @hide
4543      */
addTransientView(View view, int index)4544     public void addTransientView(View view, int index) {
4545         if (index < 0) {
4546             return;
4547         }
4548         if (mTransientIndices == null) {
4549             mTransientIndices = new ArrayList<Integer>();
4550             mTransientViews = new ArrayList<View>();
4551         }
4552         final int oldSize = mTransientIndices.size();
4553         if (oldSize > 0) {
4554             int insertionIndex;
4555             for (insertionIndex = 0; insertionIndex < oldSize; ++insertionIndex) {
4556                 if (index < mTransientIndices.get(insertionIndex)) {
4557                     break;
4558                 }
4559             }
4560             mTransientIndices.add(insertionIndex, index);
4561             mTransientViews.add(insertionIndex, view);
4562         } else {
4563             mTransientIndices.add(index);
4564             mTransientViews.add(view);
4565         }
4566         view.mParent = this;
4567         view.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
4568         invalidate(true);
4569     }
4570 
4571     /**
4572      * Removes a view from the list of transient views in this container. If there is no
4573      * such transient view, this method does nothing.
4574      *
4575      * @param view The transient view to be removed
4576      *
4577      * @hide
4578      */
removeTransientView(View view)4579     public void removeTransientView(View view) {
4580         if (mTransientViews == null) {
4581             return;
4582         }
4583         final int size = mTransientViews.size();
4584         for (int i = 0; i < size; ++i) {
4585             if (view == mTransientViews.get(i)) {
4586                 mTransientViews.remove(i);
4587                 mTransientIndices.remove(i);
4588                 view.mParent = null;
4589                 view.dispatchDetachedFromWindow();
4590                 invalidate(true);
4591                 return;
4592             }
4593         }
4594     }
4595 
4596     /**
4597      * Returns the number of transient views in this container. Specific transient
4598      * views and the index at which they were added can be retrieved via
4599      * {@link #getTransientView(int)} and {@link #getTransientViewIndex(int)}.
4600      *
4601      * @see #addTransientView(View, int)
4602      * @return The number of transient views in this container
4603      *
4604      * @hide
4605      */
getTransientViewCount()4606     public int getTransientViewCount() {
4607         return mTransientIndices == null ? 0 : mTransientIndices.size();
4608     }
4609 
4610     /**
4611      * Given a valid position within the list of transient views, returns the index of
4612      * the transient view at that position.
4613      *
4614      * @param position The position of the index being queried. Must be at least 0
4615      * and less than the value returned by {@link #getTransientViewCount()}.
4616      * @return The index of the transient view stored in the given position if the
4617      * position is valid, otherwise -1
4618      *
4619      * @hide
4620      */
getTransientViewIndex(int position)4621     public int getTransientViewIndex(int position) {
4622         if (position < 0 || mTransientIndices == null || position >= mTransientIndices.size()) {
4623             return -1;
4624         }
4625         return mTransientIndices.get(position);
4626     }
4627 
4628     /**
4629      * Given a valid position within the list of transient views, returns the
4630      * transient view at that position.
4631      *
4632      * @param position The position of the view being queried. Must be at least 0
4633      * and less than the value returned by {@link #getTransientViewCount()}.
4634      * @return The transient view stored in the given position if the
4635      * position is valid, otherwise null
4636      *
4637      * @hide
4638      */
getTransientView(int position)4639     public View getTransientView(int position) {
4640         if (mTransientViews == null || position >= mTransientViews.size()) {
4641             return null;
4642         }
4643         return mTransientViews.get(position);
4644     }
4645 
4646     /**
4647      * <p>Adds a child view. If no layout parameters are already set on the child, the
4648      * default parameters for this ViewGroup are set on the child.</p>
4649      *
4650      * <p><strong>Note:</strong> do not invoke this method from
4651      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4652      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4653      *
4654      * @param child the child view to add
4655      *
4656      * @see #generateDefaultLayoutParams()
4657      */
addView(View child)4658     public void addView(View child) {
4659         addView(child, -1);
4660     }
4661 
4662     /**
4663      * Adds a child view. If no layout parameters are already set on the child, the
4664      * default parameters for this ViewGroup are set on the child.
4665      *
4666      * <p><strong>Note:</strong> do not invoke this method from
4667      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4668      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4669      *
4670      * @param child the child view to add
4671      * @param index the position at which to add the child
4672      *
4673      * @see #generateDefaultLayoutParams()
4674      */
addView(View child, int index)4675     public void addView(View child, int index) {
4676         if (child == null) {
4677             throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4678         }
4679         LayoutParams params = child.getLayoutParams();
4680         if (params == null) {
4681             params = generateDefaultLayoutParams();
4682             if (params == null) {
4683                 throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
4684             }
4685         }
4686         addView(child, index, params);
4687     }
4688 
4689     /**
4690      * Adds a child view with this ViewGroup's default layout parameters and the
4691      * specified width and height.
4692      *
4693      * <p><strong>Note:</strong> do not invoke this method from
4694      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4695      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4696      *
4697      * @param child the child view to add
4698      */
addView(View child, int width, int height)4699     public void addView(View child, int width, int height) {
4700         final LayoutParams params = generateDefaultLayoutParams();
4701         params.width = width;
4702         params.height = height;
4703         addView(child, -1, params);
4704     }
4705 
4706     /**
4707      * Adds a child view with the specified layout parameters.
4708      *
4709      * <p><strong>Note:</strong> do not invoke this method from
4710      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4711      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4712      *
4713      * @param child the child view to add
4714      * @param params the layout parameters to set on the child
4715      */
4716     @Override
addView(View child, LayoutParams params)4717     public void addView(View child, LayoutParams params) {
4718         addView(child, -1, params);
4719     }
4720 
4721     /**
4722      * Adds a child view with the specified layout parameters.
4723      *
4724      * <p><strong>Note:</strong> do not invoke this method from
4725      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4726      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4727      *
4728      * @param child the child view to add
4729      * @param index the position at which to add the child or -1 to add last
4730      * @param params the layout parameters to set on the child
4731      */
addView(View child, int index, LayoutParams params)4732     public void addView(View child, int index, LayoutParams params) {
4733         if (DBG) {
4734             System.out.println(this + " addView");
4735         }
4736 
4737         if (child == null) {
4738             throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4739         }
4740 
4741         // addViewInner() will call child.requestLayout() when setting the new LayoutParams
4742         // therefore, we call requestLayout() on ourselves before, so that the child's request
4743         // will be blocked at our level
4744         requestLayout();
4745         invalidate(true);
4746         addViewInner(child, index, params, false);
4747     }
4748 
4749     @Override
updateViewLayout(View view, ViewGroup.LayoutParams params)4750     public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
4751         if (!checkLayoutParams(params)) {
4752             throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
4753         }
4754         if (view.mParent != this) {
4755             throw new IllegalArgumentException("Given view not a child of " + this);
4756         }
4757         view.setLayoutParams(params);
4758     }
4759 
checkLayoutParams(ViewGroup.LayoutParams p)4760     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
4761         return  p != null;
4762     }
4763 
4764     /**
4765      * Interface definition for a callback to be invoked when the hierarchy
4766      * within this view changed. The hierarchy changes whenever a child is added
4767      * to or removed from this view.
4768      */
4769     public interface OnHierarchyChangeListener {
4770         /**
4771          * Called when a new child is added to a parent view.
4772          *
4773          * @param parent the view in which a child was added
4774          * @param child the new child view added in the hierarchy
4775          */
onChildViewAdded(View parent, View child)4776         void onChildViewAdded(View parent, View child);
4777 
4778         /**
4779          * Called when a child is removed from a parent view.
4780          *
4781          * @param parent the view from which the child was removed
4782          * @param child the child removed from the hierarchy
4783          */
onChildViewRemoved(View parent, View child)4784         void onChildViewRemoved(View parent, View child);
4785     }
4786 
4787     /**
4788      * Register a callback to be invoked when a child is added to or removed
4789      * from this view.
4790      *
4791      * @param listener the callback to invoke on hierarchy change
4792      */
setOnHierarchyChangeListener(OnHierarchyChangeListener listener)4793     public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
4794         mOnHierarchyChangeListener = listener;
4795     }
4796 
dispatchViewAdded(View child)4797     void dispatchViewAdded(View child) {
4798         onViewAdded(child);
4799         if (mOnHierarchyChangeListener != null) {
4800             mOnHierarchyChangeListener.onChildViewAdded(this, child);
4801         }
4802     }
4803 
4804     /**
4805      * Called when a new child is added to this ViewGroup. Overrides should always
4806      * call super.onViewAdded.
4807      *
4808      * @param child the added child view
4809      */
onViewAdded(View child)4810     public void onViewAdded(View child) {
4811     }
4812 
dispatchViewRemoved(View child)4813     void dispatchViewRemoved(View child) {
4814         onViewRemoved(child);
4815         if (mOnHierarchyChangeListener != null) {
4816             mOnHierarchyChangeListener.onChildViewRemoved(this, child);
4817         }
4818     }
4819 
4820     /**
4821      * Called when a child view is removed from this ViewGroup. Overrides should always
4822      * call super.onViewRemoved.
4823      *
4824      * @param child the removed child view
4825      */
onViewRemoved(View child)4826     public void onViewRemoved(View child) {
4827     }
4828 
clearCachedLayoutMode()4829     private void clearCachedLayoutMode() {
4830         if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
4831            mLayoutMode = LAYOUT_MODE_UNDEFINED;
4832         }
4833     }
4834 
4835     @Override
onAttachedToWindow()4836     protected void onAttachedToWindow() {
4837         super.onAttachedToWindow();
4838         clearCachedLayoutMode();
4839     }
4840 
4841     @Override
onDetachedFromWindow()4842     protected void onDetachedFromWindow() {
4843         super.onDetachedFromWindow();
4844         clearCachedLayoutMode();
4845     }
4846 
4847     /** @hide */
4848     @Override
destroyHardwareResources()4849     protected void destroyHardwareResources() {
4850         super.destroyHardwareResources();
4851         int count = getChildCount();
4852         for (int i = 0; i < count; i++) {
4853             getChildAt(i).destroyHardwareResources();
4854         }
4855     }
4856 
4857     /**
4858      * Adds a view during layout. This is useful if in your onLayout() method,
4859      * you need to add more views (as does the list view for example).
4860      *
4861      * If index is negative, it means put it at the end of the list.
4862      *
4863      * @param child the view to add to the group
4864      * @param index the index at which the child must be added or -1 to add last
4865      * @param params the layout parameters to associate with the child
4866      * @return true if the child was added, false otherwise
4867      */
addViewInLayout(View child, int index, LayoutParams params)4868     protected boolean addViewInLayout(View child, int index, LayoutParams params) {
4869         return addViewInLayout(child, index, params, false);
4870     }
4871 
4872     /**
4873      * Adds a view during layout. This is useful if in your onLayout() method,
4874      * you need to add more views (as does the list view for example).
4875      *
4876      * If index is negative, it means put it at the end of the list.
4877      *
4878      * @param child the view to add to the group
4879      * @param index the index at which the child must be added or -1 to add last
4880      * @param params the layout parameters to associate with the child
4881      * @param preventRequestLayout if true, calling this method will not trigger a
4882      *        layout request on child
4883      * @return true if the child was added, false otherwise
4884      */
addViewInLayout(View child, int index, LayoutParams params, boolean preventRequestLayout)4885     protected boolean addViewInLayout(View child, int index, LayoutParams params,
4886             boolean preventRequestLayout) {
4887         if (child == null) {
4888             throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
4889         }
4890         child.mParent = null;
4891         addViewInner(child, index, params, preventRequestLayout);
4892         child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
4893         return true;
4894     }
4895 
4896     /**
4897      * Prevents the specified child to be laid out during the next layout pass.
4898      *
4899      * @param child the child on which to perform the cleanup
4900      */
cleanupLayoutState(View child)4901     protected void cleanupLayoutState(View child) {
4902         child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
4903     }
4904 
addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout)4905     private void addViewInner(View child, int index, LayoutParams params,
4906             boolean preventRequestLayout) {
4907 
4908         if (mTransition != null) {
4909             // Don't prevent other add transitions from completing, but cancel remove
4910             // transitions to let them complete the process before we add to the container
4911             mTransition.cancel(LayoutTransition.DISAPPEARING);
4912         }
4913 
4914         if (child.getParent() != null) {
4915             throw new IllegalStateException("The specified child already has a parent. " +
4916                     "You must call removeView() on the child's parent first.");
4917         }
4918 
4919         if (mTransition != null) {
4920             mTransition.addChild(this, child);
4921         }
4922 
4923         if (!checkLayoutParams(params)) {
4924             params = generateLayoutParams(params);
4925         }
4926 
4927         if (preventRequestLayout) {
4928             child.mLayoutParams = params;
4929         } else {
4930             child.setLayoutParams(params);
4931         }
4932 
4933         if (index < 0) {
4934             index = mChildrenCount;
4935         }
4936 
4937         addInArray(child, index);
4938 
4939         // tell our children
4940         if (preventRequestLayout) {
4941             child.assignParent(this);
4942         } else {
4943             child.mParent = this;
4944         }
4945 
4946         final boolean childHasFocus = child.hasFocus();
4947         if (childHasFocus) {
4948             requestChildFocus(child, child.findFocus());
4949         }
4950 
4951         AttachInfo ai = mAttachInfo;
4952         if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
4953             boolean lastKeepOn = ai.mKeepScreenOn;
4954             ai.mKeepScreenOn = false;
4955             child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
4956             if (ai.mKeepScreenOn) {
4957                 needGlobalAttributesUpdate(true);
4958             }
4959             ai.mKeepScreenOn = lastKeepOn;
4960         }
4961 
4962         if (child.isLayoutDirectionInherited()) {
4963             child.resetRtlProperties();
4964         }
4965 
4966         dispatchViewAdded(child);
4967 
4968         if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
4969             mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
4970         }
4971 
4972         if (child.hasTransientState()) {
4973             childHasTransientStateChanged(child, true);
4974         }
4975 
4976         if (child.getVisibility() != View.GONE) {
4977             notifySubtreeAccessibilityStateChangedIfNeeded();
4978         }
4979 
4980         if (mTransientIndices != null) {
4981             final int transientCount = mTransientIndices.size();
4982             for (int i = 0; i < transientCount; ++i) {
4983                 final int oldIndex = mTransientIndices.get(i);
4984                 if (index <= oldIndex) {
4985                     mTransientIndices.set(i, oldIndex + 1);
4986                 }
4987             }
4988         }
4989 
4990         if (mCurrentDragStartEvent != null && child.getVisibility() == VISIBLE) {
4991             notifyChildOfDragStart(child);
4992         }
4993 
4994         if (child.hasDefaultFocus()) {
4995             // When adding a child that contains default focus, either during inflation or while
4996             // manually assembling the hierarchy, update the ancestor default-focus chain.
4997             setDefaultFocus(child);
4998         }
4999     }
5000 
addInArray(View child, int index)5001     private void addInArray(View child, int index) {
5002         View[] children = mChildren;
5003         final int count = mChildrenCount;
5004         final int size = children.length;
5005         if (index == count) {
5006             if (size == count) {
5007                 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
5008                 System.arraycopy(children, 0, mChildren, 0, size);
5009                 children = mChildren;
5010             }
5011             children[mChildrenCount++] = child;
5012         } else if (index < count) {
5013             if (size == count) {
5014                 mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
5015                 System.arraycopy(children, 0, mChildren, 0, index);
5016                 System.arraycopy(children, index, mChildren, index + 1, count - index);
5017                 children = mChildren;
5018             } else {
5019                 System.arraycopy(children, index, children, index + 1, count - index);
5020             }
5021             children[index] = child;
5022             mChildrenCount++;
5023             if (mLastTouchDownIndex >= index) {
5024                 mLastTouchDownIndex++;
5025             }
5026         } else {
5027             throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
5028         }
5029     }
5030 
5031     // This method also sets the child's mParent to null
removeFromArray(int index)5032     private void removeFromArray(int index) {
5033         final View[] children = mChildren;
5034         if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
5035             children[index].mParent = null;
5036         }
5037         final int count = mChildrenCount;
5038         if (index == count - 1) {
5039             children[--mChildrenCount] = null;
5040         } else if (index >= 0 && index < count) {
5041             System.arraycopy(children, index + 1, children, index, count - index - 1);
5042             children[--mChildrenCount] = null;
5043         } else {
5044             throw new IndexOutOfBoundsException();
5045         }
5046         if (mLastTouchDownIndex == index) {
5047             mLastTouchDownTime = 0;
5048             mLastTouchDownIndex = -1;
5049         } else if (mLastTouchDownIndex > index) {
5050             mLastTouchDownIndex--;
5051         }
5052     }
5053 
5054     // This method also sets the children's mParent to null
removeFromArray(int start, int count)5055     private void removeFromArray(int start, int count) {
5056         final View[] children = mChildren;
5057         final int childrenCount = mChildrenCount;
5058 
5059         start = Math.max(0, start);
5060         final int end = Math.min(childrenCount, start + count);
5061 
5062         if (start == end) {
5063             return;
5064         }
5065 
5066         if (end == childrenCount) {
5067             for (int i = start; i < end; i++) {
5068                 children[i].mParent = null;
5069                 children[i] = null;
5070             }
5071         } else {
5072             for (int i = start; i < end; i++) {
5073                 children[i].mParent = null;
5074             }
5075 
5076             // Since we're looping above, we might as well do the copy, but is arraycopy()
5077             // faster than the extra 2 bounds checks we would do in the loop?
5078             System.arraycopy(children, end, children, start, childrenCount - end);
5079 
5080             for (int i = childrenCount - (end - start); i < childrenCount; i++) {
5081                 children[i] = null;
5082             }
5083         }
5084 
5085         mChildrenCount -= (end - start);
5086     }
5087 
bindLayoutAnimation(View child)5088     private void bindLayoutAnimation(View child) {
5089         Animation a = mLayoutAnimationController.getAnimationForView(child);
5090         child.setAnimation(a);
5091     }
5092 
5093     /**
5094      * Subclasses should override this method to set layout animation
5095      * parameters on the supplied child.
5096      *
5097      * @param child the child to associate with animation parameters
5098      * @param params the child's layout parameters which hold the animation
5099      *        parameters
5100      * @param index the index of the child in the view group
5101      * @param count the number of children in the view group
5102      */
attachLayoutAnimationParameters(View child, LayoutParams params, int index, int count)5103     protected void attachLayoutAnimationParameters(View child,
5104             LayoutParams params, int index, int count) {
5105         LayoutAnimationController.AnimationParameters animationParams =
5106                     params.layoutAnimationParameters;
5107         if (animationParams == null) {
5108             animationParams = new LayoutAnimationController.AnimationParameters();
5109             params.layoutAnimationParameters = animationParams;
5110         }
5111 
5112         animationParams.count = count;
5113         animationParams.index = index;
5114     }
5115 
5116     /**
5117      * {@inheritDoc}
5118      *
5119      * <p><strong>Note:</strong> do not invoke this method from
5120      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5121      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5122      */
5123     @Override
removeView(View view)5124     public void removeView(View view) {
5125         if (removeViewInternal(view)) {
5126             requestLayout();
5127             invalidate(true);
5128         }
5129     }
5130 
5131     /**
5132      * Removes a view during layout. This is useful if in your onLayout() method,
5133      * you need to remove more views.
5134      *
5135      * <p><strong>Note:</strong> do not invoke this method from
5136      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5137      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5138      *
5139      * @param view the view to remove from the group
5140      */
removeViewInLayout(View view)5141     public void removeViewInLayout(View view) {
5142         removeViewInternal(view);
5143     }
5144 
5145     /**
5146      * Removes a range of views during layout. This is useful if in your onLayout() method,
5147      * you need to remove more views.
5148      *
5149      * <p><strong>Note:</strong> do not invoke this method from
5150      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5151      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5152      *
5153      * @param start the index of the first view to remove from the group
5154      * @param count the number of views to remove from the group
5155      */
removeViewsInLayout(int start, int count)5156     public void removeViewsInLayout(int start, int count) {
5157         removeViewsInternal(start, count);
5158     }
5159 
5160     /**
5161      * Removes the view at the specified position in the group.
5162      *
5163      * <p><strong>Note:</strong> do not invoke this method from
5164      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5165      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5166      *
5167      * @param index the position in the group of the view to remove
5168      */
removeViewAt(int index)5169     public void removeViewAt(int index) {
5170         removeViewInternal(index, getChildAt(index));
5171         requestLayout();
5172         invalidate(true);
5173     }
5174 
5175     /**
5176      * Removes the specified range of views from the group.
5177      *
5178      * <p><strong>Note:</strong> do not invoke this method from
5179      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5180      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5181      *
5182      * @param start the first position in the group of the range of views to remove
5183      * @param count the number of views to remove
5184      */
removeViews(int start, int count)5185     public void removeViews(int start, int count) {
5186         removeViewsInternal(start, count);
5187         requestLayout();
5188         invalidate(true);
5189     }
5190 
removeViewInternal(View view)5191     private boolean removeViewInternal(View view) {
5192         final int index = indexOfChild(view);
5193         if (index >= 0) {
5194             removeViewInternal(index, view);
5195             return true;
5196         }
5197         return false;
5198     }
5199 
removeViewInternal(int index, View view)5200     private void removeViewInternal(int index, View view) {
5201         if (mTransition != null) {
5202             mTransition.removeChild(this, view);
5203         }
5204 
5205         boolean clearChildFocus = false;
5206         if (view == mFocused) {
5207             view.unFocus(null);
5208             clearChildFocus = true;
5209         }
5210         if (view == mFocusedInCluster) {
5211             clearFocusedInCluster(view);
5212         }
5213 
5214         view.clearAccessibilityFocus();
5215 
5216         cancelTouchTarget(view);
5217         cancelHoverTarget(view);
5218 
5219         if (view.getAnimation() != null ||
5220                 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
5221             addDisappearingView(view);
5222         } else if (view.mAttachInfo != null) {
5223            view.dispatchDetachedFromWindow();
5224         }
5225 
5226         if (view.hasTransientState()) {
5227             childHasTransientStateChanged(view, false);
5228         }
5229 
5230         needGlobalAttributesUpdate(false);
5231 
5232         removeFromArray(index);
5233 
5234         if (view == mDefaultFocus) {
5235             clearDefaultFocus(view);
5236         }
5237         if (clearChildFocus) {
5238             clearChildFocus(view);
5239             if (!rootViewRequestFocus()) {
5240                 notifyGlobalFocusCleared(this);
5241             }
5242         }
5243 
5244         dispatchViewRemoved(view);
5245 
5246         if (view.getVisibility() != View.GONE) {
5247             notifySubtreeAccessibilityStateChangedIfNeeded();
5248         }
5249 
5250         int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();
5251         for (int i = 0; i < transientCount; ++i) {
5252             final int oldIndex = mTransientIndices.get(i);
5253             if (index < oldIndex) {
5254                 mTransientIndices.set(i, oldIndex - 1);
5255             }
5256         }
5257 
5258         if (mCurrentDragStartEvent != null) {
5259             mChildrenInterestedInDrag.remove(view);
5260         }
5261     }
5262 
5263     /**
5264      * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
5265      * not null, changes in layout which occur because of children being added to or removed from
5266      * the ViewGroup will be animated according to the animations defined in that LayoutTransition
5267      * object. By default, the transition object is null (so layout changes are not animated).
5268      *
5269      * <p>Replacing a non-null transition will cause that previous transition to be
5270      * canceled, if it is currently running, to restore this container to
5271      * its correct post-transition state.</p>
5272      *
5273      * @param transition The LayoutTransition object that will animated changes in layout. A value
5274      * of <code>null</code> means no transition will run on layout changes.
5275      * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
5276      */
setLayoutTransition(LayoutTransition transition)5277     public void setLayoutTransition(LayoutTransition transition) {
5278         if (mTransition != null) {
5279             LayoutTransition previousTransition = mTransition;
5280             previousTransition.cancel();
5281             previousTransition.removeTransitionListener(mLayoutTransitionListener);
5282         }
5283         mTransition = transition;
5284         if (mTransition != null) {
5285             mTransition.addTransitionListener(mLayoutTransitionListener);
5286         }
5287     }
5288 
5289     /**
5290      * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
5291      * not null, changes in layout which occur because of children being added to or removed from
5292      * the ViewGroup will be animated according to the animations defined in that LayoutTransition
5293      * object. By default, the transition object is null (so layout changes are not animated).
5294      *
5295      * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
5296      * A value of <code>null</code> means no transition will run on layout changes.
5297      */
getLayoutTransition()5298     public LayoutTransition getLayoutTransition() {
5299         return mTransition;
5300     }
5301 
removeViewsInternal(int start, int count)5302     private void removeViewsInternal(int start, int count) {
5303         final int end = start + count;
5304 
5305         if (start < 0 || count < 0 || end > mChildrenCount) {
5306             throw new IndexOutOfBoundsException();
5307         }
5308 
5309         final View focused = mFocused;
5310         final boolean detach = mAttachInfo != null;
5311         boolean clearChildFocus = false;
5312         View clearDefaultFocus = null;
5313 
5314         final View[] children = mChildren;
5315 
5316         for (int i = start; i < end; i++) {
5317             final View view = children[i];
5318 
5319             if (mTransition != null) {
5320                 mTransition.removeChild(this, view);
5321             }
5322 
5323             if (view == focused) {
5324                 view.unFocus(null);
5325                 clearChildFocus = true;
5326             }
5327             if (view == mDefaultFocus) {
5328                 clearDefaultFocus = view;
5329             }
5330             if (view == mFocusedInCluster) {
5331                 clearFocusedInCluster(view);
5332             }
5333 
5334             view.clearAccessibilityFocus();
5335 
5336             cancelTouchTarget(view);
5337             cancelHoverTarget(view);
5338 
5339             if (view.getAnimation() != null ||
5340                 (mTransitioningViews != null && mTransitioningViews.contains(view))) {
5341                 addDisappearingView(view);
5342             } else if (detach) {
5343                view.dispatchDetachedFromWindow();
5344             }
5345 
5346             if (view.hasTransientState()) {
5347                 childHasTransientStateChanged(view, false);
5348             }
5349 
5350             needGlobalAttributesUpdate(false);
5351 
5352             dispatchViewRemoved(view);
5353         }
5354 
5355         removeFromArray(start, count);
5356 
5357         if (clearDefaultFocus != null) {
5358             clearDefaultFocus(clearDefaultFocus);
5359         }
5360         if (clearChildFocus) {
5361             clearChildFocus(focused);
5362             if (!rootViewRequestFocus()) {
5363                 notifyGlobalFocusCleared(focused);
5364             }
5365         }
5366     }
5367 
5368     /**
5369      * Call this method to remove all child views from the
5370      * ViewGroup.
5371      *
5372      * <p><strong>Note:</strong> do not invoke this method from
5373      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5374      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5375      */
removeAllViews()5376     public void removeAllViews() {
5377         removeAllViewsInLayout();
5378         requestLayout();
5379         invalidate(true);
5380     }
5381 
5382     /**
5383      * Called by a ViewGroup subclass to remove child views from itself,
5384      * when it must first know its size on screen before it can calculate how many
5385      * child views it will render. An example is a Gallery or a ListView, which
5386      * may "have" 50 children, but actually only render the number of children
5387      * that can currently fit inside the object on screen. Do not call
5388      * this method unless you are extending ViewGroup and understand the
5389      * view measuring and layout pipeline.
5390      *
5391      * <p><strong>Note:</strong> do not invoke this method from
5392      * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
5393      * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
5394      */
removeAllViewsInLayout()5395     public void removeAllViewsInLayout() {
5396         final int count = mChildrenCount;
5397         if (count <= 0) {
5398             return;
5399         }
5400 
5401         final View[] children = mChildren;
5402         mChildrenCount = 0;
5403 
5404         final View focused = mFocused;
5405         final boolean detach = mAttachInfo != null;
5406         boolean clearChildFocus = false;
5407 
5408         needGlobalAttributesUpdate(false);
5409 
5410         for (int i = count - 1; i >= 0; i--) {
5411             final View view = children[i];
5412 
5413             if (mTransition != null) {
5414                 mTransition.removeChild(this, view);
5415             }
5416 
5417             if (view == focused) {
5418                 view.unFocus(null);
5419                 clearChildFocus = true;
5420             }
5421 
5422             view.clearAccessibilityFocus();
5423 
5424             cancelTouchTarget(view);
5425             cancelHoverTarget(view);
5426 
5427             if (view.getAnimation() != null ||
5428                     (mTransitioningViews != null && mTransitioningViews.contains(view))) {
5429                 addDisappearingView(view);
5430             } else if (detach) {
5431                view.dispatchDetachedFromWindow();
5432             }
5433 
5434             if (view.hasTransientState()) {
5435                 childHasTransientStateChanged(view, false);
5436             }
5437 
5438             dispatchViewRemoved(view);
5439 
5440             view.mParent = null;
5441             children[i] = null;
5442         }
5443 
5444         if (mDefaultFocus != null) {
5445             clearDefaultFocus(mDefaultFocus);
5446         }
5447         if (mFocusedInCluster != null) {
5448             clearFocusedInCluster(mFocusedInCluster);
5449         }
5450         if (clearChildFocus) {
5451             clearChildFocus(focused);
5452             if (!rootViewRequestFocus()) {
5453                 notifyGlobalFocusCleared(focused);
5454             }
5455         }
5456     }
5457 
5458     /**
5459      * Finishes the removal of a detached view. This method will dispatch the detached from
5460      * window event and notify the hierarchy change listener.
5461      * <p>
5462      * This method is intended to be lightweight and makes no assumptions about whether the
5463      * parent or child should be redrawn. Proper use of this method will include also making
5464      * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
5465      * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
5466      * which performs a {@link #requestLayout()} on the next frame, after all detach/remove
5467      * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
5468      *
5469      * @param child the child to be definitely removed from the view hierarchy
5470      * @param animate if true and the view has an animation, the view is placed in the
5471      *                disappearing views list, otherwise, it is detached from the window
5472      *
5473      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5474      * @see #detachAllViewsFromParent()
5475      * @see #detachViewFromParent(View)
5476      * @see #detachViewFromParent(int)
5477      */
removeDetachedView(View child, boolean animate)5478     protected void removeDetachedView(View child, boolean animate) {
5479         if (mTransition != null) {
5480             mTransition.removeChild(this, child);
5481         }
5482 
5483         if (child == mFocused) {
5484             child.clearFocus();
5485         }
5486         if (child == mDefaultFocus) {
5487             clearDefaultFocus(child);
5488         }
5489         if (child == mFocusedInCluster) {
5490             clearFocusedInCluster(child);
5491         }
5492 
5493         child.clearAccessibilityFocus();
5494 
5495         cancelTouchTarget(child);
5496         cancelHoverTarget(child);
5497 
5498         if ((animate && child.getAnimation() != null) ||
5499                 (mTransitioningViews != null && mTransitioningViews.contains(child))) {
5500             addDisappearingView(child);
5501         } else if (child.mAttachInfo != null) {
5502             child.dispatchDetachedFromWindow();
5503         }
5504 
5505         if (child.hasTransientState()) {
5506             childHasTransientStateChanged(child, false);
5507         }
5508 
5509         dispatchViewRemoved(child);
5510     }
5511 
5512     /**
5513      * Attaches a view to this view group. Attaching a view assigns this group as the parent,
5514      * sets the layout parameters and puts the view in the list of children so that
5515      * it can be retrieved by calling {@link #getChildAt(int)}.
5516      * <p>
5517      * This method is intended to be lightweight and makes no assumptions about whether the
5518      * parent or child should be redrawn. Proper use of this method will include also making
5519      * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
5520      * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
5521      * which performs a {@link #requestLayout()} on the next frame, after all detach/attach
5522      * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
5523      * <p>
5524      * This method should be called only for views which were detached from their parent.
5525      *
5526      * @param child the child to attach
5527      * @param index the index at which the child should be attached
5528      * @param params the layout parameters of the child
5529      *
5530      * @see #removeDetachedView(View, boolean)
5531      * @see #detachAllViewsFromParent()
5532      * @see #detachViewFromParent(View)
5533      * @see #detachViewFromParent(int)
5534      */
attachViewToParent(View child, int index, LayoutParams params)5535     protected void attachViewToParent(View child, int index, LayoutParams params) {
5536         child.mLayoutParams = params;
5537 
5538         if (index < 0) {
5539             index = mChildrenCount;
5540         }
5541 
5542         addInArray(child, index);
5543 
5544         child.mParent = this;
5545         child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK
5546                         & ~PFLAG_DRAWING_CACHE_VALID)
5547                 | PFLAG_DRAWN | PFLAG_INVALIDATED;
5548         this.mPrivateFlags |= PFLAG_INVALIDATED;
5549 
5550         if (child.hasFocus()) {
5551             requestChildFocus(child, child.findFocus());
5552         }
5553         dispatchVisibilityAggregated(isAttachedToWindow() && getWindowVisibility() == VISIBLE
5554                 && isShown());
5555     }
5556 
5557     /**
5558      * Detaches a view from its parent. Detaching a view should be followed
5559      * either by a call to
5560      * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
5561      * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
5562      * temporary; reattachment or removal should happen within the same drawing cycle as
5563      * detachment. When a view is detached, its parent is null and cannot be retrieved by a
5564      * call to {@link #getChildAt(int)}.
5565      *
5566      * @param child the child to detach
5567      *
5568      * @see #detachViewFromParent(int)
5569      * @see #detachViewsFromParent(int, int)
5570      * @see #detachAllViewsFromParent()
5571      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5572      * @see #removeDetachedView(View, boolean)
5573      */
detachViewFromParent(View child)5574     protected void detachViewFromParent(View child) {
5575         removeFromArray(indexOfChild(child));
5576     }
5577 
5578     /**
5579      * Detaches a view from its parent. Detaching a view should be followed
5580      * either by a call to
5581      * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
5582      * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
5583      * temporary; reattachment or removal should happen within the same drawing cycle as
5584      * detachment. When a view is detached, its parent is null and cannot be retrieved by a
5585      * call to {@link #getChildAt(int)}.
5586      *
5587      * @param index the index of the child to detach
5588      *
5589      * @see #detachViewFromParent(View)
5590      * @see #detachAllViewsFromParent()
5591      * @see #detachViewsFromParent(int, int)
5592      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5593      * @see #removeDetachedView(View, boolean)
5594      */
detachViewFromParent(int index)5595     protected void detachViewFromParent(int index) {
5596         removeFromArray(index);
5597     }
5598 
5599     /**
5600      * Detaches a range of views from their parents. Detaching a view should be followed
5601      * either by a call to
5602      * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
5603      * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
5604      * temporary; reattachment or removal should happen within the same drawing cycle as
5605      * detachment. When a view is detached, its parent is null and cannot be retrieved by a
5606      * call to {@link #getChildAt(int)}.
5607      *
5608      * @param start the first index of the childrend range to detach
5609      * @param count the number of children to detach
5610      *
5611      * @see #detachViewFromParent(View)
5612      * @see #detachViewFromParent(int)
5613      * @see #detachAllViewsFromParent()
5614      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5615      * @see #removeDetachedView(View, boolean)
5616      */
detachViewsFromParent(int start, int count)5617     protected void detachViewsFromParent(int start, int count) {
5618         removeFromArray(start, count);
5619     }
5620 
5621     /**
5622      * Detaches all views from the parent. Detaching a view should be followed
5623      * either by a call to
5624      * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
5625      * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
5626      * temporary; reattachment or removal should happen within the same drawing cycle as
5627      * detachment. When a view is detached, its parent is null and cannot be retrieved by a
5628      * call to {@link #getChildAt(int)}.
5629      *
5630      * @see #detachViewFromParent(View)
5631      * @see #detachViewFromParent(int)
5632      * @see #detachViewsFromParent(int, int)
5633      * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
5634      * @see #removeDetachedView(View, boolean)
5635      */
detachAllViewsFromParent()5636     protected void detachAllViewsFromParent() {
5637         final int count = mChildrenCount;
5638         if (count <= 0) {
5639             return;
5640         }
5641 
5642         final View[] children = mChildren;
5643         mChildrenCount = 0;
5644 
5645         for (int i = count - 1; i >= 0; i--) {
5646             children[i].mParent = null;
5647             children[i] = null;
5648         }
5649     }
5650 
5651     @Override
5652     @CallSuper
onDescendantInvalidated(@onNull View child, @NonNull View target)5653     public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
5654         /*
5655          * HW-only, Rect-ignoring damage codepath
5656          *
5657          * We don't deal with rectangles here, since RenderThread native code computes damage for
5658          * everything drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area)
5659          */
5660 
5661         // if set, combine the animation flag into the parent
5662         mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION);
5663 
5664         if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) {
5665             // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential
5666             // optimization in provides in a DisplayList world.
5667             mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
5668 
5669             // simplified invalidateChildInParent behavior: clear cache validity to be safe...
5670             mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
5671         }
5672 
5673         // ... and mark inval if in software layer that needs to repaint (hw handled in native)
5674         if (mLayerType == LAYER_TYPE_SOFTWARE) {
5675             // Layered parents should be invalidated. Escalate to a full invalidate (and note that
5676             // we do this after consuming any relevant flags from the originating descendant)
5677             mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY;
5678             target = this;
5679         }
5680 
5681         if (mParent != null) {
5682             mParent.onDescendantInvalidated(this, target);
5683         }
5684     }
5685 
5686 
5687     /**
5688      * Don't call or override this method. It is used for the implementation of
5689      * the view hierarchy.
5690      *
5691      * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead to observe updates to
5692      * draw state in descendants.
5693      */
5694     @Deprecated
5695     @Override
invalidateChild(View child, final Rect dirty)5696     public final void invalidateChild(View child, final Rect dirty) {
5697         final AttachInfo attachInfo = mAttachInfo;
5698         if (attachInfo != null && attachInfo.mHardwareAccelerated) {
5699             // HW accelerated fast path
5700             onDescendantInvalidated(child, child);
5701             return;
5702         }
5703 
5704         ViewParent parent = this;
5705         if (attachInfo != null) {
5706             // If the child is drawing an animation, we want to copy this flag onto
5707             // ourselves and the parent to make sure the invalidate request goes
5708             // through
5709             final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
5710 
5711             // Check whether the child that requests the invalidate is fully opaque
5712             // Views being animated or transformed are not considered opaque because we may
5713             // be invalidating their old position and need the parent to paint behind them.
5714             Matrix childMatrix = child.getMatrix();
5715             final boolean isOpaque = child.isOpaque() && !drawAnimation &&
5716                     child.getAnimation() == null && childMatrix.isIdentity();
5717             // Mark the child as dirty, using the appropriate flag
5718             // Make sure we do not set both flags at the same time
5719             int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
5720 
5721             if (child.mLayerType != LAYER_TYPE_NONE) {
5722                 mPrivateFlags |= PFLAG_INVALIDATED;
5723                 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
5724             }
5725 
5726             final int[] location = attachInfo.mInvalidateChildLocation;
5727             location[CHILD_LEFT_INDEX] = child.mLeft;
5728             location[CHILD_TOP_INDEX] = child.mTop;
5729             if (!childMatrix.isIdentity() ||
5730                     (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
5731                 RectF boundingRect = attachInfo.mTmpTransformRect;
5732                 boundingRect.set(dirty);
5733                 Matrix transformMatrix;
5734                 if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
5735                     Transformation t = attachInfo.mTmpTransformation;
5736                     boolean transformed = getChildStaticTransformation(child, t);
5737                     if (transformed) {
5738                         transformMatrix = attachInfo.mTmpMatrix;
5739                         transformMatrix.set(t.getMatrix());
5740                         if (!childMatrix.isIdentity()) {
5741                             transformMatrix.preConcat(childMatrix);
5742                         }
5743                     } else {
5744                         transformMatrix = childMatrix;
5745                     }
5746                 } else {
5747                     transformMatrix = childMatrix;
5748                 }
5749                 transformMatrix.mapRect(boundingRect);
5750                 dirty.set((int) Math.floor(boundingRect.left),
5751                         (int) Math.floor(boundingRect.top),
5752                         (int) Math.ceil(boundingRect.right),
5753                         (int) Math.ceil(boundingRect.bottom));
5754             }
5755 
5756             do {
5757                 View view = null;
5758                 if (parent instanceof View) {
5759                     view = (View) parent;
5760                 }
5761 
5762                 if (drawAnimation) {
5763                     if (view != null) {
5764                         view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
5765                     } else if (parent instanceof ViewRootImpl) {
5766                         ((ViewRootImpl) parent).mIsAnimating = true;
5767                     }
5768                 }
5769 
5770                 // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
5771                 // flag coming from the child that initiated the invalidate
5772                 if (view != null) {
5773                     if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
5774                             view.getSolidColor() == 0) {
5775                         opaqueFlag = PFLAG_DIRTY;
5776                     }
5777                     if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
5778                         view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
5779                     }
5780                 }
5781 
5782                 parent = parent.invalidateChildInParent(location, dirty);
5783                 if (view != null) {
5784                     // Account for transform on current parent
5785                     Matrix m = view.getMatrix();
5786                     if (!m.isIdentity()) {
5787                         RectF boundingRect = attachInfo.mTmpTransformRect;
5788                         boundingRect.set(dirty);
5789                         m.mapRect(boundingRect);
5790                         dirty.set((int) Math.floor(boundingRect.left),
5791                                 (int) Math.floor(boundingRect.top),
5792                                 (int) Math.ceil(boundingRect.right),
5793                                 (int) Math.ceil(boundingRect.bottom));
5794                     }
5795                 }
5796             } while (parent != null);
5797         }
5798     }
5799 
5800     /**
5801      * Don't call or override this method. It is used for the implementation of
5802      * the view hierarchy.
5803      *
5804      * This implementation returns null if this ViewGroup does not have a parent,
5805      * if this ViewGroup is already fully invalidated or if the dirty rectangle
5806      * does not intersect with this ViewGroup's bounds.
5807      *
5808      * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead to observe updates to
5809      * draw state in descendants.
5810      */
5811     @Deprecated
5812     @Override
invalidateChildInParent(final int[] location, final Rect dirty)5813     public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
5814         if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
5815             // either DRAWN, or DRAWING_CACHE_VALID
5816             if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
5817                     != FLAG_OPTIMIZE_INVALIDATE) {
5818                 dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
5819                         location[CHILD_TOP_INDEX] - mScrollY);
5820                 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
5821                     dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
5822                 }
5823 
5824                 final int left = mLeft;
5825                 final int top = mTop;
5826 
5827                 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
5828                     if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
5829                         dirty.setEmpty();
5830                     }
5831                 }
5832 
5833                 location[CHILD_LEFT_INDEX] = left;
5834                 location[CHILD_TOP_INDEX] = top;
5835             } else {
5836 
5837                 if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
5838                     dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
5839                 } else {
5840                     // in case the dirty rect extends outside the bounds of this container
5841                     dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
5842                 }
5843                 location[CHILD_LEFT_INDEX] = mLeft;
5844                 location[CHILD_TOP_INDEX] = mTop;
5845 
5846                 mPrivateFlags &= ~PFLAG_DRAWN;
5847             }
5848             mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
5849             if (mLayerType != LAYER_TYPE_NONE) {
5850                 mPrivateFlags |= PFLAG_INVALIDATED;
5851             }
5852 
5853             return mParent;
5854         }
5855 
5856         return null;
5857     }
5858 
5859     /**
5860      * Offset a rectangle that is in a descendant's coordinate
5861      * space into our coordinate space.
5862      * @param descendant A descendant of this view
5863      * @param rect A rectangle defined in descendant's coordinate space.
5864      */
offsetDescendantRectToMyCoords(View descendant, Rect rect)5865     public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
5866         offsetRectBetweenParentAndChild(descendant, rect, true, false);
5867     }
5868 
5869     /**
5870      * Offset a rectangle that is in our coordinate space into an ancestor's
5871      * coordinate space.
5872      * @param descendant A descendant of this view
5873      * @param rect A rectangle defined in descendant's coordinate space.
5874      */
offsetRectIntoDescendantCoords(View descendant, Rect rect)5875     public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
5876         offsetRectBetweenParentAndChild(descendant, rect, false, false);
5877     }
5878 
5879     /**
5880      * Helper method that offsets a rect either from parent to descendant or
5881      * descendant to parent.
5882      */
offsetRectBetweenParentAndChild(View descendant, Rect rect, boolean offsetFromChildToParent, boolean clipToBounds)5883     void offsetRectBetweenParentAndChild(View descendant, Rect rect,
5884             boolean offsetFromChildToParent, boolean clipToBounds) {
5885 
5886         // already in the same coord system :)
5887         if (descendant == this) {
5888             return;
5889         }
5890 
5891         ViewParent theParent = descendant.mParent;
5892 
5893         // search and offset up to the parent
5894         while ((theParent != null)
5895                 && (theParent instanceof View)
5896                 && (theParent != this)) {
5897 
5898             if (offsetFromChildToParent) {
5899                 rect.offset(descendant.mLeft - descendant.mScrollX,
5900                         descendant.mTop - descendant.mScrollY);
5901                 if (clipToBounds) {
5902                     View p = (View) theParent;
5903                     boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
5904                             p.mBottom - p.mTop);
5905                     if (!intersected) {
5906                         rect.setEmpty();
5907                     }
5908                 }
5909             } else {
5910                 if (clipToBounds) {
5911                     View p = (View) theParent;
5912                     boolean intersected = rect.intersect(0, 0, p.mRight - p.mLeft,
5913                             p.mBottom - p.mTop);
5914                     if (!intersected) {
5915                         rect.setEmpty();
5916                     }
5917                 }
5918                 rect.offset(descendant.mScrollX - descendant.mLeft,
5919                         descendant.mScrollY - descendant.mTop);
5920             }
5921 
5922             descendant = (View) theParent;
5923             theParent = descendant.mParent;
5924         }
5925 
5926         // now that we are up to this view, need to offset one more time
5927         // to get into our coordinate space
5928         if (theParent == this) {
5929             if (offsetFromChildToParent) {
5930                 rect.offset(descendant.mLeft - descendant.mScrollX,
5931                         descendant.mTop - descendant.mScrollY);
5932             } else {
5933                 rect.offset(descendant.mScrollX - descendant.mLeft,
5934                         descendant.mScrollY - descendant.mTop);
5935             }
5936         } else {
5937             throw new IllegalArgumentException("parameter must be a descendant of this view");
5938         }
5939     }
5940 
5941     /**
5942      * Offset the vertical location of all children of this view by the specified number of pixels.
5943      *
5944      * @param offset the number of pixels to offset
5945      *
5946      * @hide
5947      */
offsetChildrenTopAndBottom(int offset)5948     public void offsetChildrenTopAndBottom(int offset) {
5949         final int count = mChildrenCount;
5950         final View[] children = mChildren;
5951         boolean invalidate = false;
5952 
5953         for (int i = 0; i < count; i++) {
5954             final View v = children[i];
5955             v.mTop += offset;
5956             v.mBottom += offset;
5957             if (v.mRenderNode != null) {
5958                 invalidate = true;
5959                 v.mRenderNode.offsetTopAndBottom(offset);
5960             }
5961         }
5962 
5963         if (invalidate) {
5964             invalidateViewProperty(false, false);
5965         }
5966         notifySubtreeAccessibilityStateChangedIfNeeded();
5967     }
5968 
5969     @Override
getChildVisibleRect(View child, Rect r, android.graphics.Point offset)5970     public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
5971         return getChildVisibleRect(child, r, offset, false);
5972     }
5973 
5974     /**
5975      * @param forceParentCheck true to guarantee that this call will propagate to all ancestors,
5976      *      false otherwise
5977      *
5978      * @hide
5979      */
getChildVisibleRect( View child, Rect r, android.graphics.Point offset, boolean forceParentCheck)5980     public boolean getChildVisibleRect(
5981             View child, Rect r, android.graphics.Point offset, boolean forceParentCheck) {
5982         // It doesn't make a whole lot of sense to call this on a view that isn't attached,
5983         // but for some simple tests it can be useful. If we don't have attach info this
5984         // will allocate memory.
5985         final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
5986         rect.set(r);
5987 
5988         if (!child.hasIdentityMatrix()) {
5989             child.getMatrix().mapRect(rect);
5990         }
5991 
5992         final int dx = child.mLeft - mScrollX;
5993         final int dy = child.mTop - mScrollY;
5994 
5995         rect.offset(dx, dy);
5996 
5997         if (offset != null) {
5998             if (!child.hasIdentityMatrix()) {
5999                 float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation
6000                         : new float[2];
6001                 position[0] = offset.x;
6002                 position[1] = offset.y;
6003                 child.getMatrix().mapPoints(position);
6004                 offset.x = Math.round(position[0]);
6005                 offset.y = Math.round(position[1]);
6006             }
6007             offset.x += dx;
6008             offset.y += dy;
6009         }
6010 
6011         final int width = mRight - mLeft;
6012         final int height = mBottom - mTop;
6013 
6014         boolean rectIsVisible = true;
6015         if (mParent == null ||
6016                 (mParent instanceof ViewGroup && ((ViewGroup) mParent).getClipChildren())) {
6017             // Clip to bounds.
6018             rectIsVisible = rect.intersect(0, 0, width, height);
6019         }
6020 
6021         if ((forceParentCheck || rectIsVisible)
6022                 && (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
6023             // Clip to padding.
6024             rectIsVisible = rect.intersect(mPaddingLeft, mPaddingTop,
6025                     width - mPaddingRight, height - mPaddingBottom);
6026         }
6027 
6028         if ((forceParentCheck || rectIsVisible) && mClipBounds != null) {
6029             // Clip to clipBounds.
6030             rectIsVisible = rect.intersect(mClipBounds.left, mClipBounds.top, mClipBounds.right,
6031                     mClipBounds.bottom);
6032         }
6033         r.set((int) Math.floor(rect.left), (int) Math.floor(rect.top),
6034                 (int) Math.ceil(rect.right), (int) Math.ceil(rect.bottom));
6035 
6036         if ((forceParentCheck || rectIsVisible) && mParent != null) {
6037             if (mParent instanceof ViewGroup) {
6038                 rectIsVisible = ((ViewGroup) mParent)
6039                         .getChildVisibleRect(this, r, offset, forceParentCheck);
6040             } else {
6041                 rectIsVisible = mParent.getChildVisibleRect(this, r, offset);
6042             }
6043         }
6044         return rectIsVisible;
6045     }
6046 
6047     @Override
layout(int l, int t, int r, int b)6048     public final void layout(int l, int t, int r, int b) {
6049         if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
6050             if (mTransition != null) {
6051                 mTransition.layoutChange(this);
6052             }
6053             super.layout(l, t, r, b);
6054         } else {
6055             // record the fact that we noop'd it; request layout when transition finishes
6056             mLayoutCalledWhileSuppressed = true;
6057         }
6058     }
6059 
6060     @Override
onLayout(boolean changed, int l, int t, int r, int b)6061     protected abstract void onLayout(boolean changed,
6062             int l, int t, int r, int b);
6063 
6064     /**
6065      * Indicates whether the view group has the ability to animate its children
6066      * after the first layout.
6067      *
6068      * @return true if the children can be animated, false otherwise
6069      */
canAnimate()6070     protected boolean canAnimate() {
6071         return mLayoutAnimationController != null;
6072     }
6073 
6074     /**
6075      * Runs the layout animation. Calling this method triggers a relayout of
6076      * this view group.
6077      */
startLayoutAnimation()6078     public void startLayoutAnimation() {
6079         if (mLayoutAnimationController != null) {
6080             mGroupFlags |= FLAG_RUN_ANIMATION;
6081             requestLayout();
6082         }
6083     }
6084 
6085     /**
6086      * Schedules the layout animation to be played after the next layout pass
6087      * of this view group. This can be used to restart the layout animation
6088      * when the content of the view group changes or when the activity is
6089      * paused and resumed.
6090      */
scheduleLayoutAnimation()6091     public void scheduleLayoutAnimation() {
6092         mGroupFlags |= FLAG_RUN_ANIMATION;
6093     }
6094 
6095     /**
6096      * Sets the layout animation controller used to animate the group's
6097      * children after the first layout.
6098      *
6099      * @param controller the animation controller
6100      */
setLayoutAnimation(LayoutAnimationController controller)6101     public void setLayoutAnimation(LayoutAnimationController controller) {
6102         mLayoutAnimationController = controller;
6103         if (mLayoutAnimationController != null) {
6104             mGroupFlags |= FLAG_RUN_ANIMATION;
6105         }
6106     }
6107 
6108     /**
6109      * Returns the layout animation controller used to animate the group's
6110      * children.
6111      *
6112      * @return the current animation controller
6113      */
getLayoutAnimation()6114     public LayoutAnimationController getLayoutAnimation() {
6115         return mLayoutAnimationController;
6116     }
6117 
6118     /**
6119      * Indicates whether the children's drawing cache is used during a layout
6120      * animation. By default, the drawing cache is enabled but this will prevent
6121      * nested layout animations from working. To nest animations, you must disable
6122      * the cache.
6123      *
6124      * @return true if the animation cache is enabled, false otherwise
6125      *
6126      * @see #setAnimationCacheEnabled(boolean)
6127      * @see View#setDrawingCacheEnabled(boolean)
6128      *
6129      * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6130      * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}.
6131      */
6132     @Deprecated
isAnimationCacheEnabled()6133     public boolean isAnimationCacheEnabled() {
6134         return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
6135     }
6136 
6137     /**
6138      * Enables or disables the children's drawing cache during a layout animation.
6139      * By default, the drawing cache is enabled but this will prevent nested
6140      * layout animations from working. To nest animations, you must disable the
6141      * cache.
6142      *
6143      * @param enabled true to enable the animation cache, false otherwise
6144      *
6145      * @see #isAnimationCacheEnabled()
6146      * @see View#setDrawingCacheEnabled(boolean)
6147      *
6148      * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6149      * Caching behavior of children may be controlled through {@link View#setLayerType(int, Paint)}.
6150      */
6151     @Deprecated
setAnimationCacheEnabled(boolean enabled)6152     public void setAnimationCacheEnabled(boolean enabled) {
6153         setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
6154     }
6155 
6156     /**
6157      * Indicates whether this ViewGroup will always try to draw its children using their
6158      * drawing cache. By default this property is enabled.
6159      *
6160      * @return true if the animation cache is enabled, false otherwise
6161      *
6162      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
6163      * @see #setChildrenDrawnWithCacheEnabled(boolean)
6164      * @see View#setDrawingCacheEnabled(boolean)
6165      *
6166      * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6167      * Child views may no longer have their caching behavior disabled by parents.
6168      */
6169     @Deprecated
isAlwaysDrawnWithCacheEnabled()6170     public boolean isAlwaysDrawnWithCacheEnabled() {
6171         return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
6172     }
6173 
6174     /**
6175      * Indicates whether this ViewGroup will always try to draw its children using their
6176      * drawing cache. This property can be set to true when the cache rendering is
6177      * slightly different from the children's normal rendering. Renderings can be different,
6178      * for instance, when the cache's quality is set to low.
6179      *
6180      * When this property is disabled, the ViewGroup will use the drawing cache of its
6181      * children only when asked to. It's usually the task of subclasses to tell ViewGroup
6182      * when to start using the drawing cache and when to stop using it.
6183      *
6184      * @param always true to always draw with the drawing cache, false otherwise
6185      *
6186      * @see #isAlwaysDrawnWithCacheEnabled()
6187      * @see #setChildrenDrawnWithCacheEnabled(boolean)
6188      * @see View#setDrawingCacheEnabled(boolean)
6189      * @see View#setDrawingCacheQuality(int)
6190      *
6191      * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6192      * Child views may no longer have their caching behavior disabled by parents.
6193      */
6194     @Deprecated
setAlwaysDrawnWithCacheEnabled(boolean always)6195     public void setAlwaysDrawnWithCacheEnabled(boolean always) {
6196         setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
6197     }
6198 
6199     /**
6200      * Indicates whether the ViewGroup is currently drawing its children using
6201      * their drawing cache.
6202      *
6203      * @return true if children should be drawn with their cache, false otherwise
6204      *
6205      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
6206      * @see #setChildrenDrawnWithCacheEnabled(boolean)
6207      *
6208      * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6209      * Child views may no longer be forced to cache their rendering state by their parents.
6210      * Use {@link View#setLayerType(int, Paint)} on individual Views instead.
6211      */
6212     @Deprecated
isChildrenDrawnWithCacheEnabled()6213     protected boolean isChildrenDrawnWithCacheEnabled() {
6214         return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
6215     }
6216 
6217     /**
6218      * Tells the ViewGroup to draw its children using their drawing cache. This property
6219      * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
6220      * will be used only if it has been enabled.
6221      *
6222      * Subclasses should call this method to start and stop using the drawing cache when
6223      * they perform performance sensitive operations, like scrolling or animating.
6224      *
6225      * @param enabled true if children should be drawn with their cache, false otherwise
6226      *
6227      * @see #setAlwaysDrawnWithCacheEnabled(boolean)
6228      * @see #isChildrenDrawnWithCacheEnabled()
6229      *
6230      * @deprecated As of {@link android.os.Build.VERSION_CODES#M}, this property is ignored.
6231      * Child views may no longer be forced to cache their rendering state by their parents.
6232      * Use {@link View#setLayerType(int, Paint)} on individual Views instead.
6233      */
6234     @Deprecated
setChildrenDrawnWithCacheEnabled(boolean enabled)6235     protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
6236         setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
6237     }
6238 
6239     /**
6240      * Indicates whether the ViewGroup is drawing its children in the order defined by
6241      * {@link #getChildDrawingOrder(int, int)}.
6242      *
6243      * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
6244      *         false otherwise
6245      *
6246      * @see #setChildrenDrawingOrderEnabled(boolean)
6247      * @see #getChildDrawingOrder(int, int)
6248      */
6249     @ViewDebug.ExportedProperty(category = "drawing")
isChildrenDrawingOrderEnabled()6250     protected boolean isChildrenDrawingOrderEnabled() {
6251         return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
6252     }
6253 
6254     /**
6255      * Tells the ViewGroup whether to draw its children in the order defined by the method
6256      * {@link #getChildDrawingOrder(int, int)}.
6257      * <p>
6258      * Note that {@link View#getZ() Z} reordering, done by {@link #dispatchDraw(Canvas)},
6259      * will override custom child ordering done via this method.
6260      *
6261      * @param enabled true if the order of the children when drawing is determined by
6262      *        {@link #getChildDrawingOrder(int, int)}, false otherwise
6263      *
6264      * @see #isChildrenDrawingOrderEnabled()
6265      * @see #getChildDrawingOrder(int, int)
6266      */
setChildrenDrawingOrderEnabled(boolean enabled)6267     protected void setChildrenDrawingOrderEnabled(boolean enabled) {
6268         setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
6269     }
6270 
hasBooleanFlag(int flag)6271     private boolean hasBooleanFlag(int flag) {
6272         return (mGroupFlags & flag) == flag;
6273     }
6274 
setBooleanFlag(int flag, boolean value)6275     private void setBooleanFlag(int flag, boolean value) {
6276         if (value) {
6277             mGroupFlags |= flag;
6278         } else {
6279             mGroupFlags &= ~flag;
6280         }
6281     }
6282 
6283     /**
6284      * Returns an integer indicating what types of drawing caches are kept in memory.
6285      *
6286      * @see #setPersistentDrawingCache(int)
6287      * @see #setAnimationCacheEnabled(boolean)
6288      *
6289      * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
6290      *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
6291      *         and {@link #PERSISTENT_ALL_CACHES}
6292      */
6293     @ViewDebug.ExportedProperty(category = "drawing", mapping = {
6294         @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
6295         @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
6296         @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
6297         @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
6298     })
getPersistentDrawingCache()6299     public int getPersistentDrawingCache() {
6300         return mPersistentDrawingCache;
6301     }
6302 
6303     /**
6304      * Indicates what types of drawing caches should be kept in memory after
6305      * they have been created.
6306      *
6307      * @see #getPersistentDrawingCache()
6308      * @see #setAnimationCacheEnabled(boolean)
6309      *
6310      * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
6311      *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
6312      *        and {@link #PERSISTENT_ALL_CACHES}
6313      */
setPersistentDrawingCache(int drawingCacheToKeep)6314     public void setPersistentDrawingCache(int drawingCacheToKeep) {
6315         mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
6316     }
6317 
setLayoutMode(int layoutMode, boolean explicitly)6318     private void setLayoutMode(int layoutMode, boolean explicitly) {
6319         mLayoutMode = layoutMode;
6320         setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly);
6321     }
6322 
6323     /**
6324      * Recursively traverse the view hierarchy, resetting the layoutMode of any
6325      * descendants that had inherited a different layoutMode from a previous parent.
6326      * Recursion terminates when a descendant's mode is:
6327      * <ul>
6328      *     <li>Undefined</li>
6329      *     <li>The same as the root node's</li>
6330      *     <li>A mode that had been explicitly set</li>
6331      * <ul/>
6332      * The first two clauses are optimizations.
6333      * @param layoutModeOfRoot
6334      */
6335     @Override
invalidateInheritedLayoutMode(int layoutModeOfRoot)6336     void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
6337         if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
6338             mLayoutMode == layoutModeOfRoot ||
6339             hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
6340             return;
6341         }
6342         setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
6343 
6344         // apply recursively
6345         for (int i = 0, N = getChildCount(); i < N; i++) {
6346             getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot);
6347         }
6348     }
6349 
6350     /**
6351      * Returns the basis of alignment during layout operations on this ViewGroup:
6352      * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
6353      * <p>
6354      * If no layoutMode was explicitly set, either programmatically or in an XML resource,
6355      * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
6356      * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
6357      *
6358      * @return the layout mode to use during layout operations
6359      *
6360      * @see #setLayoutMode(int)
6361      */
getLayoutMode()6362     public int getLayoutMode() {
6363         if (mLayoutMode == LAYOUT_MODE_UNDEFINED) {
6364             int inheritedLayoutMode = (mParent instanceof ViewGroup) ?
6365                     ((ViewGroup) mParent).getLayoutMode() : LAYOUT_MODE_DEFAULT;
6366             setLayoutMode(inheritedLayoutMode, false);
6367         }
6368         return mLayoutMode;
6369     }
6370 
6371     /**
6372      * Sets the basis of alignment during the layout of this ViewGroup.
6373      * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
6374      * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
6375      *
6376      * @param layoutMode the layout mode to use during layout operations
6377      *
6378      * @see #getLayoutMode()
6379      * @attr ref android.R.styleable#ViewGroup_layoutMode
6380      */
setLayoutMode(int layoutMode)6381     public void setLayoutMode(int layoutMode) {
6382         if (mLayoutMode != layoutMode) {
6383             invalidateInheritedLayoutMode(layoutMode);
6384             setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
6385             requestLayout();
6386         }
6387     }
6388 
6389     /**
6390      * Returns a new set of layout parameters based on the supplied attributes set.
6391      *
6392      * @param attrs the attributes to build the layout parameters from
6393      *
6394      * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
6395      *         of its descendants
6396      */
generateLayoutParams(AttributeSet attrs)6397     public LayoutParams generateLayoutParams(AttributeSet attrs) {
6398         return new LayoutParams(getContext(), attrs);
6399     }
6400 
6401     /**
6402      * Returns a safe set of layout parameters based on the supplied layout params.
6403      * When a ViewGroup is passed a View whose layout params do not pass the test of
6404      * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
6405      * is invoked. This method should return a new set of layout params suitable for
6406      * this ViewGroup, possibly by copying the appropriate attributes from the
6407      * specified set of layout params.
6408      *
6409      * @param p The layout parameters to convert into a suitable set of layout parameters
6410      *          for this ViewGroup.
6411      *
6412      * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
6413      *         of its descendants
6414      */
generateLayoutParams(ViewGroup.LayoutParams p)6415     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
6416         return p;
6417     }
6418 
6419     /**
6420      * Returns a set of default layout parameters. These parameters are requested
6421      * when the View passed to {@link #addView(View)} has no layout parameters
6422      * already set. If null is returned, an exception is thrown from addView.
6423      *
6424      * @return a set of default layout parameters or null
6425      */
generateDefaultLayoutParams()6426     protected LayoutParams generateDefaultLayoutParams() {
6427         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
6428     }
6429 
6430     @Override
debug(int depth)6431     protected void debug(int depth) {
6432         super.debug(depth);
6433         String output;
6434 
6435         if (mFocused != null) {
6436             output = debugIndent(depth);
6437             output += "mFocused";
6438             Log.d(VIEW_LOG_TAG, output);
6439             mFocused.debug(depth + 1);
6440         }
6441         if (mDefaultFocus != null) {
6442             output = debugIndent(depth);
6443             output += "mDefaultFocus";
6444             Log.d(VIEW_LOG_TAG, output);
6445             mDefaultFocus.debug(depth + 1);
6446         }
6447         if (mFocusedInCluster != null) {
6448             output = debugIndent(depth);
6449             output += "mFocusedInCluster";
6450             Log.d(VIEW_LOG_TAG, output);
6451             mFocusedInCluster.debug(depth + 1);
6452         }
6453         if (mChildrenCount != 0) {
6454             output = debugIndent(depth);
6455             output += "{";
6456             Log.d(VIEW_LOG_TAG, output);
6457         }
6458         int count = mChildrenCount;
6459         for (int i = 0; i < count; i++) {
6460             View child = mChildren[i];
6461             child.debug(depth + 1);
6462         }
6463 
6464         if (mChildrenCount != 0) {
6465             output = debugIndent(depth);
6466             output += "}";
6467             Log.d(VIEW_LOG_TAG, output);
6468         }
6469     }
6470 
6471     /**
6472      * Returns the position in the group of the specified child view.
6473      *
6474      * @param child the view for which to get the position
6475      * @return a positive integer representing the position of the view in the
6476      *         group, or -1 if the view does not exist in the group
6477      */
indexOfChild(View child)6478     public int indexOfChild(View child) {
6479         final int count = mChildrenCount;
6480         final View[] children = mChildren;
6481         for (int i = 0; i < count; i++) {
6482             if (children[i] == child) {
6483                 return i;
6484             }
6485         }
6486         return -1;
6487     }
6488 
6489     /**
6490      * Returns the number of children in the group.
6491      *
6492      * @return a positive integer representing the number of children in
6493      *         the group
6494      */
getChildCount()6495     public int getChildCount() {
6496         return mChildrenCount;
6497     }
6498 
6499     /**
6500      * Returns the view at the specified position in the group.
6501      *
6502      * @param index the position at which to get the view from
6503      * @return the view at the specified position or null if the position
6504      *         does not exist within the group
6505      */
getChildAt(int index)6506     public View getChildAt(int index) {
6507         if (index < 0 || index >= mChildrenCount) {
6508             return null;
6509         }
6510         return mChildren[index];
6511     }
6512 
6513     /**
6514      * Ask all of the children of this view to measure themselves, taking into
6515      * account both the MeasureSpec requirements for this view and its padding.
6516      * We skip children that are in the GONE state The heavy lifting is done in
6517      * getChildMeasureSpec.
6518      *
6519      * @param widthMeasureSpec The width requirements for this view
6520      * @param heightMeasureSpec The height requirements for this view
6521      */
measureChildren(int widthMeasureSpec, int heightMeasureSpec)6522     protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
6523         final int size = mChildrenCount;
6524         final View[] children = mChildren;
6525         for (int i = 0; i < size; ++i) {
6526             final View child = children[i];
6527             if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
6528                 measureChild(child, widthMeasureSpec, heightMeasureSpec);
6529             }
6530         }
6531     }
6532 
6533     /**
6534      * Ask one of the children of this view to measure itself, taking into
6535      * account both the MeasureSpec requirements for this view and its padding.
6536      * The heavy lifting is done in getChildMeasureSpec.
6537      *
6538      * @param child The child to measure
6539      * @param parentWidthMeasureSpec The width requirements for this view
6540      * @param parentHeightMeasureSpec The height requirements for this view
6541      */
measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)6542     protected void measureChild(View child, int parentWidthMeasureSpec,
6543             int parentHeightMeasureSpec) {
6544         final LayoutParams lp = child.getLayoutParams();
6545 
6546         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
6547                 mPaddingLeft + mPaddingRight, lp.width);
6548         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
6549                 mPaddingTop + mPaddingBottom, lp.height);
6550 
6551         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
6552     }
6553 
6554     /**
6555      * Ask one of the children of this view to measure itself, taking into
6556      * account both the MeasureSpec requirements for this view and its padding
6557      * and margins. The child must have MarginLayoutParams The heavy lifting is
6558      * done in getChildMeasureSpec.
6559      *
6560      * @param child The child to measure
6561      * @param parentWidthMeasureSpec The width requirements for this view
6562      * @param widthUsed Extra space that has been used up by the parent
6563      *        horizontally (possibly by other children of the parent)
6564      * @param parentHeightMeasureSpec The height requirements for this view
6565      * @param heightUsed Extra space that has been used up by the parent
6566      *        vertically (possibly by other children of the parent)
6567      */
measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)6568     protected void measureChildWithMargins(View child,
6569             int parentWidthMeasureSpec, int widthUsed,
6570             int parentHeightMeasureSpec, int heightUsed) {
6571         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
6572 
6573         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
6574                 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
6575                         + widthUsed, lp.width);
6576         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
6577                 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
6578                         + heightUsed, lp.height);
6579 
6580         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
6581     }
6582 
6583     /**
6584      * Does the hard part of measureChildren: figuring out the MeasureSpec to
6585      * pass to a particular child. This method figures out the right MeasureSpec
6586      * for one dimension (height or width) of one child view.
6587      *
6588      * The goal is to combine information from our MeasureSpec with the
6589      * LayoutParams of the child to get the best possible results. For example,
6590      * if the this view knows its size (because its MeasureSpec has a mode of
6591      * EXACTLY), and the child has indicated in its LayoutParams that it wants
6592      * to be the same size as the parent, the parent should ask the child to
6593      * layout given an exact size.
6594      *
6595      * @param spec The requirements for this view
6596      * @param padding The padding of this view for the current dimension and
6597      *        margins, if applicable
6598      * @param childDimension How big the child wants to be in the current
6599      *        dimension
6600      * @return a MeasureSpec integer for the child
6601      */
getChildMeasureSpec(int spec, int padding, int childDimension)6602     public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
6603         int specMode = MeasureSpec.getMode(spec);
6604         int specSize = MeasureSpec.getSize(spec);
6605 
6606         int size = Math.max(0, specSize - padding);
6607 
6608         int resultSize = 0;
6609         int resultMode = 0;
6610 
6611         switch (specMode) {
6612         // Parent has imposed an exact size on us
6613         case MeasureSpec.EXACTLY:
6614             if (childDimension >= 0) {
6615                 resultSize = childDimension;
6616                 resultMode = MeasureSpec.EXACTLY;
6617             } else if (childDimension == LayoutParams.MATCH_PARENT) {
6618                 // Child wants to be our size. So be it.
6619                 resultSize = size;
6620                 resultMode = MeasureSpec.EXACTLY;
6621             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
6622                 // Child wants to determine its own size. It can't be
6623                 // bigger than us.
6624                 resultSize = size;
6625                 resultMode = MeasureSpec.AT_MOST;
6626             }
6627             break;
6628 
6629         // Parent has imposed a maximum size on us
6630         case MeasureSpec.AT_MOST:
6631             if (childDimension >= 0) {
6632                 // Child wants a specific size... so be it
6633                 resultSize = childDimension;
6634                 resultMode = MeasureSpec.EXACTLY;
6635             } else if (childDimension == LayoutParams.MATCH_PARENT) {
6636                 // Child wants to be our size, but our size is not fixed.
6637                 // Constrain child to not be bigger than us.
6638                 resultSize = size;
6639                 resultMode = MeasureSpec.AT_MOST;
6640             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
6641                 // Child wants to determine its own size. It can't be
6642                 // bigger than us.
6643                 resultSize = size;
6644                 resultMode = MeasureSpec.AT_MOST;
6645             }
6646             break;
6647 
6648         // Parent asked to see how big we want to be
6649         case MeasureSpec.UNSPECIFIED:
6650             if (childDimension >= 0) {
6651                 // Child wants a specific size... let him have it
6652                 resultSize = childDimension;
6653                 resultMode = MeasureSpec.EXACTLY;
6654             } else if (childDimension == LayoutParams.MATCH_PARENT) {
6655                 // Child wants to be our size... find out how big it should
6656                 // be
6657                 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
6658                 resultMode = MeasureSpec.UNSPECIFIED;
6659             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
6660                 // Child wants to determine its own size.... find out how
6661                 // big it should be
6662                 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
6663                 resultMode = MeasureSpec.UNSPECIFIED;
6664             }
6665             break;
6666         }
6667         //noinspection ResourceType
6668         return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
6669     }
6670 
6671 
6672     /**
6673      * Removes any pending animations for views that have been removed. Call
6674      * this if you don't want animations for exiting views to stack up.
6675      */
clearDisappearingChildren()6676     public void clearDisappearingChildren() {
6677         final ArrayList<View> disappearingChildren = mDisappearingChildren;
6678         if (disappearingChildren != null) {
6679             final int count = disappearingChildren.size();
6680             for (int i = 0; i < count; i++) {
6681                 final View view = disappearingChildren.get(i);
6682                 if (view.mAttachInfo != null) {
6683                     view.dispatchDetachedFromWindow();
6684                 }
6685                 view.clearAnimation();
6686             }
6687             disappearingChildren.clear();
6688             invalidate();
6689         }
6690     }
6691 
6692     /**
6693      * Add a view which is removed from mChildren but still needs animation
6694      *
6695      * @param v View to add
6696      */
addDisappearingView(View v)6697     private void addDisappearingView(View v) {
6698         ArrayList<View> disappearingChildren = mDisappearingChildren;
6699 
6700         if (disappearingChildren == null) {
6701             disappearingChildren = mDisappearingChildren = new ArrayList<View>();
6702         }
6703 
6704         disappearingChildren.add(v);
6705     }
6706 
6707     /**
6708      * Cleanup a view when its animation is done. This may mean removing it from
6709      * the list of disappearing views.
6710      *
6711      * @param view The view whose animation has finished
6712      * @param animation The animation, cannot be null
6713      */
finishAnimatingView(final View view, Animation animation)6714     void finishAnimatingView(final View view, Animation animation) {
6715         final ArrayList<View> disappearingChildren = mDisappearingChildren;
6716         if (disappearingChildren != null) {
6717             if (disappearingChildren.contains(view)) {
6718                 disappearingChildren.remove(view);
6719 
6720                 if (view.mAttachInfo != null) {
6721                     view.dispatchDetachedFromWindow();
6722                 }
6723 
6724                 view.clearAnimation();
6725                 mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
6726             }
6727         }
6728 
6729         if (animation != null && !animation.getFillAfter()) {
6730             view.clearAnimation();
6731         }
6732 
6733         if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) {
6734             view.onAnimationEnd();
6735             // Should be performed by onAnimationEnd() but this avoid an infinite loop,
6736             // so we'd rather be safe than sorry
6737             view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED;
6738             // Draw one more frame after the animation is done
6739             mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
6740         }
6741     }
6742 
6743     /**
6744      * Utility function called by View during invalidation to determine whether a view that
6745      * is invisible or gone should still be invalidated because it is being transitioned (and
6746      * therefore still needs to be drawn).
6747      */
isViewTransitioning(View view)6748     boolean isViewTransitioning(View view) {
6749         return (mTransitioningViews != null && mTransitioningViews.contains(view));
6750     }
6751 
6752     /**
6753      * This method tells the ViewGroup that the given View object, which should have this
6754      * ViewGroup as its parent,
6755      * should be kept around  (re-displayed when the ViewGroup draws its children) even if it
6756      * is removed from its parent. This allows animations, such as those used by
6757      * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
6758      * the removal of views. A call to this method should always be accompanied by a later call
6759      * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
6760      * so that the View finally gets removed.
6761      *
6762      * @param view The View object to be kept visible even if it gets removed from its parent.
6763      */
startViewTransition(View view)6764     public void startViewTransition(View view) {
6765         if (view.mParent == this) {
6766             if (mTransitioningViews == null) {
6767                 mTransitioningViews = new ArrayList<View>();
6768             }
6769             mTransitioningViews.add(view);
6770         }
6771     }
6772 
6773     /**
6774      * This method should always be called following an earlier call to
6775      * {@link #startViewTransition(View)}. The given View is finally removed from its parent
6776      * and will no longer be displayed. Note that this method does not perform the functionality
6777      * of removing a view from its parent; it just discontinues the display of a View that
6778      * has previously been removed.
6779      *
6780      * @return view The View object that has been removed but is being kept around in the visible
6781      * hierarchy by an earlier call to {@link #startViewTransition(View)}.
6782      */
endViewTransition(View view)6783     public void endViewTransition(View view) {
6784         if (mTransitioningViews != null) {
6785             mTransitioningViews.remove(view);
6786             final ArrayList<View> disappearingChildren = mDisappearingChildren;
6787             if (disappearingChildren != null && disappearingChildren.contains(view)) {
6788                 disappearingChildren.remove(view);
6789                 if (mVisibilityChangingChildren != null &&
6790                         mVisibilityChangingChildren.contains(view)) {
6791                     mVisibilityChangingChildren.remove(view);
6792                 } else {
6793                     if (view.mAttachInfo != null) {
6794                         view.dispatchDetachedFromWindow();
6795                     }
6796                     if (view.mParent != null) {
6797                         view.mParent = null;
6798                     }
6799                 }
6800                 invalidate();
6801             }
6802         }
6803     }
6804 
6805     private LayoutTransition.TransitionListener mLayoutTransitionListener =
6806             new LayoutTransition.TransitionListener() {
6807         @Override
6808         public void startTransition(LayoutTransition transition, ViewGroup container,
6809                 View view, int transitionType) {
6810             // We only care about disappearing items, since we need special logic to keep
6811             // those items visible after they've been 'removed'
6812             if (transitionType == LayoutTransition.DISAPPEARING) {
6813                 startViewTransition(view);
6814             }
6815         }
6816 
6817         @Override
6818         public void endTransition(LayoutTransition transition, ViewGroup container,
6819                 View view, int transitionType) {
6820             if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) {
6821                 requestLayout();
6822                 mLayoutCalledWhileSuppressed = false;
6823             }
6824             if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
6825                 endViewTransition(view);
6826             }
6827         }
6828     };
6829 
6830     /**
6831      * Tells this ViewGroup to suppress all layout() calls until layout
6832      * suppression is disabled with a later call to suppressLayout(false).
6833      * When layout suppression is disabled, a requestLayout() call is sent
6834      * if layout() was attempted while layout was being suppressed.
6835      *
6836      * @hide
6837      */
suppressLayout(boolean suppress)6838     public void suppressLayout(boolean suppress) {
6839         mSuppressLayout = suppress;
6840         if (!suppress) {
6841             if (mLayoutCalledWhileSuppressed) {
6842                 requestLayout();
6843                 mLayoutCalledWhileSuppressed = false;
6844             }
6845         }
6846     }
6847 
6848     /**
6849      * Returns whether layout calls on this container are currently being
6850      * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
6851      *
6852      * @return true if layout calls are currently suppressed, false otherwise.
6853      *
6854      * @hide
6855      */
isLayoutSuppressed()6856     public boolean isLayoutSuppressed() {
6857         return mSuppressLayout;
6858     }
6859 
6860     @Override
gatherTransparentRegion(Region region)6861     public boolean gatherTransparentRegion(Region region) {
6862         // If no transparent regions requested, we are always opaque.
6863         final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0;
6864         if (meOpaque && region == null) {
6865             // The caller doesn't care about the region, so stop now.
6866             return true;
6867         }
6868         super.gatherTransparentRegion(region);
6869         // Instead of naively traversing the view tree, we have to traverse according to the Z
6870         // order here. We need to go with the same order as dispatchDraw().
6871         // One example is that after surfaceView punch a hole, we will still allow other views drawn
6872         // on top of that hole. In this case, those other views should be able to cut the
6873         // transparent region into smaller area.
6874         final int childrenCount = mChildrenCount;
6875         boolean noneOfTheChildrenAreTransparent = true;
6876         if (childrenCount > 0) {
6877             final ArrayList<View> preorderedList = buildOrderedChildList();
6878             final boolean customOrder = preorderedList == null
6879                     && isChildrenDrawingOrderEnabled();
6880             final View[] children = mChildren;
6881             for (int i = 0; i < childrenCount; i++) {
6882                 final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
6883                 final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
6884                 if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
6885                     if (!child.gatherTransparentRegion(region)) {
6886                         noneOfTheChildrenAreTransparent = false;
6887                     }
6888                 }
6889             }
6890             if (preorderedList != null) preorderedList.clear();
6891         }
6892         return meOpaque || noneOfTheChildrenAreTransparent;
6893     }
6894 
6895     @Override
requestTransparentRegion(View child)6896     public void requestTransparentRegion(View child) {
6897         if (child != null) {
6898             child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
6899             if (mParent != null) {
6900                 mParent.requestTransparentRegion(this);
6901             }
6902         }
6903     }
6904 
6905     @Override
dispatchApplyWindowInsets(WindowInsets insets)6906     public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
6907         insets = super.dispatchApplyWindowInsets(insets);
6908         if (!insets.isConsumed()) {
6909             final int count = getChildCount();
6910             for (int i = 0; i < count; i++) {
6911                 insets = getChildAt(i).dispatchApplyWindowInsets(insets);
6912                 if (insets.isConsumed()) {
6913                     break;
6914                 }
6915             }
6916         }
6917         return insets;
6918     }
6919 
6920     /**
6921      * Returns the animation listener to which layout animation events are
6922      * sent.
6923      *
6924      * @return an {@link android.view.animation.Animation.AnimationListener}
6925      */
getLayoutAnimationListener()6926     public Animation.AnimationListener getLayoutAnimationListener() {
6927         return mAnimationListener;
6928     }
6929 
6930     @Override
drawableStateChanged()6931     protected void drawableStateChanged() {
6932         super.drawableStateChanged();
6933 
6934         if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
6935             if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
6936                 throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
6937                         + " child has duplicateParentState set to true");
6938             }
6939 
6940             final View[] children = mChildren;
6941             final int count = mChildrenCount;
6942 
6943             for (int i = 0; i < count; i++) {
6944                 final View child = children[i];
6945                 if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
6946                     child.refreshDrawableState();
6947                 }
6948             }
6949         }
6950     }
6951 
6952     @Override
jumpDrawablesToCurrentState()6953     public void jumpDrawablesToCurrentState() {
6954         super.jumpDrawablesToCurrentState();
6955         final View[] children = mChildren;
6956         final int count = mChildrenCount;
6957         for (int i = 0; i < count; i++) {
6958             children[i].jumpDrawablesToCurrentState();
6959         }
6960     }
6961 
6962     @Override
onCreateDrawableState(int extraSpace)6963     protected int[] onCreateDrawableState(int extraSpace) {
6964         if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
6965             return super.onCreateDrawableState(extraSpace);
6966         }
6967 
6968         int need = 0;
6969         int n = getChildCount();
6970         for (int i = 0; i < n; i++) {
6971             int[] childState = getChildAt(i).getDrawableState();
6972 
6973             if (childState != null) {
6974                 need += childState.length;
6975             }
6976         }
6977 
6978         int[] state = super.onCreateDrawableState(extraSpace + need);
6979 
6980         for (int i = 0; i < n; i++) {
6981             int[] childState = getChildAt(i).getDrawableState();
6982 
6983             if (childState != null) {
6984                 state = mergeDrawableStates(state, childState);
6985             }
6986         }
6987 
6988         return state;
6989     }
6990 
6991     /**
6992      * Sets whether this ViewGroup's drawable states also include
6993      * its children's drawable states.  This is used, for example, to
6994      * make a group appear to be focused when its child EditText or button
6995      * is focused.
6996      */
setAddStatesFromChildren(boolean addsStates)6997     public void setAddStatesFromChildren(boolean addsStates) {
6998         if (addsStates) {
6999             mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
7000         } else {
7001             mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
7002         }
7003 
7004         refreshDrawableState();
7005     }
7006 
7007     /**
7008      * Returns whether this ViewGroup's drawable states also include
7009      * its children's drawable states.  This is used, for example, to
7010      * make a group appear to be focused when its child EditText or button
7011      * is focused.
7012      */
addStatesFromChildren()7013     public boolean addStatesFromChildren() {
7014         return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
7015     }
7016 
7017     /**
7018      * If {@link #addStatesFromChildren} is true, refreshes this group's
7019      * drawable state (to include the states from its children).
7020      */
7021     @Override
childDrawableStateChanged(View child)7022     public void childDrawableStateChanged(View child) {
7023         if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
7024             refreshDrawableState();
7025         }
7026     }
7027 
7028     /**
7029      * Specifies the animation listener to which layout animation events must
7030      * be sent. Only
7031      * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
7032      * and
7033      * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
7034      * are invoked.
7035      *
7036      * @param animationListener the layout animation listener
7037      */
setLayoutAnimationListener(Animation.AnimationListener animationListener)7038     public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
7039         mAnimationListener = animationListener;
7040     }
7041 
7042     /**
7043      * This method is called by LayoutTransition when there are 'changing' animations that need
7044      * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
7045      * starts all pending transitions prior to the drawing phase in the current traversal.
7046      *
7047      * @param transition The LayoutTransition to be started on the next traversal.
7048      *
7049      * @hide
7050      */
requestTransitionStart(LayoutTransition transition)7051     public void requestTransitionStart(LayoutTransition transition) {
7052         ViewRootImpl viewAncestor = getViewRootImpl();
7053         if (viewAncestor != null) {
7054             viewAncestor.requestTransitionStart(transition);
7055         }
7056     }
7057 
7058     /**
7059      * @hide
7060      */
7061     @Override
resolveRtlPropertiesIfNeeded()7062     public boolean resolveRtlPropertiesIfNeeded() {
7063         final boolean result = super.resolveRtlPropertiesIfNeeded();
7064         // We dont need to resolve the children RTL properties if nothing has changed for the parent
7065         if (result) {
7066             int count = getChildCount();
7067             for (int i = 0; i < count; i++) {
7068                 final View child = getChildAt(i);
7069                 if (child.isLayoutDirectionInherited()) {
7070                     child.resolveRtlPropertiesIfNeeded();
7071                 }
7072             }
7073         }
7074         return result;
7075     }
7076 
7077     /**
7078      * @hide
7079      */
7080     @Override
resolveLayoutDirection()7081     public boolean resolveLayoutDirection() {
7082         final boolean result = super.resolveLayoutDirection();
7083         if (result) {
7084             int count = getChildCount();
7085             for (int i = 0; i < count; i++) {
7086                 final View child = getChildAt(i);
7087                 if (child.isLayoutDirectionInherited()) {
7088                     child.resolveLayoutDirection();
7089                 }
7090             }
7091         }
7092         return result;
7093     }
7094 
7095     /**
7096      * @hide
7097      */
7098     @Override
resolveTextDirection()7099     public boolean resolveTextDirection() {
7100         final boolean result = super.resolveTextDirection();
7101         if (result) {
7102             int count = getChildCount();
7103             for (int i = 0; i < count; i++) {
7104                 final View child = getChildAt(i);
7105                 if (child.isTextDirectionInherited()) {
7106                     child.resolveTextDirection();
7107                 }
7108             }
7109         }
7110         return result;
7111     }
7112 
7113     /**
7114      * @hide
7115      */
7116     @Override
resolveTextAlignment()7117     public boolean resolveTextAlignment() {
7118         final boolean result = super.resolveTextAlignment();
7119         if (result) {
7120             int count = getChildCount();
7121             for (int i = 0; i < count; i++) {
7122                 final View child = getChildAt(i);
7123                 if (child.isTextAlignmentInherited()) {
7124                     child.resolveTextAlignment();
7125                 }
7126             }
7127         }
7128         return result;
7129     }
7130 
7131     /**
7132      * @hide
7133      */
7134     @Override
resolvePadding()7135     public void resolvePadding() {
7136         super.resolvePadding();
7137         int count = getChildCount();
7138         for (int i = 0; i < count; i++) {
7139             final View child = getChildAt(i);
7140             if (child.isLayoutDirectionInherited() && !child.isPaddingResolved()) {
7141                 child.resolvePadding();
7142             }
7143         }
7144     }
7145 
7146     /**
7147      * @hide
7148      */
7149     @Override
resolveDrawables()7150     protected void resolveDrawables() {
7151         super.resolveDrawables();
7152         int count = getChildCount();
7153         for (int i = 0; i < count; i++) {
7154             final View child = getChildAt(i);
7155             if (child.isLayoutDirectionInherited() && !child.areDrawablesResolved()) {
7156                 child.resolveDrawables();
7157             }
7158         }
7159     }
7160 
7161     /**
7162      * @hide
7163      */
7164     @Override
resolveLayoutParams()7165     public void resolveLayoutParams() {
7166         super.resolveLayoutParams();
7167         int count = getChildCount();
7168         for (int i = 0; i < count; i++) {
7169             final View child = getChildAt(i);
7170             child.resolveLayoutParams();
7171         }
7172     }
7173 
7174     /**
7175      * @hide
7176      */
7177     @Override
resetResolvedLayoutDirection()7178     public void resetResolvedLayoutDirection() {
7179         super.resetResolvedLayoutDirection();
7180 
7181         int count = getChildCount();
7182         for (int i = 0; i < count; i++) {
7183             final View child = getChildAt(i);
7184             if (child.isLayoutDirectionInherited()) {
7185                 child.resetResolvedLayoutDirection();
7186             }
7187         }
7188     }
7189 
7190     /**
7191      * @hide
7192      */
7193     @Override
resetResolvedTextDirection()7194     public void resetResolvedTextDirection() {
7195         super.resetResolvedTextDirection();
7196 
7197         int count = getChildCount();
7198         for (int i = 0; i < count; i++) {
7199             final View child = getChildAt(i);
7200             if (child.isTextDirectionInherited()) {
7201                 child.resetResolvedTextDirection();
7202             }
7203         }
7204     }
7205 
7206     /**
7207      * @hide
7208      */
7209     @Override
resetResolvedTextAlignment()7210     public void resetResolvedTextAlignment() {
7211         super.resetResolvedTextAlignment();
7212 
7213         int count = getChildCount();
7214         for (int i = 0; i < count; i++) {
7215             final View child = getChildAt(i);
7216             if (child.isTextAlignmentInherited()) {
7217                 child.resetResolvedTextAlignment();
7218             }
7219         }
7220     }
7221 
7222     /**
7223      * @hide
7224      */
7225     @Override
resetResolvedPadding()7226     public void resetResolvedPadding() {
7227         super.resetResolvedPadding();
7228 
7229         int count = getChildCount();
7230         for (int i = 0; i < count; i++) {
7231             final View child = getChildAt(i);
7232             if (child.isLayoutDirectionInherited()) {
7233                 child.resetResolvedPadding();
7234             }
7235         }
7236     }
7237 
7238     /**
7239      * @hide
7240      */
7241     @Override
resetResolvedDrawables()7242     protected void resetResolvedDrawables() {
7243         super.resetResolvedDrawables();
7244 
7245         int count = getChildCount();
7246         for (int i = 0; i < count; i++) {
7247             final View child = getChildAt(i);
7248             if (child.isLayoutDirectionInherited()) {
7249                 child.resetResolvedDrawables();
7250             }
7251         }
7252     }
7253 
7254     /**
7255      * Return true if the pressed state should be delayed for children or descendants of this
7256      * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
7257      * This prevents the pressed state from appearing when the user is actually trying to scroll
7258      * the content.
7259      *
7260      * The default implementation returns true for compatibility reasons. Subclasses that do
7261      * not scroll should generally override this method and return false.
7262      */
shouldDelayChildPressedState()7263     public boolean shouldDelayChildPressedState() {
7264         return true;
7265     }
7266 
7267     /**
7268      * @inheritDoc
7269      */
7270     @Override
onStartNestedScroll(View child, View target, int nestedScrollAxes)7271     public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
7272         return false;
7273     }
7274 
7275     /**
7276      * @inheritDoc
7277      */
7278     @Override
onNestedScrollAccepted(View child, View target, int axes)7279     public void onNestedScrollAccepted(View child, View target, int axes) {
7280         mNestedScrollAxes = axes;
7281     }
7282 
7283     /**
7284      * @inheritDoc
7285      *
7286      * <p>The default implementation of onStopNestedScroll calls
7287      * {@link #stopNestedScroll()} to halt any recursive nested scrolling in progress.</p>
7288      */
7289     @Override
onStopNestedScroll(View child)7290     public void onStopNestedScroll(View child) {
7291         // Stop any recursive nested scrolling.
7292         stopNestedScroll();
7293         mNestedScrollAxes = 0;
7294     }
7295 
7296     /**
7297      * @inheritDoc
7298      */
7299     @Override
onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)7300     public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
7301             int dxUnconsumed, int dyUnconsumed) {
7302         // Re-dispatch up the tree by default
7303         dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null);
7304     }
7305 
7306     /**
7307      * @inheritDoc
7308      */
7309     @Override
onNestedPreScroll(View target, int dx, int dy, int[] consumed)7310     public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
7311         // Re-dispatch up the tree by default
7312         dispatchNestedPreScroll(dx, dy, consumed, null);
7313     }
7314 
7315     /**
7316      * @inheritDoc
7317      */
7318     @Override
onNestedFling(View target, float velocityX, float velocityY, boolean consumed)7319     public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
7320         // Re-dispatch up the tree by default
7321         return dispatchNestedFling(velocityX, velocityY, consumed);
7322     }
7323 
7324     /**
7325      * @inheritDoc
7326      */
7327     @Override
onNestedPreFling(View target, float velocityX, float velocityY)7328     public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
7329         // Re-dispatch up the tree by default
7330         return dispatchNestedPreFling(velocityX, velocityY);
7331     }
7332 
7333     /**
7334      * Return the current axes of nested scrolling for this ViewGroup.
7335      *
7336      * <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
7337      * acting as a nested scrolling parent for one or more descendant views in the hierarchy.</p>
7338      *
7339      * @return Flags indicating the current axes of nested scrolling
7340      * @see #SCROLL_AXIS_HORIZONTAL
7341      * @see #SCROLL_AXIS_VERTICAL
7342      * @see #SCROLL_AXIS_NONE
7343      */
getNestedScrollAxes()7344     public int getNestedScrollAxes() {
7345         return mNestedScrollAxes;
7346     }
7347 
7348     /** @hide */
onSetLayoutParams(View child, LayoutParams layoutParams)7349     protected void onSetLayoutParams(View child, LayoutParams layoutParams) {
7350         requestLayout();
7351     }
7352 
7353     /** @hide */
7354     @Override
captureTransitioningViews(List<View> transitioningViews)7355     public void captureTransitioningViews(List<View> transitioningViews) {
7356         if (getVisibility() != View.VISIBLE) {
7357             return;
7358         }
7359         if (isTransitionGroup()) {
7360             transitioningViews.add(this);
7361         } else {
7362             int count = getChildCount();
7363             for (int i = 0; i < count; i++) {
7364                 View child = getChildAt(i);
7365                 child.captureTransitioningViews(transitioningViews);
7366             }
7367         }
7368     }
7369 
7370     /** @hide */
7371     @Override
findNamedViews(Map<String, View> namedElements)7372     public void findNamedViews(Map<String, View> namedElements) {
7373         if (getVisibility() != VISIBLE && mGhostView == null) {
7374             return;
7375         }
7376         super.findNamedViews(namedElements);
7377         int count = getChildCount();
7378         for (int i = 0; i < count; i++) {
7379             View child = getChildAt(i);
7380             child.findNamedViews(namedElements);
7381         }
7382     }
7383 
7384     /**
7385      * LayoutParams are used by views to tell their parents how they want to be
7386      * laid out. See
7387      * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
7388      * for a list of all child view attributes that this class supports.
7389      *
7390      * <p>
7391      * The base LayoutParams class just describes how big the view wants to be
7392      * for both width and height. For each dimension, it can specify one of:
7393      * <ul>
7394      * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
7395      * means that the view wants to be as big as its parent (minus padding)
7396      * <li> WRAP_CONTENT, which means that the view wants to be just big enough
7397      * to enclose its content (plus padding)
7398      * <li> an exact number
7399      * </ul>
7400      * There are subclasses of LayoutParams for different subclasses of
7401      * ViewGroup. For example, AbsoluteLayout has its own subclass of
7402      * LayoutParams which adds an X and Y value.</p>
7403      *
7404      * <div class="special reference">
7405      * <h3>Developer Guides</h3>
7406      * <p>For more information about creating user interface layouts, read the
7407      * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
7408      * guide.</p></div>
7409      *
7410      * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
7411      * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
7412      */
7413     public static class LayoutParams {
7414         /**
7415          * Special value for the height or width requested by a View.
7416          * FILL_PARENT means that the view wants to be as big as its parent,
7417          * minus the parent's padding, if any. This value is deprecated
7418          * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
7419          */
7420         @SuppressWarnings({"UnusedDeclaration"})
7421         @Deprecated
7422         public static final int FILL_PARENT = -1;
7423 
7424         /**
7425          * Special value for the height or width requested by a View.
7426          * MATCH_PARENT means that the view wants to be as big as its parent,
7427          * minus the parent's padding, if any. Introduced in API Level 8.
7428          */
7429         public static final int MATCH_PARENT = -1;
7430 
7431         /**
7432          * Special value for the height or width requested by a View.
7433          * WRAP_CONTENT means that the view wants to be just large enough to fit
7434          * its own internal content, taking its own padding into account.
7435          */
7436         public static final int WRAP_CONTENT = -2;
7437 
7438         /**
7439          * Information about how wide the view wants to be. Can be one of the
7440          * constants FILL_PARENT (replaced by MATCH_PARENT
7441          * in API Level 8) or WRAP_CONTENT, or an exact size.
7442          */
7443         @ViewDebug.ExportedProperty(category = "layout", mapping = {
7444             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
7445             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
7446         })
7447         public int width;
7448 
7449         /**
7450          * Information about how tall the view wants to be. Can be one of the
7451          * constants FILL_PARENT (replaced by MATCH_PARENT
7452          * in API Level 8) or WRAP_CONTENT, or an exact size.
7453          */
7454         @ViewDebug.ExportedProperty(category = "layout", mapping = {
7455             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
7456             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
7457         })
7458         public int height;
7459 
7460         /**
7461          * Used to animate layouts.
7462          */
7463         public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
7464 
7465         /**
7466          * Creates a new set of layout parameters. The values are extracted from
7467          * the supplied attributes set and context. The XML attributes mapped
7468          * to this set of layout parameters are:
7469          *
7470          * <ul>
7471          *   <li><code>layout_width</code>: the width, either an exact value,
7472          *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
7473          *   {@link #MATCH_PARENT} in API Level 8)</li>
7474          *   <li><code>layout_height</code>: the height, either an exact value,
7475          *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
7476          *   {@link #MATCH_PARENT} in API Level 8)</li>
7477          * </ul>
7478          *
7479          * @param c the application environment
7480          * @param attrs the set of attributes from which to extract the layout
7481          *              parameters' values
7482          */
LayoutParams(Context c, AttributeSet attrs)7483         public LayoutParams(Context c, AttributeSet attrs) {
7484             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
7485             setBaseAttributes(a,
7486                     R.styleable.ViewGroup_Layout_layout_width,
7487                     R.styleable.ViewGroup_Layout_layout_height);
7488             a.recycle();
7489         }
7490 
7491         /**
7492          * Creates a new set of layout parameters with the specified width
7493          * and height.
7494          *
7495          * @param width the width, either {@link #WRAP_CONTENT},
7496          *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
7497          *        API Level 8), or a fixed size in pixels
7498          * @param height the height, either {@link #WRAP_CONTENT},
7499          *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
7500          *        API Level 8), or a fixed size in pixels
7501          */
LayoutParams(int width, int height)7502         public LayoutParams(int width, int height) {
7503             this.width = width;
7504             this.height = height;
7505         }
7506 
7507         /**
7508          * Copy constructor. Clones the width and height values of the source.
7509          *
7510          * @param source The layout params to copy from.
7511          */
LayoutParams(LayoutParams source)7512         public LayoutParams(LayoutParams source) {
7513             this.width = source.width;
7514             this.height = source.height;
7515         }
7516 
7517         /**
7518          * Used internally by MarginLayoutParams.
7519          * @hide
7520          */
LayoutParams()7521         LayoutParams() {
7522         }
7523 
7524         /**
7525          * Extracts the layout parameters from the supplied attributes.
7526          *
7527          * @param a the style attributes to extract the parameters from
7528          * @param widthAttr the identifier of the width attribute
7529          * @param heightAttr the identifier of the height attribute
7530          */
setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)7531         protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
7532             width = a.getLayoutDimension(widthAttr, "layout_width");
7533             height = a.getLayoutDimension(heightAttr, "layout_height");
7534         }
7535 
7536         /**
7537          * Resolve layout parameters depending on the layout direction. Subclasses that care about
7538          * layoutDirection changes should override this method. The default implementation does
7539          * nothing.
7540          *
7541          * @param layoutDirection the direction of the layout
7542          *
7543          * {@link View#LAYOUT_DIRECTION_LTR}
7544          * {@link View#LAYOUT_DIRECTION_RTL}
7545          */
resolveLayoutDirection(int layoutDirection)7546         public void resolveLayoutDirection(int layoutDirection) {
7547         }
7548 
7549         /**
7550          * Returns a String representation of this set of layout parameters.
7551          *
7552          * @param output the String to prepend to the internal representation
7553          * @return a String with the following format: output +
7554          *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
7555          *
7556          * @hide
7557          */
debug(String output)7558         public String debug(String output) {
7559             return output + "ViewGroup.LayoutParams={ width="
7560                     + sizeToString(width) + ", height=" + sizeToString(height) + " }";
7561         }
7562 
7563         /**
7564          * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
7565          *
7566          * @param view the view that contains these layout parameters
7567          * @param canvas the canvas on which to draw
7568          *
7569          * @hide
7570          */
onDebugDraw(View view, Canvas canvas, Paint paint)7571         public void onDebugDraw(View view, Canvas canvas, Paint paint) {
7572         }
7573 
7574         /**
7575          * Converts the specified size to a readable String.
7576          *
7577          * @param size the size to convert
7578          * @return a String instance representing the supplied size
7579          *
7580          * @hide
7581          */
sizeToString(int size)7582         protected static String sizeToString(int size) {
7583             if (size == WRAP_CONTENT) {
7584                 return "wrap-content";
7585             }
7586             if (size == MATCH_PARENT) {
7587                 return "match-parent";
7588             }
7589             return String.valueOf(size);
7590         }
7591 
7592         /** @hide */
encode(@onNull ViewHierarchyEncoder encoder)7593         void encode(@NonNull ViewHierarchyEncoder encoder) {
7594             encoder.beginObject(this);
7595             encodeProperties(encoder);
7596             encoder.endObject();
7597         }
7598 
7599         /** @hide */
encodeProperties(@onNull ViewHierarchyEncoder encoder)7600         protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
7601             encoder.addProperty("width", width);
7602             encoder.addProperty("height", height);
7603         }
7604     }
7605 
7606     /**
7607      * Per-child layout information for layouts that support margins.
7608      * See
7609      * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
7610      * for a list of all child view attributes that this class supports.
7611      *
7612      * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_margin
7613      * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginHorizontal
7614      * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginVertical
7615      * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
7616      * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
7617      * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
7618      * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
7619      * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7620      * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7621      */
7622     public static class MarginLayoutParams extends ViewGroup.LayoutParams {
7623         /**
7624          * The left margin in pixels of the child. Margin values should be positive.
7625          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7626          * to this field.
7627          */
7628         @ViewDebug.ExportedProperty(category = "layout")
7629         public int leftMargin;
7630 
7631         /**
7632          * The top margin in pixels of the child. Margin values should be positive.
7633          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7634          * to this field.
7635          */
7636         @ViewDebug.ExportedProperty(category = "layout")
7637         public int topMargin;
7638 
7639         /**
7640          * The right margin in pixels of the child. Margin values should be positive.
7641          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7642          * to this field.
7643          */
7644         @ViewDebug.ExportedProperty(category = "layout")
7645         public int rightMargin;
7646 
7647         /**
7648          * The bottom margin in pixels of the child. Margin values should be positive.
7649          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7650          * to this field.
7651          */
7652         @ViewDebug.ExportedProperty(category = "layout")
7653         public int bottomMargin;
7654 
7655         /**
7656          * The start margin in pixels of the child. Margin values should be positive.
7657          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7658          * to this field.
7659          */
7660         @ViewDebug.ExportedProperty(category = "layout")
7661         private int startMargin = DEFAULT_MARGIN_RELATIVE;
7662 
7663         /**
7664          * The end margin in pixels of the child. Margin values should be positive.
7665          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
7666          * to this field.
7667          */
7668         @ViewDebug.ExportedProperty(category = "layout")
7669         private int endMargin = DEFAULT_MARGIN_RELATIVE;
7670 
7671         /**
7672          * The default start and end margin.
7673          * @hide
7674          */
7675         public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
7676 
7677         /**
7678          * Bit  0: layout direction
7679          * Bit  1: layout direction
7680          * Bit  2: left margin undefined
7681          * Bit  3: right margin undefined
7682          * Bit  4: is RTL compatibility mode
7683          * Bit  5: need resolution
7684          *
7685          * Bit 6 to 7 not used
7686          *
7687          * @hide
7688          */
7689         @ViewDebug.ExportedProperty(category = "layout", flagMapping = {
7690                 @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK,
7691                         equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"),
7692                 @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK,
7693                         equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"),
7694                 @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK,
7695                         equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"),
7696                 @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK,
7697                         equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"),
7698                 @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK,
7699                         equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK")
7700         }, formatToHexString = true)
7701         byte mMarginFlags;
7702 
7703         private static final int LAYOUT_DIRECTION_MASK = 0x00000003;
7704         private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004;
7705         private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008;
7706         private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010;
7707         private static final int NEED_RESOLUTION_MASK = 0x00000020;
7708 
7709         private static final int DEFAULT_MARGIN_RESOLVED = 0;
7710         private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
7711 
7712         /**
7713          * Creates a new set of layout parameters. The values are extracted from
7714          * the supplied attributes set and context.
7715          *
7716          * @param c the application environment
7717          * @param attrs the set of attributes from which to extract the layout
7718          *              parameters' values
7719          */
MarginLayoutParams(Context c, AttributeSet attrs)7720         public MarginLayoutParams(Context c, AttributeSet attrs) {
7721             super();
7722 
7723             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
7724             setBaseAttributes(a,
7725                     R.styleable.ViewGroup_MarginLayout_layout_width,
7726                     R.styleable.ViewGroup_MarginLayout_layout_height);
7727 
7728             int margin = a.getDimensionPixelSize(
7729                     com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
7730             if (margin >= 0) {
7731                 leftMargin = margin;
7732                 topMargin = margin;
7733                 rightMargin= margin;
7734                 bottomMargin = margin;
7735             } else {
7736                 int horizontalMargin = a.getDimensionPixelSize(
7737                         R.styleable.ViewGroup_MarginLayout_layout_marginHorizontal, -1);
7738                 int verticalMargin = a.getDimensionPixelSize(
7739                         R.styleable.ViewGroup_MarginLayout_layout_marginVertical, -1);
7740 
7741                 if (horizontalMargin >= 0) {
7742                     leftMargin = horizontalMargin;
7743                     rightMargin = horizontalMargin;
7744                 } else {
7745                     leftMargin = a.getDimensionPixelSize(
7746                             R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
7747                             UNDEFINED_MARGIN);
7748                     if (leftMargin == UNDEFINED_MARGIN) {
7749                         mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
7750                         leftMargin = DEFAULT_MARGIN_RESOLVED;
7751                     }
7752                     rightMargin = a.getDimensionPixelSize(
7753                             R.styleable.ViewGroup_MarginLayout_layout_marginRight,
7754                             UNDEFINED_MARGIN);
7755                     if (rightMargin == UNDEFINED_MARGIN) {
7756                         mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
7757                         rightMargin = DEFAULT_MARGIN_RESOLVED;
7758                     }
7759                 }
7760 
7761                 startMargin = a.getDimensionPixelSize(
7762                         R.styleable.ViewGroup_MarginLayout_layout_marginStart,
7763                         DEFAULT_MARGIN_RELATIVE);
7764                 endMargin = a.getDimensionPixelSize(
7765                         R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
7766                         DEFAULT_MARGIN_RELATIVE);
7767 
7768                 if (verticalMargin >= 0) {
7769                     topMargin = verticalMargin;
7770                     bottomMargin = verticalMargin;
7771                 } else {
7772                     topMargin = a.getDimensionPixelSize(
7773                             R.styleable.ViewGroup_MarginLayout_layout_marginTop,
7774                             DEFAULT_MARGIN_RESOLVED);
7775                     bottomMargin = a.getDimensionPixelSize(
7776                             R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
7777                             DEFAULT_MARGIN_RESOLVED);
7778                 }
7779 
7780                 if (isMarginRelative()) {
7781                    mMarginFlags |= NEED_RESOLUTION_MASK;
7782                 }
7783             }
7784 
7785             final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
7786             final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
7787             if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
7788                 mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
7789             }
7790 
7791             // Layout direction is LTR by default
7792             mMarginFlags |= LAYOUT_DIRECTION_LTR;
7793 
7794             a.recycle();
7795         }
7796 
MarginLayoutParams(int width, int height)7797         public MarginLayoutParams(int width, int height) {
7798             super(width, height);
7799 
7800             mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
7801             mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
7802 
7803             mMarginFlags &= ~NEED_RESOLUTION_MASK;
7804             mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
7805         }
7806 
7807         /**
7808          * Copy constructor. Clones the width, height and margin values of the source.
7809          *
7810          * @param source The layout params to copy from.
7811          */
MarginLayoutParams(MarginLayoutParams source)7812         public MarginLayoutParams(MarginLayoutParams source) {
7813             this.width = source.width;
7814             this.height = source.height;
7815 
7816             this.leftMargin = source.leftMargin;
7817             this.topMargin = source.topMargin;
7818             this.rightMargin = source.rightMargin;
7819             this.bottomMargin = source.bottomMargin;
7820             this.startMargin = source.startMargin;
7821             this.endMargin = source.endMargin;
7822 
7823             this.mMarginFlags = source.mMarginFlags;
7824         }
7825 
MarginLayoutParams(LayoutParams source)7826         public MarginLayoutParams(LayoutParams source) {
7827             super(source);
7828 
7829             mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
7830             mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
7831 
7832             mMarginFlags &= ~NEED_RESOLUTION_MASK;
7833             mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
7834         }
7835 
7836         /**
7837          * @hide Used internally.
7838          */
copyMarginsFrom(MarginLayoutParams source)7839         public final void copyMarginsFrom(MarginLayoutParams source) {
7840             this.leftMargin = source.leftMargin;
7841             this.topMargin = source.topMargin;
7842             this.rightMargin = source.rightMargin;
7843             this.bottomMargin = source.bottomMargin;
7844             this.startMargin = source.startMargin;
7845             this.endMargin = source.endMargin;
7846 
7847             this.mMarginFlags = source.mMarginFlags;
7848         }
7849 
7850         /**
7851          * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
7852          * to be done so that the new margins are taken into account. Left and right margins may be
7853          * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
7854          * Margin values should be positive.
7855          *
7856          * @param left the left margin size
7857          * @param top the top margin size
7858          * @param right the right margin size
7859          * @param bottom the bottom margin size
7860          *
7861          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
7862          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
7863          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
7864          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
7865          */
setMargins(int left, int top, int right, int bottom)7866         public void setMargins(int left, int top, int right, int bottom) {
7867             leftMargin = left;
7868             topMargin = top;
7869             rightMargin = right;
7870             bottomMargin = bottom;
7871             mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK;
7872             mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK;
7873             if (isMarginRelative()) {
7874                 mMarginFlags |= NEED_RESOLUTION_MASK;
7875             } else {
7876                 mMarginFlags &= ~NEED_RESOLUTION_MASK;
7877             }
7878         }
7879 
7880         /**
7881          * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
7882          * needs to be done so that the new relative margins are taken into account. Left and right
7883          * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
7884          * direction. Margin values should be positive.
7885          *
7886          * @param start the start margin size
7887          * @param top the top margin size
7888          * @param end the right margin size
7889          * @param bottom the bottom margin size
7890          *
7891          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7892          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
7893          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7894          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
7895          *
7896          * @hide
7897          */
setMarginsRelative(int start, int top, int end, int bottom)7898         public void setMarginsRelative(int start, int top, int end, int bottom) {
7899             startMargin = start;
7900             topMargin = top;
7901             endMargin = end;
7902             bottomMargin = bottom;
7903             mMarginFlags |= NEED_RESOLUTION_MASK;
7904         }
7905 
7906         /**
7907          * Sets the relative start margin. Margin values should be positive.
7908          *
7909          * @param start the start margin size
7910          *
7911          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7912          */
setMarginStart(int start)7913         public void setMarginStart(int start) {
7914             startMargin = start;
7915             mMarginFlags |= NEED_RESOLUTION_MASK;
7916         }
7917 
7918         /**
7919          * Returns the start margin in pixels.
7920          *
7921          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7922          *
7923          * @return the start margin in pixels.
7924          */
getMarginStart()7925         public int getMarginStart() {
7926             if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
7927             if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
7928                 doResolveMargins();
7929             }
7930             switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
7931                 case View.LAYOUT_DIRECTION_RTL:
7932                     return rightMargin;
7933                 case View.LAYOUT_DIRECTION_LTR:
7934                 default:
7935                     return leftMargin;
7936             }
7937         }
7938 
7939         /**
7940          * Sets the relative end margin. Margin values should be positive.
7941          *
7942          * @param end the end margin size
7943          *
7944          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7945          */
setMarginEnd(int end)7946         public void setMarginEnd(int end) {
7947             endMargin = end;
7948             mMarginFlags |= NEED_RESOLUTION_MASK;
7949         }
7950 
7951         /**
7952          * Returns the end margin in pixels.
7953          *
7954          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7955          *
7956          * @return the end margin in pixels.
7957          */
getMarginEnd()7958         public int getMarginEnd() {
7959             if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
7960             if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
7961                 doResolveMargins();
7962             }
7963             switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
7964                 case View.LAYOUT_DIRECTION_RTL:
7965                     return leftMargin;
7966                 case View.LAYOUT_DIRECTION_LTR:
7967                 default:
7968                     return rightMargin;
7969             }
7970         }
7971 
7972         /**
7973          * Check if margins are relative.
7974          *
7975          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
7976          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
7977          *
7978          * @return true if either marginStart or marginEnd has been set.
7979          */
isMarginRelative()7980         public boolean isMarginRelative() {
7981             return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
7982         }
7983 
7984         /**
7985          * Set the layout direction
7986          * @param layoutDirection the layout direction.
7987          *        Should be either {@link View#LAYOUT_DIRECTION_LTR}
7988          *                     or {@link View#LAYOUT_DIRECTION_RTL}.
7989          */
setLayoutDirection(int layoutDirection)7990         public void setLayoutDirection(int layoutDirection) {
7991             if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
7992                     layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
7993             if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) {
7994                 mMarginFlags &= ~LAYOUT_DIRECTION_MASK;
7995                 mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK);
7996                 if (isMarginRelative()) {
7997                     mMarginFlags |= NEED_RESOLUTION_MASK;
7998                 } else {
7999                     mMarginFlags &= ~NEED_RESOLUTION_MASK;
8000                 }
8001             }
8002         }
8003 
8004         /**
8005          * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
8006          * {@link View#LAYOUT_DIRECTION_RTL}.
8007          *
8008          * @return the layout direction.
8009          */
getLayoutDirection()8010         public int getLayoutDirection() {
8011             return (mMarginFlags & LAYOUT_DIRECTION_MASK);
8012         }
8013 
8014         /**
8015          * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
8016          * may be overridden depending on layout direction.
8017          */
8018         @Override
resolveLayoutDirection(int layoutDirection)8019         public void resolveLayoutDirection(int layoutDirection) {
8020             setLayoutDirection(layoutDirection);
8021 
8022             // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
8023             // Will use the left and right margins if no relative margin is defined.
8024             if (!isMarginRelative() ||
8025                     (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return;
8026 
8027             // Proceed with resolution
8028             doResolveMargins();
8029         }
8030 
doResolveMargins()8031         private void doResolveMargins() {
8032             if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) {
8033                 // if left or right margins are not defined and if we have some start or end margin
8034                 // defined then use those start and end margins.
8035                 if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK
8036                         && startMargin > DEFAULT_MARGIN_RELATIVE) {
8037                     leftMargin = startMargin;
8038                 }
8039                 if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
8040                         && endMargin > DEFAULT_MARGIN_RELATIVE) {
8041                     rightMargin = endMargin;
8042                 }
8043             } else {
8044                 // We have some relative margins (either the start one or the end one or both). So use
8045                 // them and override what has been defined for left and right margins. If either start
8046                 // or end margin is not defined, just set it to default "0".
8047                 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
8048                     case View.LAYOUT_DIRECTION_RTL:
8049                         leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
8050                                 endMargin : DEFAULT_MARGIN_RESOLVED;
8051                         rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
8052                                 startMargin : DEFAULT_MARGIN_RESOLVED;
8053                         break;
8054                     case View.LAYOUT_DIRECTION_LTR:
8055                     default:
8056                         leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
8057                                 startMargin : DEFAULT_MARGIN_RESOLVED;
8058                         rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
8059                                 endMargin : DEFAULT_MARGIN_RESOLVED;
8060                         break;
8061                 }
8062             }
8063             mMarginFlags &= ~NEED_RESOLUTION_MASK;
8064         }
8065 
8066         /**
8067          * @hide
8068          */
isLayoutRtl()8069         public boolean isLayoutRtl() {
8070             return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
8071         }
8072 
8073         /**
8074          * @hide
8075          */
8076         @Override
onDebugDraw(View view, Canvas canvas, Paint paint)8077         public void onDebugDraw(View view, Canvas canvas, Paint paint) {
8078             Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
8079 
8080             fillDifference(canvas,
8081                     view.getLeft()   + oi.left,
8082                     view.getTop()    + oi.top,
8083                     view.getRight()  - oi.right,
8084                     view.getBottom() - oi.bottom,
8085                     leftMargin,
8086                     topMargin,
8087                     rightMargin,
8088                     bottomMargin,
8089                     paint);
8090         }
8091 
8092         /** @hide */
8093         @Override
encodeProperties(@onNull ViewHierarchyEncoder encoder)8094         protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
8095             super.encodeProperties(encoder);
8096             encoder.addProperty("leftMargin", leftMargin);
8097             encoder.addProperty("topMargin", topMargin);
8098             encoder.addProperty("rightMargin", rightMargin);
8099             encoder.addProperty("bottomMargin", bottomMargin);
8100             encoder.addProperty("startMargin", startMargin);
8101             encoder.addProperty("endMargin", endMargin);
8102         }
8103     }
8104 
8105     /* Describes a touched view and the ids of the pointers that it has captured.
8106      *
8107      * This code assumes that pointer ids are always in the range 0..31 such that
8108      * it can use a bitfield to track which pointer ids are present.
8109      * As it happens, the lower layers of the input dispatch pipeline also use the
8110      * same trick so the assumption should be safe here...
8111      */
8112     private static final class TouchTarget {
8113         private static final int MAX_RECYCLED = 32;
8114         private static final Object sRecycleLock = new Object[0];
8115         private static TouchTarget sRecycleBin;
8116         private static int sRecycledCount;
8117 
8118         public static final int ALL_POINTER_IDS = -1; // all ones
8119 
8120         // The touched child view.
8121         public View child;
8122 
8123         // The combined bit mask of pointer ids for all pointers captured by the target.
8124         public int pointerIdBits;
8125 
8126         // The next target in the target list.
8127         public TouchTarget next;
8128 
TouchTarget()8129         private TouchTarget() {
8130         }
8131 
obtain(@onNull View child, int pointerIdBits)8132         public static TouchTarget obtain(@NonNull View child, int pointerIdBits) {
8133             if (child == null) {
8134                 throw new IllegalArgumentException("child must be non-null");
8135             }
8136 
8137             final TouchTarget target;
8138             synchronized (sRecycleLock) {
8139                 if (sRecycleBin == null) {
8140                     target = new TouchTarget();
8141                 } else {
8142                     target = sRecycleBin;
8143                     sRecycleBin = target.next;
8144                      sRecycledCount--;
8145                     target.next = null;
8146                 }
8147             }
8148             target.child = child;
8149             target.pointerIdBits = pointerIdBits;
8150             return target;
8151         }
8152 
recycle()8153         public void recycle() {
8154             if (child == null) {
8155                 throw new IllegalStateException("already recycled once");
8156             }
8157 
8158             synchronized (sRecycleLock) {
8159                 if (sRecycledCount < MAX_RECYCLED) {
8160                     next = sRecycleBin;
8161                     sRecycleBin = this;
8162                     sRecycledCount += 1;
8163                 } else {
8164                     next = null;
8165                 }
8166                 child = null;
8167             }
8168         }
8169     }
8170 
8171     /* Describes a hovered view. */
8172     private static final class HoverTarget {
8173         private static final int MAX_RECYCLED = 32;
8174         private static final Object sRecycleLock = new Object[0];
8175         private static HoverTarget sRecycleBin;
8176         private static int sRecycledCount;
8177 
8178         // The hovered child view.
8179         public View child;
8180 
8181         // The next target in the target list.
8182         public HoverTarget next;
8183 
HoverTarget()8184         private HoverTarget() {
8185         }
8186 
obtain(@onNull View child)8187         public static HoverTarget obtain(@NonNull View child) {
8188             if (child == null) {
8189                 throw new IllegalArgumentException("child must be non-null");
8190             }
8191 
8192             final HoverTarget target;
8193             synchronized (sRecycleLock) {
8194                 if (sRecycleBin == null) {
8195                     target = new HoverTarget();
8196                 } else {
8197                     target = sRecycleBin;
8198                     sRecycleBin = target.next;
8199                     sRecycledCount--;
8200                     target.next = null;
8201                 }
8202             }
8203             target.child = child;
8204             return target;
8205         }
8206 
recycle()8207         public void recycle() {
8208             if (child == null) {
8209                 throw new IllegalStateException("already recycled once");
8210             }
8211 
8212             synchronized (sRecycleLock) {
8213                 if (sRecycledCount < MAX_RECYCLED) {
8214                     next = sRecycleBin;
8215                     sRecycleBin = this;
8216                     sRecycledCount += 1;
8217                 } else {
8218                     next = null;
8219                 }
8220                 child = null;
8221             }
8222         }
8223     }
8224 
8225     /**
8226      * Pooled class that to hold the children for autifill.
8227      */
8228     static class ChildListForAutoFill extends ArrayList<View> {
8229         private static final int MAX_POOL_SIZE = 32;
8230 
8231         private static final Pools.SimplePool<ChildListForAutoFill> sPool =
8232                 new Pools.SimplePool<>(MAX_POOL_SIZE);
8233 
obtain()8234         public static ChildListForAutoFill obtain() {
8235             ChildListForAutoFill list = sPool.acquire();
8236             if (list == null) {
8237                 list = new ChildListForAutoFill();
8238             }
8239             return list;
8240         }
8241 
recycle()8242         public void recycle() {
8243             clear();
8244             sPool.release(this);
8245         }
8246     }
8247 
8248     /**
8249      * Pooled class that orderes the children of a ViewGroup from start
8250      * to end based on how they are laid out and the layout direction.
8251      */
8252     static class ChildListForAccessibility {
8253 
8254         private static final int MAX_POOL_SIZE = 32;
8255 
8256         private static final SynchronizedPool<ChildListForAccessibility> sPool =
8257                 new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE);
8258 
8259         private final ArrayList<View> mChildren = new ArrayList<View>();
8260 
8261         private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
8262 
obtain(ViewGroup parent, boolean sort)8263         public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
8264             ChildListForAccessibility list = sPool.acquire();
8265             if (list == null) {
8266                 list = new ChildListForAccessibility();
8267             }
8268             list.init(parent, sort);
8269             return list;
8270         }
8271 
recycle()8272         public void recycle() {
8273             clear();
8274             sPool.release(this);
8275         }
8276 
getChildCount()8277         public int getChildCount() {
8278             return mChildren.size();
8279         }
8280 
getChildAt(int index)8281         public View getChildAt(int index) {
8282             return mChildren.get(index);
8283         }
8284 
init(ViewGroup parent, boolean sort)8285         private void init(ViewGroup parent, boolean sort) {
8286             ArrayList<View> children = mChildren;
8287             final int childCount = parent.getChildCount();
8288             for (int i = 0; i < childCount; i++) {
8289                 View child = parent.getChildAt(i);
8290                 children.add(child);
8291             }
8292             if (sort) {
8293                 ArrayList<ViewLocationHolder> holders = mHolders;
8294                 for (int i = 0; i < childCount; i++) {
8295                     View child = children.get(i);
8296                     ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
8297                     holders.add(holder);
8298                 }
8299                 sort(holders);
8300                 for (int i = 0; i < childCount; i++) {
8301                     ViewLocationHolder holder = holders.get(i);
8302                     children.set(i, holder.mView);
8303                     holder.recycle();
8304                 }
8305                 holders.clear();
8306             }
8307         }
8308 
sort(ArrayList<ViewLocationHolder> holders)8309         private void sort(ArrayList<ViewLocationHolder> holders) {
8310             // This is gross but the least risky solution. The current comparison
8311             // strategy breaks transitivity but produces very good results. Coming
8312             // up with a new strategy requires time which we do not have, so ...
8313             try {
8314                 ViewLocationHolder.setComparisonStrategy(
8315                         ViewLocationHolder.COMPARISON_STRATEGY_STRIPE);
8316                 Collections.sort(holders);
8317             } catch (IllegalArgumentException iae) {
8318                 // Note that in practice this occurs extremely rarely in a couple
8319                 // of pathological cases.
8320                 ViewLocationHolder.setComparisonStrategy(
8321                         ViewLocationHolder.COMPARISON_STRATEGY_LOCATION);
8322                 Collections.sort(holders);
8323             }
8324         }
8325 
clear()8326         private void clear() {
8327             mChildren.clear();
8328         }
8329     }
8330 
8331     /**
8332      * Pooled class that holds a View and its location with respect to
8333      * a specified root. This enables sorting of views based on their
8334      * coordinates without recomputing the position relative to the root
8335      * on every comparison.
8336      */
8337     static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
8338 
8339         private static final int MAX_POOL_SIZE = 32;
8340 
8341         private static final SynchronizedPool<ViewLocationHolder> sPool =
8342                 new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE);
8343 
8344         public static final int COMPARISON_STRATEGY_STRIPE = 1;
8345 
8346         public static final int COMPARISON_STRATEGY_LOCATION = 2;
8347 
8348         private static int sComparisonStrategy = COMPARISON_STRATEGY_STRIPE;
8349 
8350         private final Rect mLocation = new Rect();
8351 
8352         public View mView;
8353 
8354         private int mLayoutDirection;
8355 
obtain(ViewGroup root, View view)8356         public static ViewLocationHolder obtain(ViewGroup root, View view) {
8357             ViewLocationHolder holder = sPool.acquire();
8358             if (holder == null) {
8359                 holder = new ViewLocationHolder();
8360             }
8361             holder.init(root, view);
8362             return holder;
8363         }
8364 
setComparisonStrategy(int strategy)8365         public static void setComparisonStrategy(int strategy) {
8366             sComparisonStrategy = strategy;
8367         }
8368 
recycle()8369         public void recycle() {
8370             clear();
8371             sPool.release(this);
8372         }
8373 
8374         @Override
compareTo(ViewLocationHolder another)8375         public int compareTo(ViewLocationHolder another) {
8376             // This instance is greater than an invalid argument.
8377             if (another == null) {
8378                 return 1;
8379             }
8380 
8381             if (sComparisonStrategy == COMPARISON_STRATEGY_STRIPE) {
8382                 // First is above second.
8383                 if (mLocation.bottom - another.mLocation.top <= 0) {
8384                     return -1;
8385                 }
8386                 // First is below second.
8387                 if (mLocation.top - another.mLocation.bottom >= 0) {
8388                     return 1;
8389                 }
8390             }
8391 
8392             // We are ordering left-to-right, top-to-bottom.
8393             if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
8394                 final int leftDifference = mLocation.left - another.mLocation.left;
8395                 if (leftDifference != 0) {
8396                     return leftDifference;
8397                 }
8398             } else { // RTL
8399                 final int rightDifference = mLocation.right - another.mLocation.right;
8400                 if (rightDifference != 0) {
8401                     return -rightDifference;
8402                 }
8403             }
8404             // We are ordering left-to-right, top-to-bottom.
8405             final int topDifference = mLocation.top - another.mLocation.top;
8406             if (topDifference != 0) {
8407                 return topDifference;
8408             }
8409             // Break tie by height.
8410             final int heightDiference = mLocation.height() - another.mLocation.height();
8411             if (heightDiference != 0) {
8412                 return -heightDiference;
8413             }
8414             // Break tie by width.
8415             final int widthDiference = mLocation.width() - another.mLocation.width();
8416             if (widthDiference != 0) {
8417                 return -widthDiference;
8418             }
8419             // Just break the tie somehow. The accessibliity ids are unique
8420             // and stable, hence this is deterministic tie breaking.
8421             return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId();
8422         }
8423 
init(ViewGroup root, View view)8424         private void init(ViewGroup root, View view) {
8425             Rect viewLocation = mLocation;
8426             view.getDrawingRect(viewLocation);
8427             root.offsetDescendantRectToMyCoords(view, viewLocation);
8428             mView = view;
8429             mLayoutDirection = root.getLayoutDirection();
8430         }
8431 
clear()8432         private void clear() {
8433             mView = null;
8434             mLocation.set(0, 0, 0, 0);
8435         }
8436     }
8437 
drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2)8438     private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
8439         if (sDebugLines== null) {
8440             // TODO: This won't work with multiple UI threads in a single process
8441             sDebugLines = new float[16];
8442         }
8443 
8444         sDebugLines[0] = x1;
8445         sDebugLines[1] = y1;
8446         sDebugLines[2] = x2;
8447         sDebugLines[3] = y1;
8448 
8449         sDebugLines[4] = x2;
8450         sDebugLines[5] = y1;
8451         sDebugLines[6] = x2;
8452         sDebugLines[7] = y2;
8453 
8454         sDebugLines[8] = x2;
8455         sDebugLines[9] = y2;
8456         sDebugLines[10] = x1;
8457         sDebugLines[11] = y2;
8458 
8459         sDebugLines[12] = x1;
8460         sDebugLines[13] = y2;
8461         sDebugLines[14] = x1;
8462         sDebugLines[15] = y1;
8463 
8464         canvas.drawLines(sDebugLines, paint);
8465     }
8466 
8467     /** @hide */
8468     @Override
encodeProperties(@onNull ViewHierarchyEncoder encoder)8469     protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
8470         super.encodeProperties(encoder);
8471 
8472         encoder.addProperty("focus:descendantFocusability", getDescendantFocusability());
8473         encoder.addProperty("drawing:clipChildren", getClipChildren());
8474         encoder.addProperty("drawing:clipToPadding", getClipToPadding());
8475         encoder.addProperty("drawing:childrenDrawingOrderEnabled", isChildrenDrawingOrderEnabled());
8476         encoder.addProperty("drawing:persistentDrawingCache", getPersistentDrawingCache());
8477 
8478         int n = getChildCount();
8479         encoder.addProperty("meta:__childCount__", (short)n);
8480         for (int i = 0; i < n; i++) {
8481             encoder.addPropertyKey("meta:__child__" + i);
8482             getChildAt(i).encode(encoder);
8483         }
8484     }
8485 }
8486