ViewGroup.java revision f2f7d8f9dd75c6e32a46a28fd3f53435addf0746
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                child.mPrivateFlags &= ~DIRTY_MASK;
2353                ((HardwareCanvas) canvas).drawDisplayList(displayList);
2354            }
2355        } else if (cache != null) {
2356            child.mPrivateFlags &= ~DIRTY_MASK;
2357            final Paint cachePaint = mCachePaint;
2358            if (alpha < 1.0f) {
2359                cachePaint.setAlpha((int) (alpha * 255));
2360                mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
2361            } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
2362                cachePaint.setAlpha(255);
2363                mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
2364            }
2365            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
2366        }
2367
2368        canvas.restoreToCount(restoreTo);
2369
2370        if (a != null && !more) {
2371            child.onSetAlpha(255);
2372            finishAnimatingView(child, a);
2373        }
2374
2375        return more;
2376    }
2377
2378    /**
2379     * By default, children are clipped to their bounds before drawing. This
2380     * allows view groups to override this behavior for animations, etc.
2381     *
2382     * @param clipChildren true to clip children to their bounds,
2383     *        false otherwise
2384     * @attr ref android.R.styleable#ViewGroup_clipChildren
2385     */
2386    public void setClipChildren(boolean clipChildren) {
2387        setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
2388    }
2389
2390    /**
2391     * By default, children are clipped to the padding of the ViewGroup. This
2392     * allows view groups to override this behavior
2393     *
2394     * @param clipToPadding true to clip children to the padding of the
2395     *        group, false otherwise
2396     * @attr ref android.R.styleable#ViewGroup_clipToPadding
2397     */
2398    public void setClipToPadding(boolean clipToPadding) {
2399        setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
2400    }
2401
2402    /**
2403     * {@inheritDoc}
2404     */
2405    @Override
2406    public void dispatchSetSelected(boolean selected) {
2407        final View[] children = mChildren;
2408        final int count = mChildrenCount;
2409        for (int i = 0; i < count; i++) {
2410
2411            children[i].setSelected(selected);
2412        }
2413    }
2414
2415    /**
2416     * {@inheritDoc}
2417     */
2418    @Override
2419    public void dispatchSetActivated(boolean activated) {
2420        final View[] children = mChildren;
2421        final int count = mChildrenCount;
2422        for (int i = 0; i < count; i++) {
2423
2424            children[i].setActivated(activated);
2425        }
2426    }
2427
2428    @Override
2429    protected void dispatchSetPressed(boolean pressed) {
2430        final View[] children = mChildren;
2431        final int count = mChildrenCount;
2432        for (int i = 0; i < count; i++) {
2433            children[i].setPressed(pressed);
2434        }
2435    }
2436
2437    /**
2438     * When this property is set to true, this ViewGroup supports static transformations on
2439     * children; this causes
2440     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
2441     * invoked when a child is drawn.
2442     *
2443     * Any subclass overriding
2444     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
2445     * set this property to true.
2446     *
2447     * @param enabled True to enable static transformations on children, false otherwise.
2448     *
2449     * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
2450     */
2451    protected void setStaticTransformationsEnabled(boolean enabled) {
2452        setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
2453    }
2454
2455    /**
2456     * {@inheritDoc}
2457     *
2458     * @see #setStaticTransformationsEnabled(boolean)
2459     */
2460    protected boolean getChildStaticTransformation(View child, Transformation t) {
2461        return false;
2462    }
2463
2464    /**
2465     * {@hide}
2466     */
2467    @Override
2468    protected View findViewTraversal(int id) {
2469        if (id == mID) {
2470            return this;
2471        }
2472
2473        final View[] where = mChildren;
2474        final int len = mChildrenCount;
2475
2476        for (int i = 0; i < len; i++) {
2477            View v = where[i];
2478
2479            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
2480                v = v.findViewById(id);
2481
2482                if (v != null) {
2483                    return v;
2484                }
2485            }
2486        }
2487
2488        return null;
2489    }
2490
2491    /**
2492     * {@hide}
2493     */
2494    @Override
2495    protected View findViewWithTagTraversal(Object tag) {
2496        if (tag != null && tag.equals(mTag)) {
2497            return this;
2498        }
2499
2500        final View[] where = mChildren;
2501        final int len = mChildrenCount;
2502
2503        for (int i = 0; i < len; i++) {
2504            View v = where[i];
2505
2506            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
2507                v = v.findViewWithTag(tag);
2508
2509                if (v != null) {
2510                    return v;
2511                }
2512            }
2513        }
2514
2515        return null;
2516    }
2517
2518    /**
2519     * Adds a child view. If no layout parameters are already set on the child, the
2520     * default parameters for this ViewGroup are set on the child.
2521     *
2522     * @param child the child view to add
2523     *
2524     * @see #generateDefaultLayoutParams()
2525     */
2526    public void addView(View child) {
2527        addView(child, -1);
2528    }
2529
2530    /**
2531     * Adds a child view. If no layout parameters are already set on the child, the
2532     * default parameters for this ViewGroup are set on the child.
2533     *
2534     * @param child the child view to add
2535     * @param index the position at which to add the child
2536     *
2537     * @see #generateDefaultLayoutParams()
2538     */
2539    public void addView(View child, int index) {
2540        LayoutParams params = child.getLayoutParams();
2541        if (params == null) {
2542            params = generateDefaultLayoutParams();
2543            if (params == null) {
2544                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
2545            }
2546        }
2547        addView(child, index, params);
2548    }
2549
2550    /**
2551     * Adds a child view with this ViewGroup's default layout parameters and the
2552     * specified width and height.
2553     *
2554     * @param child the child view to add
2555     */
2556    public void addView(View child, int width, int height) {
2557        final LayoutParams params = generateDefaultLayoutParams();
2558        params.width = width;
2559        params.height = height;
2560        addView(child, -1, params);
2561    }
2562
2563    /**
2564     * Adds a child view with the specified layout parameters.
2565     *
2566     * @param child the child view to add
2567     * @param params the layout parameters to set on the child
2568     */
2569    public void addView(View child, LayoutParams params) {
2570        addView(child, -1, params);
2571    }
2572
2573    /**
2574     * Adds a child view with the specified layout parameters.
2575     *
2576     * @param child the child view to add
2577     * @param index the position at which to add the child
2578     * @param params the layout parameters to set on the child
2579     */
2580    public void addView(View child, int index, LayoutParams params) {
2581        if (DBG) {
2582            System.out.println(this + " addView");
2583        }
2584
2585        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
2586        // therefore, we call requestLayout() on ourselves before, so that the child's request
2587        // will be blocked at our level
2588        child.mPrivateFlags &= ~DIRTY_MASK;
2589        requestLayout();
2590        invalidate();
2591        addViewInner(child, index, params, false);
2592    }
2593
2594    /**
2595     * {@inheritDoc}
2596     */
2597    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
2598        if (!checkLayoutParams(params)) {
2599            throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
2600        }
2601        if (view.mParent != this) {
2602            throw new IllegalArgumentException("Given view not a child of " + this);
2603        }
2604        view.setLayoutParams(params);
2605    }
2606
2607    /**
2608     * {@inheritDoc}
2609     */
2610    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
2611        return  p != null;
2612    }
2613
2614    /**
2615     * Interface definition for a callback to be invoked when the hierarchy
2616     * within this view changed. The hierarchy changes whenever a child is added
2617     * to or removed from this view.
2618     */
2619    public interface OnHierarchyChangeListener {
2620        /**
2621         * Called when a new child is added to a parent view.
2622         *
2623         * @param parent the view in which a child was added
2624         * @param child the new child view added in the hierarchy
2625         */
2626        void onChildViewAdded(View parent, View child);
2627
2628        /**
2629         * Called when a child is removed from a parent view.
2630         *
2631         * @param parent the view from which the child was removed
2632         * @param child the child removed from the hierarchy
2633         */
2634        void onChildViewRemoved(View parent, View child);
2635    }
2636
2637    /**
2638     * Register a callback to be invoked when a child is added to or removed
2639     * from this view.
2640     *
2641     * @param listener the callback to invoke on hierarchy change
2642     */
2643    public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
2644        mOnHierarchyChangeListener = listener;
2645    }
2646
2647    /**
2648     * Adds a view during layout. This is useful if in your onLayout() method,
2649     * you need to add more views (as does the list view for example).
2650     *
2651     * If index is negative, it means put it at the end of the list.
2652     *
2653     * @param child the view to add to the group
2654     * @param index the index at which the child must be added
2655     * @param params the layout parameters to associate with the child
2656     * @return true if the child was added, false otherwise
2657     */
2658    protected boolean addViewInLayout(View child, int index, LayoutParams params) {
2659        return addViewInLayout(child, index, params, false);
2660    }
2661
2662    /**
2663     * Adds a view during layout. This is useful if in your onLayout() method,
2664     * you need to add more views (as does the list view for example).
2665     *
2666     * If index is negative, it means put it at the end of the list.
2667     *
2668     * @param child the view to add to the group
2669     * @param index the index at which the child must be added
2670     * @param params the layout parameters to associate with the child
2671     * @param preventRequestLayout if true, calling this method will not trigger a
2672     *        layout request on child
2673     * @return true if the child was added, false otherwise
2674     */
2675    protected boolean addViewInLayout(View child, int index, LayoutParams params,
2676            boolean preventRequestLayout) {
2677        child.mParent = null;
2678        addViewInner(child, index, params, preventRequestLayout);
2679        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
2680        return true;
2681    }
2682
2683    /**
2684     * Prevents the specified child to be laid out during the next layout pass.
2685     *
2686     * @param child the child on which to perform the cleanup
2687     */
2688    protected void cleanupLayoutState(View child) {
2689        child.mPrivateFlags &= ~View.FORCE_LAYOUT;
2690    }
2691
2692    private void addViewInner(View child, int index, LayoutParams params,
2693            boolean preventRequestLayout) {
2694
2695        if (child.getParent() != null) {
2696            throw new IllegalStateException("The specified child already has a parent. " +
2697                    "You must call removeView() on the child's parent first.");
2698        }
2699
2700        if (mTransition != null) {
2701            mTransition.addChild(this, child);
2702        }
2703
2704        if (!checkLayoutParams(params)) {
2705            params = generateLayoutParams(params);
2706        }
2707
2708        if (preventRequestLayout) {
2709            child.mLayoutParams = params;
2710        } else {
2711            child.setLayoutParams(params);
2712        }
2713
2714        if (index < 0) {
2715            index = mChildrenCount;
2716        }
2717
2718        addInArray(child, index);
2719
2720        // tell our children
2721        if (preventRequestLayout) {
2722            child.assignParent(this);
2723        } else {
2724            child.mParent = this;
2725        }
2726
2727        if (child.hasFocus()) {
2728            requestChildFocus(child, child.findFocus());
2729        }
2730
2731        AttachInfo ai = mAttachInfo;
2732        if (ai != null) {
2733            boolean lastKeepOn = ai.mKeepScreenOn;
2734            ai.mKeepScreenOn = false;
2735            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
2736            if (ai.mKeepScreenOn) {
2737                needGlobalAttributesUpdate(true);
2738            }
2739            ai.mKeepScreenOn = lastKeepOn;
2740        }
2741
2742        if (mOnHierarchyChangeListener != null) {
2743            mOnHierarchyChangeListener.onChildViewAdded(this, child);
2744        }
2745
2746        if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
2747            mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
2748        }
2749    }
2750
2751    private void addInArray(View child, int index) {
2752        View[] children = mChildren;
2753        final int count = mChildrenCount;
2754        final int size = children.length;
2755        if (index == count) {
2756            if (size == count) {
2757                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
2758                System.arraycopy(children, 0, mChildren, 0, size);
2759                children = mChildren;
2760            }
2761            children[mChildrenCount++] = child;
2762        } else if (index < count) {
2763            if (size == count) {
2764                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
2765                System.arraycopy(children, 0, mChildren, 0, index);
2766                System.arraycopy(children, index, mChildren, index + 1, count - index);
2767                children = mChildren;
2768            } else {
2769                System.arraycopy(children, index, children, index + 1, count - index);
2770            }
2771            children[index] = child;
2772            mChildrenCount++;
2773        } else {
2774            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
2775        }
2776    }
2777
2778    // This method also sets the child's mParent to null
2779    private void removeFromArray(int index) {
2780        final View[] children = mChildren;
2781        if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
2782            children[index].mParent = null;
2783        }
2784        final int count = mChildrenCount;
2785        if (index == count - 1) {
2786            children[--mChildrenCount] = null;
2787        } else if (index >= 0 && index < count) {
2788            System.arraycopy(children, index + 1, children, index, count - index - 1);
2789            children[--mChildrenCount] = null;
2790        } else {
2791            throw new IndexOutOfBoundsException();
2792        }
2793    }
2794
2795    // This method also sets the children's mParent to null
2796    private void removeFromArray(int start, int count) {
2797        final View[] children = mChildren;
2798        final int childrenCount = mChildrenCount;
2799
2800        start = Math.max(0, start);
2801        final int end = Math.min(childrenCount, start + count);
2802
2803        if (start == end) {
2804            return;
2805        }
2806
2807        if (end == childrenCount) {
2808            for (int i = start; i < end; i++) {
2809                children[i].mParent = null;
2810                children[i] = null;
2811            }
2812        } else {
2813            for (int i = start; i < end; i++) {
2814                children[i].mParent = null;
2815            }
2816
2817            // Since we're looping above, we might as well do the copy, but is arraycopy()
2818            // faster than the extra 2 bounds checks we would do in the loop?
2819            System.arraycopy(children, end, children, start, childrenCount - end);
2820
2821            for (int i = childrenCount - (end - start); i < childrenCount; i++) {
2822                children[i] = null;
2823            }
2824        }
2825
2826        mChildrenCount -= (end - start);
2827    }
2828
2829    private void bindLayoutAnimation(View child) {
2830        Animation a = mLayoutAnimationController.getAnimationForView(child);
2831        child.setAnimation(a);
2832    }
2833
2834    /**
2835     * Subclasses should override this method to set layout animation
2836     * parameters on the supplied child.
2837     *
2838     * @param child the child to associate with animation parameters
2839     * @param params the child's layout parameters which hold the animation
2840     *        parameters
2841     * @param index the index of the child in the view group
2842     * @param count the number of children in the view group
2843     */
2844    protected void attachLayoutAnimationParameters(View child,
2845            LayoutParams params, int index, int count) {
2846        LayoutAnimationController.AnimationParameters animationParams =
2847                    params.layoutAnimationParameters;
2848        if (animationParams == null) {
2849            animationParams = new LayoutAnimationController.AnimationParameters();
2850            params.layoutAnimationParameters = animationParams;
2851        }
2852
2853        animationParams.count = count;
2854        animationParams.index = index;
2855    }
2856
2857    /**
2858     * {@inheritDoc}
2859     */
2860    public void removeView(View view) {
2861        removeViewInternal(view);
2862        requestLayout();
2863        invalidate();
2864    }
2865
2866    /**
2867     * Removes a view during layout. This is useful if in your onLayout() method,
2868     * you need to remove more views.
2869     *
2870     * @param view the view to remove from the group
2871     */
2872    public void removeViewInLayout(View view) {
2873        removeViewInternal(view);
2874    }
2875
2876    /**
2877     * Removes a range of views during layout. This is useful if in your onLayout() method,
2878     * you need to remove more views.
2879     *
2880     * @param start the index of the first view to remove from the group
2881     * @param count the number of views to remove from the group
2882     */
2883    public void removeViewsInLayout(int start, int count) {
2884        removeViewsInternal(start, count);
2885    }
2886
2887    /**
2888     * Removes the view at the specified position in the group.
2889     *
2890     * @param index the position in the group of the view to remove
2891     */
2892    public void removeViewAt(int index) {
2893        removeViewInternal(index, getChildAt(index));
2894        requestLayout();
2895        invalidate();
2896    }
2897
2898    /**
2899     * Removes the specified range of views from the group.
2900     *
2901     * @param start the first position in the group of the range of views to remove
2902     * @param count the number of views to remove
2903     */
2904    public void removeViews(int start, int count) {
2905        removeViewsInternal(start, count);
2906        requestLayout();
2907        invalidate();
2908    }
2909
2910    private void removeViewInternal(View view) {
2911        final int index = indexOfChild(view);
2912        if (index >= 0) {
2913            removeViewInternal(index, view);
2914        }
2915    }
2916
2917    private void removeViewInternal(int index, View view) {
2918
2919        if (mTransition != null) {
2920            mTransition.removeChild(this, view);
2921        }
2922
2923        boolean clearChildFocus = false;
2924        if (view == mFocused) {
2925            view.clearFocusForRemoval();
2926            clearChildFocus = true;
2927        }
2928
2929        if (view.getAnimation() != null ||
2930                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
2931            addDisappearingView(view);
2932        } else if (view.mAttachInfo != null) {
2933           view.dispatchDetachedFromWindow();
2934        }
2935
2936        if (mOnHierarchyChangeListener != null) {
2937            mOnHierarchyChangeListener.onChildViewRemoved(this, view);
2938        }
2939
2940        needGlobalAttributesUpdate(false);
2941
2942        removeFromArray(index);
2943
2944        if (clearChildFocus) {
2945            clearChildFocus(view);
2946        }
2947    }
2948
2949    /**
2950     * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
2951     * not null, changes in layout which occur because of children being added to or removed from
2952     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
2953     * object. By default, the transition object is null (so layout changes are not animated).
2954     *
2955     * @param transition The LayoutTransition object that will animated changes in layout. A value
2956     * of <code>null</code> means no transition will run on layout changes.
2957     * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
2958     */
2959    public void setLayoutTransition(LayoutTransition transition) {
2960        if (mTransition != null) {
2961            mTransition.removeTransitionListener(mLayoutTransitionListener);
2962        }
2963        mTransition = transition;
2964        if (mTransition != null) {
2965            mTransition.addTransitionListener(mLayoutTransitionListener);
2966        }
2967    }
2968
2969    /**
2970     * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
2971     * not null, changes in layout which occur because of children being added to or removed from
2972     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
2973     * object. By default, the transition object is null (so layout changes are not animated).
2974     *
2975     * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
2976     * A value of <code>null</code> means no transition will run on layout changes.
2977     */
2978    public LayoutTransition getLayoutTransition() {
2979        return mTransition;
2980    }
2981
2982    private void removeViewsInternal(int start, int count) {
2983        final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
2984        final boolean notifyListener = onHierarchyChangeListener != null;
2985        final View focused = mFocused;
2986        final boolean detach = mAttachInfo != null;
2987        View clearChildFocus = null;
2988
2989        final View[] children = mChildren;
2990        final int end = start + count;
2991
2992        for (int i = start; i < end; i++) {
2993            final View view = children[i];
2994
2995            if (mTransition != null) {
2996                mTransition.removeChild(this, view);
2997            }
2998
2999            if (view == focused) {
3000                view.clearFocusForRemoval();
3001                clearChildFocus = view;
3002            }
3003
3004            if (view.getAnimation() != null ||
3005                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
3006                addDisappearingView(view);
3007            } else if (detach) {
3008               view.dispatchDetachedFromWindow();
3009            }
3010
3011            needGlobalAttributesUpdate(false);
3012
3013            if (notifyListener) {
3014                onHierarchyChangeListener.onChildViewRemoved(this, view);
3015            }
3016        }
3017
3018        removeFromArray(start, count);
3019
3020        if (clearChildFocus != null) {
3021            clearChildFocus(clearChildFocus);
3022        }
3023    }
3024
3025    /**
3026     * Call this method to remove all child views from the
3027     * ViewGroup.
3028     */
3029    public void removeAllViews() {
3030        removeAllViewsInLayout();
3031        requestLayout();
3032        invalidate();
3033    }
3034
3035    /**
3036     * Called by a ViewGroup subclass to remove child views from itself,
3037     * when it must first know its size on screen before it can calculate how many
3038     * child views it will render. An example is a Gallery or a ListView, which
3039     * may "have" 50 children, but actually only render the number of children
3040     * that can currently fit inside the object on screen. Do not call
3041     * this method unless you are extending ViewGroup and understand the
3042     * view measuring and layout pipeline.
3043     */
3044    public void removeAllViewsInLayout() {
3045        final int count = mChildrenCount;
3046        if (count <= 0) {
3047            return;
3048        }
3049
3050        final View[] children = mChildren;
3051        mChildrenCount = 0;
3052
3053        final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
3054        final boolean notify = listener != null;
3055        final View focused = mFocused;
3056        final boolean detach = mAttachInfo != null;
3057        View clearChildFocus = null;
3058
3059        needGlobalAttributesUpdate(false);
3060
3061        for (int i = count - 1; i >= 0; i--) {
3062            final View view = children[i];
3063
3064            if (mTransition != null) {
3065                mTransition.removeChild(this, view);
3066            }
3067
3068            if (view == focused) {
3069                view.clearFocusForRemoval();
3070                clearChildFocus = view;
3071            }
3072
3073            if (view.getAnimation() != null ||
3074                    (mTransitioningViews != null && mTransitioningViews.contains(view))) {
3075                addDisappearingView(view);
3076            } else if (detach) {
3077               view.dispatchDetachedFromWindow();
3078            }
3079
3080            if (notify) {
3081                listener.onChildViewRemoved(this, view);
3082            }
3083
3084            view.mParent = null;
3085            children[i] = null;
3086        }
3087
3088        if (clearChildFocus != null) {
3089            clearChildFocus(clearChildFocus);
3090        }
3091    }
3092
3093    /**
3094     * Finishes the removal of a detached view. This method will dispatch the detached from
3095     * window event and notify the hierarchy change listener.
3096     *
3097     * @param child the child to be definitely removed from the view hierarchy
3098     * @param animate if true and the view has an animation, the view is placed in the
3099     *                disappearing views list, otherwise, it is detached from the window
3100     *
3101     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3102     * @see #detachAllViewsFromParent()
3103     * @see #detachViewFromParent(View)
3104     * @see #detachViewFromParent(int)
3105     */
3106    protected void removeDetachedView(View child, boolean animate) {
3107        if (mTransition != null) {
3108            mTransition.removeChild(this, child);
3109        }
3110
3111        if (child == mFocused) {
3112            child.clearFocus();
3113        }
3114
3115        if ((animate && child.getAnimation() != null) ||
3116                (mTransitioningViews != null && mTransitioningViews.contains(child))) {
3117            addDisappearingView(child);
3118        } else if (child.mAttachInfo != null) {
3119            child.dispatchDetachedFromWindow();
3120        }
3121
3122        if (mOnHierarchyChangeListener != null) {
3123            mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3124        }
3125    }
3126
3127    /**
3128     * Attaches a view to this view group. Attaching a view assigns this group as the parent,
3129     * sets the layout parameters and puts the view in the list of children so it can be retrieved
3130     * by calling {@link #getChildAt(int)}.
3131     *
3132     * This method should be called only for view which were detached from their parent.
3133     *
3134     * @param child the child to attach
3135     * @param index the index at which the child should be attached
3136     * @param params the layout parameters of the child
3137     *
3138     * @see #removeDetachedView(View, boolean)
3139     * @see #detachAllViewsFromParent()
3140     * @see #detachViewFromParent(View)
3141     * @see #detachViewFromParent(int)
3142     */
3143    protected void attachViewToParent(View child, int index, LayoutParams params) {
3144        child.mLayoutParams = params;
3145
3146        if (index < 0) {
3147            index = mChildrenCount;
3148        }
3149
3150        addInArray(child, index);
3151
3152        child.mParent = this;
3153        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) | DRAWN;
3154
3155        if (child.hasFocus()) {
3156            requestChildFocus(child, child.findFocus());
3157        }
3158    }
3159
3160    /**
3161     * Detaches a view from its parent. Detaching a view should be temporary and followed
3162     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3163     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3164     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3165     *
3166     * @param child the child to detach
3167     *
3168     * @see #detachViewFromParent(int)
3169     * @see #detachViewsFromParent(int, int)
3170     * @see #detachAllViewsFromParent()
3171     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3172     * @see #removeDetachedView(View, boolean)
3173     */
3174    protected void detachViewFromParent(View child) {
3175        removeFromArray(indexOfChild(child));
3176    }
3177
3178    /**
3179     * Detaches a view from its parent. Detaching a view should be temporary and followed
3180     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3181     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3182     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3183     *
3184     * @param index the index of the child to detach
3185     *
3186     * @see #detachViewFromParent(View)
3187     * @see #detachAllViewsFromParent()
3188     * @see #detachViewsFromParent(int, int)
3189     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3190     * @see #removeDetachedView(View, boolean)
3191     */
3192    protected void detachViewFromParent(int index) {
3193        removeFromArray(index);
3194    }
3195
3196    /**
3197     * Detaches a range of view from their parent. Detaching a view should be temporary and followed
3198     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3199     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
3200     * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3201     *
3202     * @param start the first index of the childrend range to detach
3203     * @param count the number of children to detach
3204     *
3205     * @see #detachViewFromParent(View)
3206     * @see #detachViewFromParent(int)
3207     * @see #detachAllViewsFromParent()
3208     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3209     * @see #removeDetachedView(View, boolean)
3210     */
3211    protected void detachViewsFromParent(int start, int count) {
3212        removeFromArray(start, count);
3213    }
3214
3215    /**
3216     * Detaches all views from the parent. Detaching a view should be temporary and followed
3217     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
3218     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
3219     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
3220     *
3221     * @see #detachViewFromParent(View)
3222     * @see #detachViewFromParent(int)
3223     * @see #detachViewsFromParent(int, int)
3224     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
3225     * @see #removeDetachedView(View, boolean)
3226     */
3227    protected void detachAllViewsFromParent() {
3228        final int count = mChildrenCount;
3229        if (count <= 0) {
3230            return;
3231        }
3232
3233        final View[] children = mChildren;
3234        mChildrenCount = 0;
3235
3236        for (int i = count - 1; i >= 0; i--) {
3237            children[i].mParent = null;
3238            children[i] = null;
3239        }
3240    }
3241
3242    /**
3243     * Don't call or override this method. It is used for the implementation of
3244     * the view hierarchy.
3245     */
3246    public final void invalidateChild(View child, final Rect dirty) {
3247        if (ViewDebug.TRACE_HIERARCHY) {
3248            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
3249        }
3250
3251        ViewParent parent = this;
3252
3253        final AttachInfo attachInfo = mAttachInfo;
3254        if (attachInfo != null) {
3255            // If the child is drawing an animation, we want to copy this flag onto
3256            // ourselves and the parent to make sure the invalidate request goes
3257            // through
3258            final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
3259
3260            if (dirty == null) {
3261                do {
3262                    View view = null;
3263                    if (parent instanceof View) {
3264                        view = (View) parent;
3265                        if ((view.mPrivateFlags & DIRTY_MASK) != 0) {
3266                            // already marked dirty - we're done
3267                            break;
3268                        }
3269                    }
3270
3271                    if (drawAnimation) {
3272                        if (view != null) {
3273                            view.mPrivateFlags |= DRAW_ANIMATION;
3274                        } else if (parent instanceof ViewRoot) {
3275                            ((ViewRoot) parent).mIsAnimating = true;
3276                        }
3277                    }
3278
3279                    if (parent instanceof ViewRoot) {
3280                        ((ViewRoot) parent).invalidate();
3281                        parent = null;
3282                    } else if (view != null) {
3283                        if ((mPrivateFlags & DRAWN) == DRAWN) {
3284                            view.mPrivateFlags &= ~DRAWING_CACHE_VALID;
3285                            view.mPrivateFlags |= DIRTY;
3286                            parent = view.mParent;
3287                        } else {
3288                            parent = null;
3289                        }
3290                    }
3291                } while (parent != null);
3292            } else {
3293                // Check whether the child that requests the invalidate is fully opaque
3294                final boolean isOpaque = child.isOpaque() && !drawAnimation &&
3295                        child.getAnimation() == null;
3296                // Mark the child as dirty, using the appropriate flag
3297                // Make sure we do not set both flags at the same time
3298                final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
3299
3300                final int[] location = attachInfo.mInvalidateChildLocation;
3301                location[CHILD_LEFT_INDEX] = child.mLeft;
3302                location[CHILD_TOP_INDEX] = child.mTop;
3303                Matrix childMatrix = child.getMatrix();
3304                if (!childMatrix.isIdentity()) {
3305                    RectF boundingRect = attachInfo.mTmpTransformRect;
3306                    boundingRect.set(dirty);
3307                    childMatrix.mapRect(boundingRect);
3308                    dirty.set((int) boundingRect.left, (int) boundingRect.top,
3309                            (int) (boundingRect.right + 0.5f),
3310                            (int) (boundingRect.bottom + 0.5f));
3311                }
3312
3313                do {
3314                    View view = null;
3315                    if (parent instanceof View) {
3316                        view = (View) parent;
3317                    }
3318
3319                    if (drawAnimation) {
3320                        if (view != null) {
3321                            view.mPrivateFlags |= DRAW_ANIMATION;
3322                        } else if (parent instanceof ViewRoot) {
3323                            ((ViewRoot) parent).mIsAnimating = true;
3324                        }
3325                    }
3326
3327                    // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
3328                    // flag coming from the child that initiated the invalidate
3329                    if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
3330                        view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
3331                    }
3332
3333                    parent = parent.invalidateChildInParent(location, dirty);
3334                    if (view != null) {
3335                        // Account for transform on current parent
3336                        Matrix m = view.getMatrix();
3337                        if (!m.isIdentity()) {
3338                            RectF boundingRect = attachInfo.mTmpTransformRect;
3339                            boundingRect.set(dirty);
3340                            m.mapRect(boundingRect);
3341                            dirty.set((int) boundingRect.left, (int) boundingRect.top,
3342                                    (int) (boundingRect.right + 0.5f),
3343                                    (int) (boundingRect.bottom + 0.5f));
3344                        }
3345                    }
3346                } while (parent != null);
3347            }
3348        }
3349    }
3350
3351    /**
3352     * Don't call or override this method. It is used for the implementation of
3353     * the view hierarchy.
3354     *
3355     * This implementation returns null if this ViewGroup does not have a parent,
3356     * if this ViewGroup is already fully invalidated or if the dirty rectangle
3357     * does not intersect with this ViewGroup's bounds.
3358     */
3359    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
3360        if (ViewDebug.TRACE_HIERARCHY) {
3361            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
3362        }
3363
3364        if ((mPrivateFlags & DRAWN) == DRAWN) {
3365            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
3366                        FLAG_OPTIMIZE_INVALIDATE) {
3367                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
3368                        location[CHILD_TOP_INDEX] - mScrollY);
3369
3370                final int left = mLeft;
3371                final int top = mTop;
3372
3373                if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
3374                        (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
3375                    mPrivateFlags &= ~DRAWING_CACHE_VALID;
3376
3377                    location[CHILD_LEFT_INDEX] = left;
3378                    location[CHILD_TOP_INDEX] = top;
3379
3380                    return mParent;
3381                }
3382            } else {
3383                mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
3384
3385                location[CHILD_LEFT_INDEX] = mLeft;
3386                location[CHILD_TOP_INDEX] = mTop;
3387
3388                dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX],
3389                        mBottom - location[CHILD_TOP_INDEX]);
3390
3391                return mParent;
3392            }
3393        }
3394
3395        return null;
3396    }
3397
3398    /**
3399     * Offset a rectangle that is in a descendant's coordinate
3400     * space into our coordinate space.
3401     * @param descendant A descendant of this view
3402     * @param rect A rectangle defined in descendant's coordinate space.
3403     */
3404    public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
3405        offsetRectBetweenParentAndChild(descendant, rect, true, false);
3406    }
3407
3408    /**
3409     * Offset a rectangle that is in our coordinate space into an ancestor's
3410     * coordinate space.
3411     * @param descendant A descendant of this view
3412     * @param rect A rectangle defined in descendant's coordinate space.
3413     */
3414    public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
3415        offsetRectBetweenParentAndChild(descendant, rect, false, false);
3416    }
3417
3418    /**
3419     * Helper method that offsets a rect either from parent to descendant or
3420     * descendant to parent.
3421     */
3422    void offsetRectBetweenParentAndChild(View descendant, Rect rect,
3423            boolean offsetFromChildToParent, boolean clipToBounds) {
3424
3425        // already in the same coord system :)
3426        if (descendant == this) {
3427            return;
3428        }
3429
3430        ViewParent theParent = descendant.mParent;
3431
3432        // search and offset up to the parent
3433        while ((theParent != null)
3434                && (theParent instanceof View)
3435                && (theParent != this)) {
3436
3437            if (offsetFromChildToParent) {
3438                rect.offset(descendant.mLeft - descendant.mScrollX,
3439                        descendant.mTop - descendant.mScrollY);
3440                if (clipToBounds) {
3441                    View p = (View) theParent;
3442                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
3443                }
3444            } else {
3445                if (clipToBounds) {
3446                    View p = (View) theParent;
3447                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
3448                }
3449                rect.offset(descendant.mScrollX - descendant.mLeft,
3450                        descendant.mScrollY - descendant.mTop);
3451            }
3452
3453            descendant = (View) theParent;
3454            theParent = descendant.mParent;
3455        }
3456
3457        // now that we are up to this view, need to offset one more time
3458        // to get into our coordinate space
3459        if (theParent == this) {
3460            if (offsetFromChildToParent) {
3461                rect.offset(descendant.mLeft - descendant.mScrollX,
3462                        descendant.mTop - descendant.mScrollY);
3463            } else {
3464                rect.offset(descendant.mScrollX - descendant.mLeft,
3465                        descendant.mScrollY - descendant.mTop);
3466            }
3467        } else {
3468            throw new IllegalArgumentException("parameter must be a descendant of this view");
3469        }
3470    }
3471
3472    /**
3473     * Offset the vertical location of all children of this view by the specified number of pixels.
3474     *
3475     * @param offset the number of pixels to offset
3476     *
3477     * @hide
3478     */
3479    public void offsetChildrenTopAndBottom(int offset) {
3480        final int count = mChildrenCount;
3481        final View[] children = mChildren;
3482
3483        for (int i = 0; i < count; i++) {
3484            final View v = children[i];
3485            v.mTop += offset;
3486            v.mBottom += offset;
3487        }
3488    }
3489
3490    /**
3491     * {@inheritDoc}
3492     */
3493    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
3494        int dx = child.mLeft - mScrollX;
3495        int dy = child.mTop - mScrollY;
3496        if (offset != null) {
3497            offset.x += dx;
3498            offset.y += dy;
3499        }
3500        r.offset(dx, dy);
3501        return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
3502               (mParent == null || mParent.getChildVisibleRect(this, r, offset));
3503    }
3504
3505    /**
3506     * {@inheritDoc}
3507     */
3508    @Override
3509    protected abstract void onLayout(boolean changed,
3510            int l, int t, int r, int b);
3511
3512    /**
3513     * Indicates whether the view group has the ability to animate its children
3514     * after the first layout.
3515     *
3516     * @return true if the children can be animated, false otherwise
3517     */
3518    protected boolean canAnimate() {
3519        return mLayoutAnimationController != null;
3520    }
3521
3522    /**
3523     * Runs the layout animation. Calling this method triggers a relayout of
3524     * this view group.
3525     */
3526    public void startLayoutAnimation() {
3527        if (mLayoutAnimationController != null) {
3528            mGroupFlags |= FLAG_RUN_ANIMATION;
3529            requestLayout();
3530        }
3531    }
3532
3533    /**
3534     * Schedules the layout animation to be played after the next layout pass
3535     * of this view group. This can be used to restart the layout animation
3536     * when the content of the view group changes or when the activity is
3537     * paused and resumed.
3538     */
3539    public void scheduleLayoutAnimation() {
3540        mGroupFlags |= FLAG_RUN_ANIMATION;
3541    }
3542
3543    /**
3544     * Sets the layout animation controller used to animate the group's
3545     * children after the first layout.
3546     *
3547     * @param controller the animation controller
3548     */
3549    public void setLayoutAnimation(LayoutAnimationController controller) {
3550        mLayoutAnimationController = controller;
3551        if (mLayoutAnimationController != null) {
3552            mGroupFlags |= FLAG_RUN_ANIMATION;
3553        }
3554    }
3555
3556    /**
3557     * Returns the layout animation controller used to animate the group's
3558     * children.
3559     *
3560     * @return the current animation controller
3561     */
3562    public LayoutAnimationController getLayoutAnimation() {
3563        return mLayoutAnimationController;
3564    }
3565
3566    /**
3567     * Indicates whether the children's drawing cache is used during a layout
3568     * animation. By default, the drawing cache is enabled but this will prevent
3569     * nested layout animations from working. To nest animations, you must disable
3570     * the cache.
3571     *
3572     * @return true if the animation cache is enabled, false otherwise
3573     *
3574     * @see #setAnimationCacheEnabled(boolean)
3575     * @see View#setDrawingCacheEnabled(boolean)
3576     */
3577    @ViewDebug.ExportedProperty
3578    public boolean isAnimationCacheEnabled() {
3579        return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
3580    }
3581
3582    /**
3583     * Enables or disables the children's drawing cache during a layout animation.
3584     * By default, the drawing cache is enabled but this will prevent nested
3585     * layout animations from working. To nest animations, you must disable the
3586     * cache.
3587     *
3588     * @param enabled true to enable the animation cache, false otherwise
3589     *
3590     * @see #isAnimationCacheEnabled()
3591     * @see View#setDrawingCacheEnabled(boolean)
3592     */
3593    public void setAnimationCacheEnabled(boolean enabled) {
3594        setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
3595    }
3596
3597    /**
3598     * Indicates whether this ViewGroup will always try to draw its children using their
3599     * drawing cache. By default this property is enabled.
3600     *
3601     * @return true if the animation cache is enabled, false otherwise
3602     *
3603     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
3604     * @see #setChildrenDrawnWithCacheEnabled(boolean)
3605     * @see View#setDrawingCacheEnabled(boolean)
3606     */
3607    @ViewDebug.ExportedProperty(category = "drawing")
3608    public boolean isAlwaysDrawnWithCacheEnabled() {
3609        return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
3610    }
3611
3612    /**
3613     * Indicates whether this ViewGroup will always try to draw its children using their
3614     * drawing cache. This property can be set to true when the cache rendering is
3615     * slightly different from the children's normal rendering. Renderings can be different,
3616     * for instance, when the cache's quality is set to low.
3617     *
3618     * When this property is disabled, the ViewGroup will use the drawing cache of its
3619     * children only when asked to. It's usually the task of subclasses to tell ViewGroup
3620     * when to start using the drawing cache and when to stop using it.
3621     *
3622     * @param always true to always draw with the drawing cache, false otherwise
3623     *
3624     * @see #isAlwaysDrawnWithCacheEnabled()
3625     * @see #setChildrenDrawnWithCacheEnabled(boolean)
3626     * @see View#setDrawingCacheEnabled(boolean)
3627     * @see View#setDrawingCacheQuality(int)
3628     */
3629    public void setAlwaysDrawnWithCacheEnabled(boolean always) {
3630        setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
3631    }
3632
3633    /**
3634     * Indicates whether the ViewGroup is currently drawing its children using
3635     * their drawing cache.
3636     *
3637     * @return true if children should be drawn with their cache, false otherwise
3638     *
3639     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
3640     * @see #setChildrenDrawnWithCacheEnabled(boolean)
3641     */
3642    @ViewDebug.ExportedProperty(category = "drawing")
3643    protected boolean isChildrenDrawnWithCacheEnabled() {
3644        return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
3645    }
3646
3647    /**
3648     * Tells the ViewGroup to draw its children using their drawing cache. This property
3649     * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
3650     * will be used only if it has been enabled.
3651     *
3652     * Subclasses should call this method to start and stop using the drawing cache when
3653     * they perform performance sensitive operations, like scrolling or animating.
3654     *
3655     * @param enabled true if children should be drawn with their cache, false otherwise
3656     *
3657     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
3658     * @see #isChildrenDrawnWithCacheEnabled()
3659     */
3660    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
3661        setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
3662    }
3663
3664    /**
3665     * Indicates whether the ViewGroup is drawing its children in the order defined by
3666     * {@link #getChildDrawingOrder(int, int)}.
3667     *
3668     * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
3669     *         false otherwise
3670     *
3671     * @see #setChildrenDrawingOrderEnabled(boolean)
3672     * @see #getChildDrawingOrder(int, int)
3673     */
3674    @ViewDebug.ExportedProperty(category = "drawing")
3675    protected boolean isChildrenDrawingOrderEnabled() {
3676        return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
3677    }
3678
3679    /**
3680     * Tells the ViewGroup whether to draw its children in the order defined by the method
3681     * {@link #getChildDrawingOrder(int, int)}.
3682     *
3683     * @param enabled true if the order of the children when drawing is determined by
3684     *        {@link #getChildDrawingOrder(int, int)}, false otherwise
3685     *
3686     * @see #isChildrenDrawingOrderEnabled()
3687     * @see #getChildDrawingOrder(int, int)
3688     */
3689    protected void setChildrenDrawingOrderEnabled(boolean enabled) {
3690        setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
3691    }
3692
3693    private void setBooleanFlag(int flag, boolean value) {
3694        if (value) {
3695            mGroupFlags |= flag;
3696        } else {
3697            mGroupFlags &= ~flag;
3698        }
3699    }
3700
3701    /**
3702     * Returns an integer indicating what types of drawing caches are kept in memory.
3703     *
3704     * @see #setPersistentDrawingCache(int)
3705     * @see #setAnimationCacheEnabled(boolean)
3706     *
3707     * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
3708     *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
3709     *         and {@link #PERSISTENT_ALL_CACHES}
3710     */
3711    @ViewDebug.ExportedProperty(category = "drawing", mapping = {
3712        @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
3713        @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
3714        @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
3715        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
3716    })
3717    public int getPersistentDrawingCache() {
3718        return mPersistentDrawingCache;
3719    }
3720
3721    /**
3722     * Indicates what types of drawing caches should be kept in memory after
3723     * they have been created.
3724     *
3725     * @see #getPersistentDrawingCache()
3726     * @see #setAnimationCacheEnabled(boolean)
3727     *
3728     * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
3729     *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
3730     *        and {@link #PERSISTENT_ALL_CACHES}
3731     */
3732    public void setPersistentDrawingCache(int drawingCacheToKeep) {
3733        mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
3734    }
3735
3736    /**
3737     * Returns a new set of layout parameters based on the supplied attributes set.
3738     *
3739     * @param attrs the attributes to build the layout parameters from
3740     *
3741     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
3742     *         of its descendants
3743     */
3744    public LayoutParams generateLayoutParams(AttributeSet attrs) {
3745        return new LayoutParams(getContext(), attrs);
3746    }
3747
3748    /**
3749     * Returns a safe set of layout parameters based on the supplied layout params.
3750     * When a ViewGroup is passed a View whose layout params do not pass the test of
3751     * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
3752     * is invoked. This method should return a new set of layout params suitable for
3753     * this ViewGroup, possibly by copying the appropriate attributes from the
3754     * specified set of layout params.
3755     *
3756     * @param p The layout parameters to convert into a suitable set of layout parameters
3757     *          for this ViewGroup.
3758     *
3759     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
3760     *         of its descendants
3761     */
3762    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
3763        return p;
3764    }
3765
3766    /**
3767     * Returns a set of default layout parameters. These parameters are requested
3768     * when the View passed to {@link #addView(View)} has no layout parameters
3769     * already set. If null is returned, an exception is thrown from addView.
3770     *
3771     * @return a set of default layout parameters or null
3772     */
3773    protected LayoutParams generateDefaultLayoutParams() {
3774        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
3775    }
3776
3777    /**
3778     * @hide
3779     */
3780    @Override
3781    protected boolean dispatchConsistencyCheck(int consistency) {
3782        boolean result = super.dispatchConsistencyCheck(consistency);
3783
3784        final int count = mChildrenCount;
3785        final View[] children = mChildren;
3786        for (int i = 0; i < count; i++) {
3787            if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
3788        }
3789
3790        return result;
3791    }
3792
3793    /**
3794     * @hide
3795     */
3796    @Override
3797    protected boolean onConsistencyCheck(int consistency) {
3798        boolean result = super.onConsistencyCheck(consistency);
3799
3800        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
3801        final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
3802
3803        if (checkLayout) {
3804            final int count = mChildrenCount;
3805            final View[] children = mChildren;
3806            for (int i = 0; i < count; i++) {
3807                if (children[i].getParent() != this) {
3808                    result = false;
3809                    android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
3810                            "View " + children[i] + " has no parent/a parent that is not " + this);
3811                }
3812            }
3813        }
3814
3815        if (checkDrawing) {
3816            // If this group is dirty, check that the parent is dirty as well
3817            if ((mPrivateFlags & DIRTY_MASK) != 0) {
3818                final ViewParent parent = getParent();
3819                if (parent != null && !(parent instanceof ViewRoot)) {
3820                    if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
3821                        result = false;
3822                        android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
3823                                "ViewGroup " + this + " is dirty but its parent is not: " + this);
3824                    }
3825                }
3826            }
3827        }
3828
3829        return result;
3830    }
3831
3832    /**
3833     * {@inheritDoc}
3834     */
3835    @Override
3836    protected void debug(int depth) {
3837        super.debug(depth);
3838        String output;
3839
3840        if (mFocused != null) {
3841            output = debugIndent(depth);
3842            output += "mFocused";
3843            Log.d(VIEW_LOG_TAG, output);
3844        }
3845        if (mChildrenCount != 0) {
3846            output = debugIndent(depth);
3847            output += "{";
3848            Log.d(VIEW_LOG_TAG, output);
3849        }
3850        int count = mChildrenCount;
3851        for (int i = 0; i < count; i++) {
3852            View child = mChildren[i];
3853            child.debug(depth + 1);
3854        }
3855
3856        if (mChildrenCount != 0) {
3857            output = debugIndent(depth);
3858            output += "}";
3859            Log.d(VIEW_LOG_TAG, output);
3860        }
3861    }
3862
3863    /**
3864     * Returns the position in the group of the specified child view.
3865     *
3866     * @param child the view for which to get the position
3867     * @return a positive integer representing the position of the view in the
3868     *         group, or -1 if the view does not exist in the group
3869     */
3870    public int indexOfChild(View child) {
3871        final int count = mChildrenCount;
3872        final View[] children = mChildren;
3873        for (int i = 0; i < count; i++) {
3874            if (children[i] == child) {
3875                return i;
3876            }
3877        }
3878        return -1;
3879    }
3880
3881    /**
3882     * Returns the number of children in the group.
3883     *
3884     * @return a positive integer representing the number of children in
3885     *         the group
3886     */
3887    public int getChildCount() {
3888        return mChildrenCount;
3889    }
3890
3891    /**
3892     * Returns the view at the specified position in the group.
3893     *
3894     * @param index the position at which to get the view from
3895     * @return the view at the specified position or null if the position
3896     *         does not exist within the group
3897     */
3898    public View getChildAt(int index) {
3899        try {
3900            return mChildren[index];
3901        } catch (IndexOutOfBoundsException ex) {
3902            return null;
3903        }
3904    }
3905
3906    /**
3907     * Ask all of the children of this view to measure themselves, taking into
3908     * account both the MeasureSpec requirements for this view and its padding.
3909     * We skip children that are in the GONE state The heavy lifting is done in
3910     * getChildMeasureSpec.
3911     *
3912     * @param widthMeasureSpec The width requirements for this view
3913     * @param heightMeasureSpec The height requirements for this view
3914     */
3915    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
3916        final int size = mChildrenCount;
3917        final View[] children = mChildren;
3918        for (int i = 0; i < size; ++i) {
3919            final View child = children[i];
3920            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
3921                measureChild(child, widthMeasureSpec, heightMeasureSpec);
3922            }
3923        }
3924    }
3925
3926    /**
3927     * Ask one of the children of this view to measure itself, taking into
3928     * account both the MeasureSpec requirements for this view and its padding.
3929     * The heavy lifting is done in getChildMeasureSpec.
3930     *
3931     * @param child The child to measure
3932     * @param parentWidthMeasureSpec The width requirements for this view
3933     * @param parentHeightMeasureSpec The height requirements for this view
3934     */
3935    protected void measureChild(View child, int parentWidthMeasureSpec,
3936            int parentHeightMeasureSpec) {
3937        final LayoutParams lp = child.getLayoutParams();
3938
3939        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
3940                mPaddingLeft + mPaddingRight, lp.width);
3941        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
3942                mPaddingTop + mPaddingBottom, lp.height);
3943
3944        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
3945    }
3946
3947    /**
3948     * Ask one of the children of this view to measure itself, taking into
3949     * account both the MeasureSpec requirements for this view and its padding
3950     * and margins. The child must have MarginLayoutParams The heavy lifting is
3951     * done in getChildMeasureSpec.
3952     *
3953     * @param child The child to measure
3954     * @param parentWidthMeasureSpec The width requirements for this view
3955     * @param widthUsed Extra space that has been used up by the parent
3956     *        horizontally (possibly by other children of the parent)
3957     * @param parentHeightMeasureSpec The height requirements for this view
3958     * @param heightUsed Extra space that has been used up by the parent
3959     *        vertically (possibly by other children of the parent)
3960     */
3961    protected void measureChildWithMargins(View child,
3962            int parentWidthMeasureSpec, int widthUsed,
3963            int parentHeightMeasureSpec, int heightUsed) {
3964        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
3965
3966        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
3967                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
3968                        + widthUsed, lp.width);
3969        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
3970                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
3971                        + heightUsed, lp.height);
3972
3973        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
3974    }
3975
3976    /**
3977     * Does the hard part of measureChildren: figuring out the MeasureSpec to
3978     * pass to a particular child. This method figures out the right MeasureSpec
3979     * for one dimension (height or width) of one child view.
3980     *
3981     * The goal is to combine information from our MeasureSpec with the
3982     * LayoutParams of the child to get the best possible results. For example,
3983     * if the this view knows its size (because its MeasureSpec has a mode of
3984     * EXACTLY), and the child has indicated in its LayoutParams that it wants
3985     * to be the same size as the parent, the parent should ask the child to
3986     * layout given an exact size.
3987     *
3988     * @param spec The requirements for this view
3989     * @param padding The padding of this view for the current dimension and
3990     *        margins, if applicable
3991     * @param childDimension How big the child wants to be in the current
3992     *        dimension
3993     * @return a MeasureSpec integer for the child
3994     */
3995    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
3996        int specMode = MeasureSpec.getMode(spec);
3997        int specSize = MeasureSpec.getSize(spec);
3998
3999        int size = Math.max(0, specSize - padding);
4000
4001        int resultSize = 0;
4002        int resultMode = 0;
4003
4004        switch (specMode) {
4005        // Parent has imposed an exact size on us
4006        case MeasureSpec.EXACTLY:
4007            if (childDimension >= 0) {
4008                resultSize = childDimension;
4009                resultMode = MeasureSpec.EXACTLY;
4010            } else if (childDimension == LayoutParams.MATCH_PARENT) {
4011                // Child wants to be our size. So be it.
4012                resultSize = size;
4013                resultMode = MeasureSpec.EXACTLY;
4014            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4015                // Child wants to determine its own size. It can't be
4016                // bigger than us.
4017                resultSize = size;
4018                resultMode = MeasureSpec.AT_MOST;
4019            }
4020            break;
4021
4022        // Parent has imposed a maximum size on us
4023        case MeasureSpec.AT_MOST:
4024            if (childDimension >= 0) {
4025                // Child wants a specific size... so be it
4026                resultSize = childDimension;
4027                resultMode = MeasureSpec.EXACTLY;
4028            } else if (childDimension == LayoutParams.MATCH_PARENT) {
4029                // Child wants to be our size, but our size is not fixed.
4030                // Constrain child to not be bigger than us.
4031                resultSize = size;
4032                resultMode = MeasureSpec.AT_MOST;
4033            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4034                // Child wants to determine its own size. It can't be
4035                // bigger than us.
4036                resultSize = size;
4037                resultMode = MeasureSpec.AT_MOST;
4038            }
4039            break;
4040
4041        // Parent asked to see how big we want to be
4042        case MeasureSpec.UNSPECIFIED:
4043            if (childDimension >= 0) {
4044                // Child wants a specific size... let him have it
4045                resultSize = childDimension;
4046                resultMode = MeasureSpec.EXACTLY;
4047            } else if (childDimension == LayoutParams.MATCH_PARENT) {
4048                // Child wants to be our size... find out how big it should
4049                // be
4050                resultSize = 0;
4051                resultMode = MeasureSpec.UNSPECIFIED;
4052            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
4053                // Child wants to determine its own size.... find out how
4054                // big it should be
4055                resultSize = 0;
4056                resultMode = MeasureSpec.UNSPECIFIED;
4057            }
4058            break;
4059        }
4060        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
4061    }
4062
4063
4064    /**
4065     * Removes any pending animations for views that have been removed. Call
4066     * this if you don't want animations for exiting views to stack up.
4067     */
4068    public void clearDisappearingChildren() {
4069        if (mDisappearingChildren != null) {
4070            mDisappearingChildren.clear();
4071        }
4072    }
4073
4074    /**
4075     * Add a view which is removed from mChildren but still needs animation
4076     *
4077     * @param v View to add
4078     */
4079    private void addDisappearingView(View v) {
4080        ArrayList<View> disappearingChildren = mDisappearingChildren;
4081
4082        if (disappearingChildren == null) {
4083            disappearingChildren = mDisappearingChildren = new ArrayList<View>();
4084        }
4085
4086        disappearingChildren.add(v);
4087    }
4088
4089    /**
4090     * Cleanup a view when its animation is done. This may mean removing it from
4091     * the list of disappearing views.
4092     *
4093     * @param view The view whose animation has finished
4094     * @param animation The animation, cannot be null
4095     */
4096    private void finishAnimatingView(final View view, Animation animation) {
4097        final ArrayList<View> disappearingChildren = mDisappearingChildren;
4098        if (disappearingChildren != null) {
4099            if (disappearingChildren.contains(view)) {
4100                disappearingChildren.remove(view);
4101
4102                if (view.mAttachInfo != null) {
4103                    view.dispatchDetachedFromWindow();
4104                }
4105
4106                view.clearAnimation();
4107                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4108            }
4109        }
4110
4111        if (animation != null && !animation.getFillAfter()) {
4112            view.clearAnimation();
4113        }
4114
4115        if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
4116            view.onAnimationEnd();
4117            // Should be performed by onAnimationEnd() but this avoid an infinite loop,
4118            // so we'd rather be safe than sorry
4119            view.mPrivateFlags &= ~ANIMATION_STARTED;
4120            // Draw one more frame after the animation is done
4121            mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4122        }
4123    }
4124
4125    /**
4126     * This method tells the ViewGroup that the given View object, which should have this
4127     * ViewGroup as its parent,
4128     * should be kept around  (re-displayed when the ViewGroup draws its children) even if it
4129     * is removed from its parent. This allows animations, such as those used by
4130     * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
4131     * the removal of views. A call to this method should always be accompanied by a later call
4132     * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
4133     * so that the View finally gets removed.
4134     *
4135     * @param view The View object to be kept visible even if it gets removed from its parent.
4136     */
4137    public void startViewTransition(View view) {
4138        if (view.mParent == this) {
4139            if (mTransitioningViews == null) {
4140                mTransitioningViews = new ArrayList<View>();
4141            }
4142            mTransitioningViews.add(view);
4143        }
4144    }
4145
4146    /**
4147     * This method should always be called following an earlier call to
4148     * {@link #startViewTransition(View)}. The given View is finally removed from its parent
4149     * and will no longer be displayed. Note that this method does not perform the functionality
4150     * of removing a view from its parent; it just discontinues the display of a View that
4151     * has previously been removed.
4152     *
4153     * @return view The View object that has been removed but is being kept around in the visible
4154     * hierarchy by an earlier call to {@link #startViewTransition(View)}.
4155     */
4156    public void endViewTransition(View view) {
4157        if (mTransitioningViews != null) {
4158            mTransitioningViews.remove(view);
4159            final ArrayList<View> disappearingChildren = mDisappearingChildren;
4160            if (disappearingChildren != null && disappearingChildren.contains(view)) {
4161                disappearingChildren.remove(view);
4162                if (mVisibilityChangingChildren != null &&
4163                        mVisibilityChangingChildren.contains(view)) {
4164                    mVisibilityChangingChildren.remove(view);
4165                } else {
4166                    if (view.mAttachInfo != null) {
4167                        view.dispatchDetachedFromWindow();
4168                    }
4169                    if (view.mParent != null) {
4170                        view.mParent = null;
4171                    }
4172                }
4173                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
4174            }
4175        }
4176    }
4177
4178    private LayoutTransition.TransitionListener mLayoutTransitionListener =
4179            new LayoutTransition.TransitionListener() {
4180        @Override
4181        public void startTransition(LayoutTransition transition, ViewGroup container,
4182                View view, int transitionType) {
4183            // We only care about disappearing items, since we need special logic to keep
4184            // those items visible after they've been 'removed'
4185            if (transitionType == LayoutTransition.DISAPPEARING) {
4186                startViewTransition(view);
4187            }
4188        }
4189
4190        @Override
4191        public void endTransition(LayoutTransition transition, ViewGroup container,
4192                View view, int transitionType) {
4193            if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
4194                endViewTransition(view);
4195            }
4196        }
4197    };
4198
4199    /**
4200     * {@inheritDoc}
4201     */
4202    @Override
4203    public boolean gatherTransparentRegion(Region region) {
4204        // If no transparent regions requested, we are always opaque.
4205        final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
4206        if (meOpaque && region == null) {
4207            // The caller doesn't care about the region, so stop now.
4208            return true;
4209        }
4210        super.gatherTransparentRegion(region);
4211        final View[] children = mChildren;
4212        final int count = mChildrenCount;
4213        boolean noneOfTheChildrenAreTransparent = true;
4214        for (int i = 0; i < count; i++) {
4215            final View child = children[i];
4216            if ((child.mViewFlags & VISIBILITY_MASK) != GONE || child.getAnimation() != null) {
4217                if (!child.gatherTransparentRegion(region)) {
4218                    noneOfTheChildrenAreTransparent = false;
4219                }
4220            }
4221        }
4222        return meOpaque || noneOfTheChildrenAreTransparent;
4223    }
4224
4225    /**
4226     * {@inheritDoc}
4227     */
4228    public void requestTransparentRegion(View child) {
4229        if (child != null) {
4230            child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
4231            if (mParent != null) {
4232                mParent.requestTransparentRegion(this);
4233            }
4234        }
4235    }
4236
4237
4238    @Override
4239    protected boolean fitSystemWindows(Rect insets) {
4240        boolean done = super.fitSystemWindows(insets);
4241        if (!done) {
4242            final int count = mChildrenCount;
4243            final View[] children = mChildren;
4244            for (int i = 0; i < count; i++) {
4245                done = children[i].fitSystemWindows(insets);
4246                if (done) {
4247                    break;
4248                }
4249            }
4250        }
4251        return done;
4252    }
4253
4254    /**
4255     * Returns the animation listener to which layout animation events are
4256     * sent.
4257     *
4258     * @return an {@link android.view.animation.Animation.AnimationListener}
4259     */
4260    public Animation.AnimationListener getLayoutAnimationListener() {
4261        return mAnimationListener;
4262    }
4263
4264    @Override
4265    protected void drawableStateChanged() {
4266        super.drawableStateChanged();
4267
4268        if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
4269            if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
4270                throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
4271                        + " child has duplicateParentState set to true");
4272            }
4273
4274            final View[] children = mChildren;
4275            final int count = mChildrenCount;
4276
4277            for (int i = 0; i < count; i++) {
4278                final View child = children[i];
4279                if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
4280                    child.refreshDrawableState();
4281                }
4282            }
4283        }
4284    }
4285
4286    @Override
4287    public void jumpDrawablesToCurrentState() {
4288        super.jumpDrawablesToCurrentState();
4289        final View[] children = mChildren;
4290        final int count = mChildrenCount;
4291        for (int i = 0; i < count; i++) {
4292            children[i].jumpDrawablesToCurrentState();
4293        }
4294    }
4295
4296    @Override
4297    protected int[] onCreateDrawableState(int extraSpace) {
4298        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
4299            return super.onCreateDrawableState(extraSpace);
4300        }
4301
4302        int need = 0;
4303        int n = getChildCount();
4304        for (int i = 0; i < n; i++) {
4305            int[] childState = getChildAt(i).getDrawableState();
4306
4307            if (childState != null) {
4308                need += childState.length;
4309            }
4310        }
4311
4312        int[] state = super.onCreateDrawableState(extraSpace + need);
4313
4314        for (int i = 0; i < n; i++) {
4315            int[] childState = getChildAt(i).getDrawableState();
4316
4317            if (childState != null) {
4318                state = mergeDrawableStates(state, childState);
4319            }
4320        }
4321
4322        return state;
4323    }
4324
4325    /**
4326     * Sets whether this ViewGroup's drawable states also include
4327     * its children's drawable states.  This is used, for example, to
4328     * make a group appear to be focused when its child EditText or button
4329     * is focused.
4330     */
4331    public void setAddStatesFromChildren(boolean addsStates) {
4332        if (addsStates) {
4333            mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
4334        } else {
4335            mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
4336        }
4337
4338        refreshDrawableState();
4339    }
4340
4341    /**
4342     * Returns whether this ViewGroup's drawable states also include
4343     * its children's drawable states.  This is used, for example, to
4344     * make a group appear to be focused when its child EditText or button
4345     * is focused.
4346     */
4347    public boolean addStatesFromChildren() {
4348        return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
4349    }
4350
4351    /**
4352     * If {link #addStatesFromChildren} is true, refreshes this group's
4353     * drawable state (to include the states from its children).
4354     */
4355    public void childDrawableStateChanged(View child) {
4356        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
4357            refreshDrawableState();
4358        }
4359    }
4360
4361    /**
4362     * Specifies the animation listener to which layout animation events must
4363     * be sent. Only
4364     * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
4365     * and
4366     * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
4367     * are invoked.
4368     *
4369     * @param animationListener the layout animation listener
4370     */
4371    public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
4372        mAnimationListener = animationListener;
4373    }
4374
4375    /**
4376     * LayoutParams are used by views to tell their parents how they want to be
4377     * laid out. See
4378     * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
4379     * for a list of all child view attributes that this class supports.
4380     *
4381     * <p>
4382     * The base LayoutParams class just describes how big the view wants to be
4383     * for both width and height. For each dimension, it can specify one of:
4384     * <ul>
4385     * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
4386     * means that the view wants to be as big as its parent (minus padding)
4387     * <li> WRAP_CONTENT, which means that the view wants to be just big enough
4388     * to enclose its content (plus padding)
4389     * <li> an exact number
4390     * </ul>
4391     * There are subclasses of LayoutParams for different subclasses of
4392     * ViewGroup. For example, AbsoluteLayout has its own subclass of
4393     * LayoutParams which adds an X and Y value.
4394     *
4395     * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
4396     * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
4397     */
4398    public static class LayoutParams {
4399        /**
4400         * Special value for the height or width requested by a View.
4401         * FILL_PARENT means that the view wants to be as big as its parent,
4402         * minus the parent's padding, if any. This value is deprecated
4403         * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
4404         */
4405        @SuppressWarnings({"UnusedDeclaration"})
4406        @Deprecated
4407        public static final int FILL_PARENT = -1;
4408
4409        /**
4410         * Special value for the height or width requested by a View.
4411         * MATCH_PARENT means that the view wants to be as big as its parent,
4412         * minus the parent's padding, if any. Introduced in API Level 8.
4413         */
4414        public static final int MATCH_PARENT = -1;
4415
4416        /**
4417         * Special value for the height or width requested by a View.
4418         * WRAP_CONTENT means that the view wants to be just large enough to fit
4419         * its own internal content, taking its own padding into account.
4420         */
4421        public static final int WRAP_CONTENT = -2;
4422
4423        /**
4424         * Information about how wide the view wants to be. Can be one of the
4425         * constants FILL_PARENT (replaced by MATCH_PARENT ,
4426         * in API Level 8) or WRAP_CONTENT. or an exact size.
4427         */
4428        @ViewDebug.ExportedProperty(category = "layout", mapping = {
4429            @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
4430            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
4431        })
4432        public int width;
4433
4434        /**
4435         * Information about how tall the view wants to be. Can be one of the
4436         * constants FILL_PARENT (replaced by MATCH_PARENT ,
4437         * in API Level 8) or WRAP_CONTENT. or an exact size.
4438         */
4439        @ViewDebug.ExportedProperty(category = "layout", mapping = {
4440            @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
4441            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
4442        })
4443        public int height;
4444
4445        /**
4446         * Used to animate layouts.
4447         */
4448        public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
4449
4450        /**
4451         * Creates a new set of layout parameters. The values are extracted from
4452         * the supplied attributes set and context. The XML attributes mapped
4453         * to this set of layout parameters are:
4454         *
4455         * <ul>
4456         *   <li><code>layout_width</code>: the width, either an exact value,
4457         *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
4458         *   {@link #MATCH_PARENT} in API Level 8)</li>
4459         *   <li><code>layout_height</code>: the height, either an exact value,
4460         *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
4461         *   {@link #MATCH_PARENT} in API Level 8)</li>
4462         * </ul>
4463         *
4464         * @param c the application environment
4465         * @param attrs the set of attributes from which to extract the layout
4466         *              parameters' values
4467         */
4468        public LayoutParams(Context c, AttributeSet attrs) {
4469            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
4470            setBaseAttributes(a,
4471                    R.styleable.ViewGroup_Layout_layout_width,
4472                    R.styleable.ViewGroup_Layout_layout_height);
4473            a.recycle();
4474        }
4475
4476        /**
4477         * Creates a new set of layout parameters with the specified width
4478         * and height.
4479         *
4480         * @param width the width, 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         * @param height the height, either {@link #WRAP_CONTENT},
4484         *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
4485         *        API Level 8), or a fixed size in pixels
4486         */
4487        public LayoutParams(int width, int height) {
4488            this.width = width;
4489            this.height = height;
4490        }
4491
4492        /**
4493         * Copy constructor. Clones the width and height values of the source.
4494         *
4495         * @param source The layout params to copy from.
4496         */
4497        public LayoutParams(LayoutParams source) {
4498            this.width = source.width;
4499            this.height = source.height;
4500        }
4501
4502        /**
4503         * Used internally by MarginLayoutParams.
4504         * @hide
4505         */
4506        LayoutParams() {
4507        }
4508
4509        /**
4510         * Extracts the layout parameters from the supplied attributes.
4511         *
4512         * @param a the style attributes to extract the parameters from
4513         * @param widthAttr the identifier of the width attribute
4514         * @param heightAttr the identifier of the height attribute
4515         */
4516        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
4517            width = a.getLayoutDimension(widthAttr, "layout_width");
4518            height = a.getLayoutDimension(heightAttr, "layout_height");
4519        }
4520
4521        /**
4522         * Returns a String representation of this set of layout parameters.
4523         *
4524         * @param output the String to prepend to the internal representation
4525         * @return a String with the following format: output +
4526         *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
4527         *
4528         * @hide
4529         */
4530        public String debug(String output) {
4531            return output + "ViewGroup.LayoutParams={ width="
4532                    + sizeToString(width) + ", height=" + sizeToString(height) + " }";
4533        }
4534
4535        /**
4536         * Converts the specified size to a readable String.
4537         *
4538         * @param size the size to convert
4539         * @return a String instance representing the supplied size
4540         *
4541         * @hide
4542         */
4543        protected static String sizeToString(int size) {
4544            if (size == WRAP_CONTENT) {
4545                return "wrap-content";
4546            }
4547            if (size == MATCH_PARENT) {
4548                return "match-parent";
4549            }
4550            return String.valueOf(size);
4551        }
4552    }
4553
4554    /**
4555     * Per-child layout information for layouts that support margins.
4556     * See
4557     * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
4558     * for a list of all child view attributes that this class supports.
4559     */
4560    public static class MarginLayoutParams extends ViewGroup.LayoutParams {
4561        /**
4562         * The left margin in pixels of the child.
4563         */
4564        @ViewDebug.ExportedProperty(category = "layout")
4565        public int leftMargin;
4566
4567        /**
4568         * The top margin in pixels of the child.
4569         */
4570        @ViewDebug.ExportedProperty(category = "layout")
4571        public int topMargin;
4572
4573        /**
4574         * The right margin in pixels of the child.
4575         */
4576        @ViewDebug.ExportedProperty(category = "layout")
4577        public int rightMargin;
4578
4579        /**
4580         * The bottom margin in pixels of the child.
4581         */
4582        @ViewDebug.ExportedProperty(category = "layout")
4583        public int bottomMargin;
4584
4585        /**
4586         * Creates a new set of layout parameters. The values are extracted from
4587         * the supplied attributes set and context.
4588         *
4589         * @param c the application environment
4590         * @param attrs the set of attributes from which to extract the layout
4591         *              parameters' values
4592         */
4593        public MarginLayoutParams(Context c, AttributeSet attrs) {
4594            super();
4595
4596            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
4597            setBaseAttributes(a,
4598                    R.styleable.ViewGroup_MarginLayout_layout_width,
4599                    R.styleable.ViewGroup_MarginLayout_layout_height);
4600
4601            int margin = a.getDimensionPixelSize(
4602                    com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
4603            if (margin >= 0) {
4604                leftMargin = margin;
4605                topMargin = margin;
4606                rightMargin= margin;
4607                bottomMargin = margin;
4608            } else {
4609                leftMargin = a.getDimensionPixelSize(
4610                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
4611                topMargin = a.getDimensionPixelSize(
4612                        R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
4613                rightMargin = a.getDimensionPixelSize(
4614                        R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
4615                bottomMargin = a.getDimensionPixelSize(
4616                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
4617            }
4618
4619            a.recycle();
4620        }
4621
4622        /**
4623         * {@inheritDoc}
4624         */
4625        public MarginLayoutParams(int width, int height) {
4626            super(width, height);
4627        }
4628
4629        /**
4630         * Copy constructor. Clones the width, height and margin values of the source.
4631         *
4632         * @param source The layout params to copy from.
4633         */
4634        public MarginLayoutParams(MarginLayoutParams source) {
4635            this.width = source.width;
4636            this.height = source.height;
4637
4638            this.leftMargin = source.leftMargin;
4639            this.topMargin = source.topMargin;
4640            this.rightMargin = source.rightMargin;
4641            this.bottomMargin = source.bottomMargin;
4642        }
4643
4644        /**
4645         * {@inheritDoc}
4646         */
4647        public MarginLayoutParams(LayoutParams source) {
4648            super(source);
4649        }
4650
4651        /**
4652         * Sets the margins, in pixels.
4653         *
4654         * @param left the left margin size
4655         * @param top the top margin size
4656         * @param right the right margin size
4657         * @param bottom the bottom margin size
4658         *
4659         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
4660         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
4661         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
4662         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
4663         */
4664        public void setMargins(int left, int top, int right, int bottom) {
4665            leftMargin = left;
4666            topMargin = top;
4667            rightMargin = right;
4668            bottomMargin = bottom;
4669        }
4670    }
4671
4672    /* Describes a touched view and the ids of the pointers that it has captured.
4673     *
4674     * This code assumes that pointer ids are always in the range 0..31 such that
4675     * it can use a bitfield to track which pointer ids are present.
4676     * As it happens, the lower layers of the input dispatch pipeline also use the
4677     * same trick so the assumption should be safe here...
4678     */
4679    private static final class TouchTarget {
4680        private static final int MAX_RECYCLED = 32;
4681        private static final Object sRecycleLock = new Object();
4682        private static TouchTarget sRecycleBin;
4683        private static int sRecycledCount;
4684
4685        public static final int ALL_POINTER_IDS = -1; // all ones
4686
4687        // The touched child view.
4688        public View child;
4689
4690        // The combined bit mask of pointer ids for all pointers captured by the target.
4691        public int pointerIdBits;
4692
4693        // The next target in the target list.
4694        public TouchTarget next;
4695
4696        private TouchTarget() {
4697        }
4698
4699        public static TouchTarget obtain(View child, int pointerIdBits) {
4700            final TouchTarget target;
4701            synchronized (sRecycleLock) {
4702                if (sRecycleBin == null) {
4703                    target = new TouchTarget();
4704                } else {
4705                    target = sRecycleBin;
4706                    sRecycleBin = target.next;
4707                     sRecycledCount--;
4708                    target.next = null;
4709                }
4710            }
4711            target.child = child;
4712            target.pointerIdBits = pointerIdBits;
4713            return target;
4714        }
4715
4716        public void recycle() {
4717            synchronized (sRecycleLock) {
4718                if (sRecycledCount < MAX_RECYCLED) {
4719                    next = sRecycleBin;
4720                    sRecycleBin = this;
4721                    sRecycledCount += 1;
4722                } else {
4723                    next = null;
4724                }
4725                child = null;
4726            }
4727        }
4728    }
4729}
4730