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