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