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