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