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