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