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