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