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