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