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