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