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