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