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