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