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