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