ViewGroup.java revision 0d200833fdc53f9796698f97c18cefc4a1b82df2
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 com.android.internal.R;
21
22import android.content.Context;
23import android.content.res.Configuration;
24import android.content.res.TypedArray;
25import android.graphics.Bitmap;
26import android.graphics.Canvas;
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.Parcelable;
34import android.os.SystemClock;
35import android.util.AttributeSet;
36import android.util.Log;
37import android.util.SparseArray;
38import android.view.accessibility.AccessibilityEvent;
39import android.view.animation.Animation;
40import android.view.animation.AnimationUtils;
41import android.view.animation.LayoutAnimationController;
42import android.view.animation.Transformation;
43
44import java.util.ArrayList;
45
46/**
47 * <p>
48 * A <code>ViewGroup</code> is a special view that can contain other views
49 * (called children.) The view group is the base class for layouts and views
50 * containers. This class also defines the
51 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
52 * class for layouts parameters.
53 * </p>
54 *
55 * <p>
56 * Also see {@link LayoutParams} for layout attributes.
57 * </p>
58 *
59 * @attr ref android.R.styleable#ViewGroup_clipChildren
60 * @attr ref android.R.styleable#ViewGroup_clipToPadding
61 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
62 * @attr ref android.R.styleable#ViewGroup_animationCache
63 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
64 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
65 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
66 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
67 * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
68 */
69public abstract class ViewGroup extends View implements ViewParent, ViewManager {
70
71    private static final boolean DBG = false;
72
73    /**
74     * Views which have been hidden or removed which need to be animated on
75     * their way out.
76     * This field should be made private, so it is hidden from the SDK.
77     * {@hide}
78     */
79    protected ArrayList<View> mDisappearingChildren;
80
81    /**
82     * Listener used to propagate events indicating when children are added
83     * and/or removed from a view group.
84     * This field should be made private, so it is hidden from the SDK.
85     * {@hide}
86     */
87    protected OnHierarchyChangeListener mOnHierarchyChangeListener;
88
89    // The view contained within this ViewGroup that has or contains focus.
90    private View mFocused;
91
92    /**
93     * A Transformation used when drawing children, to
94     * apply on the child being drawn.
95     */
96    private final Transformation mChildTransformation = new Transformation();
97
98    /**
99     * Used to track the current invalidation region.
100     */
101    private RectF mInvalidateRegion;
102
103    /**
104     * A Transformation used to calculate a correct
105     * invalidation area when the application is autoscaled.
106     */
107    private Transformation mInvalidationTransformation;
108
109    // View currently under an ongoing drag
110    private View mCurrentDragView;
111
112    // Does this group have a child that can accept the current drag payload?
113    private boolean mChildAcceptsDrag;
114
115    // Used during drag dispatch
116    private final PointF mLocalPoint = new PointF();
117
118    // Layout animation
119    private LayoutAnimationController mLayoutAnimationController;
120    private Animation.AnimationListener mAnimationListener;
121
122    // First touch target in the linked list of touch targets.
123    private TouchTarget mFirstTouchTarget;
124
125    // Temporary arrays for splitting pointers.
126    private int[] mTmpPointerIndexMap;
127    private int[] mTmpPointerIds;
128    private MotionEvent.PointerCoords[] mTmpPointerCoords;
129
130    /**
131     * Internal flags.
132     *
133     * This field should be made private, so it is hidden from the SDK.
134     * {@hide}
135     */
136    protected int mGroupFlags;
137
138    // When set, ViewGroup invalidates only the child's rectangle
139    // Set by default
140    private static final int FLAG_CLIP_CHILDREN = 0x1;
141
142    // When set, ViewGroup excludes the padding area from the invalidate rectangle
143    // Set by default
144    private static final int FLAG_CLIP_TO_PADDING = 0x2;
145
146    // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
147    // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
148    private static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
149
150    // When set, dispatchDraw() will run the layout animation and unset the flag
151    private static final int FLAG_RUN_ANIMATION = 0x8;
152
153    // When set, there is either no layout animation on the ViewGroup or the layout
154    // animation is over
155    // Set by default
156    private static final int FLAG_ANIMATION_DONE = 0x10;
157
158    // If set, this ViewGroup has padding; if unset there is no padding and we don't need
159    // to clip it, even if FLAG_CLIP_TO_PADDING is set
160    private static final int FLAG_PADDING_NOT_NULL = 0x20;
161
162    // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
163    // Set by default
164    private static final int FLAG_ANIMATION_CACHE = 0x40;
165
166    // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
167    // layout animation; this avoid clobbering the hierarchy
168    // Automatically set when the layout animation starts, depending on the animation's
169    // characteristics
170    private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
171
172    // When set, the next call to drawChild() will clear mChildTransformation's matrix
173    private static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
174
175    // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
176    // the children's Bitmap caches if necessary
177    // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
178    private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
179
180    /**
181     * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
182     * to get the index of the child to draw for that iteration.
183     *
184     * @hide
185     */
186    protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
187
188    /**
189     * When set, this ViewGroup supports static transformations on children; this causes
190     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
191     * invoked when a child is drawn.
192     *
193     * Any subclass overriding
194     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
195     * set this flags in {@link #mGroupFlags}.
196     *
197     * {@hide}
198     */
199    protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
200
201    // When the previous drawChild() invocation used an alpha value that was lower than
202    // 1.0 and set it in mCachePaint
203    private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
204
205    /**
206     * When set, this ViewGroup's drawable states also include those
207     * of its children.
208     */
209    private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
210
211    /**
212     * When set, this ViewGroup tries to always draw its children using their drawing cache.
213     */
214    private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
215
216    /**
217     * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
218     * draw its children with their drawing cache.
219     */
220    private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
221
222    /**
223     * When set, this group will go through its list of children to notify them of
224     * any drawable state change.
225     */
226    private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
227
228    private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
229
230    /**
231     * This view will get focus before any of its descendants.
232     */
233    public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
234
235    /**
236     * This view will get focus only if none of its descendants want it.
237     */
238    public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
239
240    /**
241     * This view will block any of its descendants from getting focus, even
242     * if they are focusable.
243     */
244    public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
245
246    /**
247     * Used to map between enum in attrubutes and flag values.
248     */
249    private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
250            {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
251                    FOCUS_BLOCK_DESCENDANTS};
252
253    /**
254     * When set, this ViewGroup should not intercept touch events.
255     * {@hide}
256     */
257    protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
258
259    /**
260     * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
261     */
262    private static final int FLAG_SPLIT_MOTION_EVENTS = 0x200000;
263
264    /**
265     * Indicates which types of drawing caches are to be kept in memory.
266     * This field should be made private, so it is hidden from the SDK.
267     * {@hide}
268     */
269    protected int mPersistentDrawingCache;
270
271    /**
272     * Used to indicate that no drawing cache should be kept in memory.
273     */
274    public static final int PERSISTENT_NO_CACHE = 0x0;
275
276    /**
277     * Used to indicate that the animation drawing cache should be kept in memory.
278     */
279    public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
280
281    /**
282     * Used to indicate that the scrolling drawing cache should be kept in memory.
283     */
284    public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
285
286    /**
287     * Used to indicate that all drawing caches should be kept in memory.
288     */
289    public static final int PERSISTENT_ALL_CACHES = 0x3;
290
291    /**
292     * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
293     * are set at the same time.
294     */
295    protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
296
297    // Index of the child's left position in the mLocation array
298    private static final int CHILD_LEFT_INDEX = 0;
299    // Index of the child's top position in the mLocation array
300    private static final int CHILD_TOP_INDEX = 1;
301
302    // Child views of this ViewGroup
303    private View[] mChildren;
304    // Number of valid children in the mChildren array, the rest should be null or not
305    // considered as children
306    private int mChildrenCount;
307
308    private static final int ARRAY_INITIAL_CAPACITY = 12;
309    private static final int ARRAY_CAPACITY_INCREMENT = 12;
310
311    // Used to draw cached views
312    private final Paint mCachePaint = new Paint();
313
314    // Used to animate add/remove changes in layout
315    private LayoutTransition mTransition;
316
317    // The set of views that are currently being transitioned. This list is used to track views
318    // being removed that should not actually be removed from the parent yet because they are
319    // being animated.
320    private ArrayList<View> mTransitioningViews;
321
322    // List of children changing visibility. This is used to potentially keep rendering
323    // views during a transition when they otherwise would have become gone/invisible
324    private ArrayList<View> mVisibilityChangingChildren;
325
326    public ViewGroup(Context context) {
327        super(context);
328        initViewGroup();
329    }
330
331    public ViewGroup(Context context, AttributeSet attrs) {
332        super(context, attrs);
333        initViewGroup();
334        initFromAttributes(context, attrs);
335    }
336
337    public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
338        super(context, attrs, defStyle);
339        initViewGroup();
340        initFromAttributes(context, attrs);
341    }
342
343    private void initViewGroup() {
344        // ViewGroup doesn't draw by default
345        setFlags(WILL_NOT_DRAW, DRAW_MASK);
346        mGroupFlags |= FLAG_CLIP_CHILDREN;
347        mGroupFlags |= FLAG_CLIP_TO_PADDING;
348        mGroupFlags |= FLAG_ANIMATION_DONE;
349        mGroupFlags |= FLAG_ANIMATION_CACHE;
350        mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
351
352        setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
353
354        mChildren = new View[ARRAY_INITIAL_CAPACITY];
355        mChildrenCount = 0;
356
357        mCachePaint.setDither(false);
358
359        mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
360    }
361
362    private void initFromAttributes(Context context, AttributeSet attrs) {
363        TypedArray a = context.obtainStyledAttributes(attrs,
364                R.styleable.ViewGroup);
365
366        final int N = a.getIndexCount();
367        for (int i = 0; i < N; i++) {
368            int attr = a.getIndex(i);
369            switch (attr) {
370                case R.styleable.ViewGroup_clipChildren:
371                    setClipChildren(a.getBoolean(attr, true));
372                    break;
373                case R.styleable.ViewGroup_clipToPadding:
374                    setClipToPadding(a.getBoolean(attr, true));
375                    break;
376                case R.styleable.ViewGroup_animationCache:
377                    setAnimationCacheEnabled(a.getBoolean(attr, true));
378                    break;
379                case R.styleable.ViewGroup_persistentDrawingCache:
380                    setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
381                    break;
382                case R.styleable.ViewGroup_addStatesFromChildren:
383                    setAddStatesFromChildren(a.getBoolean(attr, false));
384                    break;
385                case R.styleable.ViewGroup_alwaysDrawnWithCache:
386                    setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
387                    break;
388                case R.styleable.ViewGroup_layoutAnimation:
389                    int id = a.getResourceId(attr, -1);
390                    if (id > 0) {
391                        setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
392                    }
393                    break;
394                case R.styleable.ViewGroup_descendantFocusability:
395                    setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
396                    break;
397                case R.styleable.ViewGroup_splitMotionEvents:
398                    setMotionEventSplittingEnabled(a.getBoolean(attr, false));
399                    break;
400                case R.styleable.ViewGroup_animateLayoutChanges:
401                    boolean animateLayoutChanges = a.getBoolean(attr, false);
402                    if (animateLayoutChanges) {
403                        setLayoutTransition(new LayoutTransition());
404                    }
405                    break;
406            }
407        }
408
409        a.recycle();
410    }
411
412    /**
413     * Gets the descendant focusability of this view group.  The descendant
414     * focusability defines the relationship between this view group and its
415     * descendants when looking for a view to take focus in
416     * {@link #requestFocus(int, android.graphics.Rect)}.
417     *
418     * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
419     *   {@link #FOCUS_BLOCK_DESCENDANTS}.
420     */
421    @ViewDebug.ExportedProperty(category = "focus", mapping = {
422        @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
423        @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
424        @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
425    })
426    public int getDescendantFocusability() {
427        return mGroupFlags & FLAG_MASK_FOCUSABILITY;
428    }
429
430    /**
431     * Set the descendant focusability of this view group. This defines the relationship
432     * between this view group and its descendants when looking for a view to
433     * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
434     *
435     * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
436     *   {@link #FOCUS_BLOCK_DESCENDANTS}.
437     */
438    public void setDescendantFocusability(int focusability) {
439        switch (focusability) {
440            case FOCUS_BEFORE_DESCENDANTS:
441            case FOCUS_AFTER_DESCENDANTS:
442            case FOCUS_BLOCK_DESCENDANTS:
443                break;
444            default:
445                throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
446                        + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
447        }
448        mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
449        mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
450    }
451
452    /**
453     * {@inheritDoc}
454     */
455    @Override
456    void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
457        if (mFocused != null) {
458            mFocused.unFocus();
459            mFocused = null;
460        }
461        super.handleFocusGainInternal(direction, previouslyFocusedRect);
462    }
463
464    /**
465     * {@inheritDoc}
466     */
467    public void requestChildFocus(View child, View focused) {
468        if (DBG) {
469            System.out.println(this + " requestChildFocus()");
470        }
471        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
472            return;
473        }
474
475        // Unfocus us, if necessary
476        super.unFocus();
477
478        // We had a previous notion of who had focus. Clear it.
479        if (mFocused != child) {
480            if (mFocused != null) {
481                mFocused.unFocus();
482            }
483
484            mFocused = child;
485        }
486        if (mParent != null) {
487            mParent.requestChildFocus(this, focused);
488        }
489    }
490
491    /**
492     * {@inheritDoc}
493     */
494    public void focusableViewAvailable(View v) {
495        if (mParent != null
496                // shortcut: don't report a new focusable view if we block our descendants from
497                // getting focus
498                && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
499                // shortcut: don't report a new focusable view if we already are focused
500                // (and we don't prefer our descendants)
501                //
502                // note: knowing that mFocused is non-null is not a good enough reason
503                // to break the traversal since in that case we'd actually have to find
504                // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
505                // an ancestor of v; this will get checked for at ViewRoot
506                && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
507            mParent.focusableViewAvailable(v);
508        }
509    }
510
511    /**
512     * {@inheritDoc}
513     */
514    public boolean showContextMenuForChild(View originalView) {
515        return mParent != null && mParent.showContextMenuForChild(originalView);
516    }
517
518    /**
519     * {@inheritDoc}
520     */
521    public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
522        return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
523    }
524
525    /**
526     * Find the nearest view in the specified direction that wants to take
527     * focus.
528     *
529     * @param focused The view that currently has focus
530     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
531     *        FOCUS_RIGHT, or 0 for not applicable.
532     */
533    public View focusSearch(View focused, int direction) {
534        if (isRootNamespace()) {
535            // root namespace means we should consider ourselves the top of the
536            // tree for focus searching; otherwise we could be focus searching
537            // into other tabs.  see LocalActivityManager and TabHost for more info
538            return FocusFinder.getInstance().findNextFocus(this, focused, direction);
539        } else if (mParent != null) {
540            return mParent.focusSearch(focused, direction);
541        }
542        return null;
543    }
544
545    /**
546     * {@inheritDoc}
547     */
548    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
549        return false;
550    }
551
552    /**
553     * {@inheritDoc}
554     */
555    @Override
556    public boolean dispatchUnhandledMove(View focused, int direction) {
557        return mFocused != null &&
558                mFocused.dispatchUnhandledMove(focused, direction);
559    }
560
561    /**
562     * {@inheritDoc}
563     */
564    public void clearChildFocus(View child) {
565        if (DBG) {
566            System.out.println(this + " clearChildFocus()");
567        }
568
569        mFocused = null;
570        if (mParent != null) {
571            mParent.clearChildFocus(this);
572        }
573    }
574
575    /**
576     * {@inheritDoc}
577     */
578    @Override
579    public void clearFocus() {
580        super.clearFocus();
581
582        // clear any child focus if it exists
583        if (mFocused != null) {
584            mFocused.clearFocus();
585        }
586    }
587
588    /**
589     * {@inheritDoc}
590     */
591    @Override
592    void unFocus() {
593        if (DBG) {
594            System.out.println(this + " unFocus()");
595        }
596
597        super.unFocus();
598        if (mFocused != null) {
599            mFocused.unFocus();
600        }
601        mFocused = null;
602    }
603
604    /**
605     * Returns the focused child of this view, if any. The child may have focus
606     * or contain focus.
607     *
608     * @return the focused child or null.
609     */
610    public View getFocusedChild() {
611        return mFocused;
612    }
613
614    /**
615     * Returns true if this view has or contains focus
616     *
617     * @return true if this view has or contains focus
618     */
619    @Override
620    public boolean hasFocus() {
621        return (mPrivateFlags & FOCUSED) != 0 || mFocused != null;
622    }
623
624    /*
625     * (non-Javadoc)
626     *
627     * @see android.view.View#findFocus()
628     */
629    @Override
630    public View findFocus() {
631        if (DBG) {
632            System.out.println("Find focus in " + this + ": flags="
633                    + isFocused() + ", child=" + mFocused);
634        }
635
636        if (isFocused()) {
637            return this;
638        }
639
640        if (mFocused != null) {
641            return mFocused.findFocus();
642        }
643        return null;
644    }
645
646    /**
647     * {@inheritDoc}
648     */
649    @Override
650    public boolean hasFocusable() {
651        if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
652            return false;
653        }
654
655        if (isFocusable()) {
656            return true;
657        }
658
659        final int descendantFocusability = getDescendantFocusability();
660        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
661            final int count = mChildrenCount;
662            final View[] children = mChildren;
663
664            for (int i = 0; i < count; i++) {
665                final View child = children[i];
666                if (child.hasFocusable()) {
667                    return true;
668                }
669            }
670        }
671
672        return false;
673    }
674
675    /**
676     * {@inheritDoc}
677     */
678    @Override
679    public void addFocusables(ArrayList<View> views, int direction) {
680        addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
681    }
682
683    /**
684     * {@inheritDoc}
685     */
686    @Override
687    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
688        final int focusableCount = views.size();
689
690        final int descendantFocusability = getDescendantFocusability();
691
692        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
693            final int count = mChildrenCount;
694            final View[] children = mChildren;
695
696            for (int i = 0; i < count; i++) {
697                final View child = children[i];
698                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
699                    child.addFocusables(views, direction, focusableMode);
700                }
701            }
702        }
703
704        // we add ourselves (if focusable) in all cases except for when we are
705        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
706        // to avoid the focus search finding layouts when a more precise search
707        // among the focusable children would be more interesting.
708        if (
709            descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
710                // No focusable descendants
711                (focusableCount == views.size())) {
712            super.addFocusables(views, direction, focusableMode);
713        }
714    }
715
716    /**
717     * {@inheritDoc}
718     */
719    @Override
720    public void dispatchWindowFocusChanged(boolean hasFocus) {
721        super.dispatchWindowFocusChanged(hasFocus);
722        final int count = mChildrenCount;
723        final View[] children = mChildren;
724        for (int i = 0; i < count; i++) {
725            children[i].dispatchWindowFocusChanged(hasFocus);
726        }
727    }
728
729    /**
730     * {@inheritDoc}
731     */
732    @Override
733    public void addTouchables(ArrayList<View> views) {
734        super.addTouchables(views);
735
736        final int count = mChildrenCount;
737        final View[] children = mChildren;
738
739        for (int i = 0; i < count; i++) {
740            final View child = children[i];
741            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
742                child.addTouchables(views);
743            }
744        }
745    }
746
747    /**
748     * {@inheritDoc}
749     */
750    @Override
751    public void dispatchDisplayHint(int hint) {
752        super.dispatchDisplayHint(hint);
753        final int count = mChildrenCount;
754        final View[] children = mChildren;
755        for (int i = 0; i < count; i++) {
756            children[i].dispatchDisplayHint(hint);
757        }
758    }
759
760    /**
761     * @hide
762     * @param child
763     * @param visibility
764     */
765    void onChildVisibilityChanged(View child, int visibility) {
766        if (mTransition != null) {
767            if (visibility == VISIBLE) {
768                mTransition.showChild(this, child);
769            } else {
770                mTransition.hideChild(this, child);
771            }
772            if (visibility != VISIBLE) {
773                // Only track this on disappearing views - appearing views are already visible
774                // and don't need special handling during drawChild()
775                if (mVisibilityChangingChildren == null) {
776                    mVisibilityChangingChildren = new ArrayList<View>();
777                }
778                mVisibilityChangingChildren.add(child);
779                if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
780                    addDisappearingView(child);
781                }
782            }
783        }
784    }
785
786    /**
787     * {@inheritDoc}
788     */
789    @Override
790    protected void dispatchVisibilityChanged(View changedView, int visibility) {
791        super.dispatchVisibilityChanged(changedView, visibility);
792        final int count = mChildrenCount;
793        final View[] children = mChildren;
794        for (int i = 0; i < count; i++) {
795            children[i].dispatchVisibilityChanged(changedView, visibility);
796        }
797    }
798
799    /**
800     * {@inheritDoc}
801     */
802    @Override
803    public void dispatchWindowVisibilityChanged(int visibility) {
804        super.dispatchWindowVisibilityChanged(visibility);
805        final int count = mChildrenCount;
806        final View[] children = mChildren;
807        for (int i = 0; i < count; i++) {
808            children[i].dispatchWindowVisibilityChanged(visibility);
809        }
810    }
811
812    /**
813     * {@inheritDoc}
814     */
815    @Override
816    public void dispatchConfigurationChanged(Configuration newConfig) {
817        super.dispatchConfigurationChanged(newConfig);
818        final int count = mChildrenCount;
819        final View[] children = mChildren;
820        for (int i = 0; i < count; i++) {
821            children[i].dispatchConfigurationChanged(newConfig);
822        }
823    }
824
825    /**
826     * {@inheritDoc}
827     */
828    public void recomputeViewAttributes(View child) {
829        ViewParent parent = mParent;
830        if (parent != null) parent.recomputeViewAttributes(this);
831    }
832
833    @Override
834    void dispatchCollectViewAttributes(int visibility) {
835        visibility |= mViewFlags&VISIBILITY_MASK;
836        super.dispatchCollectViewAttributes(visibility);
837        final int count = mChildrenCount;
838        final View[] children = mChildren;
839        for (int i = 0; i < count; i++) {
840            children[i].dispatchCollectViewAttributes(visibility);
841        }
842    }
843
844    /**
845     * {@inheritDoc}
846     */
847    public void bringChildToFront(View child) {
848        int index = indexOfChild(child);
849        if (index >= 0) {
850            removeFromArray(index);
851            addInArray(child, mChildrenCount);
852            child.mParent = this;
853        }
854    }
855
856    /**
857     * {@inheritDoc}
858     *
859     * !!! TODO: write real docs
860     */
861    @Override
862    public boolean dispatchDragEvent(DragEvent event) {
863        boolean retval = false;
864        final float tx = event.mX;
865        final float ty = event.mY;
866
867        ViewRoot root = getViewRoot();
868
869        // Dispatch down the view hierarchy
870        switch (event.mAction) {
871        case DragEvent.ACTION_DRAG_STARTED: {
872            // clear state to recalculate which views we drag over
873            mCurrentDragView = null;
874
875            // Now dispatch down to our children, caching the responses
876            mChildAcceptsDrag = false;
877            final int count = mChildrenCount;
878            final View[] children = mChildren;
879            for (int i = 0; i < count; i++) {
880                final View child = children[i];
881                if (child.getVisibility() == VISIBLE) {
882                    final boolean handled = children[i].dispatchDragEvent(event);
883                    children[i].mCanAcceptDrop = handled;
884                    if (handled) {
885                        mChildAcceptsDrag = true;
886                    }
887                }
888            }
889
890            // Return HANDLED if one of our children can accept the drag
891            if (mChildAcceptsDrag) {
892                retval = true;
893            }
894        } break;
895
896        case DragEvent.ACTION_DRAG_ENDED: {
897            // Notify all of our children that the drag is over
898            final int count = mChildrenCount;
899            final View[] children = mChildren;
900            for (int i = 0; i < count; i++) {
901                final View child = children[i];
902                if (child.getVisibility() == VISIBLE) {
903                    child.dispatchDragEvent(event);
904                }
905            }
906            // We consider drag-ended to have been handled if one of our children
907            // had offered to handle the drag.
908            if (mChildAcceptsDrag) {
909                retval = true;
910            }
911        } break;
912
913        case DragEvent.ACTION_DRAG_LOCATION: {
914            // Find the [possibly new] drag target
915            final View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
916
917            // If we've changed apparent drag target, tell the view root which view
918            // we're over now [for purposes of the eventual drag-recipient-changed
919            // notifications to the framework] and tell the new target that the drag
920            // has entered its bounds.  The root will see setDragFocus() calls all
921            // the way down to the final leaf view that is handling the LOCATION event
922            // before reporting the new potential recipient to the framework.
923            if (mCurrentDragView != target) {
924                root.setDragFocus(target);
925
926                final int action = event.mAction;
927                // If we've dragged off of a child view, send it the EXITED message
928                if (mCurrentDragView != null) {
929                    event.mAction = DragEvent.ACTION_DRAG_EXITED;
930                    mCurrentDragView.dispatchDragEvent(event);
931                }
932                mCurrentDragView = target;
933
934                // If we've dragged over a new child view, send it the ENTERED message
935                if (target != null) {
936                    event.mAction = DragEvent.ACTION_DRAG_ENTERED;
937                    target.dispatchDragEvent(event);
938                }
939                event.mAction = action;  // restore the event's original state
940            }
941
942            // Dispatch the actual drag location notice, localized into its coordinates
943            if (target != null) {
944                event.mX = mLocalPoint.x;
945                event.mY = mLocalPoint.y;
946
947                retval = target.dispatchDragEvent(event);
948
949                event.mX = tx;
950                event.mY = ty;
951            }
952        } break;
953
954        /* Entered / exited dispatch
955         *
956         * DRAG_ENTERED is not dispatched downwards from ViewGroup.  The reason for this is
957         * that we're about to get the corresponding LOCATION event, which we will use to
958         * determine which of our children is the new target; at that point we will
959         * push a DRAG_ENTERED down to the new target child [which may itself be a ViewGroup].
960         *
961         * DRAG_EXITED *is* dispatched all the way down immediately: once we know the
962         * drag has left this ViewGroup, we know by definition that every contained subview
963         * is also no longer under the drag point.
964         */
965
966        case DragEvent.ACTION_DRAG_EXITED: {
967            if (mCurrentDragView != null) {
968                mCurrentDragView.dispatchDragEvent(event);
969                mCurrentDragView = null;
970            }
971        } break;
972
973        case DragEvent.ACTION_DROP: {
974            if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "Drop event: " + event);
975            View target = findFrontmostDroppableChildAt(event.mX, event.mY, mLocalPoint);
976            if (target != null) {
977                if (ViewDebug.DEBUG_DRAG) Log.d(View.VIEW_LOG_TAG, "   dispatch drop to " + target);
978                event.mX = mLocalPoint.x;
979                event.mY = mLocalPoint.y;
980                retval = target.dispatchDragEvent(event);
981                event.mX = tx;
982                event.mY = ty;
983            } else {
984                if (ViewDebug.DEBUG_DRAG) {
985                    Log.d(View.VIEW_LOG_TAG, "   not dropped on an accepting view");
986                }
987            }
988        } break;
989        }
990
991        // If none of our children could handle the event, try here
992        if (!retval) {
993            // Call up to the View implementation that dispatches to installed listeners
994            retval = super.dispatchDragEvent(event);
995        }
996        return retval;
997    }
998
999    // Find the frontmost child view that lies under the given point, and calculate
1000    // the position within its own local coordinate system.
1001    View findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint) {
1002        final int count = mChildrenCount;
1003        final View[] children = mChildren;
1004        for (int i = count - 1; i >= 0; i--) {
1005            final View child = children[i];
1006            if (!child.mCanAcceptDrop) {
1007                continue;
1008            }
1009
1010            if (isTransformedTouchPointInView(x, y, child, outLocalPoint)) {
1011                return child;
1012            }
1013        }
1014        return null;
1015    }
1016
1017    /**
1018     * {@inheritDoc}
1019     */
1020    @Override
1021    public boolean dispatchKeyEventPreIme(KeyEvent event) {
1022        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1023            return super.dispatchKeyEventPreIme(event);
1024        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1025            return mFocused.dispatchKeyEventPreIme(event);
1026        }
1027        return false;
1028    }
1029
1030    /**
1031     * {@inheritDoc}
1032     */
1033    @Override
1034    public boolean dispatchKeyEvent(KeyEvent event) {
1035        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1036            return super.dispatchKeyEvent(event);
1037        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1038            return mFocused.dispatchKeyEvent(event);
1039        }
1040        return false;
1041    }
1042
1043    /**
1044     * {@inheritDoc}
1045     */
1046    @Override
1047    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
1048        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1049            return super.dispatchKeyShortcutEvent(event);
1050        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1051            return mFocused.dispatchKeyShortcutEvent(event);
1052        }
1053        return false;
1054    }
1055
1056    /**
1057     * {@inheritDoc}
1058     */
1059    @Override
1060    public boolean dispatchTrackballEvent(MotionEvent event) {
1061        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
1062            return super.dispatchTrackballEvent(event);
1063        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
1064            return mFocused.dispatchTrackballEvent(event);
1065        }
1066        return false;
1067    }
1068
1069    /**
1070     * {@inheritDoc}
1071     */
1072    @Override
1073    public boolean dispatchTouchEvent(MotionEvent ev) {
1074        if (!onFilterTouchEventForSecurity(ev)) {
1075            return false;
1076        }
1077
1078        final int action = ev.getAction();
1079        final int actionMasked = action & MotionEvent.ACTION_MASK;
1080
1081        // Handle an initial down.
1082        if (actionMasked == MotionEvent.ACTION_DOWN) {
1083            // Throw away all previous state when starting a new touch gesture.
1084            // The framework may have dropped the up or cancel event for the previous gesture
1085            // due to an app switch, ANR, or some other state change.
1086            cancelAndClearTouchTargets(ev);
1087            resetTouchState();
1088        }
1089
1090        // Check for interception.
1091        final boolean intercepted;
1092        if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
1093            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
1094            if (!disallowIntercept) {
1095                intercepted = onInterceptTouchEvent(ev);
1096                ev.setAction(action); // restore action in case onInterceptTouchEvent() changed it
1097            } else {
1098                intercepted = false;
1099            }
1100        } else {
1101            intercepted = true;
1102        }
1103
1104        // Check for cancelation.
1105        final boolean canceled = resetCancelNextUpFlag(this)
1106                || actionMasked == MotionEvent.ACTION_CANCEL;
1107
1108        // Update list of touch targets for pointer down, if needed.
1109        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
1110        TouchTarget newTouchTarget = null;
1111        boolean alreadyDispatchedToNewTouchTarget = false;
1112        if (!canceled && !intercepted) {
1113            if (actionMasked == MotionEvent.ACTION_DOWN
1114                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)) {
1115                final int actionIndex = ev.getActionIndex(); // always 0 for down
1116                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
1117                        : TouchTarget.ALL_POINTER_IDS;
1118
1119                // Clean up earlier touch targets for this pointer id in case they
1120                // have become out of sync.
1121                removePointersFromTouchTargets(idBitsToAssign);
1122
1123                final int childrenCount = mChildrenCount;
1124                if (childrenCount != 0) {
1125                    // Find a child that can receive the event.  Scan children from front to back.
1126                    final View[] children = mChildren;
1127                    final float x = ev.getX(actionIndex);
1128                    final float y = ev.getY(actionIndex);
1129
1130                    for (int i = childrenCount - 1; i >= 0; i--) {
1131                        final View child = children[i];
1132                        if ((child.mViewFlags & VISIBILITY_MASK) != VISIBLE
1133                                && child.getAnimation() == null) {
1134                            // Skip invisible child unless it is animating.
1135                            continue;
1136                        }
1137
1138                        if (!isTransformedTouchPointInView(x, y, child, null)) {
1139                            // New pointer is out of child's bounds.
1140                            continue;
1141                        }
1142
1143                        newTouchTarget = getTouchTarget(child);
1144                        if (newTouchTarget != null) {
1145                            // Child is already receiving touch within its bounds.
1146                            // Give it the new pointer in addition to the ones it is handling.
1147                            newTouchTarget.pointerIdBits |= idBitsToAssign;
1148                            break;
1149                        }
1150
1151                        resetCancelNextUpFlag(child);
1152                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
1153                            // Child wants to receive touch within its bounds.
1154                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
1155                            alreadyDispatchedToNewTouchTarget = true;
1156                            break;
1157                        }
1158                    }
1159                }
1160
1161                if (newTouchTarget == null && mFirstTouchTarget != null) {
1162                    // Did not find a child to receive the event.
1163                    // Assign the pointer to the least recently added target.
1164                    newTouchTarget = mFirstTouchTarget;
1165                    while (newTouchTarget.next != null) {
1166                        newTouchTarget = newTouchTarget.next;
1167                    }
1168                    newTouchTarget.pointerIdBits |= idBitsToAssign;
1169                }
1170            }
1171        }
1172
1173        // Dispatch to touch targets.
1174        boolean handled = false;
1175        if (mFirstTouchTarget == null) {
1176            // No touch targets so treat this as an ordinary view.
1177            handled = dispatchTransformedTouchEvent(ev, canceled, null,
1178                    TouchTarget.ALL_POINTER_IDS);
1179        } else {
1180            // Dispatch to touch targets, excluding the new touch target if we already
1181            // dispatched to it.  Cancel touch targets if necessary.
1182            TouchTarget predecessor = null;
1183            TouchTarget target = mFirstTouchTarget;
1184            while (target != null) {
1185                final TouchTarget next = target.next;
1186                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
1187                    handled = true;
1188                } else {
1189                    final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
1190                    if (dispatchTransformedTouchEvent(ev, cancelChild,
1191                            target.child, target.pointerIdBits)) {
1192                        handled = true;
1193                    }
1194                    if (cancelChild) {
1195                        if (predecessor == null) {
1196                            mFirstTouchTarget = next;
1197                        } else {
1198                            predecessor.next = next;
1199                        }
1200                        target.recycle();
1201                        target = next;
1202                        continue;
1203                    }
1204                }
1205                predecessor = target;
1206                target = next;
1207            }
1208        }
1209
1210        // Update list of touch targets for pointer up or cancel, if needed.
1211        if (canceled || actionMasked == MotionEvent.ACTION_UP) {
1212            resetTouchState();
1213        } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
1214            final int actionIndex = ev.getActionIndex();
1215            final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
1216            removePointersFromTouchTargets(idBitsToRemove);
1217        }
1218
1219        return handled;
1220    }
1221
1222    /**
1223     * Resets all touch state in preparation for a new cycle.
1224     */
1225    private void resetTouchState() {
1226        clearTouchTargets();
1227        resetCancelNextUpFlag(this);
1228        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1229    }
1230
1231    /**
1232     * Resets the cancel next up flag.
1233     * Returns true if the flag was previously set.
1234     */
1235    private boolean resetCancelNextUpFlag(View view) {
1236        if ((view.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
1237            view.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
1238            return true;
1239        }
1240        return false;
1241    }
1242
1243    /**
1244     * Clears all touch targets.
1245     */
1246    private void clearTouchTargets() {
1247        TouchTarget target = mFirstTouchTarget;
1248        if (target != null) {
1249            do {
1250                TouchTarget next = target.next;
1251                target.recycle();
1252                target = next;
1253            } while (target != null);
1254            mFirstTouchTarget = null;
1255        }
1256    }
1257
1258    /**
1259     * Cancels and clears all touch targets.
1260     */
1261    private void cancelAndClearTouchTargets(MotionEvent event) {
1262        if (mFirstTouchTarget != null) {
1263            boolean syntheticEvent = false;
1264            if (event == null) {
1265                final long now = SystemClock.uptimeMillis();
1266                event = MotionEvent.obtain(now, now,
1267                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1268                syntheticEvent = true;
1269            }
1270
1271            for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1272                resetCancelNextUpFlag(target.child);
1273                dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
1274            }
1275            clearTouchTargets();
1276
1277            if (syntheticEvent) {
1278                event.recycle();
1279            }
1280        }
1281    }
1282
1283    /**
1284     * Gets the touch target for specified child view.
1285     * Returns null if not found.
1286     */
1287    private TouchTarget getTouchTarget(View child) {
1288        for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
1289            if (target.child == child) {
1290                return target;
1291            }
1292        }
1293        return null;
1294    }
1295
1296    /**
1297     * Adds a touch target for specified child to the beginning of the list.
1298     * Assumes the target child is not already present.
1299     */
1300    private TouchTarget addTouchTarget(View child, int pointerIdBits) {
1301        TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
1302        target.next = mFirstTouchTarget;
1303        mFirstTouchTarget = target;
1304        return target;
1305    }
1306
1307    /**
1308     * Removes the pointer ids from consideration.
1309     */
1310    private void removePointersFromTouchTargets(int pointerIdBits) {
1311        TouchTarget predecessor = null;
1312        TouchTarget target = mFirstTouchTarget;
1313        while (target != null) {
1314            final TouchTarget next = target.next;
1315            if ((target.pointerIdBits & pointerIdBits) != 0) {
1316                target.pointerIdBits &= ~pointerIdBits;
1317                if (target.pointerIdBits == 0) {
1318                    if (predecessor == null) {
1319                        mFirstTouchTarget = next;
1320                    } else {
1321                        predecessor.next = next;
1322                    }
1323                    target.recycle();
1324                    target = next;
1325                    continue;
1326                }
1327            }
1328            predecessor = target;
1329            target = next;
1330        }
1331    }
1332
1333    /**
1334     * Returns true if a child view contains the specified point when transformed
1335     * into its coordinate space.
1336     * Child must not be null.
1337     * @hide
1338     */
1339    protected boolean isTransformedTouchPointInView(float x, float y, View child,
1340            PointF outLocalPoint) {
1341        float localX = x + mScrollX - child.mLeft;
1342        float localY = y + mScrollY - child.mTop;
1343        if (! child.hasIdentityMatrix() && mAttachInfo != null) {
1344            final float[] localXY = mAttachInfo.mTmpTransformLocation;
1345            localXY[0] = localX;
1346            localXY[1] = localY;
1347            child.getInverseMatrix().mapPoints(localXY);
1348            localX = localXY[0];
1349            localY = localXY[1];
1350        }
1351        final boolean isInView = child.pointInView(localX, localY);
1352        if (isInView && outLocalPoint != null) {
1353            outLocalPoint.set(localX, localY);
1354        }
1355        return isInView;
1356    }
1357
1358    /**
1359     * Transforms a motion event into the coordinate space of a particular child view,
1360     * filters out irrelevant pointer ids, and overrides its action if necessary.
1361     * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
1362     */
1363    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
1364            View child, int desiredPointerIdBits) {
1365        final boolean handled;
1366
1367        // Canceling motions is a special case.  We don't need to perform any transformations
1368        // or filtering.  The important part is the action, not the contents.
1369        final int oldAction = event.getAction();
1370        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
1371            event.setAction(MotionEvent.ACTION_CANCEL);
1372            if (child == null) {
1373                handled = super.dispatchTouchEvent(event);
1374            } else {
1375                handled = child.dispatchTouchEvent(event);
1376            }
1377            event.setAction(oldAction);
1378            return handled;
1379        }
1380
1381        // Calculate the number of pointers to deliver.
1382        final int oldPointerCount = event.getPointerCount();
1383        int newPointerCount = 0;
1384        if (desiredPointerIdBits == TouchTarget.ALL_POINTER_IDS) {
1385            newPointerCount = oldPointerCount;
1386        } else {
1387            for (int i = 0; i < oldPointerCount; i++) {
1388                final int pointerId = event.getPointerId(i);
1389                final int pointerIdBit = 1 << pointerId;
1390                if ((pointerIdBit & desiredPointerIdBits) != 0) {
1391                    newPointerCount += 1;
1392                }
1393            }
1394        }
1395
1396        // If for some reason we ended up in an inconsistent state where it looks like we
1397        // might produce a motion event with no pointers in it, then drop the event.
1398        if (newPointerCount == 0) {
1399            return false;
1400        }
1401
1402        // If the number of pointers is the same and we don't need to perform any fancy
1403        // irreversible transformations, then we can reuse the motion event for this
1404        // dispatch as long as we are careful to revert any changes we make.
1405        final boolean reuse = newPointerCount == oldPointerCount
1406                && (child == null || child.hasIdentityMatrix());
1407        if (reuse) {
1408            if (child == null) {
1409                handled = super.dispatchTouchEvent(event);
1410            } else {
1411                final float offsetX = mScrollX - child.mLeft;
1412                final float offsetY = mScrollY - child.mTop;
1413                event.offsetLocation(offsetX, offsetY);
1414
1415                handled = child.dispatchTouchEvent(event);
1416
1417                event.offsetLocation(-offsetX, -offsetY);
1418            }
1419            return handled;
1420        }
1421
1422        // Make a copy of the event.
1423        // If the number of pointers is different, then we need to filter out irrelevant pointers
1424        // as we make a copy of the motion event.
1425        MotionEvent transformedEvent;
1426        if (newPointerCount == oldPointerCount) {
1427            transformedEvent = MotionEvent.obtain(event);
1428        } else {
1429            growTmpPointerArrays(newPointerCount);
1430            final int[] newPointerIndexMap = mTmpPointerIndexMap;
1431            final int[] newPointerIds = mTmpPointerIds;
1432            final MotionEvent.PointerCoords[] newPointerCoords = mTmpPointerCoords;
1433
1434            int newPointerIndex = 0;
1435            int oldPointerIndex = 0;
1436            while (newPointerIndex < newPointerCount) {
1437                final int pointerId = event.getPointerId(oldPointerIndex);
1438                final int pointerIdBits = 1 << pointerId;
1439                if ((pointerIdBits & desiredPointerIdBits) != 0) {
1440                    newPointerIndexMap[newPointerIndex] = oldPointerIndex;
1441                    newPointerIds[newPointerIndex] = pointerId;
1442                    if (newPointerCoords[newPointerIndex] == null) {
1443                        newPointerCoords[newPointerIndex] = new MotionEvent.PointerCoords();
1444                    }
1445
1446                    newPointerIndex += 1;
1447                }
1448                oldPointerIndex += 1;
1449            }
1450
1451            final int newAction;
1452            if (cancel) {
1453                newAction = MotionEvent.ACTION_CANCEL;
1454            } else {
1455                final int oldMaskedAction = oldAction & MotionEvent.ACTION_MASK;
1456                if (oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
1457                        || oldMaskedAction == MotionEvent.ACTION_POINTER_UP) {
1458                    final int changedPointerId = event.getPointerId(
1459                            (oldAction & MotionEvent.ACTION_POINTER_INDEX_MASK)
1460                                    >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
1461                    final int changedPointerIdBits = 1 << changedPointerId;
1462                    if ((changedPointerIdBits & desiredPointerIdBits) != 0) {
1463                        if (newPointerCount == 1) {
1464                            // The first/last pointer went down/up.
1465                            newAction = oldMaskedAction == MotionEvent.ACTION_POINTER_DOWN
1466                                    ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP;
1467                        } else {
1468                            // A secondary pointer went down/up.
1469                            int newChangedPointerIndex = 0;
1470                            while (newPointerIds[newChangedPointerIndex] != changedPointerId) {
1471                                newChangedPointerIndex += 1;
1472                            }
1473                            newAction = oldMaskedAction | (newChangedPointerIndex
1474                                    << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
1475                        }
1476                    } else {
1477                        // An unrelated pointer changed.
1478                        newAction = MotionEvent.ACTION_MOVE;
1479                    }
1480                } else {
1481                    // Simple up/down/cancel/move motion action.
1482                    newAction = oldMaskedAction;
1483                }
1484            }
1485
1486            transformedEvent = null;
1487            final int historySize = event.getHistorySize();
1488            for (int historyIndex = 0; historyIndex <= historySize; historyIndex++) {
1489                for (newPointerIndex = 0; newPointerIndex < newPointerCount; newPointerIndex++) {
1490                    final MotionEvent.PointerCoords c = newPointerCoords[newPointerIndex];
1491                    oldPointerIndex = newPointerIndexMap[newPointerIndex];
1492                    if (historyIndex != historySize) {
1493                        event.getHistoricalPointerCoords(oldPointerIndex, historyIndex, c);
1494                    } else {
1495                        event.getPointerCoords(oldPointerIndex, c);
1496                    }
1497                }
1498
1499                final long eventTime;
1500                if (historyIndex != historySize) {
1501                    eventTime = event.getHistoricalEventTime(historyIndex);
1502                } else {
1503                    eventTime = event.getEventTime();
1504                }
1505
1506                if (transformedEvent == null) {
1507                    transformedEvent = MotionEvent.obtain(
1508                            event.getDownTime(), eventTime, newAction,
1509                            newPointerCount, newPointerIds, newPointerCoords,
1510                            event.getMetaState(), event.getXPrecision(), event.getYPrecision(),
1511                            event.getDeviceId(), event.getEdgeFlags(), event.getSource(),
1512                            event.getFlags());
1513                } else {
1514                    transformedEvent.addBatch(eventTime, newPointerCoords, 0);
1515                }
1516            }
1517        }
1518
1519        // Perform any necessary transformations and dispatch.
1520        if (child == null) {
1521            handled = super.dispatchTouchEvent(transformedEvent);
1522        } else {
1523            final float offsetX = mScrollX - child.mLeft;
1524            final float offsetY = mScrollY - child.mTop;
1525            transformedEvent.offsetLocation(offsetX, offsetY);
1526            if (! child.hasIdentityMatrix()) {
1527                transformedEvent.transform(child.getInverseMatrix());
1528            }
1529
1530            handled = child.dispatchTouchEvent(transformedEvent);
1531        }
1532
1533        // Done.
1534        transformedEvent.recycle();
1535        return handled;
1536    }
1537
1538    /**
1539     * Enlarge the temporary pointer arrays for splitting pointers.
1540     * May discard contents (but keeps PointerCoords objects to avoid reallocating them).
1541     */
1542    private void growTmpPointerArrays(int desiredCapacity) {
1543        final MotionEvent.PointerCoords[] oldTmpPointerCoords = mTmpPointerCoords;
1544        int capacity;
1545        if (oldTmpPointerCoords != null) {
1546            capacity = oldTmpPointerCoords.length;
1547            if (desiredCapacity <= capacity) {
1548                return;
1549            }
1550        } else {
1551            capacity = 4;
1552        }
1553
1554        while (capacity < desiredCapacity) {
1555            capacity *= 2;
1556        }
1557
1558        mTmpPointerIndexMap = new int[capacity];
1559        mTmpPointerIds = new int[capacity];
1560        mTmpPointerCoords = new MotionEvent.PointerCoords[capacity];
1561
1562        if (oldTmpPointerCoords != null) {
1563            System.arraycopy(oldTmpPointerCoords, 0, mTmpPointerCoords, 0,
1564                    oldTmpPointerCoords.length);
1565        }
1566    }
1567
1568    /**
1569     * Enable or disable the splitting of MotionEvents to multiple children during touch event
1570     * dispatch. This behavior is disabled by default.
1571     *
1572     * <p>When this option is enabled MotionEvents may be split and dispatched to different child
1573     * views depending on where each pointer initially went down. This allows for user interactions
1574     * such as scrolling two panes of content independently, chording of buttons, and performing
1575     * independent gestures on different pieces of content.
1576     *
1577     * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
1578     *              child views. <code>false</code> to only allow one child view to be the target of
1579     *              any MotionEvent received by this ViewGroup.
1580     */
1581    public void setMotionEventSplittingEnabled(boolean split) {
1582        // TODO Applications really shouldn't change this setting mid-touch event,
1583        // but perhaps this should handle that case and send ACTION_CANCELs to any child views
1584        // with gestures in progress when this is changed.
1585        if (split) {
1586            mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
1587        } else {
1588            mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
1589        }
1590    }
1591
1592    /**
1593     * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
1594     */
1595    public boolean isMotionEventSplittingEnabled() {
1596        return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
1597    }
1598
1599    /**
1600     * {@inheritDoc}
1601     */
1602    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
1603
1604        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
1605            // We're already in this state, assume our ancestors are too
1606            return;
1607        }
1608
1609        if (disallowIntercept) {
1610            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
1611        } else {
1612            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1613        }
1614
1615        // Pass it up to our parent
1616        if (mParent != null) {
1617            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
1618        }
1619    }
1620
1621    /**
1622     * Implement this method to intercept all touch screen motion events.  This
1623     * allows you to watch events as they are dispatched to your children, and
1624     * take ownership of the current gesture at any point.
1625     *
1626     * <p>Using this function takes some care, as it has a fairly complicated
1627     * interaction with {@link View#onTouchEvent(MotionEvent)
1628     * View.onTouchEvent(MotionEvent)}, and using it requires implementing
1629     * that method as well as this one in the correct way.  Events will be
1630     * received in the following order:
1631     *
1632     * <ol>
1633     * <li> You will receive the down event here.
1634     * <li> The down event will be handled either by a child of this view
1635     * group, or given to your own onTouchEvent() method to handle; this means
1636     * you should implement onTouchEvent() to return true, so you will
1637     * continue to see the rest of the gesture (instead of looking for
1638     * a parent view to handle it).  Also, by returning true from
1639     * onTouchEvent(), you will not receive any following
1640     * events in onInterceptTouchEvent() and all touch processing must
1641     * happen in onTouchEvent() like normal.
1642     * <li> For as long as you return false from this function, each following
1643     * event (up to and including the final up) will be delivered first here
1644     * and then to the target's onTouchEvent().
1645     * <li> If you return true from here, you will not receive any
1646     * following events: the target view will receive the same event but
1647     * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
1648     * events will be delivered to your onTouchEvent() method and no longer
1649     * appear here.
1650     * </ol>
1651     *
1652     * @param ev The motion event being dispatched down the hierarchy.
1653     * @return Return true to steal motion events from the children and have
1654     * them dispatched to this ViewGroup through onTouchEvent().
1655     * The current target will receive an ACTION_CANCEL event, and no further
1656     * messages will be delivered here.
1657     */
1658    public boolean onInterceptTouchEvent(MotionEvent ev) {
1659        return false;
1660    }
1661
1662    /**
1663     * {@inheritDoc}
1664     *
1665     * Looks for a view to give focus to respecting the setting specified by
1666     * {@link #getDescendantFocusability()}.
1667     *
1668     * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
1669     * find focus within the children of this group when appropriate.
1670     *
1671     * @see #FOCUS_BEFORE_DESCENDANTS
1672     * @see #FOCUS_AFTER_DESCENDANTS
1673     * @see #FOCUS_BLOCK_DESCENDANTS
1674     * @see #onRequestFocusInDescendants
1675     */
1676    @Override
1677    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
1678        if (DBG) {
1679            System.out.println(this + " ViewGroup.requestFocus direction="
1680                    + direction);
1681        }
1682        int descendantFocusability = getDescendantFocusability();
1683
1684        switch (descendantFocusability) {
1685            case FOCUS_BLOCK_DESCENDANTS:
1686                return super.requestFocus(direction, previouslyFocusedRect);
1687            case FOCUS_BEFORE_DESCENDANTS: {
1688                final boolean took = super.requestFocus(direction, previouslyFocusedRect);
1689                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
1690            }
1691            case FOCUS_AFTER_DESCENDANTS: {
1692                final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
1693                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
1694            }
1695            default:
1696                throw new IllegalStateException("descendant focusability must be "
1697                        + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
1698                        + "but is " + descendantFocusability);
1699        }
1700    }
1701
1702    /**
1703     * Look for a descendant to call {@link View#requestFocus} on.
1704     * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
1705     * when it wants to request focus within its children.  Override this to
1706     * customize how your {@link ViewGroup} requests focus within its children.
1707     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
1708     * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
1709     *        to give a finer grained hint about where focus is coming from.  May be null
1710     *        if there is no hint.
1711     * @return Whether focus was taken.
1712     */
1713    @SuppressWarnings({"ConstantConditions"})
1714    protected boolean onRequestFocusInDescendants(int direction,
1715            Rect previouslyFocusedRect) {
1716        int index;
1717        int increment;
1718        int end;
1719        int count = mChildrenCount;
1720        if ((direction & FOCUS_FORWARD) != 0) {
1721            index = 0;
1722            increment = 1;
1723            end = count;
1724        } else {
1725            index = count - 1;
1726            increment = -1;
1727            end = -1;
1728        }
1729        final View[] children = mChildren;
1730        for (int i = index; i != end; i += increment) {
1731            View child = children[i];
1732            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1733                if (child.requestFocus(direction, previouslyFocusedRect)) {
1734                    return true;
1735                }
1736            }
1737        }
1738        return false;
1739    }
1740
1741    /**
1742     * {@inheritDoc}
1743     *
1744     * @hide
1745     */
1746    @Override
1747    public void dispatchStartTemporaryDetach() {
1748        super.dispatchStartTemporaryDetach();
1749        final int count = mChildrenCount;
1750        final View[] children = mChildren;
1751        for (int i = 0; i < count; i++) {
1752            children[i].dispatchStartTemporaryDetach();
1753        }
1754    }
1755
1756    /**
1757     * {@inheritDoc}
1758     *
1759     * @hide
1760     */
1761    @Override
1762    public void dispatchFinishTemporaryDetach() {
1763        super.dispatchFinishTemporaryDetach();
1764        final int count = mChildrenCount;
1765        final View[] children = mChildren;
1766        for (int i = 0; i < count; i++) {
1767            children[i].dispatchFinishTemporaryDetach();
1768        }
1769    }
1770
1771    /**
1772     * {@inheritDoc}
1773     */
1774    @Override
1775    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
1776        super.dispatchAttachedToWindow(info, visibility);
1777        visibility |= mViewFlags & VISIBILITY_MASK;
1778        final int count = mChildrenCount;
1779        final View[] children = mChildren;
1780        for (int i = 0; i < count; i++) {
1781            children[i].dispatchAttachedToWindow(info, visibility);
1782        }
1783    }
1784
1785    @Override
1786    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1787        boolean populated = false;
1788        for (int i = 0, count = getChildCount(); i < count; i++) {
1789            populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event);
1790        }
1791        return populated;
1792    }
1793
1794    /**
1795     * {@inheritDoc}
1796     */
1797    @Override
1798    void dispatchDetachedFromWindow() {
1799        // If we still have a touch target, we are still in the process of
1800        // dispatching motion events to a child; we need to get rid of that
1801        // child to avoid dispatching events to it after the window is torn
1802        // down. To make sure we keep the child in a consistent state, we
1803        // first send it an ACTION_CANCEL motion event.
1804        cancelAndClearTouchTargets(null);
1805
1806        final int count = mChildrenCount;
1807        final View[] children = mChildren;
1808        for (int i = 0; i < count; i++) {
1809            children[i].dispatchDetachedFromWindow();
1810        }
1811        super.dispatchDetachedFromWindow();
1812    }
1813
1814    /**
1815     * {@inheritDoc}
1816     */
1817    @Override
1818    public void setPadding(int left, int top, int right, int bottom) {
1819        super.setPadding(left, top, right, bottom);
1820
1821        if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingRight) != 0) {
1822            mGroupFlags |= FLAG_PADDING_NOT_NULL;
1823        } else {
1824            mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
1825        }
1826    }
1827
1828    /**
1829     * {@inheritDoc}
1830     */
1831    @Override
1832    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1833        super.dispatchSaveInstanceState(container);
1834        final int count = mChildrenCount;
1835        final View[] children = mChildren;
1836        for (int i = 0; i < count; i++) {
1837            View c = children[i];
1838            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
1839                c.dispatchSaveInstanceState(container);
1840            }
1841        }
1842    }
1843
1844    /**
1845     * Perform dispatching of a {@link #saveHierarchyState freeze()} to only this view,
1846     * not to its children.  For use when overriding
1847     * {@link #dispatchSaveInstanceState dispatchFreeze()} to allow subclasses to freeze
1848     * their own state but not the state of their children.
1849     *
1850     * @param container the container
1851     */
1852    protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
1853        super.dispatchSaveInstanceState(container);
1854    }
1855
1856    /**
1857     * {@inheritDoc}
1858     */
1859    @Override
1860    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1861        super.dispatchRestoreInstanceState(container);
1862        final int count = mChildrenCount;
1863        final View[] children = mChildren;
1864        for (int i = 0; i < count; i++) {
1865            View c = children[i];
1866            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
1867                c.dispatchRestoreInstanceState(container);
1868            }
1869        }
1870    }
1871
1872    /**
1873     * Perform dispatching of a {@link #restoreHierarchyState thaw()} to only this view,
1874     * not to its children.  For use when overriding
1875     * {@link #dispatchRestoreInstanceState dispatchThaw()} to allow subclasses to thaw
1876     * their own state but not the state of their children.
1877     *
1878     * @param container the container
1879     */
1880    protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
1881        super.dispatchRestoreInstanceState(container);
1882    }
1883
1884    /**
1885     * Enables or disables the drawing cache for each child of this view group.
1886     *
1887     * @param enabled true to enable the cache, false to dispose of it
1888     */
1889    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
1890        if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
1891            final View[] children = mChildren;
1892            final int count = mChildrenCount;
1893            for (int i = 0; i < count; i++) {
1894                children[i].setDrawingCacheEnabled(enabled);
1895            }
1896        }
1897    }
1898
1899    @Override
1900    protected void onAnimationStart() {
1901        super.onAnimationStart();
1902
1903        // When this ViewGroup's animation starts, build the cache for the children
1904        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
1905            final int count = mChildrenCount;
1906            final View[] children = mChildren;
1907            final boolean buildCache = !isHardwareAccelerated();
1908
1909            for (int i = 0; i < count; i++) {
1910                final View child = children[i];
1911                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1912                    child.setDrawingCacheEnabled(true);
1913                    if (buildCache) {
1914                        child.buildDrawingCache(true);
1915                    }
1916                }
1917            }
1918
1919            mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
1920        }
1921    }
1922
1923    @Override
1924    protected void onAnimationEnd() {
1925        super.onAnimationEnd();
1926
1927        // When this ViewGroup's animation ends, destroy the cache of the children
1928        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
1929            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
1930
1931            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
1932                setChildrenDrawingCacheEnabled(false);
1933            }
1934        }
1935    }
1936
1937    @Override
1938    Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
1939        int count = mChildrenCount;
1940        int[] visibilities = null;
1941
1942        if (skipChildren) {
1943            visibilities = new int[count];
1944            for (int i = 0; i < count; i++) {
1945                View child = getChildAt(i);
1946                visibilities[i] = child.getVisibility();
1947                if (visibilities[i] == View.VISIBLE) {
1948                    child.setVisibility(INVISIBLE);
1949                }
1950            }
1951        }
1952
1953        Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
1954
1955        if (skipChildren) {
1956            for (int i = 0; i < count; i++) {
1957                getChildAt(i).setVisibility(visibilities[i]);
1958            }
1959        }
1960
1961        return b;
1962    }
1963
1964    /**
1965     * {@inheritDoc}
1966     */
1967    @Override
1968    protected void dispatchDraw(Canvas canvas) {
1969        final int count = mChildrenCount;
1970        final View[] children = mChildren;
1971        int flags = mGroupFlags;
1972
1973        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
1974            final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
1975
1976            final boolean buildCache = !isHardwareAccelerated();
1977            for (int i = 0; i < count; i++) {
1978                final View child = children[i];
1979                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1980                    final LayoutParams params = child.getLayoutParams();
1981                    attachLayoutAnimationParameters(child, params, i, count);
1982                    bindLayoutAnimation(child);
1983                    if (cache) {
1984                        child.setDrawingCacheEnabled(true);
1985                        if (buildCache) {
1986                            child.buildDrawingCache(true);
1987                        }
1988                    }
1989                }
1990            }
1991
1992            final LayoutAnimationController controller = mLayoutAnimationController;
1993            if (controller.willOverlap()) {
1994                mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
1995            }
1996
1997            controller.start();
1998
1999            mGroupFlags &= ~FLAG_RUN_ANIMATION;
2000            mGroupFlags &= ~FLAG_ANIMATION_DONE;
2001
2002            if (cache) {
2003                mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2004            }
2005
2006            if (mAnimationListener != null) {
2007                mAnimationListener.onAnimationStart(controller.getAnimation());
2008            }
2009        }
2010
2011        int saveCount = 0;
2012        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
2013        if (clipToPadding) {
2014            saveCount = canvas.save();
2015            canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
2016                    mScrollX + mRight - mLeft - mPaddingRight,
2017                    mScrollY + mBottom - mTop - mPaddingBottom);
2018
2019        }
2020
2021        // We will draw our child's animation, let's reset the flag
2022        mPrivateFlags &= ~DRAW_ANIMATION;
2023        mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
2024
2025        boolean more = false;
2026        final long drawingTime = getDrawingTime();
2027
2028        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
2029            for (int i = 0; i < count; i++) {
2030                final View child = children[i];
2031                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2032                    more |= drawChild(canvas, child, drawingTime);
2033                }
2034            }
2035        } else {
2036            for (int i = 0; i < count; i++) {
2037                final View child = children[getChildDrawingOrder(count, i)];
2038                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
2039                    more |= drawChild(canvas, child, drawingTime);
2040                }
2041            }
2042        }
2043
2044        // Draw any disappearing views that have animations
2045        if (mDisappearingChildren != null) {
2046            final ArrayList<View> disappearingChildren = mDisappearingChildren;
2047            final int disappearingCount = disappearingChildren.size() - 1;
2048            // Go backwards -- we may delete as animations finish
2049            for (int i = disappearingCount; i >= 0; i--) {
2050                final View child = disappearingChildren.get(i);
2051                more |= drawChild(canvas, child, drawingTime);
2052            }
2053        }
2054
2055        if (clipToPadding) {
2056            canvas.restoreToCount(saveCount);
2057        }
2058
2059        // mGroupFlags might have been updated by drawChild()
2060        flags = mGroupFlags;
2061
2062        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
2063            invalidate();
2064        }
2065
2066        if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
2067                mLayoutAnimationController.isDone() && !more) {
2068            // We want to erase the drawing cache and notify the listener after the
2069            // next frame is drawn because one extra invalidate() is caused by
2070            // drawChild() after the animation is over
2071            mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
2072            final Runnable end = new Runnable() {
2073               public void run() {
2074                   notifyAnimationListener();
2075               }
2076            };
2077            post(end);
2078        }
2079    }
2080
2081    /**
2082     * Returns the index of the child to draw for this iteration. Override this
2083     * if you want to change the drawing order of children. By default, it
2084     * returns i.
2085     * <p>
2086     * NOTE: In order for this method to be called, you must enable child ordering
2087     * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
2088     *
2089     * @param i The current iteration.
2090     * @return The index of the child to draw this iteration.
2091     *
2092     * @see #setChildrenDrawingOrderEnabled(boolean)
2093     * @see #isChildrenDrawingOrderEnabled()
2094     */
2095    protected int getChildDrawingOrder(int childCount, int i) {
2096        return i;
2097    }
2098
2099    private void notifyAnimationListener() {
2100        mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
2101        mGroupFlags |= FLAG_ANIMATION_DONE;
2102
2103        if (mAnimationListener != null) {
2104           final Runnable end = new Runnable() {
2105               public void run() {
2106                   mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
2107               }
2108           };
2109           post(end);
2110        }
2111
2112        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2113            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2114            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2115                setChildrenDrawingCacheEnabled(false);
2116            }
2117        }
2118
2119        invalidate();
2120    }
2121
2122    /**
2123     * Draw one child of this View Group. This method is responsible for getting
2124     * the canvas in the right state. This includes clipping, translating so
2125     * that the child's scrolled origin is at 0, 0, and applying any animation
2126     * transformations.
2127     *
2128     * @param canvas The canvas on which to draw the child
2129     * @param child Who to draw
2130     * @param drawingTime The time at which draw is occuring
2131     * @return True if an invalidate() was issued
2132     */
2133    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
2134        boolean more = false;
2135
2136        final int cl = child.mLeft;
2137        final int ct = child.mTop;
2138        final int cr = child.mRight;
2139        final int cb = child.mBottom;
2140
2141        final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
2142
2143        final int flags = mGroupFlags;
2144
2145        if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
2146            mChildTransformation.clear();
2147            mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
2148        }
2149
2150        Transformation transformToApply = null;
2151        Transformation invalidationTransform;
2152        final Animation a = child.getAnimation();
2153        boolean concatMatrix = false;
2154
2155        boolean scalingRequired = false;
2156        boolean caching = false;
2157        if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
2158                (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
2159            caching = true;
2160            if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
2161        }
2162
2163        if (a != null) {
2164            final boolean initialized = a.isInitialized();
2165            if (!initialized) {
2166                a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
2167                a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
2168                child.onAnimationStart();
2169            }
2170
2171            more = a.getTransformation(drawingTime, mChildTransformation,
2172                    scalingRequired ? mAttachInfo.mApplicationScale : 1f);
2173            if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
2174                if (mInvalidationTransformation == null) {
2175                    mInvalidationTransformation = new Transformation();
2176                }
2177                invalidationTransform = mInvalidationTransformation;
2178                a.getTransformation(drawingTime, invalidationTransform, 1f);
2179            } else {
2180                invalidationTransform = mChildTransformation;
2181            }
2182            transformToApply = mChildTransformation;
2183
2184            concatMatrix = a.willChangeTransformationMatrix();
2185
2186            if (more) {
2187                if (!a.willChangeBounds()) {
2188                    if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
2189                            FLAG_OPTIMIZE_INVALIDATE) {
2190                        mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
2191                    } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
2192                        // The child need to draw an animation, potentially offscreen, so
2193                        // make sure we do not cancel invalidate requests
2194                        mPrivateFlags |= DRAW_ANIMATION;
2195                        invalidate(cl, ct, cr, cb);
2196                    }
2197                } else {
2198                    if (mInvalidateRegion == null) {
2199                        mInvalidateRegion = new RectF();
2200                    }
2201                    final RectF region = mInvalidateRegion;
2202                    a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform);
2203
2204                    // The child need to draw an animation, potentially offscreen, so
2205                    // make sure we do not cancel invalidate requests
2206                    mPrivateFlags |= DRAW_ANIMATION;
2207
2208                    final int left = cl + (int) region.left;
2209                    final int top = ct + (int) region.top;
2210                    invalidate(left, top, left + (int) region.width(), top + (int) region.height());
2211                }
2212            }
2213        } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
2214                FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
2215            final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
2216            if (hasTransform) {
2217                final int transformType = mChildTransformation.getTransformationType();
2218                transformToApply = transformType != Transformation.TYPE_IDENTITY ?
2219                        mChildTransformation : null;
2220                concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
2221            }
2222        }
2223
2224        concatMatrix |= !childHasIdentityMatrix;
2225
2226        // Sets the flag as early as possible to allow draw() implementations
2227        // to call invalidate() successfully when doing animations
2228        child.mPrivateFlags |= DRAWN;
2229
2230        if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
2231                (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
2232            return more;
2233        }
2234
2235        float alpha = child.getAlpha();
2236        // Bail out early if the view does not need to be drawn
2237        if (alpha <= ViewConfiguration.ALPHA_THRESHOLD && (child.mPrivateFlags & ALPHA_SET) == 0 &&
2238                !(child instanceof SurfaceView)) {
2239            return more;
2240        }
2241
2242        child.computeScroll();
2243
2244        final int sx = child.mScrollX;
2245        final int sy = child.mScrollY;
2246
2247        DisplayList displayList = null;
2248        Bitmap cache = null;
2249        if (caching) {
2250            if (!canvas.isHardwareAccelerated()) {
2251                cache = child.getDrawingCache(true);
2252            } else {
2253                displayList = child.getDisplayList();
2254            }
2255        }
2256
2257        final boolean hasDisplayList = displayList != null && displayList.isReady();
2258        final boolean hasNoCache = cache == null || hasDisplayList;
2259
2260        final int restoreTo = canvas.save();
2261        if (hasNoCache) {
2262            canvas.translate(cl - sx, ct - sy);
2263        } else {
2264            canvas.translate(cl, ct);
2265            if (scalingRequired) {
2266                // mAttachInfo cannot be null, otherwise scalingRequired == false
2267                final float scale = 1.0f / mAttachInfo.mApplicationScale;
2268                canvas.scale(scale, scale);
2269            }
2270        }
2271
2272        if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
2273            if (transformToApply != null || !childHasIdentityMatrix) {
2274                int transX = 0;
2275                int transY = 0;
2276
2277                if (hasNoCache) {
2278                    transX = -sx;
2279                    transY = -sy;
2280                }
2281
2282                if (transformToApply != null) {
2283                    if (concatMatrix) {
2284                        // Undo the scroll translation, apply the transformation matrix,
2285                        // then redo the scroll translate to get the correct result.
2286                        canvas.translate(-transX, -transY);
2287                        canvas.concat(transformToApply.getMatrix());
2288                        canvas.translate(transX, transY);
2289                        mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2290                    }
2291
2292                    float transformAlpha = transformToApply.getAlpha();
2293                    if (transformAlpha < 1.0f) {
2294                        alpha *= transformToApply.getAlpha();
2295                        mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2296                    }
2297                }
2298
2299                if (!childHasIdentityMatrix) {
2300                    canvas.translate(-transX, -transY);
2301                    canvas.concat(child.getMatrix());
2302                    canvas.translate(transX, transY);
2303                }
2304            }
2305
2306            if (alpha < 1.0f) {
2307                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
2308                if (hasNoCache) {
2309                    final int multipliedAlpha = (int) (255 * alpha);
2310                    if (!child.onSetAlpha(multipliedAlpha)) {
2311                        int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
2312                        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
2313                            layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
2314                        }
2315                        canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha,
2316                                layerFlags);
2317                    } else {
2318                        child.mPrivateFlags |= ALPHA_SET;
2319                    }
2320                }
2321            }
2322        } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
2323            child.onSetAlpha(255);
2324            child.mPrivateFlags &= ~ALPHA_SET;
2325        }
2326
2327        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
2328            if (hasNoCache) {
2329                canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
2330            } else {
2331                if (!scalingRequired) {
2332                    canvas.clipRect(0, 0, cr - cl, cb - ct);
2333                } else {
2334                    canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
2335                }
2336            }
2337        }
2338
2339        if (hasNoCache) {
2340            if (!hasDisplayList) {
2341                // Fast path for layouts with no backgrounds
2342                if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
2343                    if (ViewDebug.TRACE_HIERARCHY) {
2344                        ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
2345                    }
2346                    child.mPrivateFlags &= ~DIRTY_MASK;
2347                    child.dispatchDraw(canvas);
2348                } else {
2349                    child.draw(canvas);
2350                }
2351            } else {
2352                ((HardwareCanvas) canvas).drawDisplayList(displayList);
2353            }
2354        } else if (cache != null) {
2355            final Paint cachePaint = mCachePaint;
2356            if (alpha < 1.0f) {
2357                cachePaint.setAlpha((int) (alpha * 255));
2358                mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
2359            } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
2360                cachePaint.setAlpha(255);
2361                mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
2362            }
2363            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
2364        }
2365
2366        canvas.restoreToCount(restoreTo);
2367
2368        if (a != null && !more) {
2369            child.onSetAlpha(255);
2370            finishAnimatingView(child, a);
2371        }
2372
2373        return more;
2374    }
2375
2376    /**
2377     * By default, children are clipped to their bounds before drawing. This
2378     * allows view groups to override this behavior for animations, etc.
2379     *
2380     * @param clipChildren true to clip children to their bounds,
2381     *        false otherwise
2382     * @attr ref android.R.styleable#ViewGroup_clipChildren
2383     */
2384    public void setClipChildren(boolean clipChildren) {
2385        setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
2386    }
2387
2388    /**
2389     * By default, children are clipped to the padding of the ViewGroup. This
2390     * allows view groups to override this behavior
2391     *
2392     * @param clipToPadding true to clip children to the padding of the
2393     *        group, false otherwise
2394     * @attr ref android.R.styleable#ViewGroup_clipToPadding
2395     */
2396    public void setClipToPadding(boolean clipToPadding) {
2397        setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
2398    }
2399
2400    /**
2401     * {@inheritDoc}
2402     */
2403    @Override
2404    public void dispatchSetSelected(boolean selected) {
2405        final View[] children = mChildren;
2406        final int count = mChildrenCount;
2407        for (int i = 0; i < count; i++) {
2408
2409            children[i].setSelected(selected);
2410        }
2411    }
2412
2413    /**
2414     * {@inheritDoc}
2415     */
2416    @Override
2417    public void dispatchSetActivated(boolean activated) {
2418        final View[] children = mChildren;
2419        final int count = mChildrenCount;
2420        for (int i = 0; i < count; i++) {
2421
2422            children[i].setActivated(activated);
2423        }
2424    }
2425
2426    @Override
2427    protected void dispatchSetPressed(boolean pressed) {
2428        final View[] children = mChildren;
2429        final int count = mChildrenCount;
2430        for (int i = 0; i < count; i++) {
2431            children[i].setPressed(pressed);
2432        }
2433    }
2434
2435    /**
2436     * When this property is set to true, this ViewGroup supports static transformations on
2437     * children; this causes
2438     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
2439     * invoked when a child is drawn.
2440     *
2441     * Any subclass overriding
2442     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
2443     * set this property to true.
2444     *
2445     * @param enabled True to enable static transformations on children, false otherwise.
2446     *
2447     * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
2448     */
2449    protected void setStaticTransformationsEnabled(boolean enabled) {
2450        setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
2451    }
2452
2453    /**
2454     * {@inheritDoc}
2455     *
2456     * @see #setStaticTransformationsEnabled(boolean)
2457     */
2458    protected boolean getChildStaticTransformation(View child, Transformation t) {
2459        return false;
2460    }
2461
2462    /**
2463     * {@hide}
2464     */
2465    @Override
2466    protected View findViewTraversal(int id) {
2467        if (id == mID) {
2468            return this;
2469        }
2470
2471        final View[] where = mChildren;
2472        final int len = mChildrenCount;
2473
2474        for (int i = 0; i < len; i++) {
2475            View v = where[i];
2476
2477            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
2478                v = v.findViewById(id);
2479
2480                if (v != null) {
2481                    return v;
2482                }
2483            }
2484        }
2485
2486        return null;
2487    }
2488
2489    /**
2490     * {@hide}
2491     */
2492    @Override
2493    protected View findViewWithTagTraversal(Object tag) {
2494        if (tag != null && tag.equals(mTag)) {
2495            return this;
2496        }
2497
2498        final View[] where = mChildren;
2499        final int len = mChildrenCount;
2500
2501        for (int i = 0; i < len; i++) {
2502            View v = where[i];
2503
2504            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
2505                v = v.findViewWithTag(tag);
2506
2507                if (v != null) {
2508                    return v;
2509                }
2510            }
2511        }
2512
2513        return null;
2514    }
2515
2516    /**
2517     * Adds a child view. If no layout parameters are already set on the child, the
2518     * default parameters for this ViewGroup are set on the child.
2519     *
2520     * @param child the child view to add
2521     *
2522     * @see #generateDefaultLayoutParams()
2523     */
2524    public void addView(View child) {
2525        addView(child, -1);
2526    }
2527
2528    /**
2529     * Adds a child view. If no layout parameters are already set on the child, the
2530     * default parameters for this ViewGroup are set on the child.
2531     *
2532     * @param child the child view to add
2533     * @param index the position at which to add the child
2534     *
2535     * @see #generateDefaultLayoutParams()
2536     */
2537    public void addView(View child, int index) {
2538        LayoutParams params = child.getLayoutParams();
2539        if (params == null) {
2540            params = generateDefaultLayoutParams();
2541            if (params == null) {
2542                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
2543            }
2544        }
2545        addView(child, index, params);
2546    }
2547
2548    /**
2549     * Adds a child view with this ViewGroup's default layout parameters and the
2550     * specified width and height.
2551     *
2552     * @param child the child view to add
2553     */
2554    public void addView(View child, int width, int height) {
2555        final LayoutParams params = generateDefaultLayoutParams();
2556        params.width = width;
2557        params.height = height;
2558        addView(child, -1, params);
2559    }
2560
2561    /**
2562     * Adds a child view with the specified layout parameters.
2563     *
2564     * @param child the child view to add
2565     * @param params the layout parameters to set on the child
2566     */
2567    public void addView(View child, LayoutParams params) {
2568        addView(child, -1, params);
2569    }
2570
2571    /**
2572     * Adds a child view with the specified layout parameters.
2573     *
2574     * @param child the child view to add
2575     * @param index the position at which to add the child
2576     * @param params the layout parameters to set on the child
2577     */
2578    public void addView(View child, int index, LayoutParams params) {
2579        if (DBG) {
2580            System.out.println(this + " addView");
2581        }
2582
2583        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
2584        // therefore, we call requestLayout() on ourselves before, so that the child's request
2585        // will be blocked at our level
2586        requestLayout();
2587        invalidate();
2588        addViewInner(child, index, params, false);
2589    }
2590
2591    /**
2592     * {@inheritDoc}
2593     */
2594    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
2595        if (!checkLayoutParams(params)) {
2596            throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
2597        }
2598        if (view.mParent != this) {
2599            throw new IllegalArgumentException("Given view not a child of " + this);
2600        }
2601        view.setLayoutParams(params);
2602    }
2603
2604    /**
2605     * {@inheritDoc}
2606     */
2607    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
2608        return  p != null;
2609    }
2610
2611    /**
2612     * Interface definition for a callback to be invoked when the hierarchy
2613     * within this view changed. The hierarchy changes whenever a child is added
2614     * to or removed from this view.
2615     */
2616    public interface OnHierarchyChangeListener {
2617        /**
2618         * Called when a new child is added to a parent view.
2619         *
2620         * @param parent the view in which a child was added
2621         * @param child the new child view added in the hierarchy
2622         */
2623        void onChildViewAdded(View parent, View child);
2624
2625        /**
2626         * Called when a child is removed from a parent view.
2627         *
2628         * @param parent the view from which the child was removed
2629         * @param child the child removed from the hierarchy
2630         */
2631        void onChildViewRemoved(View parent, View child);
2632    }
2633
2634    /**
2635     * Register a callback to be invoked when a child is added to or removed
2636     * from this view.
2637     *
2638     * @param listener the callback to invoke on hierarchy change
2639     */
2640    public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
2641        mOnHierarchyChangeListener = listener;
2642    }
2643
2644    /**
2645     * Adds a view during layout. This is useful if in your onLayout() method,
2646     * you need to add more views (as does the list view for example).
2647     *
2648     * If index is negative, it means put it at the end of the list.
2649     *
2650     * @param child the view to add to the group
2651     * @param index the index at which the child must be added
2652     * @param params the layout parameters to associate with the child
2653     * @return true if the child was added, false otherwise
2654     */
2655    protected boolean addViewInLayout(View child, int index, LayoutParams params) {
2656        return addViewInLayout(child, index, params, false);
2657    }
2658
2659    /**
2660     * Adds a view during layout. This is useful if in your onLayout() method,
2661     * you need to add more views (as does the list view for example).
2662     *
2663     * If index is negative, it means put it at the end of the list.
2664     *
2665     * @param child the view to add to the group
2666     * @param index the index at which the child must be added
2667     * @param params the layout parameters to associate with the child
2668     * @param preventRequestLayout if true, calling this method will not trigger a
2669     *        layout request on child
2670     * @return true if the child was added, false otherwise
2671     */
2672    protected boolean addViewInLayout(View child, int index, LayoutParams params,
2673            boolean preventRequestLayout) {
2674        child.mParent = null;
2675        addViewInner(child, index, params, preventRequestLayout);
2676        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
2677        return true;
2678    }
2679
2680    /**
2681     * Prevents the specified child to be laid out during the next layout pass.
2682     *
2683     * @param child the child on which to perform the cleanup
2684     */
2685    protected void cleanupLayoutState(View child) {
2686        child.mPrivateFlags &= ~View.FORCE_LAYOUT;
2687    }
2688
2689    private void addViewInner(View child, int index, LayoutParams params,
2690            boolean preventRequestLayout) {
2691
2692        if (child.getParent() != null) {
2693            throw new IllegalStateException("The specified child already has a parent. " +
2694                    "You must call removeView() on the child's parent first.");
2695        }
2696
2697        if (mTransition != null) {
2698            mTransition.addChild(this, child);
2699        }
2700
2701        if (!checkLayoutParams(params)) {
2702            params = generateLayoutParams(params);
2703        }
2704
2705        if (preventRequestLayout) {
2706            child.mLayoutParams = params;
2707        } else {
2708            child.setLayoutParams(params);
2709        }
2710
2711        if (index < 0) {
2712            index = mChildrenCount;
2713        }
2714
2715        addInArray(child, index);
2716
2717        // tell our children
2718        if (preventRequestLayout) {
2719            child.assignParent(this);
2720        } else {
2721            child.mParent = this;
2722        }
2723
2724        if (child.hasFocus()) {
2725            requestChildFocus(child, child.findFocus());
2726        }
2727
2728        AttachInfo ai = mAttachInfo;
2729        if (ai != null) {
2730            boolean lastKeepOn = ai.mKeepScreenOn;
2731            ai.mKeepScreenOn = false;
2732            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
2733            if (ai.mKeepScreenOn) {
2734                needGlobalAttributesUpdate(true);
2735            }
2736            ai.mKeepScreenOn = lastKeepOn;
2737        }
2738
2739        if (mOnHierarchyChangeListener != null) {
2740            mOnHierarchyChangeListener.onChildViewAdded(this, child);
2741        }
2742
2743        if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
2744            mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
2745        }
2746    }
2747
2748    private void addInArray(View child, int index) {
2749        View[] children = mChildren;
2750        final int count = mChildrenCount;
2751        final int size = children.length;
2752        if (index == count) {
2753            if (size == count) {
2754                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
2755                System.arraycopy(children, 0, mChildren, 0, size);
2756                children = mChildren;
2757            }
2758            children[mChildrenCount++] = child;
2759        } else if (index < count) {
2760            if (size == count) {
2761                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
2762                System.arraycopy(children, 0, mChildren, 0, index);
2763                System.arraycopy(children, index, mChildren, index + 1, count - index);
2764                children = mChildren;
2765            } else {
2766                System.arraycopy(children, index, children, index + 1, count - index);
2767            }
2768            children[index] = child;
2769            mChildrenCount++;
2770        } else {
2771            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
2772        }
2773    }
2774
2775    // This method also sets the child's mParent to null
2776    private void removeFromArray(int index) {
2777        final View[] children = mChildren;
2778        if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
2779            children[index].mParent = null;
2780        }
2781        final int count = mChildrenCount;
2782        if (index == count - 1) {
2783            children[--mChildrenCount] = null;
2784        } else if (index >= 0 && index < count) {
2785            System.arraycopy(children, index + 1, children, index, count - index - 1);
2786            children[--mChildrenCount] = null;
2787        } else {
2788            throw new IndexOutOfBoundsException();
2789        }
2790    }
2791
2792    // This method also sets the children's mParent to null
2793    private void removeFromArray(int start, int count) {
2794        final View[] children = mChildren;
2795        final int childrenCount = mChildrenCount;
2796
2797        start = Math.max(0, start);
2798        final int end = Math.min(childrenCount, start + count);
2799
2800        if (start == end) {
2801            return;
2802        }
2803
2804        if (end == childrenCount) {
2805            for (int i = start; i < end; i++) {
2806                children[i].mParent = null;
2807                children[i] = null;
2808            }
2809        } else {
2810            for (int i = start; i < end; i++) {
2811                children[i].mParent = null;
2812            }
2813
2814            // Since we're looping above, we might as well do the copy, but is arraycopy()
2815            // faster than the extra 2 bounds checks we would do in the loop?
2816            System.arraycopy(children, end, children, start, childrenCount - end);
2817
2818            for (int i = childrenCount - (end - start); i < childrenCount; i++) {
2819                children[i] = null;
2820            }
2821        }
2822
2823        mChildrenCount -= (end - start);
2824    }
2825
2826    private void bindLayoutAnimation(View child) {
2827        Animation a = mLayoutAnimationController.getAnimationForView(child);
2828        child.setAnimation(a);
2829    }
2830
2831    /**
2832     * Subclasses should override this method to set layout animation
2833     * parameters on the supplied child.
2834     *
2835     * @param child the child to associate with animation parameters
2836     * @param params the child's layout parameters which hold the animation
2837     *        parameters
2838     * @param index the index of the child in the view group
2839     * @param count the number of children in the view group
2840     */
2841    protected void attachLayoutAnimationParameters(View child,
2842            LayoutParams params, int index, int count) {
2843        LayoutAnimationController.AnimationParameters animationParams =
2844                    params.layoutAnimationParameters;
2845        if (animationParams == null) {
2846            animationParams = new LayoutAnimationController.AnimationParameters();
2847            params.layoutAnimationParameters = animationParams;
2848        }
2849
2850        animationParams.count = count;
2851        animationParams.index = index;
2852    }
2853
2854    /**
2855     * {@inheritDoc}
2856     */
2857    public void removeView(View view) {
2858        removeViewInternal(view);
2859        requestLayout();
2860        invalidate();
2861    }
2862
2863    /**
2864     * Removes a view during layout. This is useful if in your onLayout() method,
2865     * you need to remove more views.
2866     *
2867     * @param view the view to remove from the group
2868     */
2869    public void removeViewInLayout(View view) {
2870        removeViewInternal(view);
2871    }
2872
2873    /**
2874     * Removes a range of views during layout. This is useful if in your onLayout() method,
2875     * you need to remove more views.
2876     *
2877     * @param start the index of the first view to remove from the group
2878     * @param count the number of views to remove from the group
2879     */
2880    public void removeViewsInLayout(int start, int count) {
2881        removeViewsInternal(start, count);
2882    }
2883
2884    /**
2885     * Removes the view at the specified position in the group.
2886     *
2887     * @param index the position in the group of the view to remove
2888     */
2889    public void removeViewAt(int index) {
2890        removeViewInternal(index, getChildAt(index));
2891        requestLayout();
2892        invalidate();
2893    }
2894
2895    /**
2896     * Removes the specified range of views from the group.
2897     *
2898     * @param start the first position in the group of the range of views to remove
2899     * @param count the number of views to remove
2900     */
2901    public void removeViews(int start, int count) {
2902        removeViewsInternal(start, count);
2903        requestLayout();
2904        invalidate();
2905    }
2906
2907    private void removeViewInternal(View view) {
2908        final int index = indexOfChild(view);
2909        if (index >= 0) {
2910            removeViewInternal(index, view);
2911        }
2912    }
2913
2914    private void removeViewInternal(int index, View view) {
2915
2916        if (mTransition != null) {
2917            mTransition.removeChild(this, view);
2918        }
2919
2920        boolean clearChildFocus = false;
2921        if (view == mFocused) {
2922            view.clearFocusForRemoval();
2923            clearChildFocus = true;
2924        }
2925
2926        if (view.getAnimation() != null ||
2927                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
2928            addDisappearingView(view);
2929        } else if (view.mAttachInfo != null) {
2930           view.dispatchDetachedFromWindow();
2931        }
2932
2933        if (mOnHierarchyChangeListener != null) {
2934            mOnHierarchyChangeListener.onChildViewRemoved(this, view);
2935        }
2936
2937        needGlobalAttributesUpdate(false);
2938
2939        removeFromArray(index);
2940
2941        if (clearChildFocus) {
2942            clearChildFocus(view);
2943        }
2944    }
2945
2946    /**
2947     * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
2948     * not null, changes in layout which occur because of children being added to or removed from
2949     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
2950     * object. By default, the transition object is null (so layout changes are not animated).
2951     *
2952     * @param transition The LayoutTransition object that will animated changes in layout. A value
2953     * of <code>null</code> means no transition will run on layout changes.
2954     * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
2955     */
2956    public void setLayoutTransition(LayoutTransition transition) {
2957        if (mTransition != null) {
2958            mTransition.removeTransitionListener(mLayoutTransitionListener);
2959        }
2960        mTransition = transition;
2961        if (mTransition != null) {
2962            mTransition.addTransitionListener(mLayoutTransitionListener);
2963        }
2964    }
2965
2966    /**
2967     * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
2968     * not null, changes in layout which occur because of children being added to or removed from
2969     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
2970     * object. By default, the transition object is null (so layout changes are not animated).
2971     *
2972     * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
2973     * A value of <code>null</code> means no transition will run on layout changes.
2974     */
2975    public LayoutTransition getLayoutTransition() {
2976        return mTransition;
2977    }
2978
2979    private void removeViewsInternal(int start, int count) {
2980        final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
2981        final boolean notifyListener = onHierarchyChangeListener != null;
2982        final View focused = mFocused;
2983        final boolean detach = mAttachInfo != null;
2984        View clearChildFocus = null;
2985
2986        final View[] children = mChildren;
2987        final int end = start + count;
2988
2989        for (int i = start; i < end; i++) {
2990            final View view = children[i];
2991
2992            if (mTransition != null) {
2993                mTransition.removeChild(this, view);
2994            }
2995
2996            if (view == focused) {
2997                view.clearFocusForRemoval();
2998                clearChildFocus = view;
2999            }
3000
3001            if (view.getAnimation() != null ||
3002                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
3003                addDisappearingView(view);
3004            } else if (detach) {
3005               view.dispatchDetachedFromWindow();
3006            }
3007
3008            needGlobalAttributesUpdate(false);
3009
3010            if (notifyListener) {
3011                onHierarchyChangeListener.onChildViewRemoved(this, view);
3012            }
3013        }
3014
3015        removeFromArray(start, count);
3016
3017        if (clearChildFocus != null) {
3018            clearChildFocus(clearChildFocus);
3019        }
3020    }
3021
3022    /**
3023     * Call this method to remove all child views from the
3024     * ViewGroup.
3025     */
3026    public void removeAllViews() {
3027        removeAllViewsInLayout();
3028        requestLayout();
3029        invalidate();
3030    }
3031
3032    /**
3033     * Called by a ViewGroup subclass to remove child views from itself,
3034     * when it must first know its size on screen before it can calculate how many
3035     * child views it will render. An example is a Gallery or a ListView, which
3036     * may "have" 50 children, but actually only render the number of children
3037     * that can currently fit inside the object on screen. Do not call
3038     * this method unless you are extending ViewGroup and understand the
3039     * view measuring and layout pipeline.
3040     */
3041    public void removeAllViewsInLayout() {
3042        final int count = mChildrenCount;
3043        if (count <= 0) {
3044            return;
3045        }
3046
3047        final View[] children = mChildren;
3048        mChildrenCount = 0;
3049
3050        final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
3051        final boolean notify = listener != null;
3052        final View focused = mFocused;
3053        final boolean detach = mAttachInfo != null;
3054        View clearChildFocus = null;
3055
3056        needGlobalAttributesUpdate(false);
3057
3058        for (int i = count - 1; i >= 0; i--) {
3059            final View view = children[i];
3060
3061            if (mTransition != null) {
3062                mTransition.removeChild(this, view);
3063            }
3064
3065            if (view == focused) {
3066                view.clearFocusForRemoval();
3067                clearChildFocus = view;
3068            }
3069
3070            if (view.getAnimation() != null ||
3071                    (mTransitioningViews != null && mTransitioningViews.contains(view))) {
3072                addDisappearingView(view);
3073            } else if (detach) {
3074               view.dispatchDetachedFromWindow();
3075            }
3076
3077            if (notify) {
3078                listener.onChildViewRemoved(this, view);
3079            }
3080
3081            view.mParent = null;
3082            children[i] = null;
3083        }
3084
3085        if (clearChildFocus != null) {
3086            clearChildFocus(clearChildFocus);
3087        }
3088    }
3089
3090    /**
3091     * Finishes the removal of a detached view. This method will dispatch the detached from
3092     * window event and notify the hierarchy change listener.
3093     *
3094     * @param child the child to be definitely removed from the view hierarchy
3095     * @param animate if true and the view has an animation, the view is placed in the
3096     *                disappearing views list, otherwise, it is detached from the window
3097     *
3098     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3099     * @see #detachAllViewsFromParent()
3100     * @see #detachViewFromParent(View)
3101     * @see #detachViewFromParent(int)
3102     */
3103    protected void removeDetachedView(View child, boolean animate) {
3104        if (mTransition != null) {
3105            mTransition.removeChild(this, child);
3106        }
3107
3108        if (child == mFocused) {
3109            child.clearFocus();
3110        }
3111
3112        if ((animate && child.getAnimation() != null) ||
3113                (mTransitioningViews != null && mTransitioningViews.contains(child))) {
3114            addDisappearingView(child);
3115        } else if (child.mAttachInfo != null) {
3116            child.dispatchDetachedFromWindow();
3117        }
3118
3119        if (mOnHierarchyChangeListener != null) {
3120            mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3121        }
3122    }
3123
3124    /**
3125     * Attaches a view to this view group. Attaching a view assigns this group as the parent,
3126     * sets the layout parameters and puts the view in the list of children so it can be retrieved
3127     * by calling {@link #getChildAt(int)}.
3128     *
3129     * This method should be called only for view which were detached from their parent.
3130     *
3131     * @param child the child to attach
3132     * @param index the index at which the child should be attached
3133     * @param params the layout parameters of the child
3134     *
3135     * @see #removeDetachedView(View, boolean)
3136     * @see #detachAllViewsFromParent()
3137     * @see #detachViewFromParent(View)
3138     * @see #detachViewFromParent(int)
3139     */
3140    protected void attachViewToParent(View child, int index, LayoutParams params) {
3141        child.mLayoutParams = params;
3142
3143        if (index < 0) {
3144            index = mChildrenCount;
3145        }
3146
3147        addInArray(child, index);
3148
3149        child.mParent = this;
3150        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) | DRAWN;
3151
3152        if (child.hasFocus()) {
3153            requestChildFocus(child, child.findFocus());
3154        }
3155    }
3156
3157    /**
3158     * Detaches a view from its parent. Detaching a view should be temporary and followed
3159     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3160     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3161     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3162     *
3163     * @param child the child to detach
3164     *
3165     * @see #detachViewFromParent(int)
3166     * @see #detachViewsFromParent(int, int)
3167     * @see #detachAllViewsFromParent()
3168     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3169     * @see #removeDetachedView(View, boolean)
3170     */
3171    protected void detachViewFromParent(View child) {
3172        removeFromArray(indexOfChild(child));
3173    }
3174
3175    /**
3176     * Detaches a view from its parent. Detaching a view should be temporary and followed
3177     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3178     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3179     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3180     *
3181     * @param index the index of the child to detach
3182     *
3183     * @see #detachViewFromParent(View)
3184     * @see #detachAllViewsFromParent()
3185     * @see #detachViewsFromParent(int, int)
3186     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3187     * @see #removeDetachedView(View, boolean)
3188     */
3189    protected void detachViewFromParent(int index) {
3190        removeFromArray(index);
3191    }
3192
3193    /**
3194     * Detaches a range of view from their parent. Detaching a view should be temporary and followed
3195     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3196     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
3197     * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3198     *
3199     * @param start the first index of the childrend range to detach
3200     * @param count the number of children to detach
3201     *
3202     * @see #detachViewFromParent(View)
3203     * @see #detachViewFromParent(int)
3204     * @see #detachAllViewsFromParent()
3205     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3206     * @see #removeDetachedView(View, boolean)
3207     */
3208    protected void detachViewsFromParent(int start, int count) {
3209        removeFromArray(start, count);
3210    }
3211
3212    /**
3213     * Detaches all views from the parent. Detaching a view should be temporary and followed
3214     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3215     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3216     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3217     *
3218     * @see #detachViewFromParent(View)
3219     * @see #detachViewFromParent(int)
3220     * @see #detachViewsFromParent(int, int)
3221     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3222     * @see #removeDetachedView(View, boolean)
3223     */
3224    protected void detachAllViewsFromParent() {
3225        final int count = mChildrenCount;
3226        if (count <= 0) {
3227            return;
3228        }
3229
3230        final View[] children = mChildren;
3231        mChildrenCount = 0;
3232
3233        for (int i = count - 1; i >= 0; i--) {
3234            children[i].mParent = null;
3235            children[i] = null;
3236        }
3237    }
3238
3239    /**
3240     * Don't call or override this method. It is used for the implementation of
3241     * the view hierarchy.
3242     */
3243    public final void invalidateChild(View child, final Rect dirty) {
3244        if (ViewDebug.TRACE_HIERARCHY) {
3245            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
3246        }
3247
3248        ViewParent parent = this;
3249
3250        final AttachInfo attachInfo = mAttachInfo;
3251        if (attachInfo != null) {
3252            // If the child is drawing an animation, we want to copy this flag onto
3253            // ourselves and the parent to make sure the invalidate request goes
3254            // through
3255            final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
3256
3257            if (dirty == null) {
3258                do {
3259                    View view = null;
3260                    if (parent instanceof View) {
3261                        view = (View) parent;
3262                        if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
3263                            // already marked dirty - we're done
3264                            break;
3265                        }
3266                    }
3267
3268                    if (drawAnimation) {
3269                        if (view != null) {
3270                            view.mPrivateFlags |= DRAW_ANIMATION;
3271                        } else if (parent instanceof ViewRoot) {
3272                            ((ViewRoot) parent).mIsAnimating = true;
3273                        }
3274                    }
3275
3276                    if (parent instanceof ViewRoot) {
3277                        ((ViewRoot) parent).invalidate();
3278                        parent = null;
3279                    } else if (view != null) {
3280                        if ((mPrivateFlags & DRAWN) == DRAWN) {
3281                            view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3282                            view.mPrivateFlags |= DIRTY;
3283                            parent = view.mParent;
3284                        } else {
3285                            parent = null;
3286                        }
3287                    }
3288                } while (parent != null);
3289            } else {
3290                // Check whether the child that requests the invalidate is fully opaque
3291                final boolean isOpaque = child.isOpaque() && !drawAnimation &&
3292                        child.getAnimation() == null;
3293                // Mark the child as dirty, using the appropriate flag
3294                // Make sure we do not set both flags at the same time
3295                final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
3296
3297                final int[] location = attachInfo.mInvalidateChildLocation;
3298                location[CHILD_LEFT_INDEX] = child.mLeft;
3299                location[CHILD_TOP_INDEX] = child.mTop;
3300                Matrix childMatrix = child.getMatrix();
3301                if (!childMatrix.isIdentity()) {
3302                    RectF boundingRect = attachInfo.mTmpTransformRect;
3303                    boundingRect.set(dirty);
3304                    childMatrix.mapRect(boundingRect);
3305                    dirty.set((int) boundingRect.left, (int) boundingRect.top,
3306                            (int) (boundingRect.right + 0.5f),
3307                            (int) (boundingRect.bottom + 0.5f));
3308                }
3309
3310                do {
3311                    View view = null;
3312                    if (parent instanceof View) {
3313                        view = (View) parent;
3314                    }
3315
3316                    if (drawAnimation) {
3317                        if (view != null) {
3318                            view.mPrivateFlags |= DRAW_ANIMATION;
3319                        } else if (parent instanceof ViewRoot) {
3320                            ((ViewRoot) parent).mIsAnimating = true;
3321                        }
3322                    }
3323
3324                    // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
3325                    // flag coming from the child that initiated the invalidate
3326                    if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
3327                        view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
3328                    }
3329
3330                    parent = parent.invalidateChildInParent(location, dirty);
3331                    if (view != null) {
3332                        // Account for transform on current parent
3333                        Matrix m = view.getMatrix();
3334                        if (!m.isIdentity()) {
3335                            RectF boundingRect = attachInfo.mTmpTransformRect;
3336                            boundingRect.set(dirty);
3337                            m.mapRect(boundingRect);
3338                            dirty.set((int) boundingRect.left, (int) boundingRect.top,
3339                                    (int) (boundingRect.right + 0.5f),
3340                                    (int) (boundingRect.bottom + 0.5f));
3341                        }
3342                    }
3343                } while (parent != null);
3344            }
3345        }
3346    }
3347
3348    /**
3349     * Don't call or override this method. It is used for the implementation of
3350     * the view hierarchy.
3351     *
3352     * This implementation returns null if this ViewGroup does not have a parent,
3353     * if this ViewGroup is already fully invalidated or if the dirty rectangle
3354     * does not intersect with this ViewGroup's bounds.
3355     */
3356    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
3357        if (ViewDebug.TRACE_HIERARCHY) {
3358            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
3359        }
3360
3361        if ((mPrivateFlags & DRAWN) == DRAWN) {
3362            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
3363                        FLAG_OPTIMIZE_INVALIDATE) {
3364                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
3365                        location[CHILD_TOP_INDEX] - mScrollY);
3366
3367                final int left = mLeft;
3368                final int top = mTop;
3369
3370                if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
3371                        (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
3372                    mPrivateFlags &= ~DRAWING_CACHE_VALID;
3373
3374                    location[CHILD_LEFT_INDEX] = left;
3375                    location[CHILD_TOP_INDEX] = top;
3376
3377                    return mParent;
3378                }
3379            } else {
3380                mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
3381
3382                location[CHILD_LEFT_INDEX] = mLeft;
3383                location[CHILD_TOP_INDEX] = mTop;
3384
3385                dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX],
3386                        mBottom - location[CHILD_TOP_INDEX]);
3387
3388                return mParent;
3389            }
3390        }
3391
3392        return null;
3393    }
3394
3395    /**
3396     * Offset a rectangle that is in a descendant's coordinate
3397     * space into our coordinate space.
3398     * @param descendant A descendant of this view
3399     * @param rect A rectangle defined in descendant's coordinate space.
3400     */
3401    public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
3402        offsetRectBetweenParentAndChild(descendant, rect, true, false);
3403    }
3404
3405    /**
3406     * Offset a rectangle that is in our coordinate space into an ancestor's
3407     * coordinate space.
3408     * @param descendant A descendant of this view
3409     * @param rect A rectangle defined in descendant's coordinate space.
3410     */
3411    public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
3412        offsetRectBetweenParentAndChild(descendant, rect, false, false);
3413    }
3414
3415    /**
3416     * Helper method that offsets a rect either from parent to descendant or
3417     * descendant to parent.
3418     */
3419    void offsetRectBetweenParentAndChild(View descendant, Rect rect,
3420            boolean offsetFromChildToParent, boolean clipToBounds) {
3421
3422        // already in the same coord system :)
3423        if (descendant == this) {
3424            return;
3425        }
3426
3427        ViewParent theParent = descendant.mParent;
3428
3429        // search and offset up to the parent
3430        while ((theParent != null)
3431                && (theParent instanceof View)
3432                && (theParent != this)) {
3433
3434            if (offsetFromChildToParent) {
3435                rect.offset(descendant.mLeft - descendant.mScrollX,
3436                        descendant.mTop - descendant.mScrollY);
3437                if (clipToBounds) {
3438                    View p = (View) theParent;
3439                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
3440                }
3441            } else {
3442                if (clipToBounds) {
3443                    View p = (View) theParent;
3444                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
3445                }
3446                rect.offset(descendant.mScrollX - descendant.mLeft,
3447                        descendant.mScrollY - descendant.mTop);
3448            }
3449
3450            descendant = (View) theParent;
3451            theParent = descendant.mParent;
3452        }
3453
3454        // now that we are up to this view, need to offset one more time
3455        // to get into our coordinate space
3456        if (theParent == this) {
3457            if (offsetFromChildToParent) {
3458                rect.offset(descendant.mLeft - descendant.mScrollX,
3459                        descendant.mTop - descendant.mScrollY);
3460            } else {
3461                rect.offset(descendant.mScrollX - descendant.mLeft,
3462                        descendant.mScrollY - descendant.mTop);
3463            }
3464        } else {
3465            throw new IllegalArgumentException("parameter must be a descendant of this view");
3466        }
3467    }
3468
3469    /**
3470     * Offset the vertical location of all children of this view by the specified number of pixels.
3471     *
3472     * @param offset the number of pixels to offset
3473     *
3474     * @hide
3475     */
3476    public void offsetChildrenTopAndBottom(int offset) {
3477        final int count = mChildrenCount;
3478        final View[] children = mChildren;
3479
3480        for (int i = 0; i < count; i++) {
3481            final View v = children[i];
3482            v.mTop += offset;
3483            v.mBottom += offset;
3484        }
3485    }
3486
3487    /**
3488     * {@inheritDoc}
3489     */
3490    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
3491        int dx = child.mLeft - mScrollX;
3492        int dy = child.mTop - mScrollY;
3493        if (offset != null) {
3494            offset.x += dx;
3495            offset.y += dy;
3496        }
3497        r.offset(dx, dy);
3498        return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
3499               (mParent == null || mParent.getChildVisibleRect(this, r, offset));
3500    }
3501
3502    /**
3503     * {@inheritDoc}
3504     */
3505    @Override
3506    protected abstract void onLayout(boolean changed,
3507            int l, int t, int r, int b);
3508
3509    /**
3510     * Indicates whether the view group has the ability to animate its children
3511     * after the first layout.
3512     *
3513     * @return true if the children can be animated, false otherwise
3514     */
3515    protected boolean canAnimate() {
3516        return mLayoutAnimationController != null;
3517    }
3518
3519    /**
3520     * Runs the layout animation. Calling this method triggers a relayout of
3521     * this view group.
3522     */
3523    public void startLayoutAnimation() {
3524        if (mLayoutAnimationController != null) {
3525            mGroupFlags |= FLAG_RUN_ANIMATION;
3526            requestLayout();
3527        }
3528    }
3529
3530    /**
3531     * Schedules the layout animation to be played after the next layout pass
3532     * of this view group. This can be used to restart the layout animation
3533     * when the content of the view group changes or when the activity is
3534     * paused and resumed.
3535     */
3536    public void scheduleLayoutAnimation() {
3537        mGroupFlags |= FLAG_RUN_ANIMATION;
3538    }
3539
3540    /**
3541     * Sets the layout animation controller used to animate the group's
3542     * children after the first layout.
3543     *
3544     * @param controller the animation controller
3545     */
3546    public void setLayoutAnimation(LayoutAnimationController controller) {
3547        mLayoutAnimationController = controller;
3548        if (mLayoutAnimationController != null) {
3549            mGroupFlags |= FLAG_RUN_ANIMATION;
3550        }
3551    }
3552
3553    /**
3554     * Returns the layout animation controller used to animate the group's
3555     * children.
3556     *
3557     * @return the current animation controller
3558     */
3559    public LayoutAnimationController getLayoutAnimation() {
3560        return mLayoutAnimationController;
3561    }
3562
3563    /**
3564     * Indicates whether the children's drawing cache is used during a layout
3565     * animation. By default, the drawing cache is enabled but this will prevent
3566     * nested layout animations from working. To nest animations, you must disable
3567     * the cache.
3568     *
3569     * @return true if the animation cache is enabled, false otherwise
3570     *
3571     * @see #setAnimationCacheEnabled(boolean)
3572     * @see View#setDrawingCacheEnabled(boolean)
3573     */
3574    @ViewDebug.ExportedProperty
3575    public boolean isAnimationCacheEnabled() {
3576        return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
3577    }
3578
3579    /**
3580     * Enables or disables the children's drawing cache during a layout animation.
3581     * By default, the drawing cache is enabled but this will prevent nested
3582     * layout animations from working. To nest animations, you must disable the
3583     * cache.
3584     *
3585     * @param enabled true to enable the animation cache, false otherwise
3586     *
3587     * @see #isAnimationCacheEnabled()
3588     * @see View#setDrawingCacheEnabled(boolean)
3589     */
3590    public void setAnimationCacheEnabled(boolean enabled) {
3591        setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
3592    }
3593
3594    /**
3595     * Indicates whether this ViewGroup will always try to draw its children using their
3596     * drawing cache. By default this property is enabled.
3597     *
3598     * @return true if the animation cache is enabled, false otherwise
3599     *
3600     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
3601     * @see #setChildrenDrawnWithCacheEnabled(boolean)
3602     * @see View#setDrawingCacheEnabled(boolean)
3603     */
3604    @ViewDebug.ExportedProperty(category = "drawing")
3605    public boolean isAlwaysDrawnWithCacheEnabled() {
3606        return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
3607    }
3608
3609    /**
3610     * Indicates whether this ViewGroup will always try to draw its children using their
3611     * drawing cache. This property can be set to true when the cache rendering is
3612     * slightly different from the children's normal rendering. Renderings can be different,
3613     * for instance, when the cache's quality is set to low.
3614     *
3615     * When this property is disabled, the ViewGroup will use the drawing cache of its
3616     * children only when asked to. It's usually the task of subclasses to tell ViewGroup
3617     * when to start using the drawing cache and when to stop using it.
3618     *
3619     * @param always true to always draw with the drawing cache, false otherwise
3620     *
3621     * @see #isAlwaysDrawnWithCacheEnabled()
3622     * @see #setChildrenDrawnWithCacheEnabled(boolean)
3623     * @see View#setDrawingCacheEnabled(boolean)
3624     * @see View#setDrawingCacheQuality(int)
3625     */
3626    public void setAlwaysDrawnWithCacheEnabled(boolean always) {
3627        setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
3628    }
3629
3630    /**
3631     * Indicates whether the ViewGroup is currently drawing its children using
3632     * their drawing cache.
3633     *
3634     * @return true if children should be drawn with their cache, false otherwise
3635     *
3636     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
3637     * @see #setChildrenDrawnWithCacheEnabled(boolean)
3638     */
3639    @ViewDebug.ExportedProperty(category = "drawing")
3640    protected boolean isChildrenDrawnWithCacheEnabled() {
3641        return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
3642    }
3643
3644    /**
3645     * Tells the ViewGroup to draw its children using their drawing cache. This property
3646     * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
3647     * will be used only if it has been enabled.
3648     *
3649     * Subclasses should call this method to start and stop using the drawing cache when
3650     * they perform performance sensitive operations, like scrolling or animating.
3651     *
3652     * @param enabled true if children should be drawn with their cache, false otherwise
3653     *
3654     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
3655     * @see #isChildrenDrawnWithCacheEnabled()
3656     */
3657    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
3658        setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
3659    }
3660
3661    /**
3662     * Indicates whether the ViewGroup is drawing its children in the order defined by
3663     * {@link #getChildDrawingOrder(int, int)}.
3664     *
3665     * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
3666     *         false otherwise
3667     *
3668     * @see #setChildrenDrawingOrderEnabled(boolean)
3669     * @see #getChildDrawingOrder(int, int)
3670     */
3671    @ViewDebug.ExportedProperty(category = "drawing")
3672    protected boolean isChildrenDrawingOrderEnabled() {
3673        return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
3674    }
3675
3676    /**
3677     * Tells the ViewGroup whether to draw its children in the order defined by the method
3678     * {@link #getChildDrawingOrder(int, int)}.
3679     *
3680     * @param enabled true if the order of the children when drawing is determined by
3681     *        {@link #getChildDrawingOrder(int, int)}, false otherwise
3682     *
3683     * @see #isChildrenDrawingOrderEnabled()
3684     * @see #getChildDrawingOrder(int, int)
3685     */
3686    protected void setChildrenDrawingOrderEnabled(boolean enabled) {
3687        setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
3688    }
3689
3690    private void setBooleanFlag(int flag, boolean value) {
3691        if (value) {
3692            mGroupFlags |= flag;
3693        } else {
3694            mGroupFlags &= ~flag;
3695        }
3696    }
3697
3698    /**
3699     * Returns an integer indicating what types of drawing caches are kept in memory.
3700     *
3701     * @see #setPersistentDrawingCache(int)
3702     * @see #setAnimationCacheEnabled(boolean)
3703     *
3704     * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
3705     *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
3706     *         and {@link #PERSISTENT_ALL_CACHES}
3707     */
3708    @ViewDebug.ExportedProperty(category = "drawing", mapping = {
3709        @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
3710        @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
3711        @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
3712        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
3713    })
3714    public int getPersistentDrawingCache() {
3715        return mPersistentDrawingCache;
3716    }
3717
3718    /**
3719     * Indicates what types of drawing caches should be kept in memory after
3720     * they have been created.
3721     *
3722     * @see #getPersistentDrawingCache()
3723     * @see #setAnimationCacheEnabled(boolean)
3724     *
3725     * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
3726     *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
3727     *        and {@link #PERSISTENT_ALL_CACHES}
3728     */
3729    public void setPersistentDrawingCache(int drawingCacheToKeep) {
3730        mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
3731    }
3732
3733    /**
3734     * Returns a new set of layout parameters based on the supplied attributes set.
3735     *
3736     * @param attrs the attributes to build the layout parameters from
3737     *
3738     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
3739     *         of its descendants
3740     */
3741    public LayoutParams generateLayoutParams(AttributeSet attrs) {
3742        return new LayoutParams(getContext(), attrs);
3743    }
3744
3745    /**
3746     * Returns a safe set of layout parameters based on the supplied layout params.
3747     * When a ViewGroup is passed a View whose layout params do not pass the test of
3748     * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
3749     * is invoked. This method should return a new set of layout params suitable for
3750     * this ViewGroup, possibly by copying the appropriate attributes from the
3751     * specified set of layout params.
3752     *
3753     * @param p The layout parameters to convert into a suitable set of layout parameters
3754     *          for this ViewGroup.
3755     *
3756     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
3757     *         of its descendants
3758     */
3759    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
3760        return p;
3761    }
3762
3763    /**
3764     * Returns a set of default layout parameters. These parameters are requested
3765     * when the View passed to {@link #addView(View)} has no layout parameters
3766     * already set. If null is returned, an exception is thrown from addView.
3767     *
3768     * @return a set of default layout parameters or null
3769     */
3770    protected LayoutParams generateDefaultLayoutParams() {
3771        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
3772    }
3773
3774    /**
3775     * @hide
3776     */
3777    @Override
3778    protected boolean dispatchConsistencyCheck(int consistency) {
3779        boolean result = super.dispatchConsistencyCheck(consistency);
3780
3781        final int count = mChildrenCount;
3782        final View[] children = mChildren;
3783        for (int i = 0; i < count; i++) {
3784            if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
3785        }
3786
3787        return result;
3788    }
3789
3790    /**
3791     * @hide
3792     */
3793    @Override
3794    protected boolean onConsistencyCheck(int consistency) {
3795        boolean result = super.onConsistencyCheck(consistency);
3796
3797        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
3798        final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
3799
3800        if (checkLayout) {
3801            final int count = mChildrenCount;
3802            final View[] children = mChildren;
3803            for (int i = 0; i < count; i++) {
3804                if (children[i].getParent() != this) {
3805                    result = false;
3806                    android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
3807                            "View " + children[i] + " has no parent/a parent that is not " + this);
3808                }
3809            }
3810        }
3811
3812        if (checkDrawing) {
3813            // If this group is dirty, check that the parent is dirty as well
3814            if ((mPrivateFlags & DIRTY_MASK) != 0) {
3815                final ViewParent parent = getParent();
3816                if (parent != null && !(parent instanceof ViewRoot)) {
3817                    if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
3818                        result = false;
3819                        android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
3820                                "ViewGroup " + this + " is dirty but its parent is not: " + this);
3821                    }
3822                }
3823            }
3824        }
3825
3826        return result;
3827    }
3828
3829    /**
3830     * {@inheritDoc}
3831     */
3832    @Override
3833    protected void debug(int depth) {
3834        super.debug(depth);
3835        String output;
3836
3837        if (mFocused != null) {
3838            output = debugIndent(depth);
3839            output += "mFocused";
3840            Log.d(VIEW_LOG_TAG, output);
3841        }
3842        if (mChildrenCount != 0) {
3843            output = debugIndent(depth);
3844            output += "{";
3845            Log.d(VIEW_LOG_TAG, output);
3846        }
3847        int count = mChildrenCount;
3848        for (int i = 0; i < count; i++) {
3849            View child = mChildren[i];
3850            child.debug(depth + 1);
3851        }
3852
3853        if (mChildrenCount != 0) {
3854            output = debugIndent(depth);
3855            output += "}";
3856            Log.d(VIEW_LOG_TAG, output);
3857        }
3858    }
3859
3860    /**
3861     * Returns the position in the group of the specified child view.
3862     *
3863     * @param child the view for which to get the position
3864     * @return a positive integer representing the position of the view in the
3865     *         group, or -1 if the view does not exist in the group
3866     */
3867    public int indexOfChild(View child) {
3868        final int count = mChildrenCount;
3869        final View[] children = mChildren;
3870        for (int i = 0; i < count; i++) {
3871            if (children[i] == child) {
3872                return i;
3873            }
3874        }
3875        return -1;
3876    }
3877
3878    /**
3879     * Returns the number of children in the group.
3880     *
3881     * @return a positive integer representing the number of children in
3882     *         the group
3883     */
3884    public int getChildCount() {
3885        return mChildrenCount;
3886    }
3887
3888    /**
3889     * Returns the view at the specified position in the group.
3890     *
3891     * @param index the position at which to get the view from
3892     * @return the view at the specified position or null if the position
3893     *         does not exist within the group
3894     */
3895    public View getChildAt(int index) {
3896        try {
3897            return mChildren[index];
3898        } catch (IndexOutOfBoundsException ex) {
3899            return null;
3900        }
3901    }
3902
3903    /**
3904     * Ask all of the children of this view to measure themselves, taking into
3905     * account both the MeasureSpec requirements for this view and its padding.
3906     * We skip children that are in the GONE state The heavy lifting is done in
3907     * getChildMeasureSpec.
3908     *
3909     * @param widthMeasureSpec The width requirements for this view
3910     * @param heightMeasureSpec The height requirements for this view
3911     */
3912    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
3913        final int size = mChildrenCount;
3914        final View[] children = mChildren;
3915        for (int i = 0; i < size; ++i) {
3916            final View child = children[i];
3917            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
3918                measureChild(child, widthMeasureSpec, heightMeasureSpec);
3919            }
3920        }
3921    }
3922
3923    /**
3924     * Ask one of the children of this view to measure itself, taking into
3925     * account both the MeasureSpec requirements for this view and its padding.
3926     * The heavy lifting is done in getChildMeasureSpec.
3927     *
3928     * @param child The child to measure
3929     * @param parentWidthMeasureSpec The width requirements for this view
3930     * @param parentHeightMeasureSpec The height requirements for this view
3931     */
3932    protected void measureChild(View child, int parentWidthMeasureSpec,
3933            int parentHeightMeasureSpec) {
3934        final LayoutParams lp = child.getLayoutParams();
3935
3936        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
3937                mPaddingLeft + mPaddingRight, lp.width);
3938        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
3939                mPaddingTop + mPaddingBottom, lp.height);
3940
3941        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
3942    }
3943
3944    /**
3945     * Ask one of the children of this view to measure itself, taking into
3946     * account both the MeasureSpec requirements for this view and its padding
3947     * and margins. The child must have MarginLayoutParams The heavy lifting is
3948     * done in getChildMeasureSpec.
3949     *
3950     * @param child The child to measure
3951     * @param parentWidthMeasureSpec The width requirements for this view
3952     * @param widthUsed Extra space that has been used up by the parent
3953     *        horizontally (possibly by other children of the parent)
3954     * @param parentHeightMeasureSpec The height requirements for this view
3955     * @param heightUsed Extra space that has been used up by the parent
3956     *        vertically (possibly by other children of the parent)
3957     */
3958    protected void measureChildWithMargins(View child,
3959            int parentWidthMeasureSpec, int widthUsed,
3960            int parentHeightMeasureSpec, int heightUsed) {
3961        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
3962
3963        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
3964                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
3965                        + widthUsed, lp.width);
3966        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
3967                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
3968                        + heightUsed, lp.height);
3969
3970        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
3971    }
3972
3973    /**
3974     * Does the hard part of measureChildren: figuring out the MeasureSpec to
3975     * pass to a particular child. This method figures out the right MeasureSpec
3976     * for one dimension (height or width) of one child view.
3977     *
3978     * The goal is to combine information from our MeasureSpec with the
3979     * LayoutParams of the child to get the best possible results. For example,
3980     * if the this view knows its size (because its MeasureSpec has a mode of
3981     * EXACTLY), and the child has indicated in its LayoutParams that it wants
3982     * to be the same size as the parent, the parent should ask the child to
3983     * layout given an exact size.
3984     *
3985     * @param spec The requirements for this view
3986     * @param padding The padding of this view for the current dimension and
3987     *        margins, if applicable
3988     * @param childDimension How big the child wants to be in the current
3989     *        dimension
3990     * @return a MeasureSpec integer for the child
3991     */
3992    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
3993        int specMode = MeasureSpec.getMode(spec);
3994        int specSize = MeasureSpec.getSize(spec);
3995
3996        int size = Math.max(0, specSize - padding);
3997
3998        int resultSize = 0;
3999        int resultMode = 0;
4000
4001        switch (specMode) {
4002        // Parent has imposed an exact size on us
4003        case MeasureSpec.EXACTLY:
4004            if (childDimension >= 0) {
4005                resultSize = childDimension;
4006                resultMode = MeasureSpec.EXACTLY;
4007            } else if (childDimension == LayoutParams.MATCH_PARENT) {
4008                // Child wants to be our size. So be it.
4009                resultSize = size;
4010                resultMode = MeasureSpec.EXACTLY;
4011            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4012                // Child wants to determine its own size. It can't be
4013                // bigger than us.
4014                resultSize = size;
4015                resultMode = MeasureSpec.AT_MOST;
4016            }
4017            break;
4018
4019        // Parent has imposed a maximum size on us
4020        case MeasureSpec.AT_MOST:
4021            if (childDimension >= 0) {
4022                // Child wants a specific size... so be it
4023                resultSize = childDimension;
4024                resultMode = MeasureSpec.EXACTLY;
4025            } else if (childDimension == LayoutParams.MATCH_PARENT) {
4026                // Child wants to be our size, but our size is not fixed.
4027                // Constrain child to not be bigger than us.
4028                resultSize = size;
4029                resultMode = MeasureSpec.AT_MOST;
4030            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4031                // Child wants to determine its own size. It can't be
4032                // bigger than us.
4033                resultSize = size;
4034                resultMode = MeasureSpec.AT_MOST;
4035            }
4036            break;
4037
4038        // Parent asked to see how big we want to be
4039        case MeasureSpec.UNSPECIFIED:
4040            if (childDimension >= 0) {
4041                // Child wants a specific size... let him have it
4042                resultSize = childDimension;
4043                resultMode = MeasureSpec.EXACTLY;
4044            } else if (childDimension == LayoutParams.MATCH_PARENT) {
4045                // Child wants to be our size... find out how big it should
4046                // be
4047                resultSize = 0;
4048                resultMode = MeasureSpec.UNSPECIFIED;
4049            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4050                // Child wants to determine its own size.... find out how
4051                // big it should be
4052                resultSize = 0;
4053                resultMode = MeasureSpec.UNSPECIFIED;
4054            }
4055            break;
4056        }
4057        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
4058    }
4059
4060
4061    /**
4062     * Removes any pending animations for views that have been removed. Call
4063     * this if you don't want animations for exiting views to stack up.
4064     */
4065    public void clearDisappearingChildren() {
4066        if (mDisappearingChildren != null) {
4067            mDisappearingChildren.clear();
4068        }
4069    }
4070
4071    /**
4072     * Add a view which is removed from mChildren but still needs animation
4073     *
4074     * @param v View to add
4075     */
4076    private void addDisappearingView(View v) {
4077        ArrayList<View> disappearingChildren = mDisappearingChildren;
4078
4079        if (disappearingChildren == null) {
4080            disappearingChildren = mDisappearingChildren = new ArrayList<View>();
4081        }
4082
4083        disappearingChildren.add(v);
4084    }
4085
4086    /**
4087     * Cleanup a view when its animation is done. This may mean removing it from
4088     * the list of disappearing views.
4089     *
4090     * @param view The view whose animation has finished
4091     * @param animation The animation, cannot be null
4092     */
4093    private void finishAnimatingView(final View view, Animation animation) {
4094        final ArrayList<View> disappearingChildren = mDisappearingChildren;
4095        if (disappearingChildren != null) {
4096            if (disappearingChildren.contains(view)) {
4097                disappearingChildren.remove(view);
4098
4099                if (view.mAttachInfo != null) {
4100                    view.dispatchDetachedFromWindow();
4101                }
4102
4103                view.clearAnimation();
4104                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4105            }
4106        }
4107
4108        if (animation != null && !animation.getFillAfter()) {
4109            view.clearAnimation();
4110        }
4111
4112        if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
4113            view.onAnimationEnd();
4114            // Should be performed by onAnimationEnd() but this avoid an infinite loop,
4115            // so we'd rather be safe than sorry
4116            view.mPrivateFlags &= ~ANIMATION_STARTED;
4117            // Draw one more frame after the animation is done
4118            mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4119        }
4120    }
4121
4122    /**
4123     * This method tells the ViewGroup that the given View object, which should have this
4124     * ViewGroup as its parent,
4125     * should be kept around  (re-displayed when the ViewGroup draws its children) even if it
4126     * is removed from its parent. This allows animations, such as those used by
4127     * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
4128     * the removal of views. A call to this method should always be accompanied by a later call
4129     * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
4130     * so that the View finally gets removed.
4131     *
4132     * @param view The View object to be kept visible even if it gets removed from its parent.
4133     */
4134    public void startViewTransition(View view) {
4135        if (view.mParent == this) {
4136            if (mTransitioningViews == null) {
4137                mTransitioningViews = new ArrayList<View>();
4138            }
4139            mTransitioningViews.add(view);
4140        }
4141    }
4142
4143    /**
4144     * This method should always be called following an earlier call to
4145     * {@link #startViewTransition(View)}. The given View is finally removed from its parent
4146     * and will no longer be displayed. Note that this method does not perform the functionality
4147     * of removing a view from its parent; it just discontinues the display of a View that
4148     * has previously been removed.
4149     *
4150     * @return view The View object that has been removed but is being kept around in the visible
4151     * hierarchy by an earlier call to {@link #startViewTransition(View)}.
4152     */
4153    public void endViewTransition(View view) {
4154        if (mTransitioningViews != null) {
4155            mTransitioningViews.remove(view);
4156            final ArrayList<View> disappearingChildren = mDisappearingChildren;
4157            if (disappearingChildren != null && disappearingChildren.contains(view)) {
4158                disappearingChildren.remove(view);
4159                if (mVisibilityChangingChildren != null &&
4160                        mVisibilityChangingChildren.contains(view)) {
4161                    mVisibilityChangingChildren.remove(view);
4162                } else {
4163                    if (view.mAttachInfo != null) {
4164                        view.dispatchDetachedFromWindow();
4165                    }
4166                    if (view.mParent != null) {
4167                        view.mParent = null;
4168                    }
4169                }
4170                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4171            }
4172        }
4173    }
4174
4175    private LayoutTransition.TransitionListener mLayoutTransitionListener =
4176            new LayoutTransition.TransitionListener() {
4177        @Override
4178        public void startTransition(LayoutTransition transition, ViewGroup container,
4179                View view, int transitionType) {
4180            // We only care about disappearing items, since we need special logic to keep
4181            // those items visible after they've been 'removed'
4182            if (transitionType == LayoutTransition.DISAPPEARING) {
4183                startViewTransition(view);
4184            }
4185        }
4186
4187        @Override
4188        public void endTransition(LayoutTransition transition, ViewGroup container,
4189                View view, int transitionType) {
4190            if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
4191                endViewTransition(view);
4192            }
4193        }
4194    };
4195
4196    /**
4197     * {@inheritDoc}
4198     */
4199    @Override
4200    public boolean gatherTransparentRegion(Region region) {
4201        // If no transparent regions requested, we are always opaque.
4202        final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
4203        if (meOpaque && region == null) {
4204            // The caller doesn't care about the region, so stop now.
4205            return true;
4206        }
4207        super.gatherTransparentRegion(region);
4208        final View[] children = mChildren;
4209        final int count = mChildrenCount;
4210        boolean noneOfTheChildrenAreTransparent = true;
4211        for (int i = 0; i < count; i++) {
4212            final View child = children[i];
4213            if ((child.mViewFlags & VISIBILITY_MASK) != GONE || child.getAnimation() != null) {
4214                if (!child.gatherTransparentRegion(region)) {
4215                    noneOfTheChildrenAreTransparent = false;
4216                }
4217            }
4218        }
4219        return meOpaque || noneOfTheChildrenAreTransparent;
4220    }
4221
4222    /**
4223     * {@inheritDoc}
4224     */
4225    public void requestTransparentRegion(View child) {
4226        if (child != null) {
4227            child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
4228            if (mParent != null) {
4229                mParent.requestTransparentRegion(this);
4230            }
4231        }
4232    }
4233
4234
4235    @Override
4236    protected boolean fitSystemWindows(Rect insets) {
4237        boolean done = super.fitSystemWindows(insets);
4238        if (!done) {
4239            final int count = mChildrenCount;
4240            final View[] children = mChildren;
4241            for (int i = 0; i < count; i++) {
4242                done = children[i].fitSystemWindows(insets);
4243                if (done) {
4244                    break;
4245                }
4246            }
4247        }
4248        return done;
4249    }
4250
4251    /**
4252     * Returns the animation listener to which layout animation events are
4253     * sent.
4254     *
4255     * @return an {@link android.view.animation.Animation.AnimationListener}
4256     */
4257    public Animation.AnimationListener getLayoutAnimationListener() {
4258        return mAnimationListener;
4259    }
4260
4261    @Override
4262    protected void drawableStateChanged() {
4263        super.drawableStateChanged();
4264
4265        if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
4266            if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
4267                throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
4268                        + " child has duplicateParentState set to true");
4269            }
4270
4271            final View[] children = mChildren;
4272            final int count = mChildrenCount;
4273
4274            for (int i = 0; i < count; i++) {
4275                final View child = children[i];
4276                if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
4277                    child.refreshDrawableState();
4278                }
4279            }
4280        }
4281    }
4282
4283    @Override
4284    public void jumpDrawablesToCurrentState() {
4285        super.jumpDrawablesToCurrentState();
4286        final View[] children = mChildren;
4287        final int count = mChildrenCount;
4288        for (int i = 0; i < count; i++) {
4289            children[i].jumpDrawablesToCurrentState();
4290        }
4291    }
4292
4293    @Override
4294    protected int[] onCreateDrawableState(int extraSpace) {
4295        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
4296            return super.onCreateDrawableState(extraSpace);
4297        }
4298
4299        int need = 0;
4300        int n = getChildCount();
4301        for (int i = 0; i < n; i++) {
4302            int[] childState = getChildAt(i).getDrawableState();
4303
4304            if (childState != null) {
4305                need += childState.length;
4306            }
4307        }
4308
4309        int[] state = super.onCreateDrawableState(extraSpace + need);
4310
4311        for (int i = 0; i < n; i++) {
4312            int[] childState = getChildAt(i).getDrawableState();
4313
4314            if (childState != null) {
4315                state = mergeDrawableStates(state, childState);
4316            }
4317        }
4318
4319        return state;
4320    }
4321
4322    /**
4323     * Sets whether this ViewGroup's drawable states also include
4324     * its children's drawable states.  This is used, for example, to
4325     * make a group appear to be focused when its child EditText or button
4326     * is focused.
4327     */
4328    public void setAddStatesFromChildren(boolean addsStates) {
4329        if (addsStates) {
4330            mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
4331        } else {
4332            mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
4333        }
4334
4335        refreshDrawableState();
4336    }
4337
4338    /**
4339     * Returns whether this ViewGroup's drawable states also include
4340     * its children's drawable states.  This is used, for example, to
4341     * make a group appear to be focused when its child EditText or button
4342     * is focused.
4343     */
4344    public boolean addStatesFromChildren() {
4345        return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
4346    }
4347
4348    /**
4349     * If {link #addStatesFromChildren} is true, refreshes this group's
4350     * drawable state (to include the states from its children).
4351     */
4352    public void childDrawableStateChanged(View child) {
4353        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
4354            refreshDrawableState();
4355        }
4356    }
4357
4358    /**
4359     * Specifies the animation listener to which layout animation events must
4360     * be sent. Only
4361     * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
4362     * and
4363     * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
4364     * are invoked.
4365     *
4366     * @param animationListener the layout animation listener
4367     */
4368    public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
4369        mAnimationListener = animationListener;
4370    }
4371
4372    /**
4373     * LayoutParams are used by views to tell their parents how they want to be
4374     * laid out. See
4375     * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
4376     * for a list of all child view attributes that this class supports.
4377     *
4378     * <p>
4379     * The base LayoutParams class just describes how big the view wants to be
4380     * for both width and height. For each dimension, it can specify one of:
4381     * <ul>
4382     * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
4383     * means that the view wants to be as big as its parent (minus padding)
4384     * <li> WRAP_CONTENT, which means that the view wants to be just big enough
4385     * to enclose its content (plus padding)
4386     * <li> an exact number
4387     * </ul>
4388     * There are subclasses of LayoutParams for different subclasses of
4389     * ViewGroup. For example, AbsoluteLayout has its own subclass of
4390     * LayoutParams which adds an X and Y value.
4391     *
4392     * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
4393     * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
4394     */
4395    public static class LayoutParams {
4396        /**
4397         * Special value for the height or width requested by a View.
4398         * FILL_PARENT means that the view wants to be as big as its parent,
4399         * minus the parent's padding, if any. This value is deprecated
4400         * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
4401         */
4402        @SuppressWarnings({"UnusedDeclaration"})
4403        @Deprecated
4404        public static final int FILL_PARENT = -1;
4405
4406        /**
4407         * Special value for the height or width requested by a View.
4408         * MATCH_PARENT means that the view wants to be as big as its parent,
4409         * minus the parent's padding, if any. Introduced in API Level 8.
4410         */
4411        public static final int MATCH_PARENT = -1;
4412
4413        /**
4414         * Special value for the height or width requested by a View.
4415         * WRAP_CONTENT means that the view wants to be just large enough to fit
4416         * its own internal content, taking its own padding into account.
4417         */
4418        public static final int WRAP_CONTENT = -2;
4419
4420        /**
4421         * Information about how wide the view wants to be. Can be one of the
4422         * constants FILL_PARENT (replaced by MATCH_PARENT ,
4423         * in API Level 8) or WRAP_CONTENT. or an exact size.
4424         */
4425        @ViewDebug.ExportedProperty(category = "layout", mapping = {
4426            @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
4427            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
4428        })
4429        public int width;
4430
4431        /**
4432         * Information about how tall the view wants to be. Can be one of the
4433         * constants FILL_PARENT (replaced by MATCH_PARENT ,
4434         * in API Level 8) or WRAP_CONTENT. or an exact size.
4435         */
4436        @ViewDebug.ExportedProperty(category = "layout", mapping = {
4437            @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
4438            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
4439        })
4440        public int height;
4441
4442        /**
4443         * Used to animate layouts.
4444         */
4445        public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
4446
4447        /**
4448         * Creates a new set of layout parameters. The values are extracted from
4449         * the supplied attributes set and context. The XML attributes mapped
4450         * to this set of layout parameters are:
4451         *
4452         * <ul>
4453         *   <li><code>layout_width</code>: the width, either an exact value,
4454         *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
4455         *   {@link #MATCH_PARENT} in API Level 8)</li>
4456         *   <li><code>layout_height</code>: the height, either an exact value,
4457         *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
4458         *   {@link #MATCH_PARENT} in API Level 8)</li>
4459         * </ul>
4460         *
4461         * @param c the application environment
4462         * @param attrs the set of attributes from which to extract the layout
4463         *              parameters' values
4464         */
4465        public LayoutParams(Context c, AttributeSet attrs) {
4466            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
4467            setBaseAttributes(a,
4468                    R.styleable.ViewGroup_Layout_layout_width,
4469                    R.styleable.ViewGroup_Layout_layout_height);
4470            a.recycle();
4471        }
4472
4473        /**
4474         * Creates a new set of layout parameters with the specified width
4475         * and height.
4476         *
4477         * @param width the width, either {@link #WRAP_CONTENT},
4478         *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
4479         *        API Level 8), or a fixed size in pixels
4480         * @param height the height, either {@link #WRAP_CONTENT},
4481         *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
4482         *        API Level 8), or a fixed size in pixels
4483         */
4484        public LayoutParams(int width, int height) {
4485            this.width = width;
4486            this.height = height;
4487        }
4488
4489        /**
4490         * Copy constructor. Clones the width and height values of the source.
4491         *
4492         * @param source The layout params to copy from.
4493         */
4494        public LayoutParams(LayoutParams source) {
4495            this.width = source.width;
4496            this.height = source.height;
4497        }
4498
4499        /**
4500         * Used internally by MarginLayoutParams.
4501         * @hide
4502         */
4503        LayoutParams() {
4504        }
4505
4506        /**
4507         * Extracts the layout parameters from the supplied attributes.
4508         *
4509         * @param a the style attributes to extract the parameters from
4510         * @param widthAttr the identifier of the width attribute
4511         * @param heightAttr the identifier of the height attribute
4512         */
4513        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
4514            width = a.getLayoutDimension(widthAttr, "layout_width");
4515            height = a.getLayoutDimension(heightAttr, "layout_height");
4516        }
4517
4518        /**
4519         * Returns a String representation of this set of layout parameters.
4520         *
4521         * @param output the String to prepend to the internal representation
4522         * @return a String with the following format: output +
4523         *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
4524         *
4525         * @hide
4526         */
4527        public String debug(String output) {
4528            return output + "ViewGroup.LayoutParams={ width="
4529                    + sizeToString(width) + ", height=" + sizeToString(height) + " }";
4530        }
4531
4532        /**
4533         * Converts the specified size to a readable String.
4534         *
4535         * @param size the size to convert
4536         * @return a String instance representing the supplied size
4537         *
4538         * @hide
4539         */
4540        protected static String sizeToString(int size) {
4541            if (size == WRAP_CONTENT) {
4542                return "wrap-content";
4543            }
4544            if (size == MATCH_PARENT) {
4545                return "match-parent";
4546            }
4547            return String.valueOf(size);
4548        }
4549    }
4550
4551    /**
4552     * Per-child layout information for layouts that support margins.
4553     * See
4554     * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
4555     * for a list of all child view attributes that this class supports.
4556     */
4557    public static class MarginLayoutParams extends ViewGroup.LayoutParams {
4558        /**
4559         * The left margin in pixels of the child.
4560         */
4561        @ViewDebug.ExportedProperty(category = "layout")
4562        public int leftMargin;
4563
4564        /**
4565         * The top margin in pixels of the child.
4566         */
4567        @ViewDebug.ExportedProperty(category = "layout")
4568        public int topMargin;
4569
4570        /**
4571         * The right margin in pixels of the child.
4572         */
4573        @ViewDebug.ExportedProperty(category = "layout")
4574        public int rightMargin;
4575
4576        /**
4577         * The bottom margin in pixels of the child.
4578         */
4579        @ViewDebug.ExportedProperty(category = "layout")
4580        public int bottomMargin;
4581
4582        /**
4583         * Creates a new set of layout parameters. The values are extracted from
4584         * the supplied attributes set and context.
4585         *
4586         * @param c the application environment
4587         * @param attrs the set of attributes from which to extract the layout
4588         *              parameters' values
4589         */
4590        public MarginLayoutParams(Context c, AttributeSet attrs) {
4591            super();
4592
4593            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
4594            setBaseAttributes(a,
4595                    R.styleable.ViewGroup_MarginLayout_layout_width,
4596                    R.styleable.ViewGroup_MarginLayout_layout_height);
4597
4598            int margin = a.getDimensionPixelSize(
4599                    com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
4600            if (margin >= 0) {
4601                leftMargin = margin;
4602                topMargin = margin;
4603                rightMargin= margin;
4604                bottomMargin = margin;
4605            } else {
4606                leftMargin = a.getDimensionPixelSize(
4607                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
4608                topMargin = a.getDimensionPixelSize(
4609                        R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
4610                rightMargin = a.getDimensionPixelSize(
4611                        R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
4612                bottomMargin = a.getDimensionPixelSize(
4613                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
4614            }
4615
4616            a.recycle();
4617        }
4618
4619        /**
4620         * {@inheritDoc}
4621         */
4622        public MarginLayoutParams(int width, int height) {
4623            super(width, height);
4624        }
4625
4626        /**
4627         * Copy constructor. Clones the width, height and margin values of the source.
4628         *
4629         * @param source The layout params to copy from.
4630         */
4631        public MarginLayoutParams(MarginLayoutParams source) {
4632            this.width = source.width;
4633            this.height = source.height;
4634
4635            this.leftMargin = source.leftMargin;
4636            this.topMargin = source.topMargin;
4637            this.rightMargin = source.rightMargin;
4638            this.bottomMargin = source.bottomMargin;
4639        }
4640
4641        /**
4642         * {@inheritDoc}
4643         */
4644        public MarginLayoutParams(LayoutParams source) {
4645            super(source);
4646        }
4647
4648        /**
4649         * Sets the margins, in pixels.
4650         *
4651         * @param left the left margin size
4652         * @param top the top margin size
4653         * @param right the right margin size
4654         * @param bottom the bottom margin size
4655         *
4656         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
4657         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
4658         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
4659         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
4660         */
4661        public void setMargins(int left, int top, int right, int bottom) {
4662            leftMargin = left;
4663            topMargin = top;
4664            rightMargin = right;
4665            bottomMargin = bottom;
4666        }
4667    }
4668
4669    /* Describes a touched view and the ids of the pointers that it has captured.
4670     *
4671     * This code assumes that pointer ids are always in the range 0..31 such that
4672     * it can use a bitfield to track which pointer ids are present.
4673     * As it happens, the lower layers of the input dispatch pipeline also use the
4674     * same trick so the assumption should be safe here...
4675     */
4676    private static final class TouchTarget {
4677        private static final int MAX_RECYCLED = 32;
4678        private static final Object sRecycleLock = new Object();
4679        private static TouchTarget sRecycleBin;
4680        private static int sRecycledCount;
4681
4682        public static final int ALL_POINTER_IDS = -1; // all ones
4683
4684        // The touched child view.
4685        public View child;
4686
4687        // The combined bit mask of pointer ids for all pointers captured by the target.
4688        public int pointerIdBits;
4689
4690        // The next target in the target list.
4691        public TouchTarget next;
4692
4693        private TouchTarget() {
4694        }
4695
4696        public static TouchTarget obtain(View child, int pointerIdBits) {
4697            final TouchTarget target;
4698            synchronized (sRecycleLock) {
4699                if (sRecycleBin == null) {
4700                    target = new TouchTarget();
4701                } else {
4702                    target = sRecycleBin;
4703                    sRecycleBin = target.next;
4704                     sRecycledCount--;
4705                    target.next = null;
4706                }
4707            }
4708            target.child = child;
4709            target.pointerIdBits = pointerIdBits;
4710            return target;
4711        }
4712
4713        public void recycle() {
4714            synchronized (sRecycleLock) {
4715                if (sRecycledCount < MAX_RECYCLED) {
4716                    next = sRecycleBin;
4717                    sRecycleBin = this;
4718                    sRecycledCount += 1;
4719                } else {
4720                    next = null;
4721                }
4722                child = null;
4723            }
4724        }
4725    }
4726}
4727