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