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