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