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