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