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