ViewGroup.java revision 55bdb10c168615ee7721c657e0633b3aefbff777
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        // Whether this event should be handled by the accessibility focus first.
1935        final boolean targetAccessibilityFocus = ev.isTargetAccessibilityFocus();
1936
1937        boolean handled = false;
1938        if (onFilterTouchEventForSecurity(ev)) {
1939            final int action = ev.getAction();
1940            final int actionMasked = action & MotionEvent.ACTION_MASK;
1941
1942            // Handle an initial down.
1943            if (actionMasked == MotionEvent.ACTION_DOWN) {
1944                // Throw away all previous state when starting a new touch gesture.
1945                // The framework may have dropped the up or cancel event for the previous gesture
1946                // due to an app switch, ANR, or some other state change.
1947                cancelAndClearTouchTargets(ev);
1948                resetTouchState();
1949            }
1950
1951            // Check for interception.
1952            final boolean intercepted;
1953            if (!targetAccessibilityFocus) {
1954                if (actionMasked == MotionEvent.ACTION_DOWN
1955                        || mFirstTouchTarget != null) {
1956                    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
1957                    if (!disallowIntercept) {
1958                        intercepted = onInterceptTouchEvent(ev);
1959                        ev.setAction(action); // restore action in case it was changed
1960                    } else {
1961                        intercepted = false;
1962                    }
1963                } else {
1964                    // There are no touch targets and this action is not an initial down
1965                    // so this view group continues to intercept touches.
1966                    intercepted = true;
1967                }
1968            } else {
1969                // If event should reach the accessibility focus first, do not intercept it.
1970                intercepted = false;
1971            }
1972
1973            // Check for cancelation.
1974            final boolean canceled = resetCancelNextUpFlag(this)
1975                    || actionMasked == MotionEvent.ACTION_CANCEL;
1976
1977            // Update list of touch targets for pointer down, if needed.
1978            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
1979            TouchTarget newTouchTarget = null;
1980            boolean alreadyDispatchedToNewTouchTarget = false;
1981            if (!canceled && !intercepted) {
1982                if (actionMasked == MotionEvent.ACTION_DOWN
1983                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
1984                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE
1985                        || targetAccessibilityFocus) {
1986                    final int actionIndex = ev.getActionIndex(); // always 0 for down
1987                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
1988                            : TouchTarget.ALL_POINTER_IDS;
1989
1990                    // Clean up earlier touch targets for this pointer id in case they
1991                    // have become out of sync.
1992                    removePointersFromTouchTargets(idBitsToAssign);
1993
1994                    final int childrenCount = mChildrenCount;
1995                    if (newTouchTarget == null && childrenCount != 0) {
1996                        final float x = ev.getX(actionIndex);
1997                        final float y = ev.getY(actionIndex);
1998                        // Find a child that can receive the event.
1999                        // Scan children from front to back.
2000                        final ArrayList<View> preorderedList = buildOrderedChildList();
2001                        final boolean customOrder = preorderedList == null
2002                                && isChildrenDrawingOrderEnabled();
2003                        final View[] children = mChildren;
2004                        for (int i = childrenCount - 1; i >= 0; i--) {
2005                            final int childIndex = customOrder
2006                                    ? getChildDrawingOrder(childrenCount, i) : i;
2007                            final View child = (preorderedList == null)
2008                                    ? children[childIndex] : preorderedList.get(childIndex);
2009                            if (!canViewReceivePointerEvents(child)
2010                                    || !isTransformedTouchPointInView(x, y, child, null)) {
2011                                continue;
2012                            }
2013
2014                            newTouchTarget = getTouchTarget(child);
2015                            if (newTouchTarget != null) {
2016                                // Child is already receiving touch within its bounds.
2017                                // Give it the new pointer in addition to the ones it is handling.
2018                                newTouchTarget.pointerIdBits |= idBitsToAssign;
2019                                break;
2020                            }
2021
2022                            resetCancelNextUpFlag(child);
2023                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
2024                                // Child wants to receive touch within its bounds.
2025                                mLastTouchDownTime = ev.getDownTime();
2026                                if (preorderedList != null) {
2027                                    // childIndex points into presorted list, find original index
2028                                    for (int j = 0; j < childrenCount; j++) {
2029                                        if (children[childIndex] == mChildren[j]) {
2030                                            mLastTouchDownIndex = j;
2031                                            break;
2032                                        }
2033                                    }
2034                                } else {
2035                                    mLastTouchDownIndex = childIndex;
2036                                }
2037                                mLastTouchDownX = ev.getX();
2038                                mLastTouchDownY = ev.getY();
2039                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
2040                                alreadyDispatchedToNewTouchTarget = true;
2041                                break;
2042                            }
2043                        }
2044                        if (preorderedList != null) preorderedList.clear();
2045                    }
2046
2047                    if (newTouchTarget == null && mFirstTouchTarget != null) {
2048                        // Did not find a child to receive the event.
2049                        // Assign the pointer to the least recently added target.
2050                        newTouchTarget = mFirstTouchTarget;
2051                        while (newTouchTarget.next != null) {
2052                            newTouchTarget = newTouchTarget.next;
2053                        }
2054                        newTouchTarget.pointerIdBits |= idBitsToAssign;
2055                    }
2056                }
2057            }
2058
2059            // Dispatch to touch targets.
2060            if (mFirstTouchTarget == null) {
2061                // No touch targets so treat this as an ordinary view.
2062                handled = dispatchTransformedTouchEvent(ev, canceled, null,
2063                        TouchTarget.ALL_POINTER_IDS);
2064            } else {
2065                // Dispatch to touch targets, excluding the new touch target if we already
2066                // dispatched to it.  Cancel touch targets if necessary.
2067                TouchTarget predecessor = null;
2068                TouchTarget target = mFirstTouchTarget;
2069                while (target != null) {
2070                    final TouchTarget next = target.next;
2071                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
2072                        handled = true;
2073                    } else {
2074                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
2075                                || intercepted;
2076                        if (dispatchTransformedTouchEvent(ev, cancelChild,
2077                                target.child, target.pointerIdBits)) {
2078                            handled = true;
2079                        }
2080                        if (cancelChild) {
2081                            if (predecessor == null) {
2082                                mFirstTouchTarget = next;
2083                            } else {
2084                                predecessor.next = next;
2085                            }
2086                            target.recycle();
2087                            target = next;
2088                            continue;
2089                        }
2090                    }
2091                    predecessor = target;
2092                    target = next;
2093                }
2094            }
2095
2096            // Update list of touch targets for pointer up or cancel, if needed.
2097            if (canceled
2098                    || actionMasked == MotionEvent.ACTION_UP
2099                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
2100                resetTouchState();
2101            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
2102                final int actionIndex = ev.getActionIndex();
2103                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
2104                removePointersFromTouchTargets(idBitsToRemove);
2105            }
2106        }
2107
2108        if (!handled && mInputEventConsistencyVerifier != null) {
2109            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
2110        }
2111        return handled;
2112    }
2113
2114    /**
2115     * Resets all touch state in preparation for a new cycle.
2116     */
2117    private void resetTouchState() {
2118        clearTouchTargets();
2119        resetCancelNextUpFlag(this);
2120        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2121        mNestedScrollAxes = SCROLL_AXIS_NONE;
2122    }
2123
2124    /**
2125     * Resets the cancel next up flag.
2126     * Returns true if the flag was previously set.
2127     */
2128    private static boolean resetCancelNextUpFlag(View view) {
2129        if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
2130            view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
2131            return true;
2132        }
2133        return false;
2134    }
2135
2136    /**
2137     * Clears all touch targets.
2138     */
2139    private void clearTouchTargets() {
2140        TouchTarget target = mFirstTouchTarget;
2141        if (target != null) {
2142            do {
2143                TouchTarget next = target.next;
2144                target.recycle();
2145                target = next;
2146            } while (target != null);
2147            mFirstTouchTarget = null;
2148        }
2149    }
2150
2151    /**
2152     * Cancels and clears all touch targets.
2153     */
2154    private void cancelAndClearTouchTargets(MotionEvent event) {
2155        if (mFirstTouchTarget != null) {
2156            boolean syntheticEvent = false;
2157            if (event == null) {
2158                final long now = SystemClock.uptimeMillis();
2159                event = MotionEvent.obtain(now, now,
2160                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2161                event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2162                syntheticEvent = true;
2163            }
2164
2165            for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2166                resetCancelNextUpFlag(target.child);
2167                dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
2168            }
2169            clearTouchTargets();
2170
2171            if (syntheticEvent) {
2172                event.recycle();
2173            }
2174        }
2175    }
2176
2177    /**
2178     * Gets the touch target for specified child view.
2179     * Returns null if not found.
2180     */
2181    private TouchTarget getTouchTarget(View child) {
2182        for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
2183            if (target.child == child) {
2184                return target;
2185            }
2186        }
2187        return null;
2188    }
2189
2190    /**
2191     * Adds a touch target for specified child to the beginning of the list.
2192     * Assumes the target child is not already present.
2193     */
2194    private TouchTarget addTouchTarget(View child, int pointerIdBits) {
2195        TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
2196        target.next = mFirstTouchTarget;
2197        mFirstTouchTarget = target;
2198        return target;
2199    }
2200
2201    /**
2202     * Removes the pointer ids from consideration.
2203     */
2204    private void removePointersFromTouchTargets(int pointerIdBits) {
2205        TouchTarget predecessor = null;
2206        TouchTarget target = mFirstTouchTarget;
2207        while (target != null) {
2208            final TouchTarget next = target.next;
2209            if ((target.pointerIdBits & pointerIdBits) != 0) {
2210                target.pointerIdBits &= ~pointerIdBits;
2211                if (target.pointerIdBits == 0) {
2212                    if (predecessor == null) {
2213                        mFirstTouchTarget = next;
2214                    } else {
2215                        predecessor.next = next;
2216                    }
2217                    target.recycle();
2218                    target = next;
2219                    continue;
2220                }
2221            }
2222            predecessor = target;
2223            target = next;
2224        }
2225    }
2226
2227    private void cancelTouchTarget(View view) {
2228        TouchTarget predecessor = null;
2229        TouchTarget target = mFirstTouchTarget;
2230        while (target != null) {
2231            final TouchTarget next = target.next;
2232            if (target.child == view) {
2233                if (predecessor == null) {
2234                    mFirstTouchTarget = next;
2235                } else {
2236                    predecessor.next = next;
2237                }
2238                target.recycle();
2239
2240                final long now = SystemClock.uptimeMillis();
2241                MotionEvent event = MotionEvent.obtain(now, now,
2242                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
2243                event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
2244                view.dispatchTouchEvent(event);
2245                event.recycle();
2246                return;
2247            }
2248            predecessor = target;
2249            target = next;
2250        }
2251    }
2252
2253    /**
2254     * Returns true if a child view can receive pointer events.
2255     * @hide
2256     */
2257    private static boolean canViewReceivePointerEvents(View child) {
2258        return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
2259                || child.getAnimation() != null;
2260    }
2261
2262    private float[] getTempPoint() {
2263        if (mTempPoint == null) {
2264            mTempPoint = new float[2];
2265        }
2266        return mTempPoint;
2267    }
2268
2269    /**
2270     * Returns true if a child view contains the specified point when transformed
2271     * into its coordinate space.
2272     * Child must not be null.
2273     * @hide
2274     */
2275    protected boolean isTransformedTouchPointInView(float x, float y, View child,
2276            PointF outLocalPoint) {
2277        final float[] point = getTempPoint();
2278        point[0] = x;
2279        point[1] = y;
2280        transformPointToViewLocal(point, child);
2281        final boolean isInView = child.pointInView(point[0], point[1]);
2282        if (isInView && outLocalPoint != null) {
2283            outLocalPoint.set(point[0], point[1]);
2284        }
2285        return isInView;
2286    }
2287
2288    /**
2289     * @hide
2290     */
2291    public void transformPointToViewLocal(float[] point, View child) {
2292        point[0] += mScrollX - child.mLeft;
2293        point[1] += mScrollY - child.mTop;
2294
2295        if (!child.hasIdentityMatrix()) {
2296            child.getInverseMatrix().mapPoints(point);
2297        }
2298    }
2299
2300    /**
2301     * Transforms a motion event into the coordinate space of a particular child view,
2302     * filters out irrelevant pointer ids, and overrides its action if necessary.
2303     * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
2304     */
2305    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
2306            View child, int desiredPointerIdBits) {
2307        final boolean handled;
2308
2309        // Canceling motions is a special case.  We don't need to perform any transformations
2310        // or filtering.  The important part is the action, not the contents.
2311        final int oldAction = event.getAction();
2312        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
2313            event.setAction(MotionEvent.ACTION_CANCEL);
2314            if (child == null) {
2315                handled = super.dispatchTouchEvent(event);
2316            } else {
2317                handled = child.dispatchTouchEvent(event);
2318            }
2319            event.setAction(oldAction);
2320            return handled;
2321        }
2322
2323        // Calculate the number of pointers to deliver.
2324        final int oldPointerIdBits = event.getPointerIdBits();
2325        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
2326
2327        // If for some reason we ended up in an inconsistent state where it looks like we
2328        // might produce a motion event with no pointers in it, then drop the event.
2329        if (newPointerIdBits == 0) {
2330            return false;
2331        }
2332
2333        // If the number of pointers is the same and we don't need to perform any fancy
2334        // irreversible transformations, then we can reuse the motion event for this
2335        // dispatch as long as we are careful to revert any changes we make.
2336        // Otherwise we need to make a copy.
2337        final MotionEvent transformedEvent;
2338        if (newPointerIdBits == oldPointerIdBits) {
2339            if (child == null || child.hasIdentityMatrix()) {
2340                if (child == null) {
2341                    handled = super.dispatchTouchEvent(event);
2342                } else {
2343                    final float offsetX = mScrollX - child.mLeft;
2344                    final float offsetY = mScrollY - child.mTop;
2345                    event.offsetLocation(offsetX, offsetY);
2346
2347                    handled = child.dispatchTouchEvent(event);
2348
2349                    event.offsetLocation(-offsetX, -offsetY);
2350                }
2351                return handled;
2352            }
2353            transformedEvent = MotionEvent.obtain(event);
2354        } else {
2355            transformedEvent = event.split(newPointerIdBits);
2356        }
2357
2358        // Perform any necessary transformations and dispatch.
2359        if (child == null) {
2360            handled = super.dispatchTouchEvent(transformedEvent);
2361        } else {
2362            final float offsetX = mScrollX - child.mLeft;
2363            final float offsetY = mScrollY - child.mTop;
2364            transformedEvent.offsetLocation(offsetX, offsetY);
2365            if (! child.hasIdentityMatrix()) {
2366                transformedEvent.transform(child.getInverseMatrix());
2367            }
2368
2369            handled = child.dispatchTouchEvent(transformedEvent);
2370        }
2371
2372        // Done.
2373        transformedEvent.recycle();
2374        return handled;
2375    }
2376
2377    /**
2378     * Enable or disable the splitting of MotionEvents to multiple children during touch event
2379     * dispatch. This behavior is enabled by default for applications that target an
2380     * SDK version of {@link Build.VERSION_CODES#HONEYCOMB} or newer.
2381     *
2382     * <p>When this option is enabled MotionEvents may be split and dispatched to different child
2383     * views depending on where each pointer initially went down. This allows for user interactions
2384     * such as scrolling two panes of content independently, chording of buttons, and performing
2385     * independent gestures on different pieces of content.
2386     *
2387     * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
2388     *              child views. <code>false</code> to only allow one child view to be the target of
2389     *              any MotionEvent received by this ViewGroup.
2390     * @attr ref android.R.styleable#ViewGroup_splitMotionEvents
2391     */
2392    public void setMotionEventSplittingEnabled(boolean split) {
2393        // TODO Applications really shouldn't change this setting mid-touch event,
2394        // but perhaps this should handle that case and send ACTION_CANCELs to any child views
2395        // with gestures in progress when this is changed.
2396        if (split) {
2397            mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
2398        } else {
2399            mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
2400        }
2401    }
2402
2403    /**
2404     * Returns true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2405     * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
2406     */
2407    public boolean isMotionEventSplittingEnabled() {
2408        return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
2409    }
2410
2411    /**
2412     * Returns true if this ViewGroup should be considered as a single entity for removal
2413     * when executing an Activity transition. If this is false, child elements will move
2414     * individually during the transition.
2415     *
2416     * @return True if the ViewGroup should be acted on together during an Activity transition.
2417     * The default value is true when there is a non-null background or if
2418     * {@link #getTransitionName()} is not null or if a
2419     * non-null {@link android.view.ViewOutlineProvider} other than
2420     * {@link android.view.ViewOutlineProvider#BACKGROUND} was given to
2421     * {@link #setOutlineProvider(ViewOutlineProvider)} and false otherwise.
2422     */
2423    public boolean isTransitionGroup() {
2424        if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) {
2425            return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0);
2426        } else {
2427            final ViewOutlineProvider outlineProvider = getOutlineProvider();
2428            return getBackground() != null || getTransitionName() != null ||
2429                    (outlineProvider != null && outlineProvider != ViewOutlineProvider.BACKGROUND);
2430        }
2431    }
2432
2433    /**
2434     * Changes whether or not this ViewGroup should be treated as a single entity during
2435     * Activity Transitions.
2436     * @param isTransitionGroup Whether or not the ViewGroup should be treated as a unit
2437     *                          in Activity transitions. If false, the ViewGroup won't transition,
2438     *                          only its children. If true, the entire ViewGroup will transition
2439     *                          together.
2440     * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
2441     * android.util.Pair[])
2442     */
2443    public void setTransitionGroup(boolean isTransitionGroup) {
2444        mGroupFlags |= FLAG_IS_TRANSITION_GROUP_SET;
2445        if (isTransitionGroup) {
2446            mGroupFlags |= FLAG_IS_TRANSITION_GROUP;
2447        } else {
2448            mGroupFlags &= ~FLAG_IS_TRANSITION_GROUP;
2449        }
2450    }
2451
2452    /**
2453     * {@inheritDoc}
2454     */
2455    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
2456
2457        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
2458            // We're already in this state, assume our ancestors are too
2459            return;
2460        }
2461
2462        if (disallowIntercept) {
2463            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
2464        } else {
2465            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
2466        }
2467
2468        // Pass it up to our parent
2469        if (mParent != null) {
2470            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
2471        }
2472    }
2473
2474    /**
2475     * Implement this method to intercept all touch screen motion events.  This
2476     * allows you to watch events as they are dispatched to your children, and
2477     * take ownership of the current gesture at any point.
2478     *
2479     * <p>Using this function takes some care, as it has a fairly complicated
2480     * interaction with {@link View#onTouchEvent(MotionEvent)
2481     * View.onTouchEvent(MotionEvent)}, and using it requires implementing
2482     * that method as well as this one in the correct way.  Events will be
2483     * received in the following order:
2484     *
2485     * <ol>
2486     * <li> You will receive the down event here.
2487     * <li> The down event will be handled either by a child of this view
2488     * group, or given to your own onTouchEvent() method to handle; this means
2489     * you should implement onTouchEvent() to return true, so you will
2490     * continue to see the rest of the gesture (instead of looking for
2491     * a parent view to handle it).  Also, by returning true from
2492     * onTouchEvent(), you will not receive any following
2493     * events in onInterceptTouchEvent() and all touch processing must
2494     * happen in onTouchEvent() like normal.
2495     * <li> For as long as you return false from this function, each following
2496     * event (up to and including the final up) will be delivered first here
2497     * and then to the target's onTouchEvent().
2498     * <li> If you return true from here, you will not receive any
2499     * following events: the target view will receive the same event but
2500     * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
2501     * events will be delivered to your onTouchEvent() method and no longer
2502     * appear here.
2503     * </ol>
2504     *
2505     * @param ev The motion event being dispatched down the hierarchy.
2506     * @return Return true to steal motion events from the children and have
2507     * them dispatched to this ViewGroup through onTouchEvent().
2508     * The current target will receive an ACTION_CANCEL event, and no further
2509     * messages will be delivered here.
2510     */
2511    public boolean onInterceptTouchEvent(MotionEvent ev) {
2512        return false;
2513    }
2514
2515    /**
2516     * {@inheritDoc}
2517     *
2518     * Looks for a view to give focus to respecting the setting specified by
2519     * {@link #getDescendantFocusability()}.
2520     *
2521     * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
2522     * find focus within the children of this group when appropriate.
2523     *
2524     * @see #FOCUS_BEFORE_DESCENDANTS
2525     * @see #FOCUS_AFTER_DESCENDANTS
2526     * @see #FOCUS_BLOCK_DESCENDANTS
2527     * @see #onRequestFocusInDescendants(int, android.graphics.Rect)
2528     */
2529    @Override
2530    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
2531        if (DBG) {
2532            System.out.println(this + " ViewGroup.requestFocus direction="
2533                    + direction);
2534        }
2535        int descendantFocusability = getDescendantFocusability();
2536
2537        switch (descendantFocusability) {
2538            case FOCUS_BLOCK_DESCENDANTS:
2539                return super.requestFocus(direction, previouslyFocusedRect);
2540            case FOCUS_BEFORE_DESCENDANTS: {
2541                final boolean took = super.requestFocus(direction, previouslyFocusedRect);
2542                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
2543            }
2544            case FOCUS_AFTER_DESCENDANTS: {
2545                final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
2546                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
2547            }
2548            default:
2549                throw new IllegalStateException("descendant focusability must be "
2550                        + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
2551                        + "but is " + descendantFocusability);
2552        }
2553    }
2554
2555    /**
2556     * Look for a descendant to call {@link View#requestFocus} on.
2557     * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
2558     * when it wants to request focus within its children.  Override this to
2559     * customize how your {@link ViewGroup} requests focus within its children.
2560     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
2561     * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
2562     *        to give a finer grained hint about where focus is coming from.  May be null
2563     *        if there is no hint.
2564     * @return Whether focus was taken.
2565     */
2566    @SuppressWarnings({"ConstantConditions"})
2567    protected boolean onRequestFocusInDescendants(int direction,
2568            Rect previouslyFocusedRect) {
2569        int index;
2570        int increment;
2571        int end;
2572        int count = mChildrenCount;
2573        if ((direction & FOCUS_FORWARD) != 0) {
2574            index = 0;
2575            increment = 1;
2576            end = count;
2577        } else {
2578            index = count - 1;
2579            increment = -1;
2580            end = -1;
2581        }
2582        final View[] children = mChildren;
2583        for (int i = index; i != end; i += increment) {
2584            View child = children[i];
2585            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2586                if (child.requestFocus(direction, previouslyFocusedRect)) {
2587                    return true;
2588                }
2589            }
2590        }
2591        return false;
2592    }
2593
2594    /**
2595     * {@inheritDoc}
2596     *
2597     * @hide
2598     */
2599    @Override
2600    public void dispatchStartTemporaryDetach() {
2601        super.dispatchStartTemporaryDetach();
2602        final int count = mChildrenCount;
2603        final View[] children = mChildren;
2604        for (int i = 0; i < count; i++) {
2605            children[i].dispatchStartTemporaryDetach();
2606        }
2607    }
2608
2609    /**
2610     * {@inheritDoc}
2611     *
2612     * @hide
2613     */
2614    @Override
2615    public void dispatchFinishTemporaryDetach() {
2616        super.dispatchFinishTemporaryDetach();
2617        final int count = mChildrenCount;
2618        final View[] children = mChildren;
2619        for (int i = 0; i < count; i++) {
2620            children[i].dispatchFinishTemporaryDetach();
2621        }
2622    }
2623
2624    /**
2625     * {@inheritDoc}
2626     */
2627    @Override
2628    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
2629        mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2630        super.dispatchAttachedToWindow(info, visibility);
2631        mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;
2632
2633        final int count = mChildrenCount;
2634        final View[] children = mChildren;
2635        for (int i = 0; i < count; i++) {
2636            final View child = children[i];
2637            child.dispatchAttachedToWindow(info,
2638                    visibility | (child.mViewFlags & VISIBILITY_MASK));
2639        }
2640    }
2641
2642    @Override
2643    void dispatchScreenStateChanged(int screenState) {
2644        super.dispatchScreenStateChanged(screenState);
2645
2646        final int count = mChildrenCount;
2647        final View[] children = mChildren;
2648        for (int i = 0; i < count; i++) {
2649            children[i].dispatchScreenStateChanged(screenState);
2650        }
2651    }
2652
2653    @Override
2654    boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
2655        boolean handled = false;
2656        if (includeForAccessibility()) {
2657            handled = super.dispatchPopulateAccessibilityEventInternal(event);
2658            if (handled) {
2659                return handled;
2660            }
2661        }
2662        // Let our children have a shot in populating the event.
2663        ChildListForAccessibility children = ChildListForAccessibility.obtain(this, true);
2664        try {
2665            final int childCount = children.getChildCount();
2666            for (int i = 0; i < childCount; i++) {
2667                View child = children.getChildAt(i);
2668                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2669                    handled = child.dispatchPopulateAccessibilityEvent(event);
2670                    if (handled) {
2671                        return handled;
2672                    }
2673                }
2674            }
2675        } finally {
2676            children.recycle();
2677        }
2678        return false;
2679    }
2680
2681    @Override
2682    void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
2683        super.onInitializeAccessibilityNodeInfoInternal(info);
2684        if (getAccessibilityNodeProvider() != null) {
2685            return;
2686        }
2687        if (mAttachInfo != null) {
2688            final ArrayList<View> childrenForAccessibility = mAttachInfo.mTempArrayList;
2689            childrenForAccessibility.clear();
2690            addChildrenForAccessibility(childrenForAccessibility);
2691            final int childrenForAccessibilityCount = childrenForAccessibility.size();
2692            for (int i = 0; i < childrenForAccessibilityCount; i++) {
2693                final View child = childrenForAccessibility.get(i);
2694                info.addChildUnchecked(child);
2695            }
2696            childrenForAccessibility.clear();
2697        }
2698    }
2699
2700    @Override
2701    void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
2702        super.onInitializeAccessibilityEventInternal(event);
2703        event.setClassName(ViewGroup.class.getName());
2704    }
2705
2706    @Override
2707    public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
2708        // If this is a live region, we should send a subtree change event
2709        // from this view. Otherwise, we can let it propagate up.
2710        if (getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE) {
2711            notifyViewAccessibilityStateChangedIfNeeded(changeType);
2712        } else if (mParent != null) {
2713            try {
2714                mParent.notifySubtreeAccessibilityStateChanged(this, source, changeType);
2715            } catch (AbstractMethodError e) {
2716                Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
2717                        " does not fully implement ViewParent", e);
2718            }
2719        }
2720    }
2721
2722    @Override
2723    void resetSubtreeAccessibilityStateChanged() {
2724        super.resetSubtreeAccessibilityStateChanged();
2725        View[] children = mChildren;
2726        final int childCount = mChildrenCount;
2727        for (int i = 0; i < childCount; i++) {
2728            children[i].resetSubtreeAccessibilityStateChanged();
2729        }
2730    }
2731
2732    /**
2733     * {@inheritDoc}
2734     *
2735     * <p>Subclasses should always call <code>super.onNestedPrePerformAccessibilityAction</code></p>
2736     *
2737     * @param target The target view dispatching this action
2738     * @param action Action being performed; see
2739     *               {@link android.view.accessibility.AccessibilityNodeInfo}
2740     * @param args Optional action arguments
2741     * @return false by default. Subclasses should return true if they handle the event.
2742     */
2743    @Override
2744    public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
2745        return false;
2746    }
2747
2748    /**
2749     * {@inheritDoc}
2750     */
2751    @Override
2752    void dispatchDetachedFromWindow() {
2753        // If we still have a touch target, we are still in the process of
2754        // dispatching motion events to a child; we need to get rid of that
2755        // child to avoid dispatching events to it after the window is torn
2756        // down. To make sure we keep the child in a consistent state, we
2757        // first send it an ACTION_CANCEL motion event.
2758        cancelAndClearTouchTargets(null);
2759
2760        // Similarly, set ACTION_EXIT to all hover targets and clear them.
2761        exitHoverTargets();
2762
2763        // In case view is detached while transition is running
2764        mLayoutCalledWhileSuppressed = false;
2765
2766        // Tear down our drag tracking
2767        mDragNotifiedChildren = null;
2768        if (mCurrentDrag != null) {
2769            mCurrentDrag.recycle();
2770            mCurrentDrag = null;
2771        }
2772
2773        final int count = mChildrenCount;
2774        final View[] children = mChildren;
2775        for (int i = 0; i < count; i++) {
2776            children[i].dispatchDetachedFromWindow();
2777        }
2778        clearDisappearingChildren();
2779        super.dispatchDetachedFromWindow();
2780    }
2781
2782    /**
2783     * @hide
2784     */
2785    @Override
2786    protected void internalSetPadding(int left, int top, int right, int bottom) {
2787        super.internalSetPadding(left, top, right, bottom);
2788
2789        if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingBottom) != 0) {
2790            mGroupFlags |= FLAG_PADDING_NOT_NULL;
2791        } else {
2792            mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
2793        }
2794    }
2795
2796    /**
2797     * {@inheritDoc}
2798     */
2799    @Override
2800    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
2801        super.dispatchSaveInstanceState(container);
2802        final int count = mChildrenCount;
2803        final View[] children = mChildren;
2804        for (int i = 0; i < count; i++) {
2805            View c = children[i];
2806            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2807                c.dispatchSaveInstanceState(container);
2808            }
2809        }
2810    }
2811
2812    /**
2813     * Perform dispatching of a {@link #saveHierarchyState(android.util.SparseArray)}  freeze()}
2814     * to only this view, not to its children.  For use when overriding
2815     * {@link #dispatchSaveInstanceState(android.util.SparseArray)}  dispatchFreeze()} to allow
2816     * subclasses to freeze their own state but not the state of their children.
2817     *
2818     * @param container the container
2819     */
2820    protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
2821        super.dispatchSaveInstanceState(container);
2822    }
2823
2824    /**
2825     * {@inheritDoc}
2826     */
2827    @Override
2828    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
2829        super.dispatchRestoreInstanceState(container);
2830        final int count = mChildrenCount;
2831        final View[] children = mChildren;
2832        for (int i = 0; i < count; i++) {
2833            View c = children[i];
2834            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
2835                c.dispatchRestoreInstanceState(container);
2836            }
2837        }
2838    }
2839
2840    /**
2841     * Perform dispatching of a {@link #restoreHierarchyState(android.util.SparseArray)}
2842     * to only this view, not to its children.  For use when overriding
2843     * {@link #dispatchRestoreInstanceState(android.util.SparseArray)} to allow
2844     * subclasses to thaw their own state but not the state of their children.
2845     *
2846     * @param container the container
2847     */
2848    protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
2849        super.dispatchRestoreInstanceState(container);
2850    }
2851
2852    /**
2853     * Enables or disables the drawing cache for each child of this view group.
2854     *
2855     * @param enabled true to enable the cache, false to dispose of it
2856     */
2857    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
2858        if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
2859            final View[] children = mChildren;
2860            final int count = mChildrenCount;
2861            for (int i = 0; i < count; i++) {
2862                children[i].setDrawingCacheEnabled(enabled);
2863            }
2864        }
2865    }
2866
2867    @Override
2868    protected void onAnimationStart() {
2869        super.onAnimationStart();
2870
2871        // When this ViewGroup's animation starts, build the cache for the children
2872        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2873            final int count = mChildrenCount;
2874            final View[] children = mChildren;
2875            final boolean buildCache = !isHardwareAccelerated();
2876
2877            for (int i = 0; i < count; i++) {
2878                final View child = children[i];
2879                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
2880                    child.setDrawingCacheEnabled(true);
2881                    if (buildCache) {
2882                        child.buildDrawingCache(true);
2883                    }
2884                }
2885            }
2886
2887            mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
2888        }
2889    }
2890
2891    @Override
2892    protected void onAnimationEnd() {
2893        super.onAnimationEnd();
2894
2895        // When this ViewGroup's animation ends, destroy the cache of the children
2896        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
2897            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
2898
2899            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
2900                setChildrenDrawingCacheEnabled(false);
2901            }
2902        }
2903    }
2904
2905    @Override
2906    Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
2907        int count = mChildrenCount;
2908        int[] visibilities = null;
2909
2910        if (skipChildren) {
2911            visibilities = new int[count];
2912            for (int i = 0; i < count; i++) {
2913                View child = getChildAt(i);
2914                visibilities[i] = child.getVisibility();
2915                if (visibilities[i] == View.VISIBLE) {
2916                    child.setVisibility(INVISIBLE);
2917                }
2918            }
2919        }
2920
2921        Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
2922
2923        if (skipChildren) {
2924            for (int i = 0; i < count; i++) {
2925                getChildAt(i).setVisibility(visibilities[i]);
2926            }
2927        }
2928
2929        return b;
2930    }
2931
2932    /** Return true if this ViewGroup is laying out using optical bounds. */
2933    boolean isLayoutModeOptical() {
2934        return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
2935    }
2936
2937    Insets computeOpticalInsets() {
2938        if (isLayoutModeOptical()) {
2939            int left = 0;
2940            int top = 0;
2941            int right = 0;
2942            int bottom = 0;
2943            for (int i = 0; i < mChildrenCount; i++) {
2944                View child = getChildAt(i);
2945                if (child.getVisibility() == VISIBLE) {
2946                    Insets insets = child.getOpticalInsets();
2947                    left =   Math.max(left,   insets.left);
2948                    top =    Math.max(top,    insets.top);
2949                    right =  Math.max(right,  insets.right);
2950                    bottom = Math.max(bottom, insets.bottom);
2951                }
2952            }
2953            return Insets.of(left, top, right, bottom);
2954        } else {
2955            return Insets.NONE;
2956        }
2957    }
2958
2959    private static void fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
2960        if (x1 != x2 && y1 != y2) {
2961            if (x1 > x2) {
2962                int tmp = x1; x1 = x2; x2 = tmp;
2963            }
2964            if (y1 > y2) {
2965                int tmp = y1; y1 = y2; y2 = tmp;
2966            }
2967            canvas.drawRect(x1, y1, x2, y2, paint);
2968        }
2969    }
2970
2971    private static int sign(int x) {
2972        return (x >= 0) ? 1 : -1;
2973    }
2974
2975    private static void drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw) {
2976        fillRect(c, paint, x1, y1, x1 + dx, y1 + lw * sign(dy));
2977        fillRect(c, paint, x1, y1, x1 + lw * sign(dx), y1 + dy);
2978    }
2979
2980    private int dipsToPixels(int dips) {
2981        float scale = getContext().getResources().getDisplayMetrics().density;
2982        return (int) (dips * scale + 0.5f);
2983    }
2984
2985    private static void drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint,
2986            int lineLength, int lineWidth) {
2987        drawCorner(canvas, paint, x1, y1, lineLength, lineLength, lineWidth);
2988        drawCorner(canvas, paint, x1, y2, lineLength, -lineLength, lineWidth);
2989        drawCorner(canvas, paint, x2, y1, -lineLength, lineLength, lineWidth);
2990        drawCorner(canvas, paint, x2, y2, -lineLength, -lineLength, lineWidth);
2991    }
2992
2993    private static void fillDifference(Canvas canvas,
2994            int x2, int y2, int x3, int y3,
2995            int dx1, int dy1, int dx2, int dy2, Paint paint) {
2996        int x1 = x2 - dx1;
2997        int y1 = y2 - dy1;
2998
2999        int x4 = x3 + dx2;
3000        int y4 = y3 + dy2;
3001
3002        fillRect(canvas, paint, x1, y1, x4, y2);
3003        fillRect(canvas, paint, x1, y2, x2, y3);
3004        fillRect(canvas, paint, x3, y2, x4, y3);
3005        fillRect(canvas, paint, x1, y3, x4, y4);
3006    }
3007
3008    /**
3009     * @hide
3010     */
3011    protected void onDebugDrawMargins(Canvas canvas, Paint paint) {
3012        for (int i = 0; i < getChildCount(); i++) {
3013            View c = getChildAt(i);
3014            c.getLayoutParams().onDebugDraw(c, canvas, paint);
3015        }
3016    }
3017
3018    /**
3019     * @hide
3020     */
3021    protected void onDebugDraw(Canvas canvas) {
3022        Paint paint = getDebugPaint();
3023
3024        // Draw optical bounds
3025        {
3026            paint.setColor(Color.RED);
3027            paint.setStyle(Paint.Style.STROKE);
3028
3029            for (int i = 0; i < getChildCount(); i++) {
3030                View c = getChildAt(i);
3031                Insets insets = c.getOpticalInsets();
3032
3033                drawRect(canvas, paint,
3034                        c.getLeft()   + insets.left,
3035                        c.getTop()    + insets.top,
3036                        c.getRight()  - insets.right  - 1,
3037                        c.getBottom() - insets.bottom - 1);
3038            }
3039        }
3040
3041        // Draw margins
3042        {
3043            paint.setColor(Color.argb(63, 255, 0, 255));
3044            paint.setStyle(Paint.Style.FILL);
3045
3046            onDebugDrawMargins(canvas, paint);
3047        }
3048
3049        // Draw clip bounds
3050        {
3051            paint.setColor(Color.rgb(63, 127, 255));
3052            paint.setStyle(Paint.Style.FILL);
3053
3054            int lineLength = dipsToPixels(8);
3055            int lineWidth = dipsToPixels(1);
3056            for (int i = 0; i < getChildCount(); i++) {
3057                View c = getChildAt(i);
3058                drawRectCorners(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(),
3059                        paint, lineLength, lineWidth);
3060            }
3061        }
3062    }
3063
3064    /**
3065     * {@inheritDoc}
3066     */
3067    @Override
3068    protected void dispatchDraw(Canvas canvas) {
3069        boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
3070        final int childrenCount = mChildrenCount;
3071        final View[] children = mChildren;
3072        int flags = mGroupFlags;
3073
3074        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
3075            final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
3076
3077            final boolean buildCache = !isHardwareAccelerated();
3078            for (int i = 0; i < childrenCount; i++) {
3079                final View child = children[i];
3080                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
3081                    final LayoutParams params = child.getLayoutParams();
3082                    attachLayoutAnimationParameters(child, params, i, childrenCount);
3083                    bindLayoutAnimation(child);
3084                    if (cache) {
3085                        child.setDrawingCacheEnabled(true);
3086                        if (buildCache) {
3087                            child.buildDrawingCache(true);
3088                        }
3089                    }
3090                }
3091            }
3092
3093            final LayoutAnimationController controller = mLayoutAnimationController;
3094            if (controller.willOverlap()) {
3095                mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
3096            }
3097
3098            controller.start();
3099
3100            mGroupFlags &= ~FLAG_RUN_ANIMATION;
3101            mGroupFlags &= ~FLAG_ANIMATION_DONE;
3102
3103            if (cache) {
3104                mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
3105            }
3106
3107            if (mAnimationListener != null) {
3108                mAnimationListener.onAnimationStart(controller.getAnimation());
3109            }
3110        }
3111
3112        int clipSaveCount = 0;
3113        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
3114        if (clipToPadding) {
3115            clipSaveCount = canvas.save();
3116            canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
3117                    mScrollX + mRight - mLeft - mPaddingRight,
3118                    mScrollY + mBottom - mTop - mPaddingBottom);
3119        }
3120
3121        // We will draw our child's animation, let's reset the flag
3122        mPrivateFlags &= ~PFLAG_DRAW_ANIMATION;
3123        mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
3124
3125        boolean more = false;
3126        final long drawingTime = getDrawingTime();
3127
3128        if (usingRenderNodeProperties) canvas.insertReorderBarrier();
3129        // Only use the preordered list if not HW accelerated, since the HW pipeline will do the
3130        // draw reordering internally
3131        final ArrayList<View> preorderedList = usingRenderNodeProperties
3132                ? null : buildOrderedChildList();
3133        final boolean customOrder = preorderedList == null
3134                && isChildrenDrawingOrderEnabled();
3135        for (int i = 0; i < childrenCount; i++) {
3136            int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i;
3137            final View child = (preorderedList == null)
3138                    ? children[childIndex] : preorderedList.get(childIndex);
3139            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
3140                more |= drawChild(canvas, child, drawingTime);
3141            }
3142        }
3143        if (preorderedList != null) preorderedList.clear();
3144
3145        // Draw any disappearing views that have animations
3146        if (mDisappearingChildren != null) {
3147            final ArrayList<View> disappearingChildren = mDisappearingChildren;
3148            final int disappearingCount = disappearingChildren.size() - 1;
3149            // Go backwards -- we may delete as animations finish
3150            for (int i = disappearingCount; i >= 0; i--) {
3151                final View child = disappearingChildren.get(i);
3152                more |= drawChild(canvas, child, drawingTime);
3153            }
3154        }
3155        if (usingRenderNodeProperties) canvas.insertInorderBarrier();
3156
3157        if (debugDraw()) {
3158            onDebugDraw(canvas);
3159        }
3160
3161        if (clipToPadding) {
3162            canvas.restoreToCount(clipSaveCount);
3163        }
3164
3165        // mGroupFlags might have been updated by drawChild()
3166        flags = mGroupFlags;
3167
3168        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
3169            invalidate(true);
3170        }
3171
3172        if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
3173                mLayoutAnimationController.isDone() && !more) {
3174            // We want to erase the drawing cache and notify the listener after the
3175            // next frame is drawn because one extra invalidate() is caused by
3176            // drawChild() after the animation is over
3177            mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
3178            final Runnable end = new Runnable() {
3179               public void run() {
3180                   notifyAnimationListener();
3181               }
3182            };
3183            post(end);
3184        }
3185    }
3186
3187    /**
3188     * Returns the ViewGroupOverlay for this view group, creating it if it does
3189     * not yet exist. In addition to {@link ViewOverlay}'s support for drawables,
3190     * {@link ViewGroupOverlay} allows views to be added to the overlay. These
3191     * views, like overlay drawables, are visual-only; they do not receive input
3192     * events and should not be used as anything other than a temporary
3193     * representation of a view in a parent container, such as might be used
3194     * by an animation effect.
3195     *
3196     * <p>Note: Overlays do not currently work correctly with {@link
3197     * SurfaceView} or {@link TextureView}; contents in overlays for these
3198     * types of views may not display correctly.</p>
3199     *
3200     * @return The ViewGroupOverlay object for this view.
3201     * @see ViewGroupOverlay
3202     */
3203    @Override
3204    public ViewGroupOverlay getOverlay() {
3205        if (mOverlay == null) {
3206            mOverlay = new ViewGroupOverlay(mContext, this);
3207        }
3208        return (ViewGroupOverlay) mOverlay;
3209    }
3210
3211    /**
3212     * Returns the index of the child to draw for this iteration. Override this
3213     * if you want to change the drawing order of children. By default, it
3214     * returns i.
3215     * <p>
3216     * NOTE: In order for this method to be called, you must enable child ordering
3217     * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
3218     *
3219     * @param i The current iteration.
3220     * @return The index of the child to draw this iteration.
3221     *
3222     * @see #setChildrenDrawingOrderEnabled(boolean)
3223     * @see #isChildrenDrawingOrderEnabled()
3224     */
3225    protected int getChildDrawingOrder(int childCount, int i) {
3226        return i;
3227    }
3228
3229    private boolean hasChildWithZ() {
3230        for (int i = 0; i < mChildrenCount; i++) {
3231            if (mChildren[i].getZ() != 0) return true;
3232        }
3233        return false;
3234    }
3235
3236    /**
3237     * Populates (and returns) mPreSortedChildren with a pre-ordered list of the View's children,
3238     * sorted first by Z, then by child drawing order (if applicable). This list must be cleared
3239     * after use to avoid leaking child Views.
3240     *
3241     * Uses a stable, insertion sort which is commonly O(n) for ViewGroups with very few elevated
3242     * children.
3243     */
3244    ArrayList<View> buildOrderedChildList() {
3245        final int count = mChildrenCount;
3246        if (count <= 1 || !hasChildWithZ()) return null;
3247
3248        if (mPreSortedChildren == null) {
3249            mPreSortedChildren = new ArrayList<View>(count);
3250        } else {
3251            mPreSortedChildren.ensureCapacity(count);
3252        }
3253
3254        final boolean useCustomOrder = isChildrenDrawingOrderEnabled();
3255        for (int i = 0; i < mChildrenCount; i++) {
3256            // add next child (in child order) to end of list
3257            int childIndex = useCustomOrder ? getChildDrawingOrder(mChildrenCount, i) : i;
3258            View nextChild = mChildren[childIndex];
3259            float currentZ = nextChild.getZ();
3260
3261            // insert ahead of any Views with greater Z
3262            int insertIndex = i;
3263            while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
3264                insertIndex--;
3265            }
3266            mPreSortedChildren.add(insertIndex, nextChild);
3267        }
3268        return mPreSortedChildren;
3269    }
3270
3271    private void notifyAnimationListener() {
3272        mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
3273        mGroupFlags |= FLAG_ANIMATION_DONE;
3274
3275        if (mAnimationListener != null) {
3276           final Runnable end = new Runnable() {
3277               public void run() {
3278                   mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
3279               }
3280           };
3281           post(end);
3282        }
3283
3284        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
3285            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
3286            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
3287                setChildrenDrawingCacheEnabled(false);
3288            }
3289        }
3290
3291        invalidate(true);
3292    }
3293
3294    /**
3295     * This method is used to cause children of this ViewGroup to restore or recreate their
3296     * display lists. It is called by getDisplayList() when the parent ViewGroup does not need
3297     * to recreate its own display list, which would happen if it went through the normal
3298     * draw/dispatchDraw mechanisms.
3299     *
3300     * @hide
3301     */
3302    @Override
3303    protected void dispatchGetDisplayList() {
3304        final int count = mChildrenCount;
3305        final View[] children = mChildren;
3306        for (int i = 0; i < count; i++) {
3307            final View child = children[i];
3308            if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) &&
3309                    child.hasStaticLayer()) {
3310                recreateChildDisplayList(child);
3311            }
3312        }
3313        if (mOverlay != null) {
3314            View overlayView = mOverlay.getOverlayView();
3315            recreateChildDisplayList(overlayView);
3316        }
3317        if (mDisappearingChildren != null) {
3318            final ArrayList<View> disappearingChildren = mDisappearingChildren;
3319            final int disappearingCount = disappearingChildren.size();
3320            for (int i = 0; i < disappearingCount; ++i) {
3321                final View child = disappearingChildren.get(i);
3322                recreateChildDisplayList(child);
3323            }
3324        }
3325    }
3326
3327    private void recreateChildDisplayList(View child) {
3328        child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED)
3329                == PFLAG_INVALIDATED;
3330        child.mPrivateFlags &= ~PFLAG_INVALIDATED;
3331        child.getDisplayList();
3332        child.mRecreateDisplayList = false;
3333    }
3334
3335    /**
3336     * Draw one child of this View Group. This method is responsible for getting
3337     * the canvas in the right state. This includes clipping, translating so
3338     * that the child's scrolled origin is at 0, 0, and applying any animation
3339     * transformations.
3340     *
3341     * @param canvas The canvas on which to draw the child
3342     * @param child Who to draw
3343     * @param drawingTime The time at which draw is occurring
3344     * @return True if an invalidate() was issued
3345     */
3346    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
3347        return child.draw(canvas, this, drawingTime);
3348    }
3349
3350    /**
3351     * Returns whether this group's children are clipped to their bounds before drawing.
3352     * The default value is true.
3353     * @see #setClipChildren(boolean)
3354     *
3355     * @return True if the group's children will be clipped to their bounds,
3356     * false otherwise.
3357     */
3358    @ViewDebug.ExportedProperty(category = "drawing")
3359    public boolean getClipChildren() {
3360        return ((mGroupFlags & FLAG_CLIP_CHILDREN) != 0);
3361    }
3362
3363    /**
3364     * By default, children are clipped to their bounds before drawing. This
3365     * allows view groups to override this behavior for animations, etc.
3366     *
3367     * @param clipChildren true to clip children to their bounds,
3368     *        false otherwise
3369     * @attr ref android.R.styleable#ViewGroup_clipChildren
3370     */
3371    public void setClipChildren(boolean clipChildren) {
3372        boolean previousValue = (mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN;
3373        if (clipChildren != previousValue) {
3374            setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
3375            for (int i = 0; i < mChildrenCount; ++i) {
3376                View child = getChildAt(i);
3377                if (child.mRenderNode != null) {
3378                    child.mRenderNode.setClipToBounds(clipChildren);
3379                }
3380            }
3381            invalidate(true);
3382        }
3383    }
3384
3385    /**
3386     * Sets whether this ViewGroup will clip its children to its padding, if
3387     * padding is present.
3388     * <p>
3389     * By default, children are clipped to the padding of their parent
3390     * Viewgroup. This clipping behavior is only enabled if padding is non-zero.
3391     *
3392     * @param clipToPadding true to clip children to the padding of the
3393     *        group, false otherwise
3394     * @attr ref android.R.styleable#ViewGroup_clipToPadding
3395     */
3396    public void setClipToPadding(boolean clipToPadding) {
3397        if (hasBooleanFlag(FLAG_CLIP_TO_PADDING) != clipToPadding) {
3398            setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
3399            invalidate(true);
3400        }
3401    }
3402
3403    /**
3404     * Returns whether this ViewGroup will clip its children to its padding, if
3405     * padding is present.
3406     * <p>
3407     * By default, children are clipped to the padding of their parent
3408     * Viewgroup. This clipping behavior is only enabled if padding is non-zero.
3409     *
3410     * @return true if this ViewGroup clips children to its padding, false otherwise
3411     *
3412     * @attr ref android.R.styleable#ViewGroup_clipToPadding
3413     */
3414    @ViewDebug.ExportedProperty(category = "drawing")
3415    public boolean getClipToPadding() {
3416        return hasBooleanFlag(FLAG_CLIP_TO_PADDING);
3417    }
3418
3419    /**
3420     * {@inheritDoc}
3421     */
3422    @Override
3423    public void dispatchSetSelected(boolean selected) {
3424        final View[] children = mChildren;
3425        final int count = mChildrenCount;
3426        for (int i = 0; i < count; i++) {
3427            children[i].setSelected(selected);
3428        }
3429    }
3430
3431    /**
3432     * {@inheritDoc}
3433     */
3434    @Override
3435    public void dispatchSetActivated(boolean activated) {
3436        final View[] children = mChildren;
3437        final int count = mChildrenCount;
3438        for (int i = 0; i < count; i++) {
3439            children[i].setActivated(activated);
3440        }
3441    }
3442
3443    @Override
3444    protected void dispatchSetPressed(boolean pressed) {
3445        final View[] children = mChildren;
3446        final int count = mChildrenCount;
3447        for (int i = 0; i < count; i++) {
3448            final View child = children[i];
3449            // Children that are clickable on their own should not
3450            // show a pressed state when their parent view does.
3451            // Clearing a pressed state always propagates.
3452            if (!pressed || (!child.isClickable() && !child.isLongClickable())) {
3453                child.setPressed(pressed);
3454            }
3455        }
3456    }
3457
3458    /**
3459     * Dispatches drawable hotspot changes to child views that meet at least
3460     * one of the following criteria:
3461     * <ul>
3462     *     <li>Returns {@code false} from both {@link View#isClickable()} and
3463     *     {@link View#isLongClickable()}</li>
3464     *     <li>Requests duplication of parent state via
3465     *     {@link View#setDuplicateParentStateEnabled(boolean)}</li>
3466     * </ul>
3467     *
3468     * @param x hotspot x coordinate
3469     * @param y hotspot y coordinate
3470     * @see #drawableHotspotChanged(float, float)
3471     */
3472    @Override
3473    public void dispatchDrawableHotspotChanged(float x, float y) {
3474        final int count = mChildrenCount;
3475        if (count == 0) {
3476            return;
3477        }
3478
3479        final View[] children = mChildren;
3480        for (int i = 0; i < count; i++) {
3481            final View child = children[i];
3482            // Children that are clickable on their own should not
3483            // receive hotspots when their parent view does.
3484            final boolean nonActionable = !child.isClickable() && !child.isLongClickable();
3485            final boolean duplicatesState = (child.mViewFlags & DUPLICATE_PARENT_STATE) != 0;
3486            if (nonActionable || duplicatesState) {
3487                final float[] point = getTempPoint();
3488                point[0] = x;
3489                point[1] = y;
3490                transformPointToViewLocal(point, child);
3491                child.drawableHotspotChanged(point[0], point[1]);
3492            }
3493        }
3494    }
3495
3496    @Override
3497    void dispatchCancelPendingInputEvents() {
3498        super.dispatchCancelPendingInputEvents();
3499
3500        final View[] children = mChildren;
3501        final int count = mChildrenCount;
3502        for (int i = 0; i < count; i++) {
3503            children[i].dispatchCancelPendingInputEvents();
3504        }
3505    }
3506
3507    /**
3508     * When this property is set to true, this ViewGroup supports static transformations on
3509     * children; this causes
3510     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
3511     * invoked when a child is drawn.
3512     *
3513     * Any subclass overriding
3514     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
3515     * set this property to true.
3516     *
3517     * @param enabled True to enable static transformations on children, false otherwise.
3518     *
3519     * @see #getChildStaticTransformation(View, android.view.animation.Transformation)
3520     */
3521    protected void setStaticTransformationsEnabled(boolean enabled) {
3522        setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
3523    }
3524
3525    /**
3526     * Sets  <code>t</code> to be the static transformation of the child, if set, returning a
3527     * boolean to indicate whether a static transform was set. The default implementation
3528     * simply returns <code>false</code>; subclasses may override this method for different
3529     * behavior. {@link #setStaticTransformationsEnabled(boolean)} must be set to true
3530     * for this method to be called.
3531     *
3532     * @param child The child view whose static transform is being requested
3533     * @param t The Transformation which will hold the result
3534     * @return true if the transformation was set, false otherwise
3535     * @see #setStaticTransformationsEnabled(boolean)
3536     */
3537    protected boolean getChildStaticTransformation(View child, Transformation t) {
3538        return false;
3539    }
3540
3541    Transformation getChildTransformation() {
3542        if (mChildTransformation == null) {
3543            mChildTransformation = new Transformation();
3544        }
3545        return mChildTransformation;
3546    }
3547
3548    /**
3549     * {@hide}
3550     */
3551    @Override
3552    protected View findViewTraversal(int id) {
3553        if (id == mID) {
3554            return this;
3555        }
3556
3557        final View[] where = mChildren;
3558        final int len = mChildrenCount;
3559
3560        for (int i = 0; i < len; i++) {
3561            View v = where[i];
3562
3563            if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3564                v = v.findViewById(id);
3565
3566                if (v != null) {
3567                    return v;
3568                }
3569            }
3570        }
3571
3572        return null;
3573    }
3574
3575    /**
3576     * {@hide}
3577     */
3578    @Override
3579    protected View findViewWithTagTraversal(Object tag) {
3580        if (tag != null && tag.equals(mTag)) {
3581            return this;
3582        }
3583
3584        final View[] where = mChildren;
3585        final int len = mChildrenCount;
3586
3587        for (int i = 0; i < len; i++) {
3588            View v = where[i];
3589
3590            if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3591                v = v.findViewWithTag(tag);
3592
3593                if (v != null) {
3594                    return v;
3595                }
3596            }
3597        }
3598
3599        return null;
3600    }
3601
3602    /**
3603     * {@hide}
3604     */
3605    @Override
3606    protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) {
3607        if (predicate.apply(this)) {
3608            return this;
3609        }
3610
3611        final View[] where = mChildren;
3612        final int len = mChildrenCount;
3613
3614        for (int i = 0; i < len; i++) {
3615            View v = where[i];
3616
3617            if (v != childToSkip && (v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
3618                v = v.findViewByPredicate(predicate);
3619
3620                if (v != null) {
3621                    return v;
3622                }
3623            }
3624        }
3625
3626        return null;
3627    }
3628
3629    /**
3630     * <p>Adds a child view. If no layout parameters are already set on the child, the
3631     * default parameters for this ViewGroup are set on the child.</p>
3632     *
3633     * <p><strong>Note:</strong> do not invoke this method from
3634     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3635     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3636     *
3637     * @param child the child view to add
3638     *
3639     * @see #generateDefaultLayoutParams()
3640     */
3641    public void addView(View child) {
3642        addView(child, -1);
3643    }
3644
3645    /**
3646     * Adds a child view. If no layout parameters are already set on the child, the
3647     * default parameters for this ViewGroup are set on the child.
3648     *
3649     * <p><strong>Note:</strong> do not invoke this method from
3650     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3651     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3652     *
3653     * @param child the child view to add
3654     * @param index the position at which to add the child
3655     *
3656     * @see #generateDefaultLayoutParams()
3657     */
3658    public void addView(View child, int index) {
3659        if (child == null) {
3660            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
3661        }
3662        LayoutParams params = child.getLayoutParams();
3663        if (params == null) {
3664            params = generateDefaultLayoutParams();
3665            if (params == null) {
3666                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
3667            }
3668        }
3669        addView(child, index, params);
3670    }
3671
3672    /**
3673     * Adds a child view with this ViewGroup's default layout parameters and the
3674     * specified width and height.
3675     *
3676     * <p><strong>Note:</strong> do not invoke this method from
3677     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3678     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3679     *
3680     * @param child the child view to add
3681     */
3682    public void addView(View child, int width, int height) {
3683        final LayoutParams params = generateDefaultLayoutParams();
3684        params.width = width;
3685        params.height = height;
3686        addView(child, -1, params);
3687    }
3688
3689    /**
3690     * Adds a child view with the specified layout parameters.
3691     *
3692     * <p><strong>Note:</strong> do not invoke this method from
3693     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3694     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3695     *
3696     * @param child the child view to add
3697     * @param params the layout parameters to set on the child
3698     */
3699    public void addView(View child, LayoutParams params) {
3700        addView(child, -1, params);
3701    }
3702
3703    /**
3704     * Adds a child view with the specified layout parameters.
3705     *
3706     * <p><strong>Note:</strong> do not invoke this method from
3707     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
3708     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
3709     *
3710     * @param child the child view to add
3711     * @param index the position at which to add the child
3712     * @param params the layout parameters to set on the child
3713     */
3714    public void addView(View child, int index, LayoutParams params) {
3715        if (DBG) {
3716            System.out.println(this + " addView");
3717        }
3718
3719        if (child == null) {
3720            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
3721        }
3722
3723        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
3724        // therefore, we call requestLayout() on ourselves before, so that the child's request
3725        // will be blocked at our level
3726        requestLayout();
3727        invalidate(true);
3728        addViewInner(child, index, params, false);
3729    }
3730
3731    /**
3732     * {@inheritDoc}
3733     */
3734    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
3735        if (!checkLayoutParams(params)) {
3736            throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
3737        }
3738        if (view.mParent != this) {
3739            throw new IllegalArgumentException("Given view not a child of " + this);
3740        }
3741        view.setLayoutParams(params);
3742    }
3743
3744    /**
3745     * {@inheritDoc}
3746     */
3747    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
3748        return  p != null;
3749    }
3750
3751    /**
3752     * Interface definition for a callback to be invoked when the hierarchy
3753     * within this view changed. The hierarchy changes whenever a child is added
3754     * to or removed from this view.
3755     */
3756    public interface OnHierarchyChangeListener {
3757        /**
3758         * Called when a new child is added to a parent view.
3759         *
3760         * @param parent the view in which a child was added
3761         * @param child the new child view added in the hierarchy
3762         */
3763        void onChildViewAdded(View parent, View child);
3764
3765        /**
3766         * Called when a child is removed from a parent view.
3767         *
3768         * @param parent the view from which the child was removed
3769         * @param child the child removed from the hierarchy
3770         */
3771        void onChildViewRemoved(View parent, View child);
3772    }
3773
3774    /**
3775     * Register a callback to be invoked when a child is added to or removed
3776     * from this view.
3777     *
3778     * @param listener the callback to invoke on hierarchy change
3779     */
3780    public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
3781        mOnHierarchyChangeListener = listener;
3782    }
3783
3784    /**
3785     * @hide
3786     */
3787    protected void onViewAdded(View child) {
3788        if (mOnHierarchyChangeListener != null) {
3789            mOnHierarchyChangeListener.onChildViewAdded(this, child);
3790        }
3791    }
3792
3793    /**
3794     * @hide
3795     */
3796    protected void onViewRemoved(View child) {
3797        if (mOnHierarchyChangeListener != null) {
3798            mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3799        }
3800    }
3801
3802    private void clearCachedLayoutMode() {
3803        if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
3804           mLayoutMode = LAYOUT_MODE_UNDEFINED;
3805        }
3806    }
3807
3808    @Override
3809    protected void onAttachedToWindow() {
3810        super.onAttachedToWindow();
3811        clearCachedLayoutMode();
3812    }
3813
3814    @Override
3815    protected void onDetachedFromWindow() {
3816        super.onDetachedFromWindow();
3817        clearCachedLayoutMode();
3818    }
3819
3820    /**
3821     * Adds a view during layout. This is useful if in your onLayout() method,
3822     * you need to add more views (as does the list view for example).
3823     *
3824     * If index is negative, it means put it at the end of the list.
3825     *
3826     * @param child the view to add to the group
3827     * @param index the index at which the child must be added
3828     * @param params the layout parameters to associate with the child
3829     * @return true if the child was added, false otherwise
3830     */
3831    protected boolean addViewInLayout(View child, int index, LayoutParams params) {
3832        return addViewInLayout(child, index, params, false);
3833    }
3834
3835    /**
3836     * Adds a view during layout. This is useful if in your onLayout() method,
3837     * you need to add more views (as does the list view for example).
3838     *
3839     * If index is negative, it means put it at the end of the list.
3840     *
3841     * @param child the view to add to the group
3842     * @param index the index at which the child must be added
3843     * @param params the layout parameters to associate with the child
3844     * @param preventRequestLayout if true, calling this method will not trigger a
3845     *        layout request on child
3846     * @return true if the child was added, false otherwise
3847     */
3848    protected boolean addViewInLayout(View child, int index, LayoutParams params,
3849            boolean preventRequestLayout) {
3850        if (child == null) {
3851            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
3852        }
3853        child.mParent = null;
3854        addViewInner(child, index, params, preventRequestLayout);
3855        child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
3856        return true;
3857    }
3858
3859    /**
3860     * Prevents the specified child to be laid out during the next layout pass.
3861     *
3862     * @param child the child on which to perform the cleanup
3863     */
3864    protected void cleanupLayoutState(View child) {
3865        child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
3866    }
3867
3868    private void addViewInner(View child, int index, LayoutParams params,
3869            boolean preventRequestLayout) {
3870
3871        if (mTransition != null) {
3872            // Don't prevent other add transitions from completing, but cancel remove
3873            // transitions to let them complete the process before we add to the container
3874            mTransition.cancel(LayoutTransition.DISAPPEARING);
3875        }
3876
3877        if (child.getParent() != null) {
3878            throw new IllegalStateException("The specified child already has a parent. " +
3879                    "You must call removeView() on the child's parent first.");
3880        }
3881
3882        if (mTransition != null) {
3883            mTransition.addChild(this, child);
3884        }
3885
3886        if (!checkLayoutParams(params)) {
3887            params = generateLayoutParams(params);
3888        }
3889
3890        if (preventRequestLayout) {
3891            child.mLayoutParams = params;
3892        } else {
3893            child.setLayoutParams(params);
3894        }
3895
3896        if (index < 0) {
3897            index = mChildrenCount;
3898        }
3899
3900        addInArray(child, index);
3901
3902        // tell our children
3903        if (preventRequestLayout) {
3904            child.assignParent(this);
3905        } else {
3906            child.mParent = this;
3907        }
3908
3909        if (child.hasFocus()) {
3910            requestChildFocus(child, child.findFocus());
3911        }
3912
3913        AttachInfo ai = mAttachInfo;
3914        if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
3915            boolean lastKeepOn = ai.mKeepScreenOn;
3916            ai.mKeepScreenOn = false;
3917            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
3918            if (ai.mKeepScreenOn) {
3919                needGlobalAttributesUpdate(true);
3920            }
3921            ai.mKeepScreenOn = lastKeepOn;
3922        }
3923
3924        if (child.isLayoutDirectionInherited()) {
3925            child.resetRtlProperties();
3926        }
3927
3928        onViewAdded(child);
3929
3930        if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
3931            mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
3932        }
3933
3934        if (child.hasTransientState()) {
3935            childHasTransientStateChanged(child, true);
3936        }
3937
3938        if (child.getVisibility() != View.GONE) {
3939            notifySubtreeAccessibilityStateChangedIfNeeded();
3940        }
3941    }
3942
3943    private void addInArray(View child, int index) {
3944        View[] children = mChildren;
3945        final int count = mChildrenCount;
3946        final int size = children.length;
3947        if (index == count) {
3948            if (size == count) {
3949                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3950                System.arraycopy(children, 0, mChildren, 0, size);
3951                children = mChildren;
3952            }
3953            children[mChildrenCount++] = child;
3954        } else if (index < count) {
3955            if (size == count) {
3956                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
3957                System.arraycopy(children, 0, mChildren, 0, index);
3958                System.arraycopy(children, index, mChildren, index + 1, count - index);
3959                children = mChildren;
3960            } else {
3961                System.arraycopy(children, index, children, index + 1, count - index);
3962            }
3963            children[index] = child;
3964            mChildrenCount++;
3965            if (mLastTouchDownIndex >= index) {
3966                mLastTouchDownIndex++;
3967            }
3968        } else {
3969            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
3970        }
3971    }
3972
3973    // This method also sets the child's mParent to null
3974    private void removeFromArray(int index) {
3975        final View[] children = mChildren;
3976        if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
3977            children[index].mParent = null;
3978        }
3979        final int count = mChildrenCount;
3980        if (index == count - 1) {
3981            children[--mChildrenCount] = null;
3982        } else if (index >= 0 && index < count) {
3983            System.arraycopy(children, index + 1, children, index, count - index - 1);
3984            children[--mChildrenCount] = null;
3985        } else {
3986            throw new IndexOutOfBoundsException();
3987        }
3988        if (mLastTouchDownIndex == index) {
3989            mLastTouchDownTime = 0;
3990            mLastTouchDownIndex = -1;
3991        } else if (mLastTouchDownIndex > index) {
3992            mLastTouchDownIndex--;
3993        }
3994    }
3995
3996    // This method also sets the children's mParent to null
3997    private void removeFromArray(int start, int count) {
3998        final View[] children = mChildren;
3999        final int childrenCount = mChildrenCount;
4000
4001        start = Math.max(0, start);
4002        final int end = Math.min(childrenCount, start + count);
4003
4004        if (start == end) {
4005            return;
4006        }
4007
4008        if (end == childrenCount) {
4009            for (int i = start; i < end; i++) {
4010                children[i].mParent = null;
4011                children[i] = null;
4012            }
4013        } else {
4014            for (int i = start; i < end; i++) {
4015                children[i].mParent = null;
4016            }
4017
4018            // Since we're looping above, we might as well do the copy, but is arraycopy()
4019            // faster than the extra 2 bounds checks we would do in the loop?
4020            System.arraycopy(children, end, children, start, childrenCount - end);
4021
4022            for (int i = childrenCount - (end - start); i < childrenCount; i++) {
4023                children[i] = null;
4024            }
4025        }
4026
4027        mChildrenCount -= (end - start);
4028    }
4029
4030    private void bindLayoutAnimation(View child) {
4031        Animation a = mLayoutAnimationController.getAnimationForView(child);
4032        child.setAnimation(a);
4033    }
4034
4035    /**
4036     * Subclasses should override this method to set layout animation
4037     * parameters on the supplied child.
4038     *
4039     * @param child the child to associate with animation parameters
4040     * @param params the child's layout parameters which hold the animation
4041     *        parameters
4042     * @param index the index of the child in the view group
4043     * @param count the number of children in the view group
4044     */
4045    protected void attachLayoutAnimationParameters(View child,
4046            LayoutParams params, int index, int count) {
4047        LayoutAnimationController.AnimationParameters animationParams =
4048                    params.layoutAnimationParameters;
4049        if (animationParams == null) {
4050            animationParams = new LayoutAnimationController.AnimationParameters();
4051            params.layoutAnimationParameters = animationParams;
4052        }
4053
4054        animationParams.count = count;
4055        animationParams.index = index;
4056    }
4057
4058    /**
4059     * {@inheritDoc}
4060     *
4061     * <p><strong>Note:</strong> do not invoke this method from
4062     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4063     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4064     */
4065    public void removeView(View view) {
4066        if (removeViewInternal(view)) {
4067            requestLayout();
4068            invalidate(true);
4069        }
4070    }
4071
4072    /**
4073     * Removes a view during layout. This is useful if in your onLayout() method,
4074     * you need to remove more views.
4075     *
4076     * <p><strong>Note:</strong> do not invoke this method from
4077     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4078     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4079     *
4080     * @param view the view to remove from the group
4081     */
4082    public void removeViewInLayout(View view) {
4083        removeViewInternal(view);
4084    }
4085
4086    /**
4087     * Removes a range of views during layout. This is useful if in your onLayout() method,
4088     * you need to remove more views.
4089     *
4090     * <p><strong>Note:</strong> do not invoke this method from
4091     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4092     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4093     *
4094     * @param start the index of the first view to remove from the group
4095     * @param count the number of views to remove from the group
4096     */
4097    public void removeViewsInLayout(int start, int count) {
4098        removeViewsInternal(start, count);
4099    }
4100
4101    /**
4102     * Removes the view at the specified position in the group.
4103     *
4104     * <p><strong>Note:</strong> do not invoke this method from
4105     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4106     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4107     *
4108     * @param index the position in the group of the view to remove
4109     */
4110    public void removeViewAt(int index) {
4111        removeViewInternal(index, getChildAt(index));
4112        requestLayout();
4113        invalidate(true);
4114    }
4115
4116    /**
4117     * Removes the specified range of views from the group.
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     * @param start the first position in the group of the range of views to remove
4124     * @param count the number of views to remove
4125     */
4126    public void removeViews(int start, int count) {
4127        removeViewsInternal(start, count);
4128        requestLayout();
4129        invalidate(true);
4130    }
4131
4132    private boolean removeViewInternal(View view) {
4133        final int index = indexOfChild(view);
4134        if (index >= 0) {
4135            removeViewInternal(index, view);
4136            return true;
4137        }
4138        return false;
4139    }
4140
4141    private void removeViewInternal(int index, View view) {
4142
4143        if (mTransition != null) {
4144            mTransition.removeChild(this, view);
4145        }
4146
4147        boolean clearChildFocus = false;
4148        if (view == mFocused) {
4149            view.unFocus(null);
4150            clearChildFocus = true;
4151        }
4152
4153        view.clearAccessibilityFocus();
4154
4155        cancelTouchTarget(view);
4156        cancelHoverTarget(view);
4157
4158        if (view.getAnimation() != null ||
4159                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4160            addDisappearingView(view);
4161        } else if (view.mAttachInfo != null) {
4162           view.dispatchDetachedFromWindow();
4163        }
4164
4165        if (view.hasTransientState()) {
4166            childHasTransientStateChanged(view, false);
4167        }
4168
4169        needGlobalAttributesUpdate(false);
4170
4171        removeFromArray(index);
4172
4173        if (clearChildFocus) {
4174            clearChildFocus(view);
4175            if (!rootViewRequestFocus()) {
4176                notifyGlobalFocusCleared(this);
4177            }
4178        }
4179
4180        onViewRemoved(view);
4181
4182        if (view.getVisibility() != View.GONE) {
4183            notifySubtreeAccessibilityStateChangedIfNeeded();
4184        }
4185    }
4186
4187    /**
4188     * Sets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
4189     * not null, changes in layout which occur because of children being added to or removed from
4190     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
4191     * object. By default, the transition object is null (so layout changes are not animated).
4192     *
4193     * <p>Replacing a non-null transition will cause that previous transition to be
4194     * canceled, if it is currently running, to restore this container to
4195     * its correct post-transition state.</p>
4196     *
4197     * @param transition The LayoutTransition object that will animated changes in layout. A value
4198     * of <code>null</code> means no transition will run on layout changes.
4199     * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges
4200     */
4201    public void setLayoutTransition(LayoutTransition transition) {
4202        if (mTransition != null) {
4203            LayoutTransition previousTransition = mTransition;
4204            previousTransition.cancel();
4205            previousTransition.removeTransitionListener(mLayoutTransitionListener);
4206        }
4207        mTransition = transition;
4208        if (mTransition != null) {
4209            mTransition.addTransitionListener(mLayoutTransitionListener);
4210        }
4211    }
4212
4213    /**
4214     * Gets the LayoutTransition object for this ViewGroup. If the LayoutTransition object is
4215     * not null, changes in layout which occur because of children being added to or removed from
4216     * the ViewGroup will be animated according to the animations defined in that LayoutTransition
4217     * object. By default, the transition object is null (so layout changes are not animated).
4218     *
4219     * @return LayoutTranstion The LayoutTransition object that will animated changes in layout.
4220     * A value of <code>null</code> means no transition will run on layout changes.
4221     */
4222    public LayoutTransition getLayoutTransition() {
4223        return mTransition;
4224    }
4225
4226    private void removeViewsInternal(int start, int count) {
4227        final View focused = mFocused;
4228        final boolean detach = mAttachInfo != null;
4229        boolean clearChildFocus = false;
4230
4231        final View[] children = mChildren;
4232        final int end = start + count;
4233
4234        for (int i = start; i < end; i++) {
4235            final View view = children[i];
4236
4237            if (mTransition != null) {
4238                mTransition.removeChild(this, view);
4239            }
4240
4241            if (view == focused) {
4242                view.unFocus(null);
4243                clearChildFocus = true;
4244            }
4245
4246            view.clearAccessibilityFocus();
4247
4248            cancelTouchTarget(view);
4249            cancelHoverTarget(view);
4250
4251            if (view.getAnimation() != null ||
4252                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4253                addDisappearingView(view);
4254            } else if (detach) {
4255               view.dispatchDetachedFromWindow();
4256            }
4257
4258            if (view.hasTransientState()) {
4259                childHasTransientStateChanged(view, false);
4260            }
4261
4262            needGlobalAttributesUpdate(false);
4263
4264            onViewRemoved(view);
4265        }
4266
4267        removeFromArray(start, count);
4268
4269        if (clearChildFocus) {
4270            clearChildFocus(focused);
4271            if (!rootViewRequestFocus()) {
4272                notifyGlobalFocusCleared(focused);
4273            }
4274        }
4275    }
4276
4277    /**
4278     * Call this method to remove all child views from the
4279     * ViewGroup.
4280     *
4281     * <p><strong>Note:</strong> do not invoke this method from
4282     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4283     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4284     */
4285    public void removeAllViews() {
4286        removeAllViewsInLayout();
4287        requestLayout();
4288        invalidate(true);
4289    }
4290
4291    /**
4292     * Called by a ViewGroup subclass to remove child views from itself,
4293     * when it must first know its size on screen before it can calculate how many
4294     * child views it will render. An example is a Gallery or a ListView, which
4295     * may "have" 50 children, but actually only render the number of children
4296     * that can currently fit inside the object on screen. Do not call
4297     * this method unless you are extending ViewGroup and understand the
4298     * view measuring and layout pipeline.
4299     *
4300     * <p><strong>Note:</strong> do not invoke this method from
4301     * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
4302     * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
4303     */
4304    public void removeAllViewsInLayout() {
4305        final int count = mChildrenCount;
4306        if (count <= 0) {
4307            return;
4308        }
4309
4310        final View[] children = mChildren;
4311        mChildrenCount = 0;
4312
4313        final View focused = mFocused;
4314        final boolean detach = mAttachInfo != null;
4315        boolean clearChildFocus = false;
4316
4317        needGlobalAttributesUpdate(false);
4318
4319        for (int i = count - 1; i >= 0; i--) {
4320            final View view = children[i];
4321
4322            if (mTransition != null) {
4323                mTransition.removeChild(this, view);
4324            }
4325
4326            if (view == focused) {
4327                view.unFocus(null);
4328                clearChildFocus = true;
4329            }
4330
4331            view.clearAccessibilityFocus();
4332
4333            cancelTouchTarget(view);
4334            cancelHoverTarget(view);
4335
4336            if (view.getAnimation() != null ||
4337                    (mTransitioningViews != null && mTransitioningViews.contains(view))) {
4338                addDisappearingView(view);
4339            } else if (detach) {
4340               view.dispatchDetachedFromWindow();
4341            }
4342
4343            if (view.hasTransientState()) {
4344                childHasTransientStateChanged(view, false);
4345            }
4346
4347            onViewRemoved(view);
4348
4349            view.mParent = null;
4350            children[i] = null;
4351        }
4352
4353        if (clearChildFocus) {
4354            clearChildFocus(focused);
4355            if (!rootViewRequestFocus()) {
4356                notifyGlobalFocusCleared(focused);
4357            }
4358        }
4359    }
4360
4361    /**
4362     * Finishes the removal of a detached view. This method will dispatch the detached from
4363     * window event and notify the hierarchy change listener.
4364     * <p>
4365     * This method is intended to be lightweight and makes no assumptions about whether the
4366     * parent or child should be redrawn. Proper use of this method will include also making
4367     * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4368     * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4369     * which performs a {@link #requestLayout()} on the next frame, after all detach/remove
4370     * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
4371     *
4372     * @param child the child to be definitely removed from the view hierarchy
4373     * @param animate if true and the view has an animation, the view is placed in the
4374     *                disappearing views list, otherwise, it is detached from the window
4375     *
4376     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4377     * @see #detachAllViewsFromParent()
4378     * @see #detachViewFromParent(View)
4379     * @see #detachViewFromParent(int)
4380     */
4381    protected void removeDetachedView(View child, boolean animate) {
4382        if (mTransition != null) {
4383            mTransition.removeChild(this, child);
4384        }
4385
4386        if (child == mFocused) {
4387            child.clearFocus();
4388        }
4389
4390        child.clearAccessibilityFocus();
4391
4392        cancelTouchTarget(child);
4393        cancelHoverTarget(child);
4394
4395        if ((animate && child.getAnimation() != null) ||
4396                (mTransitioningViews != null && mTransitioningViews.contains(child))) {
4397            addDisappearingView(child);
4398        } else if (child.mAttachInfo != null) {
4399            child.dispatchDetachedFromWindow();
4400        }
4401
4402        if (child.hasTransientState()) {
4403            childHasTransientStateChanged(child, false);
4404        }
4405
4406        onViewRemoved(child);
4407    }
4408
4409    /**
4410     * Attaches a view to this view group. Attaching a view assigns this group as the parent,
4411     * sets the layout parameters and puts the view in the list of children so that
4412     * it can be retrieved by calling {@link #getChildAt(int)}.
4413     * <p>
4414     * This method is intended to be lightweight and makes no assumptions about whether the
4415     * parent or child should be redrawn. Proper use of this method will include also making
4416     * any appropriate {@link #requestLayout()} or {@link #invalidate()} calls.
4417     * For example, callers can {@link #post(Runnable) post} a {@link Runnable}
4418     * which performs a {@link #requestLayout()} on the next frame, after all detach/attach
4419     * calls are finished, causing layout to be run prior to redrawing the view hierarchy.
4420     * <p>
4421     * This method should be called only for views which were detached from their parent.
4422     *
4423     * @param child the child to attach
4424     * @param index the index at which the child should be attached
4425     * @param params the layout parameters of the child
4426     *
4427     * @see #removeDetachedView(View, boolean)
4428     * @see #detachAllViewsFromParent()
4429     * @see #detachViewFromParent(View)
4430     * @see #detachViewFromParent(int)
4431     */
4432    protected void attachViewToParent(View child, int index, LayoutParams params) {
4433        child.mLayoutParams = params;
4434
4435        if (index < 0) {
4436            index = mChildrenCount;
4437        }
4438
4439        addInArray(child, index);
4440
4441        child.mParent = this;
4442        child.mPrivateFlags = (child.mPrivateFlags & ~PFLAG_DIRTY_MASK
4443                        & ~PFLAG_DRAWING_CACHE_VALID)
4444                | PFLAG_DRAWN | PFLAG_INVALIDATED;
4445        this.mPrivateFlags |= PFLAG_INVALIDATED;
4446
4447        if (child.hasFocus()) {
4448            requestChildFocus(child, child.findFocus());
4449        }
4450    }
4451
4452    /**
4453     * Detaches a view from its parent. Detaching a view should be followed
4454     * either by a call to
4455     * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4456     * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4457     * temporary; reattachment or removal should happen within the same drawing cycle as
4458     * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4459     * call to {@link #getChildAt(int)}.
4460     *
4461     * @param child the child to detach
4462     *
4463     * @see #detachViewFromParent(int)
4464     * @see #detachViewsFromParent(int, int)
4465     * @see #detachAllViewsFromParent()
4466     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4467     * @see #removeDetachedView(View, boolean)
4468     */
4469    protected void detachViewFromParent(View child) {
4470        removeFromArray(indexOfChild(child));
4471    }
4472
4473    /**
4474     * Detaches a view from its parent. Detaching a view should be followed
4475     * either by a call to
4476     * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4477     * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4478     * temporary; reattachment or removal should happen within the same drawing cycle as
4479     * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4480     * call to {@link #getChildAt(int)}.
4481     *
4482     * @param index the index of the child to detach
4483     *
4484     * @see #detachViewFromParent(View)
4485     * @see #detachAllViewsFromParent()
4486     * @see #detachViewsFromParent(int, int)
4487     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4488     * @see #removeDetachedView(View, boolean)
4489     */
4490    protected void detachViewFromParent(int index) {
4491        removeFromArray(index);
4492    }
4493
4494    /**
4495     * Detaches a range of views from their parents. Detaching a view should be followed
4496     * either by a call to
4497     * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4498     * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4499     * temporary; reattachment or removal should happen within the same drawing cycle as
4500     * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4501     * call to {@link #getChildAt(int)}.
4502     *
4503     * @param start the first index of the childrend range to detach
4504     * @param count the number of children to detach
4505     *
4506     * @see #detachViewFromParent(View)
4507     * @see #detachViewFromParent(int)
4508     * @see #detachAllViewsFromParent()
4509     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4510     * @see #removeDetachedView(View, boolean)
4511     */
4512    protected void detachViewsFromParent(int start, int count) {
4513        removeFromArray(start, count);
4514    }
4515
4516    /**
4517     * Detaches all views from the parent. Detaching a view should be followed
4518     * either by a call to
4519     * {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
4520     * or a call to {@link #removeDetachedView(View, boolean)}. Detachment should only be
4521     * temporary; reattachment or removal should happen within the same drawing cycle as
4522     * detachment. When a view is detached, its parent is null and cannot be retrieved by a
4523     * call to {@link #getChildAt(int)}.
4524     *
4525     * @see #detachViewFromParent(View)
4526     * @see #detachViewFromParent(int)
4527     * @see #detachViewsFromParent(int, int)
4528     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
4529     * @see #removeDetachedView(View, boolean)
4530     */
4531    protected void detachAllViewsFromParent() {
4532        final int count = mChildrenCount;
4533        if (count <= 0) {
4534            return;
4535        }
4536
4537        final View[] children = mChildren;
4538        mChildrenCount = 0;
4539
4540        for (int i = count - 1; i >= 0; i--) {
4541            children[i].mParent = null;
4542            children[i] = null;
4543        }
4544    }
4545
4546    /**
4547     * Don't call or override this method. It is used for the implementation of
4548     * the view hierarchy.
4549     */
4550    public final void invalidateChild(View child, final Rect dirty) {
4551        ViewParent parent = this;
4552
4553        final AttachInfo attachInfo = mAttachInfo;
4554        if (attachInfo != null) {
4555            // If the child is drawing an animation, we want to copy this flag onto
4556            // ourselves and the parent to make sure the invalidate request goes
4557            // through
4558            final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
4559                    == PFLAG_DRAW_ANIMATION;
4560
4561            // Check whether the child that requests the invalidate is fully opaque
4562            // Views being animated or transformed are not considered opaque because we may
4563            // be invalidating their old position and need the parent to paint behind them.
4564            Matrix childMatrix = child.getMatrix();
4565            final boolean isOpaque = child.isOpaque() && !drawAnimation &&
4566                    child.getAnimation() == null && childMatrix.isIdentity();
4567            // Mark the child as dirty, using the appropriate flag
4568            // Make sure we do not set both flags at the same time
4569            int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
4570
4571            if (child.mLayerType != LAYER_TYPE_NONE) {
4572                mPrivateFlags |= PFLAG_INVALIDATED;
4573                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
4574            }
4575
4576            final int[] location = attachInfo.mInvalidateChildLocation;
4577            location[CHILD_LEFT_INDEX] = child.mLeft;
4578            location[CHILD_TOP_INDEX] = child.mTop;
4579            if (!childMatrix.isIdentity() ||
4580                    (mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
4581                RectF boundingRect = attachInfo.mTmpTransformRect;
4582                boundingRect.set(dirty);
4583                Matrix transformMatrix;
4584                if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
4585                    Transformation t = attachInfo.mTmpTransformation;
4586                    boolean transformed = getChildStaticTransformation(child, t);
4587                    if (transformed) {
4588                        transformMatrix = attachInfo.mTmpMatrix;
4589                        transformMatrix.set(t.getMatrix());
4590                        if (!childMatrix.isIdentity()) {
4591                            transformMatrix.preConcat(childMatrix);
4592                        }
4593                    } else {
4594                        transformMatrix = childMatrix;
4595                    }
4596                } else {
4597                    transformMatrix = childMatrix;
4598                }
4599                transformMatrix.mapRect(boundingRect);
4600                dirty.set((int) (boundingRect.left - 0.5f),
4601                        (int) (boundingRect.top - 0.5f),
4602                        (int) (boundingRect.right + 0.5f),
4603                        (int) (boundingRect.bottom + 0.5f));
4604            }
4605
4606            do {
4607                View view = null;
4608                if (parent instanceof View) {
4609                    view = (View) parent;
4610                }
4611
4612                if (drawAnimation) {
4613                    if (view != null) {
4614                        view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
4615                    } else if (parent instanceof ViewRootImpl) {
4616                        ((ViewRootImpl) parent).mIsAnimating = true;
4617                    }
4618                }
4619
4620                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
4621                // flag coming from the child that initiated the invalidate
4622                if (view != null) {
4623                    if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
4624                            view.getSolidColor() == 0) {
4625                        opaqueFlag = PFLAG_DIRTY;
4626                    }
4627                    if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
4628                        view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
4629                    }
4630                }
4631
4632                parent = parent.invalidateChildInParent(location, dirty);
4633                if (view != null) {
4634                    // Account for transform on current parent
4635                    Matrix m = view.getMatrix();
4636                    if (!m.isIdentity()) {
4637                        RectF boundingRect = attachInfo.mTmpTransformRect;
4638                        boundingRect.set(dirty);
4639                        m.mapRect(boundingRect);
4640                        dirty.set((int) (boundingRect.left - 0.5f),
4641                                (int) (boundingRect.top - 0.5f),
4642                                (int) (boundingRect.right + 0.5f),
4643                                (int) (boundingRect.bottom + 0.5f));
4644                    }
4645                }
4646            } while (parent != null);
4647        }
4648    }
4649
4650    /**
4651     * Don't call or override this method. It is used for the implementation of
4652     * the view hierarchy.
4653     *
4654     * This implementation returns null if this ViewGroup does not have a parent,
4655     * if this ViewGroup is already fully invalidated or if the dirty rectangle
4656     * does not intersect with this ViewGroup's bounds.
4657     */
4658    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
4659        if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
4660                (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
4661            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
4662                        FLAG_OPTIMIZE_INVALIDATE) {
4663                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
4664                        location[CHILD_TOP_INDEX] - mScrollY);
4665                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
4666                    dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4667                }
4668
4669                final int left = mLeft;
4670                final int top = mTop;
4671
4672                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4673                    if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
4674                        dirty.setEmpty();
4675                    }
4676                }
4677                mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
4678
4679                location[CHILD_LEFT_INDEX] = left;
4680                location[CHILD_TOP_INDEX] = top;
4681
4682                if (mLayerType != LAYER_TYPE_NONE) {
4683                    mPrivateFlags |= PFLAG_INVALIDATED;
4684                }
4685
4686                return mParent;
4687
4688            } else {
4689                mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
4690
4691                location[CHILD_LEFT_INDEX] = mLeft;
4692                location[CHILD_TOP_INDEX] = mTop;
4693                if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
4694                    dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
4695                } else {
4696                    // in case the dirty rect extends outside the bounds of this container
4697                    dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4698                }
4699
4700                if (mLayerType != LAYER_TYPE_NONE) {
4701                    mPrivateFlags |= PFLAG_INVALIDATED;
4702                }
4703
4704                return mParent;
4705            }
4706        }
4707
4708        return null;
4709    }
4710
4711    /**
4712     * Native-calculated damage path
4713     * Returns false if this path was unable to complete successfully. This means
4714     * it hit a ViewParent it doesn't recognize and needs to fall back to calculating
4715     * damage area
4716     * @hide
4717     */
4718    public boolean damageChildDeferred(View child) {
4719        ViewParent parent = getParent();
4720        while (parent != null) {
4721            if (parent instanceof ViewGroup) {
4722                parent = parent.getParent();
4723            } else if (parent instanceof ViewRootImpl) {
4724                ((ViewRootImpl) parent).invalidate();
4725                return true;
4726            } else {
4727                parent = null;
4728            }
4729        }
4730        return false;
4731    }
4732
4733    /**
4734     * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the
4735     * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods
4736     * do; all we want to do here is schedule a traversal with the appropriate dirty rect.
4737     *
4738     * @hide
4739     */
4740    public void damageChild(View child, final Rect dirty) {
4741        if (damageChildDeferred(child)) {
4742            return;
4743        }
4744
4745        ViewParent parent = this;
4746
4747        final AttachInfo attachInfo = mAttachInfo;
4748        if (attachInfo != null) {
4749            int left = child.mLeft;
4750            int top = child.mTop;
4751            if (!child.getMatrix().isIdentity()) {
4752                child.transformRect(dirty);
4753            }
4754
4755            do {
4756                if (parent instanceof ViewGroup) {
4757                    ViewGroup parentVG = (ViewGroup) parent;
4758                    if (parentVG.mLayerType != LAYER_TYPE_NONE) {
4759                        // Layered parents should be recreated, not just re-issued
4760                        parentVG.invalidate();
4761                        parent = null;
4762                    } else {
4763                        parent = parentVG.damageChildInParent(left, top, dirty);
4764                        left = parentVG.mLeft;
4765                        top = parentVG.mTop;
4766                    }
4767                } else {
4768                    // Reached the top; this calls into the usual invalidate method in
4769                    // ViewRootImpl, which schedules a traversal
4770                    final int[] location = attachInfo.mInvalidateChildLocation;
4771                    location[0] = left;
4772                    location[1] = top;
4773                    parent = parent.invalidateChildInParent(location, dirty);
4774                }
4775            } while (parent != null);
4776        }
4777    }
4778
4779    /**
4780     * Quick invalidation method that simply transforms the dirty rect into the parent's
4781     * coordinate system, pruning the invalidation if the parent has already been invalidated.
4782     *
4783     * @hide
4784     */
4785    protected ViewParent damageChildInParent(int left, int top, final Rect dirty) {
4786        if ((mPrivateFlags & PFLAG_DRAWN) != 0
4787                || (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) != 0) {
4788            dirty.offset(left - mScrollX, top - mScrollY);
4789            if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
4790                dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
4791            }
4792
4793            if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0 ||
4794                    dirty.intersect(0, 0, mRight - mLeft, mBottom - mTop)) {
4795
4796                if (!getMatrix().isIdentity()) {
4797                    transformRect(dirty);
4798                }
4799
4800                return mParent;
4801            }
4802        }
4803
4804        return null;
4805    }
4806
4807    /**
4808     * Offset a rectangle that is in a descendant's coordinate
4809     * space into our coordinate space.
4810     * @param descendant A descendant of this view
4811     * @param rect A rectangle defined in descendant's coordinate space.
4812     */
4813    public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
4814        offsetRectBetweenParentAndChild(descendant, rect, true, false);
4815    }
4816
4817    /**
4818     * Offset a rectangle that is in our coordinate space into an ancestor's
4819     * coordinate space.
4820     * @param descendant A descendant of this view
4821     * @param rect A rectangle defined in descendant's coordinate space.
4822     */
4823    public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
4824        offsetRectBetweenParentAndChild(descendant, rect, false, false);
4825    }
4826
4827    /**
4828     * Helper method that offsets a rect either from parent to descendant or
4829     * descendant to parent.
4830     */
4831    void offsetRectBetweenParentAndChild(View descendant, Rect rect,
4832            boolean offsetFromChildToParent, boolean clipToBounds) {
4833
4834        // already in the same coord system :)
4835        if (descendant == this) {
4836            return;
4837        }
4838
4839        ViewParent theParent = descendant.mParent;
4840
4841        // search and offset up to the parent
4842        while ((theParent != null)
4843                && (theParent instanceof View)
4844                && (theParent != this)) {
4845
4846            if (offsetFromChildToParent) {
4847                rect.offset(descendant.mLeft - descendant.mScrollX,
4848                        descendant.mTop - descendant.mScrollY);
4849                if (clipToBounds) {
4850                    View p = (View) theParent;
4851                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4852                }
4853            } else {
4854                if (clipToBounds) {
4855                    View p = (View) theParent;
4856                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
4857                }
4858                rect.offset(descendant.mScrollX - descendant.mLeft,
4859                        descendant.mScrollY - descendant.mTop);
4860            }
4861
4862            descendant = (View) theParent;
4863            theParent = descendant.mParent;
4864        }
4865
4866        // now that we are up to this view, need to offset one more time
4867        // to get into our coordinate space
4868        if (theParent == this) {
4869            if (offsetFromChildToParent) {
4870                rect.offset(descendant.mLeft - descendant.mScrollX,
4871                        descendant.mTop - descendant.mScrollY);
4872            } else {
4873                rect.offset(descendant.mScrollX - descendant.mLeft,
4874                        descendant.mScrollY - descendant.mTop);
4875            }
4876        } else {
4877            throw new IllegalArgumentException("parameter must be a descendant of this view");
4878        }
4879    }
4880
4881    /**
4882     * Offset the vertical location of all children of this view by the specified number of pixels.
4883     *
4884     * @param offset the number of pixels to offset
4885     *
4886     * @hide
4887     */
4888    public void offsetChildrenTopAndBottom(int offset) {
4889        final int count = mChildrenCount;
4890        final View[] children = mChildren;
4891        boolean invalidate = false;
4892
4893        for (int i = 0; i < count; i++) {
4894            final View v = children[i];
4895            v.mTop += offset;
4896            v.mBottom += offset;
4897            if (v.mRenderNode != null) {
4898                invalidate = true;
4899                v.mRenderNode.offsetTopAndBottom(offset);
4900            }
4901        }
4902
4903        if (invalidate) {
4904            invalidateViewProperty(false, false);
4905        }
4906        notifySubtreeAccessibilityStateChangedIfNeeded();
4907    }
4908
4909    /**
4910     * {@inheritDoc}
4911     */
4912    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
4913        // It doesn't make a whole lot of sense to call this on a view that isn't attached,
4914        // but for some simple tests it can be useful. If we don't have attach info this
4915        // will allocate memory.
4916        final RectF rect = mAttachInfo != null ? mAttachInfo.mTmpTransformRect : new RectF();
4917        rect.set(r);
4918
4919        if (!child.hasIdentityMatrix()) {
4920            child.getMatrix().mapRect(rect);
4921        }
4922
4923        final int dx = child.mLeft - mScrollX;
4924        final int dy = child.mTop - mScrollY;
4925
4926        rect.offset(dx, dy);
4927
4928        if (offset != null) {
4929            if (!child.hasIdentityMatrix()) {
4930                float[] position = mAttachInfo != null ? mAttachInfo.mTmpTransformLocation
4931                        : new float[2];
4932                position[0] = offset.x;
4933                position[1] = offset.y;
4934                child.getMatrix().mapPoints(position);
4935                offset.x = (int) (position[0] + 0.5f);
4936                offset.y = (int) (position[1] + 0.5f);
4937            }
4938            offset.x += dx;
4939            offset.y += dy;
4940        }
4941
4942        final int width = mRight - mLeft;
4943        final int height = mBottom - mTop;
4944
4945        boolean rectIsVisible = true;
4946        if (mParent == null ||
4947                (mParent instanceof ViewGroup && ((ViewGroup) mParent).getClipChildren())) {
4948            // Clip to bounds.
4949            rectIsVisible = rect.intersect(0, 0, width, height);
4950        }
4951
4952        if (rectIsVisible && (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
4953            // Clip to padding.
4954            rectIsVisible = rect.intersect(mPaddingLeft, mPaddingTop,
4955                    width - mPaddingRight, height - mPaddingBottom);
4956        }
4957
4958        if (rectIsVisible && mClipBounds != null) {
4959            // Clip to clipBounds.
4960            rectIsVisible = rect.intersect(mClipBounds.left, mClipBounds.top, mClipBounds.right,
4961                    mClipBounds.bottom);
4962        }
4963        r.set((int) (rect.left + 0.5f), (int) (rect.top + 0.5f), (int) (rect.right + 0.5f),
4964                (int) (rect.bottom + 0.5f));
4965        if (rectIsVisible && mParent != null) {
4966            rectIsVisible = mParent.getChildVisibleRect(this, r, offset);
4967        }
4968        return rectIsVisible;
4969    }
4970
4971    /**
4972     * {@inheritDoc}
4973     */
4974    @Override
4975    public final void layout(int l, int t, int r, int b) {
4976        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
4977            if (mTransition != null) {
4978                mTransition.layoutChange(this);
4979            }
4980            super.layout(l, t, r, b);
4981        } else {
4982            // record the fact that we noop'd it; request layout when transition finishes
4983            mLayoutCalledWhileSuppressed = true;
4984        }
4985    }
4986
4987    /**
4988     * {@inheritDoc}
4989     */
4990    @Override
4991    protected abstract void onLayout(boolean changed,
4992            int l, int t, int r, int b);
4993
4994    /**
4995     * Indicates whether the view group has the ability to animate its children
4996     * after the first layout.
4997     *
4998     * @return true if the children can be animated, false otherwise
4999     */
5000    protected boolean canAnimate() {
5001        return mLayoutAnimationController != null;
5002    }
5003
5004    /**
5005     * Runs the layout animation. Calling this method triggers a relayout of
5006     * this view group.
5007     */
5008    public void startLayoutAnimation() {
5009        if (mLayoutAnimationController != null) {
5010            mGroupFlags |= FLAG_RUN_ANIMATION;
5011            requestLayout();
5012        }
5013    }
5014
5015    /**
5016     * Schedules the layout animation to be played after the next layout pass
5017     * of this view group. This can be used to restart the layout animation
5018     * when the content of the view group changes or when the activity is
5019     * paused and resumed.
5020     */
5021    public void scheduleLayoutAnimation() {
5022        mGroupFlags |= FLAG_RUN_ANIMATION;
5023    }
5024
5025    /**
5026     * Sets the layout animation controller used to animate the group's
5027     * children after the first layout.
5028     *
5029     * @param controller the animation controller
5030     */
5031    public void setLayoutAnimation(LayoutAnimationController controller) {
5032        mLayoutAnimationController = controller;
5033        if (mLayoutAnimationController != null) {
5034            mGroupFlags |= FLAG_RUN_ANIMATION;
5035        }
5036    }
5037
5038    /**
5039     * Returns the layout animation controller used to animate the group's
5040     * children.
5041     *
5042     * @return the current animation controller
5043     */
5044    public LayoutAnimationController getLayoutAnimation() {
5045        return mLayoutAnimationController;
5046    }
5047
5048    /**
5049     * Indicates whether the children's drawing cache is used during a layout
5050     * animation. By default, the drawing cache is enabled but this will prevent
5051     * nested layout animations from working. To nest animations, you must disable
5052     * the cache.
5053     *
5054     * @return true if the animation cache is enabled, false otherwise
5055     *
5056     * @see #setAnimationCacheEnabled(boolean)
5057     * @see View#setDrawingCacheEnabled(boolean)
5058     */
5059    @ViewDebug.ExportedProperty
5060    public boolean isAnimationCacheEnabled() {
5061        return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
5062    }
5063
5064    /**
5065     * Enables or disables the children's drawing cache during a layout animation.
5066     * By default, the drawing cache is enabled but this will prevent nested
5067     * layout animations from working. To nest animations, you must disable the
5068     * cache.
5069     *
5070     * @param enabled true to enable the animation cache, false otherwise
5071     *
5072     * @see #isAnimationCacheEnabled()
5073     * @see View#setDrawingCacheEnabled(boolean)
5074     */
5075    public void setAnimationCacheEnabled(boolean enabled) {
5076        setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
5077    }
5078
5079    /**
5080     * Indicates whether this ViewGroup will always try to draw its children using their
5081     * drawing cache. By default this property is enabled.
5082     *
5083     * @return true if the animation cache is enabled, false otherwise
5084     *
5085     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5086     * @see #setChildrenDrawnWithCacheEnabled(boolean)
5087     * @see View#setDrawingCacheEnabled(boolean)
5088     */
5089    @ViewDebug.ExportedProperty(category = "drawing")
5090    public boolean isAlwaysDrawnWithCacheEnabled() {
5091        return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
5092    }
5093
5094    /**
5095     * Indicates whether this ViewGroup will always try to draw its children using their
5096     * drawing cache. This property can be set to true when the cache rendering is
5097     * slightly different from the children's normal rendering. Renderings can be different,
5098     * for instance, when the cache's quality is set to low.
5099     *
5100     * When this property is disabled, the ViewGroup will use the drawing cache of its
5101     * children only when asked to. It's usually the task of subclasses to tell ViewGroup
5102     * when to start using the drawing cache and when to stop using it.
5103     *
5104     * @param always true to always draw with the drawing cache, false otherwise
5105     *
5106     * @see #isAlwaysDrawnWithCacheEnabled()
5107     * @see #setChildrenDrawnWithCacheEnabled(boolean)
5108     * @see View#setDrawingCacheEnabled(boolean)
5109     * @see View#setDrawingCacheQuality(int)
5110     */
5111    public void setAlwaysDrawnWithCacheEnabled(boolean always) {
5112        setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
5113    }
5114
5115    /**
5116     * Indicates whether the ViewGroup is currently drawing its children using
5117     * their drawing cache.
5118     *
5119     * @return true if children should be drawn with their cache, false otherwise
5120     *
5121     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5122     * @see #setChildrenDrawnWithCacheEnabled(boolean)
5123     */
5124    @ViewDebug.ExportedProperty(category = "drawing")
5125    protected boolean isChildrenDrawnWithCacheEnabled() {
5126        return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
5127    }
5128
5129    /**
5130     * Tells the ViewGroup to draw its children using their drawing cache. This property
5131     * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
5132     * will be used only if it has been enabled.
5133     *
5134     * Subclasses should call this method to start and stop using the drawing cache when
5135     * they perform performance sensitive operations, like scrolling or animating.
5136     *
5137     * @param enabled true if children should be drawn with their cache, false otherwise
5138     *
5139     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
5140     * @see #isChildrenDrawnWithCacheEnabled()
5141     */
5142    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
5143        setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
5144    }
5145
5146    /**
5147     * Indicates whether the ViewGroup is drawing its children in the order defined by
5148     * {@link #getChildDrawingOrder(int, int)}.
5149     *
5150     * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
5151     *         false otherwise
5152     *
5153     * @see #setChildrenDrawingOrderEnabled(boolean)
5154     * @see #getChildDrawingOrder(int, int)
5155     */
5156    @ViewDebug.ExportedProperty(category = "drawing")
5157    protected boolean isChildrenDrawingOrderEnabled() {
5158        return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
5159    }
5160
5161    /**
5162     * Tells the ViewGroup whether to draw its children in the order defined by the method
5163     * {@link #getChildDrawingOrder(int, int)}.
5164     * <p>
5165     * Note that {@link View#getZ() Z} reordering, done by {@link #dispatchDraw(Canvas)},
5166     * will override custom child ordering done via this method.
5167     *
5168     * @param enabled true if the order of the children when drawing is determined by
5169     *        {@link #getChildDrawingOrder(int, int)}, false otherwise
5170     *
5171     * @see #isChildrenDrawingOrderEnabled()
5172     * @see #getChildDrawingOrder(int, int)
5173     */
5174    protected void setChildrenDrawingOrderEnabled(boolean enabled) {
5175        setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
5176    }
5177
5178    private boolean hasBooleanFlag(int flag) {
5179        return (mGroupFlags & flag) == flag;
5180    }
5181
5182    private void setBooleanFlag(int flag, boolean value) {
5183        if (value) {
5184            mGroupFlags |= flag;
5185        } else {
5186            mGroupFlags &= ~flag;
5187        }
5188    }
5189
5190    /**
5191     * Returns an integer indicating what types of drawing caches are kept in memory.
5192     *
5193     * @see #setPersistentDrawingCache(int)
5194     * @see #setAnimationCacheEnabled(boolean)
5195     *
5196     * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
5197     *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
5198     *         and {@link #PERSISTENT_ALL_CACHES}
5199     */
5200    @ViewDebug.ExportedProperty(category = "drawing", mapping = {
5201        @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
5202        @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
5203        @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
5204        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
5205    })
5206    public int getPersistentDrawingCache() {
5207        return mPersistentDrawingCache;
5208    }
5209
5210    /**
5211     * Indicates what types of drawing caches should be kept in memory after
5212     * they have been created.
5213     *
5214     * @see #getPersistentDrawingCache()
5215     * @see #setAnimationCacheEnabled(boolean)
5216     *
5217     * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
5218     *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
5219     *        and {@link #PERSISTENT_ALL_CACHES}
5220     */
5221    public void setPersistentDrawingCache(int drawingCacheToKeep) {
5222        mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
5223    }
5224
5225    private void setLayoutMode(int layoutMode, boolean explicitly) {
5226        mLayoutMode = layoutMode;
5227        setBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET, explicitly);
5228    }
5229
5230    /**
5231     * Recursively traverse the view hierarchy, resetting the layoutMode of any
5232     * descendants that had inherited a different layoutMode from a previous parent.
5233     * Recursion terminates when a descendant's mode is:
5234     * <ul>
5235     *     <li>Undefined</li>
5236     *     <li>The same as the root node's</li>
5237     *     <li>A mode that had been explicitly set</li>
5238     * <ul/>
5239     * The first two clauses are optimizations.
5240     * @param layoutModeOfRoot
5241     */
5242    @Override
5243    void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
5244        if (mLayoutMode == LAYOUT_MODE_UNDEFINED ||
5245            mLayoutMode == layoutModeOfRoot ||
5246            hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
5247            return;
5248        }
5249        setLayoutMode(LAYOUT_MODE_UNDEFINED, false);
5250
5251        // apply recursively
5252        for (int i = 0, N = getChildCount(); i < N; i++) {
5253            getChildAt(i).invalidateInheritedLayoutMode(layoutModeOfRoot);
5254        }
5255    }
5256
5257    /**
5258     * Returns the basis of alignment during layout operations on this ViewGroup:
5259     * either {@link #LAYOUT_MODE_CLIP_BOUNDS} or {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
5260     * <p>
5261     * If no layoutMode was explicitly set, either programmatically or in an XML resource,
5262     * the method returns the layoutMode of the view's parent ViewGroup if such a parent exists,
5263     * otherwise the method returns a default value of {@link #LAYOUT_MODE_CLIP_BOUNDS}.
5264     *
5265     * @return the layout mode to use during layout operations
5266     *
5267     * @see #setLayoutMode(int)
5268     */
5269    public int getLayoutMode() {
5270        if (mLayoutMode == LAYOUT_MODE_UNDEFINED) {
5271            int inheritedLayoutMode = (mParent instanceof ViewGroup) ?
5272                    ((ViewGroup) mParent).getLayoutMode() : LAYOUT_MODE_DEFAULT;
5273            setLayoutMode(inheritedLayoutMode, false);
5274        }
5275        return mLayoutMode;
5276    }
5277
5278    /**
5279     * Sets the basis of alignment during the layout of this ViewGroup.
5280     * Valid values are either {@link #LAYOUT_MODE_CLIP_BOUNDS} or
5281     * {@link #LAYOUT_MODE_OPTICAL_BOUNDS}.
5282     *
5283     * @param layoutMode the layout mode to use during layout operations
5284     *
5285     * @see #getLayoutMode()
5286     * @attr ref android.R.styleable#ViewGroup_layoutMode
5287     */
5288    public void setLayoutMode(int layoutMode) {
5289        if (mLayoutMode != layoutMode) {
5290            invalidateInheritedLayoutMode(layoutMode);
5291            setLayoutMode(layoutMode, layoutMode != LAYOUT_MODE_UNDEFINED);
5292            requestLayout();
5293        }
5294    }
5295
5296    /**
5297     * Returns a new set of layout parameters based on the supplied attributes set.
5298     *
5299     * @param attrs the attributes to build the layout parameters from
5300     *
5301     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5302     *         of its descendants
5303     */
5304    public LayoutParams generateLayoutParams(AttributeSet attrs) {
5305        return new LayoutParams(getContext(), attrs);
5306    }
5307
5308    /**
5309     * Returns a safe set of layout parameters based on the supplied layout params.
5310     * When a ViewGroup is passed a View whose layout params do not pass the test of
5311     * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
5312     * is invoked. This method should return a new set of layout params suitable for
5313     * this ViewGroup, possibly by copying the appropriate attributes from the
5314     * specified set of layout params.
5315     *
5316     * @param p The layout parameters to convert into a suitable set of layout parameters
5317     *          for this ViewGroup.
5318     *
5319     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
5320     *         of its descendants
5321     */
5322    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
5323        return p;
5324    }
5325
5326    /**
5327     * Returns a set of default layout parameters. These parameters are requested
5328     * when the View passed to {@link #addView(View)} has no layout parameters
5329     * already set. If null is returned, an exception is thrown from addView.
5330     *
5331     * @return a set of default layout parameters or null
5332     */
5333    protected LayoutParams generateDefaultLayoutParams() {
5334        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
5335    }
5336
5337    /**
5338     * {@inheritDoc}
5339     */
5340    @Override
5341    protected void debug(int depth) {
5342        super.debug(depth);
5343        String output;
5344
5345        if (mFocused != null) {
5346            output = debugIndent(depth);
5347            output += "mFocused";
5348            Log.d(VIEW_LOG_TAG, output);
5349        }
5350        if (mChildrenCount != 0) {
5351            output = debugIndent(depth);
5352            output += "{";
5353            Log.d(VIEW_LOG_TAG, output);
5354        }
5355        int count = mChildrenCount;
5356        for (int i = 0; i < count; i++) {
5357            View child = mChildren[i];
5358            child.debug(depth + 1);
5359        }
5360
5361        if (mChildrenCount != 0) {
5362            output = debugIndent(depth);
5363            output += "}";
5364            Log.d(VIEW_LOG_TAG, output);
5365        }
5366    }
5367
5368    /**
5369     * Returns the position in the group of the specified child view.
5370     *
5371     * @param child the view for which to get the position
5372     * @return a positive integer representing the position of the view in the
5373     *         group, or -1 if the view does not exist in the group
5374     */
5375    public int indexOfChild(View child) {
5376        final int count = mChildrenCount;
5377        final View[] children = mChildren;
5378        for (int i = 0; i < count; i++) {
5379            if (children[i] == child) {
5380                return i;
5381            }
5382        }
5383        return -1;
5384    }
5385
5386    /**
5387     * Returns the number of children in the group.
5388     *
5389     * @return a positive integer representing the number of children in
5390     *         the group
5391     */
5392    public int getChildCount() {
5393        return mChildrenCount;
5394    }
5395
5396    /**
5397     * Returns the view at the specified position in the group.
5398     *
5399     * @param index the position at which to get the view from
5400     * @return the view at the specified position or null if the position
5401     *         does not exist within the group
5402     */
5403    public View getChildAt(int index) {
5404        if (index < 0 || index >= mChildrenCount) {
5405            return null;
5406        }
5407        return mChildren[index];
5408    }
5409
5410    /**
5411     * Ask all of the children of this view to measure themselves, taking into
5412     * account both the MeasureSpec requirements for this view and its padding.
5413     * We skip children that are in the GONE state The heavy lifting is done in
5414     * getChildMeasureSpec.
5415     *
5416     * @param widthMeasureSpec The width requirements for this view
5417     * @param heightMeasureSpec The height requirements for this view
5418     */
5419    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
5420        final int size = mChildrenCount;
5421        final View[] children = mChildren;
5422        for (int i = 0; i < size; ++i) {
5423            final View child = children[i];
5424            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
5425                measureChild(child, widthMeasureSpec, heightMeasureSpec);
5426            }
5427        }
5428    }
5429
5430    /**
5431     * Ask one of the children of this view to measure itself, taking into
5432     * account both the MeasureSpec requirements for this view and its padding.
5433     * The heavy lifting is done in getChildMeasureSpec.
5434     *
5435     * @param child The child to measure
5436     * @param parentWidthMeasureSpec The width requirements for this view
5437     * @param parentHeightMeasureSpec The height requirements for this view
5438     */
5439    protected void measureChild(View child, int parentWidthMeasureSpec,
5440            int parentHeightMeasureSpec) {
5441        final LayoutParams lp = child.getLayoutParams();
5442
5443        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
5444                mPaddingLeft + mPaddingRight, lp.width);
5445        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
5446                mPaddingTop + mPaddingBottom, lp.height);
5447
5448        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5449    }
5450
5451    /**
5452     * Ask one of the children of this view to measure itself, taking into
5453     * account both the MeasureSpec requirements for this view and its padding
5454     * and margins. The child must have MarginLayoutParams The heavy lifting is
5455     * done in getChildMeasureSpec.
5456     *
5457     * @param child The child to measure
5458     * @param parentWidthMeasureSpec The width requirements for this view
5459     * @param widthUsed Extra space that has been used up by the parent
5460     *        horizontally (possibly by other children of the parent)
5461     * @param parentHeightMeasureSpec The height requirements for this view
5462     * @param heightUsed Extra space that has been used up by the parent
5463     *        vertically (possibly by other children of the parent)
5464     */
5465    protected void measureChildWithMargins(View child,
5466            int parentWidthMeasureSpec, int widthUsed,
5467            int parentHeightMeasureSpec, int heightUsed) {
5468        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
5469
5470        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
5471                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
5472                        + widthUsed, lp.width);
5473        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
5474                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
5475                        + heightUsed, lp.height);
5476
5477        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
5478    }
5479
5480    /**
5481     * Does the hard part of measureChildren: figuring out the MeasureSpec to
5482     * pass to a particular child. This method figures out the right MeasureSpec
5483     * for one dimension (height or width) of one child view.
5484     *
5485     * The goal is to combine information from our MeasureSpec with the
5486     * LayoutParams of the child to get the best possible results. For example,
5487     * if the this view knows its size (because its MeasureSpec has a mode of
5488     * EXACTLY), and the child has indicated in its LayoutParams that it wants
5489     * to be the same size as the parent, the parent should ask the child to
5490     * layout given an exact size.
5491     *
5492     * @param spec The requirements for this view
5493     * @param padding The padding of this view for the current dimension and
5494     *        margins, if applicable
5495     * @param childDimension How big the child wants to be in the current
5496     *        dimension
5497     * @return a MeasureSpec integer for the child
5498     */
5499    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
5500        int specMode = MeasureSpec.getMode(spec);
5501        int specSize = MeasureSpec.getSize(spec);
5502
5503        int size = Math.max(0, specSize - padding);
5504
5505        int resultSize = 0;
5506        int resultMode = 0;
5507
5508        switch (specMode) {
5509        // Parent has imposed an exact size on us
5510        case MeasureSpec.EXACTLY:
5511            if (childDimension >= 0) {
5512                resultSize = childDimension;
5513                resultMode = MeasureSpec.EXACTLY;
5514            } else if (childDimension == LayoutParams.MATCH_PARENT) {
5515                // Child wants to be our size. So be it.
5516                resultSize = size;
5517                resultMode = MeasureSpec.EXACTLY;
5518            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5519                // Child wants to determine its own size. It can't be
5520                // bigger than us.
5521                resultSize = size;
5522                resultMode = MeasureSpec.AT_MOST;
5523            }
5524            break;
5525
5526        // Parent has imposed a maximum size on us
5527        case MeasureSpec.AT_MOST:
5528            if (childDimension >= 0) {
5529                // Child wants a specific size... so be it
5530                resultSize = childDimension;
5531                resultMode = MeasureSpec.EXACTLY;
5532            } else if (childDimension == LayoutParams.MATCH_PARENT) {
5533                // Child wants to be our size, but our size is not fixed.
5534                // Constrain child to not be bigger than us.
5535                resultSize = size;
5536                resultMode = MeasureSpec.AT_MOST;
5537            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5538                // Child wants to determine its own size. It can't be
5539                // bigger than us.
5540                resultSize = size;
5541                resultMode = MeasureSpec.AT_MOST;
5542            }
5543            break;
5544
5545        // Parent asked to see how big we want to be
5546        case MeasureSpec.UNSPECIFIED:
5547            if (childDimension >= 0) {
5548                // Child wants a specific size... let him have it
5549                resultSize = childDimension;
5550                resultMode = MeasureSpec.EXACTLY;
5551            } else if (childDimension == LayoutParams.MATCH_PARENT) {
5552                // Child wants to be our size... find out how big it should
5553                // be
5554                resultSize = 0;
5555                resultMode = MeasureSpec.UNSPECIFIED;
5556            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
5557                // Child wants to determine its own size.... find out how
5558                // big it should be
5559                resultSize = 0;
5560                resultMode = MeasureSpec.UNSPECIFIED;
5561            }
5562            break;
5563        }
5564        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
5565    }
5566
5567
5568    /**
5569     * Removes any pending animations for views that have been removed. Call
5570     * this if you don't want animations for exiting views to stack up.
5571     */
5572    public void clearDisappearingChildren() {
5573        final ArrayList<View> disappearingChildren = mDisappearingChildren;
5574        if (disappearingChildren != null) {
5575            final int count = disappearingChildren.size();
5576            for (int i = 0; i < count; i++) {
5577                final View view = disappearingChildren.get(i);
5578                if (view.mAttachInfo != null) {
5579                    view.dispatchDetachedFromWindow();
5580                }
5581                view.clearAnimation();
5582            }
5583            disappearingChildren.clear();
5584            invalidate();
5585        }
5586    }
5587
5588    /**
5589     * Add a view which is removed from mChildren but still needs animation
5590     *
5591     * @param v View to add
5592     */
5593    private void addDisappearingView(View v) {
5594        ArrayList<View> disappearingChildren = mDisappearingChildren;
5595
5596        if (disappearingChildren == null) {
5597            disappearingChildren = mDisappearingChildren = new ArrayList<View>();
5598        }
5599
5600        disappearingChildren.add(v);
5601    }
5602
5603    /**
5604     * Cleanup a view when its animation is done. This may mean removing it from
5605     * the list of disappearing views.
5606     *
5607     * @param view The view whose animation has finished
5608     * @param animation The animation, cannot be null
5609     */
5610    void finishAnimatingView(final View view, Animation animation) {
5611        final ArrayList<View> disappearingChildren = mDisappearingChildren;
5612        if (disappearingChildren != null) {
5613            if (disappearingChildren.contains(view)) {
5614                disappearingChildren.remove(view);
5615
5616                if (view.mAttachInfo != null) {
5617                    view.dispatchDetachedFromWindow();
5618                }
5619
5620                view.clearAnimation();
5621                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
5622            }
5623        }
5624
5625        if (animation != null && !animation.getFillAfter()) {
5626            view.clearAnimation();
5627        }
5628
5629        if ((view.mPrivateFlags & PFLAG_ANIMATION_STARTED) == PFLAG_ANIMATION_STARTED) {
5630            view.onAnimationEnd();
5631            // Should be performed by onAnimationEnd() but this avoid an infinite loop,
5632            // so we'd rather be safe than sorry
5633            view.mPrivateFlags &= ~PFLAG_ANIMATION_STARTED;
5634            // Draw one more frame after the animation is done
5635            mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
5636        }
5637    }
5638
5639    /**
5640     * Utility function called by View during invalidation to determine whether a view that
5641     * is invisible or gone should still be invalidated because it is being transitioned (and
5642     * therefore still needs to be drawn).
5643     */
5644    boolean isViewTransitioning(View view) {
5645        return (mTransitioningViews != null && mTransitioningViews.contains(view));
5646    }
5647
5648    /**
5649     * This method tells the ViewGroup that the given View object, which should have this
5650     * ViewGroup as its parent,
5651     * should be kept around  (re-displayed when the ViewGroup draws its children) even if it
5652     * is removed from its parent. This allows animations, such as those used by
5653     * {@link android.app.Fragment} and {@link android.animation.LayoutTransition} to animate
5654     * the removal of views. A call to this method should always be accompanied by a later call
5655     * to {@link #endViewTransition(View)}, such as after an animation on the View has finished,
5656     * so that the View finally gets removed.
5657     *
5658     * @param view The View object to be kept visible even if it gets removed from its parent.
5659     */
5660    public void startViewTransition(View view) {
5661        if (view.mParent == this) {
5662            if (mTransitioningViews == null) {
5663                mTransitioningViews = new ArrayList<View>();
5664            }
5665            mTransitioningViews.add(view);
5666        }
5667    }
5668
5669    /**
5670     * This method should always be called following an earlier call to
5671     * {@link #startViewTransition(View)}. The given View is finally removed from its parent
5672     * and will no longer be displayed. Note that this method does not perform the functionality
5673     * of removing a view from its parent; it just discontinues the display of a View that
5674     * has previously been removed.
5675     *
5676     * @return view The View object that has been removed but is being kept around in the visible
5677     * hierarchy by an earlier call to {@link #startViewTransition(View)}.
5678     */
5679    public void endViewTransition(View view) {
5680        if (mTransitioningViews != null) {
5681            mTransitioningViews.remove(view);
5682            final ArrayList<View> disappearingChildren = mDisappearingChildren;
5683            if (disappearingChildren != null && disappearingChildren.contains(view)) {
5684                disappearingChildren.remove(view);
5685                if (mVisibilityChangingChildren != null &&
5686                        mVisibilityChangingChildren.contains(view)) {
5687                    mVisibilityChangingChildren.remove(view);
5688                } else {
5689                    if (view.mAttachInfo != null) {
5690                        view.dispatchDetachedFromWindow();
5691                    }
5692                    if (view.mParent != null) {
5693                        view.mParent = null;
5694                    }
5695                }
5696                invalidate();
5697            }
5698        }
5699    }
5700
5701    private LayoutTransition.TransitionListener mLayoutTransitionListener =
5702            new LayoutTransition.TransitionListener() {
5703        @Override
5704        public void startTransition(LayoutTransition transition, ViewGroup container,
5705                View view, int transitionType) {
5706            // We only care about disappearing items, since we need special logic to keep
5707            // those items visible after they've been 'removed'
5708            if (transitionType == LayoutTransition.DISAPPEARING) {
5709                startViewTransition(view);
5710            }
5711        }
5712
5713        @Override
5714        public void endTransition(LayoutTransition transition, ViewGroup container,
5715                View view, int transitionType) {
5716            if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) {
5717                requestLayout();
5718                mLayoutCalledWhileSuppressed = false;
5719            }
5720            if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
5721                endViewTransition(view);
5722            }
5723        }
5724    };
5725
5726    /**
5727     * Tells this ViewGroup to suppress all layout() calls until layout
5728     * suppression is disabled with a later call to suppressLayout(false).
5729     * When layout suppression is disabled, a requestLayout() call is sent
5730     * if layout() was attempted while layout was being suppressed.
5731     *
5732     * @hide
5733     */
5734    public void suppressLayout(boolean suppress) {
5735        mSuppressLayout = suppress;
5736        if (!suppress) {
5737            if (mLayoutCalledWhileSuppressed) {
5738                requestLayout();
5739                mLayoutCalledWhileSuppressed = false;
5740            }
5741        }
5742    }
5743
5744    /**
5745     * Returns whether layout calls on this container are currently being
5746     * suppressed, due to an earlier call to {@link #suppressLayout(boolean)}.
5747     *
5748     * @return true if layout calls are currently suppressed, false otherwise.
5749     *
5750     * @hide
5751     */
5752    public boolean isLayoutSuppressed() {
5753        return mSuppressLayout;
5754    }
5755
5756    /**
5757     * {@inheritDoc}
5758     */
5759    @Override
5760    public boolean gatherTransparentRegion(Region region) {
5761        // If no transparent regions requested, we are always opaque.
5762        final boolean meOpaque = (mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0;
5763        if (meOpaque && region == null) {
5764            // The caller doesn't care about the region, so stop now.
5765            return true;
5766        }
5767        super.gatherTransparentRegion(region);
5768        final View[] children = mChildren;
5769        final int count = mChildrenCount;
5770        boolean noneOfTheChildrenAreTransparent = true;
5771        for (int i = 0; i < count; i++) {
5772            final View child = children[i];
5773            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
5774                if (!child.gatherTransparentRegion(region)) {
5775                    noneOfTheChildrenAreTransparent = false;
5776                }
5777            }
5778        }
5779        return meOpaque || noneOfTheChildrenAreTransparent;
5780    }
5781
5782    /**
5783     * {@inheritDoc}
5784     */
5785    public void requestTransparentRegion(View child) {
5786        if (child != null) {
5787            child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
5788            if (mParent != null) {
5789                mParent.requestTransparentRegion(this);
5790            }
5791        }
5792    }
5793
5794    @Override
5795    public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
5796        insets = super.dispatchApplyWindowInsets(insets);
5797        if (!insets.isConsumed()) {
5798            final int count = getChildCount();
5799            for (int i = 0; i < count; i++) {
5800                insets = getChildAt(i).dispatchApplyWindowInsets(insets);
5801                if (insets.isConsumed()) {
5802                    break;
5803                }
5804            }
5805        }
5806        return insets;
5807    }
5808
5809    /**
5810     * Returns the animation listener to which layout animation events are
5811     * sent.
5812     *
5813     * @return an {@link android.view.animation.Animation.AnimationListener}
5814     */
5815    public Animation.AnimationListener getLayoutAnimationListener() {
5816        return mAnimationListener;
5817    }
5818
5819    @Override
5820    protected void drawableStateChanged() {
5821        super.drawableStateChanged();
5822
5823        if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
5824            if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5825                throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
5826                        + " child has duplicateParentState set to true");
5827            }
5828
5829            final View[] children = mChildren;
5830            final int count = mChildrenCount;
5831
5832            for (int i = 0; i < count; i++) {
5833                final View child = children[i];
5834                if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
5835                    child.refreshDrawableState();
5836                }
5837            }
5838        }
5839    }
5840
5841    @Override
5842    public void jumpDrawablesToCurrentState() {
5843        super.jumpDrawablesToCurrentState();
5844        final View[] children = mChildren;
5845        final int count = mChildrenCount;
5846        for (int i = 0; i < count; i++) {
5847            children[i].jumpDrawablesToCurrentState();
5848        }
5849    }
5850
5851    @Override
5852    protected int[] onCreateDrawableState(int extraSpace) {
5853        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
5854            return super.onCreateDrawableState(extraSpace);
5855        }
5856
5857        int need = 0;
5858        int n = getChildCount();
5859        for (int i = 0; i < n; i++) {
5860            int[] childState = getChildAt(i).getDrawableState();
5861
5862            if (childState != null) {
5863                need += childState.length;
5864            }
5865        }
5866
5867        int[] state = super.onCreateDrawableState(extraSpace + need);
5868
5869        for (int i = 0; i < n; i++) {
5870            int[] childState = getChildAt(i).getDrawableState();
5871
5872            if (childState != null) {
5873                state = mergeDrawableStates(state, childState);
5874            }
5875        }
5876
5877        return state;
5878    }
5879
5880    /**
5881     * Sets whether this ViewGroup's drawable states also include
5882     * its children's drawable states.  This is used, for example, to
5883     * make a group appear to be focused when its child EditText or button
5884     * is focused.
5885     */
5886    public void setAddStatesFromChildren(boolean addsStates) {
5887        if (addsStates) {
5888            mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
5889        } else {
5890            mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
5891        }
5892
5893        refreshDrawableState();
5894    }
5895
5896    /**
5897     * Returns whether this ViewGroup's drawable states also include
5898     * its children's drawable states.  This is used, for example, to
5899     * make a group appear to be focused when its child EditText or button
5900     * is focused.
5901     */
5902    public boolean addStatesFromChildren() {
5903        return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
5904    }
5905
5906    /**
5907     * If {@link #addStatesFromChildren} is true, refreshes this group's
5908     * drawable state (to include the states from its children).
5909     */
5910    public void childDrawableStateChanged(View child) {
5911        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
5912            refreshDrawableState();
5913        }
5914    }
5915
5916    /**
5917     * Specifies the animation listener to which layout animation events must
5918     * be sent. Only
5919     * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
5920     * and
5921     * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
5922     * are invoked.
5923     *
5924     * @param animationListener the layout animation listener
5925     */
5926    public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
5927        mAnimationListener = animationListener;
5928    }
5929
5930    /**
5931     * This method is called by LayoutTransition when there are 'changing' animations that need
5932     * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
5933     * starts all pending transitions prior to the drawing phase in the current traversal.
5934     *
5935     * @param transition The LayoutTransition to be started on the next traversal.
5936     *
5937     * @hide
5938     */
5939    public void requestTransitionStart(LayoutTransition transition) {
5940        ViewRootImpl viewAncestor = getViewRootImpl();
5941        if (viewAncestor != null) {
5942            viewAncestor.requestTransitionStart(transition);
5943        }
5944    }
5945
5946    /**
5947     * @hide
5948     */
5949    @Override
5950    public boolean resolveRtlPropertiesIfNeeded() {
5951        final boolean result = super.resolveRtlPropertiesIfNeeded();
5952        // We dont need to resolve the children RTL properties if nothing has changed for the parent
5953        if (result) {
5954            int count = getChildCount();
5955            for (int i = 0; i < count; i++) {
5956                final View child = getChildAt(i);
5957                if (child.isLayoutDirectionInherited()) {
5958                    child.resolveRtlPropertiesIfNeeded();
5959                }
5960            }
5961        }
5962        return result;
5963    }
5964
5965    /**
5966     * @hide
5967     */
5968    @Override
5969    public boolean resolveLayoutDirection() {
5970        final boolean result = super.resolveLayoutDirection();
5971        if (result) {
5972            int count = getChildCount();
5973            for (int i = 0; i < count; i++) {
5974                final View child = getChildAt(i);
5975                if (child.isLayoutDirectionInherited()) {
5976                    child.resolveLayoutDirection();
5977                }
5978            }
5979        }
5980        return result;
5981    }
5982
5983    /**
5984     * @hide
5985     */
5986    @Override
5987    public boolean resolveTextDirection() {
5988        final boolean result = super.resolveTextDirection();
5989        if (result) {
5990            int count = getChildCount();
5991            for (int i = 0; i < count; i++) {
5992                final View child = getChildAt(i);
5993                if (child.isTextDirectionInherited()) {
5994                    child.resolveTextDirection();
5995                }
5996            }
5997        }
5998        return result;
5999    }
6000
6001    /**
6002     * @hide
6003     */
6004    @Override
6005    public boolean resolveTextAlignment() {
6006        final boolean result = super.resolveTextAlignment();
6007        if (result) {
6008            int count = getChildCount();
6009            for (int i = 0; i < count; i++) {
6010                final View child = getChildAt(i);
6011                if (child.isTextAlignmentInherited()) {
6012                    child.resolveTextAlignment();
6013                }
6014            }
6015        }
6016        return result;
6017    }
6018
6019    /**
6020     * @hide
6021     */
6022    @Override
6023    public void resolvePadding() {
6024        super.resolvePadding();
6025        int count = getChildCount();
6026        for (int i = 0; i < count; i++) {
6027            final View child = getChildAt(i);
6028            if (child.isLayoutDirectionInherited() && !child.isPaddingResolved()) {
6029                child.resolvePadding();
6030            }
6031        }
6032    }
6033
6034    /**
6035     * @hide
6036     */
6037    @Override
6038    protected void resolveDrawables() {
6039        super.resolveDrawables();
6040        int count = getChildCount();
6041        for (int i = 0; i < count; i++) {
6042            final View child = getChildAt(i);
6043            if (child.isLayoutDirectionInherited() && !child.areDrawablesResolved()) {
6044                child.resolveDrawables();
6045            }
6046        }
6047    }
6048
6049    /**
6050     * @hide
6051     */
6052    @Override
6053    public void resolveLayoutParams() {
6054        super.resolveLayoutParams();
6055        int count = getChildCount();
6056        for (int i = 0; i < count; i++) {
6057            final View child = getChildAt(i);
6058            child.resolveLayoutParams();
6059        }
6060    }
6061
6062    /**
6063     * @hide
6064     */
6065    @Override
6066    public void resetResolvedLayoutDirection() {
6067        super.resetResolvedLayoutDirection();
6068
6069        int count = getChildCount();
6070        for (int i = 0; i < count; i++) {
6071            final View child = getChildAt(i);
6072            if (child.isLayoutDirectionInherited()) {
6073                child.resetResolvedLayoutDirection();
6074            }
6075        }
6076    }
6077
6078    /**
6079     * @hide
6080     */
6081    @Override
6082    public void resetResolvedTextDirection() {
6083        super.resetResolvedTextDirection();
6084
6085        int count = getChildCount();
6086        for (int i = 0; i < count; i++) {
6087            final View child = getChildAt(i);
6088            if (child.isTextDirectionInherited()) {
6089                child.resetResolvedTextDirection();
6090            }
6091        }
6092    }
6093
6094    /**
6095     * @hide
6096     */
6097    @Override
6098    public void resetResolvedTextAlignment() {
6099        super.resetResolvedTextAlignment();
6100
6101        int count = getChildCount();
6102        for (int i = 0; i < count; i++) {
6103            final View child = getChildAt(i);
6104            if (child.isTextAlignmentInherited()) {
6105                child.resetResolvedTextAlignment();
6106            }
6107        }
6108    }
6109
6110    /**
6111     * @hide
6112     */
6113    @Override
6114    public void resetResolvedPadding() {
6115        super.resetResolvedPadding();
6116
6117        int count = getChildCount();
6118        for (int i = 0; i < count; i++) {
6119            final View child = getChildAt(i);
6120            if (child.isLayoutDirectionInherited()) {
6121                child.resetResolvedPadding();
6122            }
6123        }
6124    }
6125
6126    /**
6127     * @hide
6128     */
6129    @Override
6130    protected void resetResolvedDrawables() {
6131        super.resetResolvedDrawables();
6132
6133        int count = getChildCount();
6134        for (int i = 0; i < count; i++) {
6135            final View child = getChildAt(i);
6136            if (child.isLayoutDirectionInherited()) {
6137                child.resetResolvedDrawables();
6138            }
6139        }
6140    }
6141
6142    /**
6143     * Return true if the pressed state should be delayed for children or descendants of this
6144     * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
6145     * This prevents the pressed state from appearing when the user is actually trying to scroll
6146     * the content.
6147     *
6148     * The default implementation returns true for compatibility reasons. Subclasses that do
6149     * not scroll should generally override this method and return false.
6150     */
6151    public boolean shouldDelayChildPressedState() {
6152        return true;
6153    }
6154
6155    /**
6156     * @inheritDoc
6157     */
6158    @Override
6159    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
6160        return false;
6161    }
6162
6163    /**
6164     * @inheritDoc
6165     */
6166    @Override
6167    public void onNestedScrollAccepted(View child, View target, int axes) {
6168        mNestedScrollAxes = axes;
6169    }
6170
6171    /**
6172     * @inheritDoc
6173     *
6174     * <p>The default implementation of onStopNestedScroll calls
6175     * {@link #stopNestedScroll()} to halt any recursive nested scrolling in progress.</p>
6176     */
6177    @Override
6178    public void onStopNestedScroll(View child) {
6179        // Stop any recursive nested scrolling.
6180        stopNestedScroll();
6181    }
6182
6183    /**
6184     * @inheritDoc
6185     */
6186    @Override
6187    public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
6188            int dxUnconsumed, int dyUnconsumed) {
6189        // Do nothing
6190    }
6191
6192    /**
6193     * @inheritDoc
6194     */
6195    @Override
6196    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
6197        // Do nothing
6198    }
6199
6200    /**
6201     * @inheritDoc
6202     */
6203    @Override
6204    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
6205        return false;
6206    }
6207
6208    /**
6209     * @inheritDoc
6210     */
6211    @Override
6212    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
6213        return false;
6214    }
6215
6216    /**
6217     * Return the current axes of nested scrolling for this ViewGroup.
6218     *
6219     * <p>A ViewGroup returning something other than {@link #SCROLL_AXIS_NONE} is currently
6220     * acting as a nested scrolling parent for one or more descendant views in the hierarchy.</p>
6221     *
6222     * @return Flags indicating the current axes of nested scrolling
6223     * @see #SCROLL_AXIS_HORIZONTAL
6224     * @see #SCROLL_AXIS_VERTICAL
6225     * @see #SCROLL_AXIS_NONE
6226     */
6227    public int getNestedScrollAxes() {
6228        return mNestedScrollAxes;
6229    }
6230
6231    /** @hide */
6232    protected void onSetLayoutParams(View child, LayoutParams layoutParams) {
6233    }
6234
6235    /** @hide */
6236    @Override
6237    public void captureTransitioningViews(List<View> transitioningViews) {
6238        if (getVisibility() != View.VISIBLE) {
6239            return;
6240        }
6241        if (isTransitionGroup()) {
6242            transitioningViews.add(this);
6243        } else {
6244            int count = getChildCount();
6245            for (int i = 0; i < count; i++) {
6246                View child = getChildAt(i);
6247                child.captureTransitioningViews(transitioningViews);
6248            }
6249        }
6250    }
6251
6252    /** @hide */
6253    @Override
6254    public void findNamedViews(Map<String, View> namedElements) {
6255        if (getVisibility() != VISIBLE && mGhostView == null) {
6256            return;
6257        }
6258        super.findNamedViews(namedElements);
6259        int count = getChildCount();
6260        for (int i = 0; i < count; i++) {
6261            View child = getChildAt(i);
6262            child.findNamedViews(namedElements);
6263        }
6264    }
6265
6266    /**
6267     * LayoutParams are used by views to tell their parents how they want to be
6268     * laid out. See
6269     * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
6270     * for a list of all child view attributes that this class supports.
6271     *
6272     * <p>
6273     * The base LayoutParams class just describes how big the view wants to be
6274     * for both width and height. For each dimension, it can specify one of:
6275     * <ul>
6276     * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
6277     * means that the view wants to be as big as its parent (minus padding)
6278     * <li> WRAP_CONTENT, which means that the view wants to be just big enough
6279     * to enclose its content (plus padding)
6280     * <li> an exact number
6281     * </ul>
6282     * There are subclasses of LayoutParams for different subclasses of
6283     * ViewGroup. For example, AbsoluteLayout has its own subclass of
6284     * LayoutParams which adds an X and Y value.</p>
6285     *
6286     * <div class="special reference">
6287     * <h3>Developer Guides</h3>
6288     * <p>For more information about creating user interface layouts, read the
6289     * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
6290     * guide.</p></div>
6291     *
6292     * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
6293     * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
6294     */
6295    public static class LayoutParams {
6296        /**
6297         * Special value for the height or width requested by a View.
6298         * FILL_PARENT means that the view wants to be as big as its parent,
6299         * minus the parent's padding, if any. This value is deprecated
6300         * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
6301         */
6302        @SuppressWarnings({"UnusedDeclaration"})
6303        @Deprecated
6304        public static final int FILL_PARENT = -1;
6305
6306        /**
6307         * Special value for the height or width requested by a View.
6308         * MATCH_PARENT means that the view wants to be as big as its parent,
6309         * minus the parent's padding, if any. Introduced in API Level 8.
6310         */
6311        public static final int MATCH_PARENT = -1;
6312
6313        /**
6314         * Special value for the height or width requested by a View.
6315         * WRAP_CONTENT means that the view wants to be just large enough to fit
6316         * its own internal content, taking its own padding into account.
6317         */
6318        public static final int WRAP_CONTENT = -2;
6319
6320        /**
6321         * Information about how wide the view wants to be. Can be one of the
6322         * constants FILL_PARENT (replaced by MATCH_PARENT ,
6323         * in API Level 8) or WRAP_CONTENT. or an exact size.
6324         */
6325        @ViewDebug.ExportedProperty(category = "layout", mapping = {
6326            @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
6327            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
6328        })
6329        public int width;
6330
6331        /**
6332         * Information about how tall the view wants to be. Can be one of the
6333         * constants FILL_PARENT (replaced by MATCH_PARENT ,
6334         * in API Level 8) or WRAP_CONTENT. or an exact size.
6335         */
6336        @ViewDebug.ExportedProperty(category = "layout", mapping = {
6337            @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
6338            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
6339        })
6340        public int height;
6341
6342        /**
6343         * Used to animate layouts.
6344         */
6345        public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
6346
6347        /**
6348         * Creates a new set of layout parameters. The values are extracted from
6349         * the supplied attributes set and context. The XML attributes mapped
6350         * to this set of layout parameters are:
6351         *
6352         * <ul>
6353         *   <li><code>layout_width</code>: the width, either an exact value,
6354         *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
6355         *   {@link #MATCH_PARENT} in API Level 8)</li>
6356         *   <li><code>layout_height</code>: the height, either an exact value,
6357         *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
6358         *   {@link #MATCH_PARENT} in API Level 8)</li>
6359         * </ul>
6360         *
6361         * @param c the application environment
6362         * @param attrs the set of attributes from which to extract the layout
6363         *              parameters' values
6364         */
6365        public LayoutParams(Context c, AttributeSet attrs) {
6366            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
6367            setBaseAttributes(a,
6368                    R.styleable.ViewGroup_Layout_layout_width,
6369                    R.styleable.ViewGroup_Layout_layout_height);
6370            a.recycle();
6371        }
6372
6373        /**
6374         * Creates a new set of layout parameters with the specified width
6375         * and height.
6376         *
6377         * @param width the width, either {@link #WRAP_CONTENT},
6378         *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
6379         *        API Level 8), or a fixed size in pixels
6380         * @param height the height, either {@link #WRAP_CONTENT},
6381         *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
6382         *        API Level 8), or a fixed size in pixels
6383         */
6384        public LayoutParams(int width, int height) {
6385            this.width = width;
6386            this.height = height;
6387        }
6388
6389        /**
6390         * Copy constructor. Clones the width and height values of the source.
6391         *
6392         * @param source The layout params to copy from.
6393         */
6394        public LayoutParams(LayoutParams source) {
6395            this.width = source.width;
6396            this.height = source.height;
6397        }
6398
6399        /**
6400         * Used internally by MarginLayoutParams.
6401         * @hide
6402         */
6403        LayoutParams() {
6404        }
6405
6406        /**
6407         * Extracts the layout parameters from the supplied attributes.
6408         *
6409         * @param a the style attributes to extract the parameters from
6410         * @param widthAttr the identifier of the width attribute
6411         * @param heightAttr the identifier of the height attribute
6412         */
6413        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
6414            width = a.getLayoutDimension(widthAttr, "layout_width");
6415            height = a.getLayoutDimension(heightAttr, "layout_height");
6416        }
6417
6418        /**
6419         * Resolve layout parameters depending on the layout direction. Subclasses that care about
6420         * layoutDirection changes should override this method. The default implementation does
6421         * nothing.
6422         *
6423         * @param layoutDirection the direction of the layout
6424         *
6425         * {@link View#LAYOUT_DIRECTION_LTR}
6426         * {@link View#LAYOUT_DIRECTION_RTL}
6427         */
6428        public void resolveLayoutDirection(int layoutDirection) {
6429        }
6430
6431        /**
6432         * Returns a String representation of this set of layout parameters.
6433         *
6434         * @param output the String to prepend to the internal representation
6435         * @return a String with the following format: output +
6436         *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
6437         *
6438         * @hide
6439         */
6440        public String debug(String output) {
6441            return output + "ViewGroup.LayoutParams={ width="
6442                    + sizeToString(width) + ", height=" + sizeToString(height) + " }";
6443        }
6444
6445        /**
6446         * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
6447         *
6448         * @param view the view that contains these layout parameters
6449         * @param canvas the canvas on which to draw
6450         *
6451         * @hide
6452         */
6453        public void onDebugDraw(View view, Canvas canvas, Paint paint) {
6454        }
6455
6456        /**
6457         * Converts the specified size to a readable String.
6458         *
6459         * @param size the size to convert
6460         * @return a String instance representing the supplied size
6461         *
6462         * @hide
6463         */
6464        protected static String sizeToString(int size) {
6465            if (size == WRAP_CONTENT) {
6466                return "wrap-content";
6467            }
6468            if (size == MATCH_PARENT) {
6469                return "match-parent";
6470            }
6471            return String.valueOf(size);
6472        }
6473    }
6474
6475    /**
6476     * Per-child layout information for layouts that support margins.
6477     * See
6478     * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
6479     * for a list of all child view attributes that this class supports.
6480     */
6481    public static class MarginLayoutParams extends ViewGroup.LayoutParams {
6482        /**
6483         * The left margin in pixels of the child. Margin values should be positive.
6484         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6485         * to this field.
6486         */
6487        @ViewDebug.ExportedProperty(category = "layout")
6488        public int leftMargin;
6489
6490        /**
6491         * The top margin in pixels of the child. Margin values should be positive.
6492         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6493         * to this field.
6494         */
6495        @ViewDebug.ExportedProperty(category = "layout")
6496        public int topMargin;
6497
6498        /**
6499         * The right margin in pixels of the child. Margin values should be positive.
6500         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6501         * to this field.
6502         */
6503        @ViewDebug.ExportedProperty(category = "layout")
6504        public int rightMargin;
6505
6506        /**
6507         * The bottom margin in pixels of the child. Margin values should be positive.
6508         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6509         * to this field.
6510         */
6511        @ViewDebug.ExportedProperty(category = "layout")
6512        public int bottomMargin;
6513
6514        /**
6515         * The start margin in pixels of the child. Margin values should be positive.
6516         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6517         * to this field.
6518         */
6519        @ViewDebug.ExportedProperty(category = "layout")
6520        private int startMargin = DEFAULT_MARGIN_RELATIVE;
6521
6522        /**
6523         * The end margin in pixels of the child. Margin values should be positive.
6524         * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
6525         * to this field.
6526         */
6527        @ViewDebug.ExportedProperty(category = "layout")
6528        private int endMargin = DEFAULT_MARGIN_RELATIVE;
6529
6530        /**
6531         * The default start and end margin.
6532         * @hide
6533         */
6534        public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
6535
6536        /**
6537         * Bit  0: layout direction
6538         * Bit  1: layout direction
6539         * Bit  2: left margin undefined
6540         * Bit  3: right margin undefined
6541         * Bit  4: is RTL compatibility mode
6542         * Bit  5: need resolution
6543         *
6544         * Bit 6 to 7 not used
6545         *
6546         * @hide
6547         */
6548        @ViewDebug.ExportedProperty(category = "layout", flagMapping = {
6549                @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK,
6550                        equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"),
6551                @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK,
6552                        equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"),
6553                @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK,
6554                        equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"),
6555                @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK,
6556                        equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"),
6557                @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK,
6558                        equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK")
6559        }, formatToHexString = true)
6560        byte mMarginFlags;
6561
6562        private static final int LAYOUT_DIRECTION_MASK = 0x00000003;
6563        private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004;
6564        private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008;
6565        private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010;
6566        private static final int NEED_RESOLUTION_MASK = 0x00000020;
6567
6568        private static final int DEFAULT_MARGIN_RESOLVED = 0;
6569        private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
6570
6571        /**
6572         * Creates a new set of layout parameters. The values are extracted from
6573         * the supplied attributes set and context.
6574         *
6575         * @param c the application environment
6576         * @param attrs the set of attributes from which to extract the layout
6577         *              parameters' values
6578         */
6579        public MarginLayoutParams(Context c, AttributeSet attrs) {
6580            super();
6581
6582            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
6583            setBaseAttributes(a,
6584                    R.styleable.ViewGroup_MarginLayout_layout_width,
6585                    R.styleable.ViewGroup_MarginLayout_layout_height);
6586
6587            int margin = a.getDimensionPixelSize(
6588                    com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
6589            if (margin >= 0) {
6590                leftMargin = margin;
6591                topMargin = margin;
6592                rightMargin= margin;
6593                bottomMargin = margin;
6594            } else {
6595                leftMargin = a.getDimensionPixelSize(
6596                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
6597                        UNDEFINED_MARGIN);
6598                if (leftMargin == UNDEFINED_MARGIN) {
6599                    mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
6600                    leftMargin = DEFAULT_MARGIN_RESOLVED;
6601                }
6602                rightMargin = a.getDimensionPixelSize(
6603                        R.styleable.ViewGroup_MarginLayout_layout_marginRight,
6604                        UNDEFINED_MARGIN);
6605                if (rightMargin == UNDEFINED_MARGIN) {
6606                    mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
6607                    rightMargin = DEFAULT_MARGIN_RESOLVED;
6608                }
6609
6610                topMargin = a.getDimensionPixelSize(
6611                        R.styleable.ViewGroup_MarginLayout_layout_marginTop,
6612                        DEFAULT_MARGIN_RESOLVED);
6613                bottomMargin = a.getDimensionPixelSize(
6614                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
6615                        DEFAULT_MARGIN_RESOLVED);
6616
6617                startMargin = a.getDimensionPixelSize(
6618                        R.styleable.ViewGroup_MarginLayout_layout_marginStart,
6619                        DEFAULT_MARGIN_RELATIVE);
6620                endMargin = a.getDimensionPixelSize(
6621                        R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
6622                        DEFAULT_MARGIN_RELATIVE);
6623
6624                if (isMarginRelative()) {
6625                   mMarginFlags |= NEED_RESOLUTION_MASK;
6626                }
6627            }
6628
6629            final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
6630            final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
6631            if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
6632                mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
6633            }
6634
6635            // Layout direction is LTR by default
6636            mMarginFlags |= LAYOUT_DIRECTION_LTR;
6637
6638            a.recycle();
6639        }
6640
6641        /**
6642         * {@inheritDoc}
6643         */
6644        public MarginLayoutParams(int width, int height) {
6645            super(width, height);
6646
6647            mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
6648            mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
6649
6650            mMarginFlags &= ~NEED_RESOLUTION_MASK;
6651            mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
6652        }
6653
6654        /**
6655         * Copy constructor. Clones the width, height and margin values of the source.
6656         *
6657         * @param source The layout params to copy from.
6658         */
6659        public MarginLayoutParams(MarginLayoutParams source) {
6660            this.width = source.width;
6661            this.height = source.height;
6662
6663            this.leftMargin = source.leftMargin;
6664            this.topMargin = source.topMargin;
6665            this.rightMargin = source.rightMargin;
6666            this.bottomMargin = source.bottomMargin;
6667            this.startMargin = source.startMargin;
6668            this.endMargin = source.endMargin;
6669
6670            this.mMarginFlags = source.mMarginFlags;
6671        }
6672
6673        /**
6674         * {@inheritDoc}
6675         */
6676        public MarginLayoutParams(LayoutParams source) {
6677            super(source);
6678
6679            mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
6680            mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
6681
6682            mMarginFlags &= ~NEED_RESOLUTION_MASK;
6683            mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
6684        }
6685
6686        /**
6687         * @hide Used internally.
6688         */
6689        public final void copyMarginsFrom(MarginLayoutParams source) {
6690            this.leftMargin = source.leftMargin;
6691            this.topMargin = source.topMargin;
6692            this.rightMargin = source.rightMargin;
6693            this.bottomMargin = source.bottomMargin;
6694            this.startMargin = source.startMargin;
6695            this.endMargin = source.endMargin;
6696
6697            this.mMarginFlags = source.mMarginFlags;
6698        }
6699
6700        /**
6701         * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
6702         * to be done so that the new margins are taken into account. Left and right margins may be
6703         * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
6704         * Margin values should be positive.
6705         *
6706         * @param left the left margin size
6707         * @param top the top margin size
6708         * @param right the right margin size
6709         * @param bottom the bottom margin size
6710         *
6711         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
6712         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
6713         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
6714         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
6715         */
6716        public void setMargins(int left, int top, int right, int bottom) {
6717            leftMargin = left;
6718            topMargin = top;
6719            rightMargin = right;
6720            bottomMargin = bottom;
6721            mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK;
6722            mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK;
6723            if (isMarginRelative()) {
6724                mMarginFlags |= NEED_RESOLUTION_MASK;
6725            } else {
6726                mMarginFlags &= ~NEED_RESOLUTION_MASK;
6727            }
6728        }
6729
6730        /**
6731         * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
6732         * needs to be done so that the new relative margins are taken into account. Left and right
6733         * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
6734         * direction. Margin values should be positive.
6735         *
6736         * @param start the start margin size
6737         * @param top the top margin size
6738         * @param end the right margin size
6739         * @param bottom the bottom margin size
6740         *
6741         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6742         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
6743         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6744         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
6745         *
6746         * @hide
6747         */
6748        public void setMarginsRelative(int start, int top, int end, int bottom) {
6749            startMargin = start;
6750            topMargin = top;
6751            endMargin = end;
6752            bottomMargin = bottom;
6753            mMarginFlags |= NEED_RESOLUTION_MASK;
6754        }
6755
6756        /**
6757         * Sets the relative start margin. Margin values should be positive.
6758         *
6759         * @param start the start margin size
6760         *
6761         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6762         */
6763        public void setMarginStart(int start) {
6764            startMargin = start;
6765            mMarginFlags |= NEED_RESOLUTION_MASK;
6766        }
6767
6768        /**
6769         * Returns the start margin in pixels.
6770         *
6771         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6772         *
6773         * @return the start margin in pixels.
6774         */
6775        public int getMarginStart() {
6776            if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
6777            if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
6778                doResolveMargins();
6779            }
6780            switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
6781                case View.LAYOUT_DIRECTION_RTL:
6782                    return rightMargin;
6783                case View.LAYOUT_DIRECTION_LTR:
6784                default:
6785                    return leftMargin;
6786            }
6787        }
6788
6789        /**
6790         * Sets the relative end margin. Margin values should be positive.
6791         *
6792         * @param end the end margin size
6793         *
6794         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6795         */
6796        public void setMarginEnd(int end) {
6797            endMargin = end;
6798            mMarginFlags |= NEED_RESOLUTION_MASK;
6799        }
6800
6801        /**
6802         * Returns the end margin in pixels.
6803         *
6804         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6805         *
6806         * @return the end margin in pixels.
6807         */
6808        public int getMarginEnd() {
6809            if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
6810            if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
6811                doResolveMargins();
6812            }
6813            switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
6814                case View.LAYOUT_DIRECTION_RTL:
6815                    return leftMargin;
6816                case View.LAYOUT_DIRECTION_LTR:
6817                default:
6818                    return rightMargin;
6819            }
6820        }
6821
6822        /**
6823         * Check if margins are relative.
6824         *
6825         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
6826         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
6827         *
6828         * @return true if either marginStart or marginEnd has been set.
6829         */
6830        public boolean isMarginRelative() {
6831            return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
6832        }
6833
6834        /**
6835         * Set the layout direction
6836         * @param layoutDirection the layout direction.
6837         *        Should be either {@link View#LAYOUT_DIRECTION_LTR}
6838         *                     or {@link View#LAYOUT_DIRECTION_RTL}.
6839         */
6840        public void setLayoutDirection(int layoutDirection) {
6841            if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
6842                    layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
6843            if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) {
6844                mMarginFlags &= ~LAYOUT_DIRECTION_MASK;
6845                mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK);
6846                if (isMarginRelative()) {
6847                    mMarginFlags |= NEED_RESOLUTION_MASK;
6848                } else {
6849                    mMarginFlags &= ~NEED_RESOLUTION_MASK;
6850                }
6851            }
6852        }
6853
6854        /**
6855         * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
6856         * {@link View#LAYOUT_DIRECTION_RTL}.
6857         *
6858         * @return the layout direction.
6859         */
6860        public int getLayoutDirection() {
6861            return (mMarginFlags & LAYOUT_DIRECTION_MASK);
6862        }
6863
6864        /**
6865         * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
6866         * may be overridden depending on layout direction.
6867         */
6868        @Override
6869        public void resolveLayoutDirection(int layoutDirection) {
6870            setLayoutDirection(layoutDirection);
6871
6872            // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
6873            // Will use the left and right margins if no relative margin is defined.
6874            if (!isMarginRelative() ||
6875                    (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return;
6876
6877            // Proceed with resolution
6878            doResolveMargins();
6879        }
6880
6881        private void doResolveMargins() {
6882            if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) {
6883                // if left or right margins are not defined and if we have some start or end margin
6884                // defined then use those start and end margins.
6885                if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK
6886                        && startMargin > DEFAULT_MARGIN_RELATIVE) {
6887                    leftMargin = startMargin;
6888                }
6889                if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
6890                        && endMargin > DEFAULT_MARGIN_RELATIVE) {
6891                    rightMargin = endMargin;
6892                }
6893            } else {
6894                // We have some relative margins (either the start one or the end one or both). So use
6895                // them and override what has been defined for left and right margins. If either start
6896                // or end margin is not defined, just set it to default "0".
6897                switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
6898                    case View.LAYOUT_DIRECTION_RTL:
6899                        leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
6900                                endMargin : DEFAULT_MARGIN_RESOLVED;
6901                        rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
6902                                startMargin : DEFAULT_MARGIN_RESOLVED;
6903                        break;
6904                    case View.LAYOUT_DIRECTION_LTR:
6905                    default:
6906                        leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
6907                                startMargin : DEFAULT_MARGIN_RESOLVED;
6908                        rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
6909                                endMargin : DEFAULT_MARGIN_RESOLVED;
6910                        break;
6911                }
6912            }
6913            mMarginFlags &= ~NEED_RESOLUTION_MASK;
6914        }
6915
6916        /**
6917         * @hide
6918         */
6919        public boolean isLayoutRtl() {
6920            return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
6921        }
6922
6923        /**
6924         * @hide
6925         */
6926        @Override
6927        public void onDebugDraw(View view, Canvas canvas, Paint paint) {
6928            Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
6929
6930            fillDifference(canvas,
6931                    view.getLeft()   + oi.left,
6932                    view.getTop()    + oi.top,
6933                    view.getRight()  - oi.right,
6934                    view.getBottom() - oi.bottom,
6935                    leftMargin,
6936                    topMargin,
6937                    rightMargin,
6938                    bottomMargin,
6939                    paint);
6940        }
6941    }
6942
6943    /* Describes a touched view and the ids of the pointers that it has captured.
6944     *
6945     * This code assumes that pointer ids are always in the range 0..31 such that
6946     * it can use a bitfield to track which pointer ids are present.
6947     * As it happens, the lower layers of the input dispatch pipeline also use the
6948     * same trick so the assumption should be safe here...
6949     */
6950    private static final class TouchTarget {
6951        private static final int MAX_RECYCLED = 32;
6952        private static final Object sRecycleLock = new Object[0];
6953        private static TouchTarget sRecycleBin;
6954        private static int sRecycledCount;
6955
6956        public static final int ALL_POINTER_IDS = -1; // all ones
6957
6958        // The touched child view.
6959        public View child;
6960
6961        // The combined bit mask of pointer ids for all pointers captured by the target.
6962        public int pointerIdBits;
6963
6964        // The next target in the target list.
6965        public TouchTarget next;
6966
6967        private TouchTarget() {
6968        }
6969
6970        public static TouchTarget obtain(View child, int pointerIdBits) {
6971            final TouchTarget target;
6972            synchronized (sRecycleLock) {
6973                if (sRecycleBin == null) {
6974                    target = new TouchTarget();
6975                } else {
6976                    target = sRecycleBin;
6977                    sRecycleBin = target.next;
6978                     sRecycledCount--;
6979                    target.next = null;
6980                }
6981            }
6982            target.child = child;
6983            target.pointerIdBits = pointerIdBits;
6984            return target;
6985        }
6986
6987        public void recycle() {
6988            synchronized (sRecycleLock) {
6989                if (sRecycledCount < MAX_RECYCLED) {
6990                    next = sRecycleBin;
6991                    sRecycleBin = this;
6992                    sRecycledCount += 1;
6993                } else {
6994                    next = null;
6995                }
6996                child = null;
6997            }
6998        }
6999    }
7000
7001    /* Describes a hovered view. */
7002    private static final class HoverTarget {
7003        private static final int MAX_RECYCLED = 32;
7004        private static final Object sRecycleLock = new Object[0];
7005        private static HoverTarget sRecycleBin;
7006        private static int sRecycledCount;
7007
7008        // The hovered child view.
7009        public View child;
7010
7011        // The next target in the target list.
7012        public HoverTarget next;
7013
7014        private HoverTarget() {
7015        }
7016
7017        public static HoverTarget obtain(View child) {
7018            final HoverTarget target;
7019            synchronized (sRecycleLock) {
7020                if (sRecycleBin == null) {
7021                    target = new HoverTarget();
7022                } else {
7023                    target = sRecycleBin;
7024                    sRecycleBin = target.next;
7025                     sRecycledCount--;
7026                    target.next = null;
7027                }
7028            }
7029            target.child = child;
7030            return target;
7031        }
7032
7033        public void recycle() {
7034            synchronized (sRecycleLock) {
7035                if (sRecycledCount < MAX_RECYCLED) {
7036                    next = sRecycleBin;
7037                    sRecycleBin = this;
7038                    sRecycledCount += 1;
7039                } else {
7040                    next = null;
7041                }
7042                child = null;
7043            }
7044        }
7045    }
7046
7047    /**
7048     * Pooled class that orderes the children of a ViewGroup from start
7049     * to end based on how they are laid out and the layout direction.
7050     */
7051    static class ChildListForAccessibility {
7052
7053        private static final int MAX_POOL_SIZE = 32;
7054
7055        private static final SynchronizedPool<ChildListForAccessibility> sPool =
7056                new SynchronizedPool<ChildListForAccessibility>(MAX_POOL_SIZE);
7057
7058        private final ArrayList<View> mChildren = new ArrayList<View>();
7059
7060        private final ArrayList<ViewLocationHolder> mHolders = new ArrayList<ViewLocationHolder>();
7061
7062        public static ChildListForAccessibility obtain(ViewGroup parent, boolean sort) {
7063            ChildListForAccessibility list = sPool.acquire();
7064            if (list == null) {
7065                list = new ChildListForAccessibility();
7066            }
7067            list.init(parent, sort);
7068            return list;
7069        }
7070
7071        public void recycle() {
7072            clear();
7073            sPool.release(this);
7074        }
7075
7076        public int getChildCount() {
7077            return mChildren.size();
7078        }
7079
7080        public View getChildAt(int index) {
7081            return mChildren.get(index);
7082        }
7083
7084        public int getChildIndex(View child) {
7085            return mChildren.indexOf(child);
7086        }
7087
7088        private void init(ViewGroup parent, boolean sort) {
7089            ArrayList<View> children = mChildren;
7090            final int childCount = parent.getChildCount();
7091            for (int i = 0; i < childCount; i++) {
7092                View child = parent.getChildAt(i);
7093                children.add(child);
7094            }
7095            if (sort) {
7096                ArrayList<ViewLocationHolder> holders = mHolders;
7097                for (int i = 0; i < childCount; i++) {
7098                    View child = children.get(i);
7099                    ViewLocationHolder holder = ViewLocationHolder.obtain(parent, child);
7100                    holders.add(holder);
7101                }
7102                sort(holders);
7103                for (int i = 0; i < childCount; i++) {
7104                    ViewLocationHolder holder = holders.get(i);
7105                    children.set(i, holder.mView);
7106                    holder.recycle();
7107                }
7108                holders.clear();
7109            }
7110        }
7111
7112        private void sort(ArrayList<ViewLocationHolder> holders) {
7113            // This is gross but the least risky solution. The current comparison
7114            // strategy breaks transitivity but produces very good results. Coming
7115            // up with a new strategy requires time which we do not have, so ...
7116            try {
7117                ViewLocationHolder.setComparisonStrategy(
7118                        ViewLocationHolder.COMPARISON_STRATEGY_STRIPE);
7119                Collections.sort(holders);
7120            } catch (IllegalArgumentException iae) {
7121                // Note that in practice this occurs extremely rarely in a couple
7122                // of pathological cases.
7123                ViewLocationHolder.setComparisonStrategy(
7124                        ViewLocationHolder.COMPARISON_STRATEGY_LOCATION);
7125                Collections.sort(holders);
7126            }
7127        }
7128
7129        private void clear() {
7130            mChildren.clear();
7131        }
7132    }
7133
7134    /**
7135     * Pooled class that holds a View and its location with respect to
7136     * a specified root. This enables sorting of views based on their
7137     * coordinates without recomputing the position relative to the root
7138     * on every comparison.
7139     */
7140    static class ViewLocationHolder implements Comparable<ViewLocationHolder> {
7141
7142        private static final int MAX_POOL_SIZE = 32;
7143
7144        private static final SynchronizedPool<ViewLocationHolder> sPool =
7145                new SynchronizedPool<ViewLocationHolder>(MAX_POOL_SIZE);
7146
7147        public static final int COMPARISON_STRATEGY_STRIPE = 1;
7148
7149        public static final int COMPARISON_STRATEGY_LOCATION = 2;
7150
7151        private static int sComparisonStrategy = COMPARISON_STRATEGY_STRIPE;
7152
7153        private final Rect mLocation = new Rect();
7154
7155        public View mView;
7156
7157        private int mLayoutDirection;
7158
7159        public static ViewLocationHolder obtain(ViewGroup root, View view) {
7160            ViewLocationHolder holder = sPool.acquire();
7161            if (holder == null) {
7162                holder = new ViewLocationHolder();
7163            }
7164            holder.init(root, view);
7165            return holder;
7166        }
7167
7168        public static void setComparisonStrategy(int strategy) {
7169            sComparisonStrategy = strategy;
7170        }
7171
7172        public void recycle() {
7173            clear();
7174            sPool.release(this);
7175        }
7176
7177        @Override
7178        public int compareTo(ViewLocationHolder another) {
7179            // This instance is greater than an invalid argument.
7180            if (another == null) {
7181                return 1;
7182            }
7183
7184            if (sComparisonStrategy == COMPARISON_STRATEGY_STRIPE) {
7185                // First is above second.
7186                if (mLocation.bottom - another.mLocation.top <= 0) {
7187                    return -1;
7188                }
7189                // First is below second.
7190                if (mLocation.top - another.mLocation.bottom >= 0) {
7191                    return 1;
7192                }
7193            }
7194
7195            // We are ordering left-to-right, top-to-bottom.
7196            if (mLayoutDirection == LAYOUT_DIRECTION_LTR) {
7197                final int leftDifference = mLocation.left - another.mLocation.left;
7198                if (leftDifference != 0) {
7199                    return leftDifference;
7200                }
7201            } else { // RTL
7202                final int rightDifference = mLocation.right - another.mLocation.right;
7203                if (rightDifference != 0) {
7204                    return -rightDifference;
7205                }
7206            }
7207            // We are ordering left-to-right, top-to-bottom.
7208            final int topDifference = mLocation.top - another.mLocation.top;
7209            if (topDifference != 0) {
7210                return topDifference;
7211            }
7212            // Break tie by height.
7213            final int heightDiference = mLocation.height() - another.mLocation.height();
7214            if (heightDiference != 0) {
7215                return -heightDiference;
7216            }
7217            // Break tie by width.
7218            final int widthDiference = mLocation.width() - another.mLocation.width();
7219            if (widthDiference != 0) {
7220                return -widthDiference;
7221            }
7222            // Just break the tie somehow. The accessibliity ids are unique
7223            // and stable, hence this is deterministic tie breaking.
7224            return mView.getAccessibilityViewId() - another.mView.getAccessibilityViewId();
7225        }
7226
7227        private void init(ViewGroup root, View view) {
7228            Rect viewLocation = mLocation;
7229            view.getDrawingRect(viewLocation);
7230            root.offsetDescendantRectToMyCoords(view, viewLocation);
7231            mView = view;
7232            mLayoutDirection = root.getLayoutDirection();
7233        }
7234
7235        private void clear() {
7236            mView = null;
7237            mLocation.set(0, 0, 0, 0);
7238        }
7239    }
7240
7241    private static Paint getDebugPaint() {
7242        if (sDebugPaint == null) {
7243            sDebugPaint = new Paint();
7244            sDebugPaint.setAntiAlias(false);
7245        }
7246        return sDebugPaint;
7247    }
7248
7249    private static void drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2) {
7250        if (sDebugLines== null) {
7251            // TODO: This won't work with multiple UI threads in a single process
7252            sDebugLines = new float[16];
7253        }
7254
7255        sDebugLines[0] = x1;
7256        sDebugLines[1] = y1;
7257        sDebugLines[2] = x2;
7258        sDebugLines[3] = y1;
7259
7260        sDebugLines[4] = x2;
7261        sDebugLines[5] = y1;
7262        sDebugLines[6] = x2;
7263        sDebugLines[7] = y2;
7264
7265        sDebugLines[8] = x2;
7266        sDebugLines[9] = y2;
7267        sDebugLines[10] = x1;
7268        sDebugLines[11] = y2;
7269
7270        sDebugLines[12] = x1;
7271        sDebugLines[13] = y2;
7272        sDebugLines[14] = x1;
7273        sDebugLines[15] = y1;
7274
7275        canvas.drawLines(sDebugLines, paint);
7276    }
7277}
7278