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