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