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