• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ViewGroup(Context context)458     public ViewGroup(Context context) {
459         super(context);
460         initViewGroup();
461     }
462 
ViewGroup(Context context, AttributeSet attrs)463     public ViewGroup(Context context, AttributeSet attrs) {
464         super(context, attrs);
465         initViewGroup();
466         initFromAttributes(context, attrs);
467     }
468 
ViewGroup(Context context, AttributeSet attrs, int defStyle)469     public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
470         super(context, attrs, defStyle);
471         initViewGroup();
472         initFromAttributes(context, attrs);
473     }
474 
debugDraw()475     private boolean debugDraw() {
476         return DEBUG_DRAW || mAttachInfo != null && mAttachInfo.mDebugLayout;
477     }
478 
initViewGroup()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 
initFromAttributes(Context context, AttributeSet attrs)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     })
getDescendantFocusability()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      */
setDescendantFocusability(int focusability)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
handleFocusGainInternal(int direction, Rect previouslyFocusedRect)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      */
requestChildFocus(View child, View focused)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      */
focusableViewAvailable(View v)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      */
showContextMenuForChild(View originalView)657     public boolean showContextMenuForChild(View originalView) {
658         return mParent != null && mParent.showContextMenuForChild(originalView);
659     }
660 
661     /**
662      * {@inheritDoc}
663      */
startActionModeForChild(View originalView, ActionMode.Callback callback)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      */
focusSearch(View focused, int direction)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      */
requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate)691     public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
692         return false;
693     }
694 
695     /**
696      * {@inheritDoc}
697      */
698     @Override
requestSendAccessibilityEvent(View child, AccessibilityEvent event)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      */
onRequestSendAccessibilityEvent(View child, AccessibilityEvent event)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      */
onRequestSendAccessibilityEventInternal(View child, AccessibilityEvent event)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      */
childHasTransientStateChanged(View child, boolean childHasTransientState)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
hasTransientState()767     public boolean hasTransientState() {
768         return mChildCountWithTransientState > 0 || super.hasTransientState();
769     }
770 
771     /**
772      * {@inheritDoc}
773      */
774     @Override
dispatchUnhandledMove(View focused, int direction)775     public boolean dispatchUnhandledMove(View focused, int direction) {
776         return mFocused != null &&
777                 mFocused.dispatchUnhandledMove(focused, direction);
778     }
779 
780     /**
781      * {@inheritDoc}
782      */
clearChildFocus(View child)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
clearFocus()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
unFocus()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      */
getFocusedChild()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
hasFocus()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
findFocus()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
hasFocusable()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
addFocusables(ArrayList<View> views, int direction, int focusableMode)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
findViewsWithText(ArrayList<View> outViews, CharSequence text, int flags)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
findViewByAccessibilityIdTraversal(int accessibilityId)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
dispatchWindowFocusChanged(boolean hasFocus)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
addTouchables(ArrayList<View> views)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
makeOptionalFitsSystemWindows()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
dispatchDisplayHint(int hint)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      */
onChildVisibilityChanged(View child, int oldVisibility, int newVisibility)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
dispatchVisibilityChanged(View changedView, int visibility)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
dispatchWindowVisibilityChanged(int visibility)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
dispatchConfigurationChanged(Configuration newConfig)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      */
recomputeViewAttributes(View child)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
dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility)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      */
bringChildToFront(View child)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 
getLocalPoint()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
dispatchDragEvent(DragEvent event)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.
findFrontmostDroppableChildAt(float x, float y, PointF outLocalPoint)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 
notifyChildOfDrag(View child)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
dispatchWindowSystemUiVisiblityChanged(int visible)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
dispatchSystemUiVisibilityChanged(int visible)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
updateLocalSystemUiVisibility(int localValue, int localChanges)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
dispatchKeyEventPreIme(KeyEvent event)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
dispatchKeyEvent(KeyEvent event)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
dispatchKeyShortcutEvent(KeyEvent event)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
dispatchTrackballEvent(MotionEvent event)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
dispatchHoverEvent(MotionEvent event)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 
exitHoverTargets()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 
cancelHoverTarget(View view)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
hasHoveredChild()1683     protected boolean hasHoveredChild() {
1684         return mFirstHoverTarget != null;
1685     }
1686 
1687     @Override
addChildrenForAccessibility(ArrayList<View> childrenForAccessibility)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      */
onInterceptHoverEvent(MotionEvent event)1745     public boolean onInterceptHoverEvent(MotionEvent event) {
1746         return false;
1747     }
1748 
obtainMotionEventNoHistoryOrSelf(MotionEvent event)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
dispatchGenericPointerEvent(MotionEvent event)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
dispatchGenericFocusedEvent(MotionEvent event)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      */
dispatchTransformedGenericPointerEvent(MotionEvent event, View child)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
dispatchTouchEvent(MotionEvent ev)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      */
resetTouchState()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      */
resetCancelNextUpFlag(View view)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      */
clearTouchTargets()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      */
cancelAndClearTouchTargets(MotionEvent event)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      */
getTouchTarget(View child)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      */
addTouchTarget(View child, int pointerIdBits)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      */
removePointersFromTouchTargets(int pointerIdBits)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 
cancelTouchTarget(View view)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      */
canViewReceivePointerEvents(View child)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      */
isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint)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      */
dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits)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      */
setMotionEventSplittingEnabled(boolean split)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      */
isMotionEventSplittingEnabled()2276     public boolean isMotionEventSplittingEnabled() {
2277         return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
2278     }
2279 
2280     /**
2281      * {@inheritDoc}
2282      */
requestDisallowInterceptTouchEvent(boolean disallowIntercept)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      */
onInterceptTouchEvent(MotionEvent ev)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
requestFocus(int direction, Rect previouslyFocusedRect)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"})
onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)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
dispatchStartTemporaryDetach()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
dispatchFinishTemporaryDetach()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
dispatchAttachedToWindow(AttachInfo info, int visibility)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
dispatchScreenStateChanged(int screenState)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
dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)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
onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info)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
onInitializeAccessibilityEventInternal(AccessibilityEvent event)2526     void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
2527         super.onInitializeAccessibilityEventInternal(event);
2528         event.setClassName(ViewGroup.class.getName());
2529     }
2530 
2531     @Override
notifySubtreeAccessibilityStateChanged(View child, View source, int changeType)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
resetSubtreeAccessibilityStateChanged()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
dispatchDetachedFromWindow()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
internalSetPadding(int left, int top, int right, int bottom)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
dispatchSaveInstanceState(SparseArray<Parcelable> container)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      */
dispatchFreezeSelfOnly(SparseArray<Parcelable> container)2628     protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
2629         super.dispatchSaveInstanceState(container);
2630     }
2631 
2632     /**
2633      * {@inheritDoc}
2634      */
2635     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)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      */
dispatchThawSelfOnly(SparseArray<Parcelable> container)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      */
setChildrenDrawingCacheEnabled(boolean enabled)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
onAnimationStart()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
onAnimationEnd()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
createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren)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. */
isLayoutModeOptical()2741     boolean isLayoutModeOptical() {
2742         return mLayoutMode == LAYOUT_MODE_OPTICAL_BOUNDS;
2743     }
2744 
computeOpticalInsets()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 
fillRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2)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 
sign(int x)2779     private static int sign(int x) {
2780         return (x >= 0) ? 1 : -1;
2781     }
2782 
drawCorner(Canvas c, Paint paint, int x1, int y1, int dx, int dy, int lw)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 
dipsToPixels(int dips)2788     private int dipsToPixels(int dips) {
2789         float scale = getContext().getResources().getDisplayMetrics().density;
2790         return (int) (dips * scale + 0.5f);
2791     }
2792 
drawRectCorners(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint, int lineLength, int lineWidth)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 
fillDifference(Canvas canvas, int x2, int y2, int x3, int y3, int dx1, int dy1, int dx2, int dy2, Paint paint)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      */
onDebugDrawMargins(Canvas canvas, Paint paint)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      */
onDebugDraw(Canvas canvas)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
dispatchDraw(Canvas canvas)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
getOverlay()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      */
getChildDrawingOrder(int childCount, int i)3031     protected int getChildDrawingOrder(int childCount, int i) {
3032         return i;
3033     }
3034 
notifyAnimationListener()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
dispatchGetDisplayList()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      */
drawChild(Canvas canvas, View child, long drawingTime)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      */
getClipChildren()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      */
setClipChildren(boolean clipChildren)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      */
setClipToPadding(boolean clipToPadding)3147     public void setClipToPadding(boolean clipToPadding) {
3148         setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
3149     }
3150 
3151     /**
3152      * {@inheritDoc}
3153      */
3154     @Override
dispatchSetSelected(boolean selected)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
dispatchSetActivated(boolean activated)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
dispatchSetPressed(boolean pressed)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
dispatchCancelPendingInputEvents()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      */
setStaticTransformationsEnabled(boolean enabled)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      */
getChildStaticTransformation(View child, Transformation t)3231     protected boolean getChildStaticTransformation(View child, Transformation t) {
3232         return false;
3233     }
3234 
getChildTransformation()3235     Transformation getChildTransformation() {
3236         if (mChildTransformation == null) {
3237             mChildTransformation = new Transformation();
3238         }
3239         return mChildTransformation;
3240     }
3241 
3242     /**
3243      * {@hide}
3244      */
3245     @Override
findViewTraversal(int id)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
findViewWithTagTraversal(Object tag)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
findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip)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      */
addView(View child)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      */
addView(View child, int index)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      */
addView(View child, int width, int height)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      */
addView(View child, LayoutParams params)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      */
addView(View child, int index, LayoutParams params)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      */
updateViewLayout(View view, ViewGroup.LayoutParams params)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      */
checkLayoutParams(ViewGroup.LayoutParams p)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          */
onChildViewAdded(View parent, View child)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          */
onChildViewRemoved(View parent, View child)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      */
setOnHierarchyChangeListener(OnHierarchyChangeListener listener)3467     public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
3468         mOnHierarchyChangeListener = listener;
3469     }
3470 
3471     /**
3472      * @hide
3473      */
onViewAdded(View child)3474     protected void onViewAdded(View child) {
3475         if (mOnHierarchyChangeListener != null) {
3476             mOnHierarchyChangeListener.onChildViewAdded(this, child);
3477         }
3478     }
3479 
3480     /**
3481      * @hide
3482      */
onViewRemoved(View child)3483     protected void onViewRemoved(View child) {
3484         if (mOnHierarchyChangeListener != null) {
3485             mOnHierarchyChangeListener.onChildViewRemoved(this, child);
3486         }
3487     }
3488 
clearCachedLayoutMode()3489     private void clearCachedLayoutMode() {
3490         if (!hasBooleanFlag(FLAG_LAYOUT_MODE_WAS_EXPLICITLY_SET)) {
3491            mLayoutMode = LAYOUT_MODE_UNDEFINED;
3492         }
3493     }
3494 
3495     @Override
onAttachedToWindow()3496     protected void onAttachedToWindow() {
3497         super.onAttachedToWindow();
3498         clearCachedLayoutMode();
3499     }
3500 
3501     @Override
onDetachedFromWindow()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      */
addViewInLayout(View child, int index, LayoutParams params)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      */
addViewInLayout(View child, int index, LayoutParams params, boolean preventRequestLayout)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      */
cleanupLayoutState(View child)3548     protected void cleanupLayoutState(View child) {
3549         child.mPrivateFlags &= ~View.PFLAG_FORCE_LAYOUT;
3550     }
3551 
addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout)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 
addInArray(View child, int index)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
removeFromArray(int index)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
removeFromArray(int start, int count)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 
bindLayoutAnimation(View child)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      */
attachLayoutAnimationParameters(View child, LayoutParams params, int index, int count)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      */
removeView(View view)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      */
removeViewInLayout(View view)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      */
removeViewsInLayout(int start, int count)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      */
removeViewAt(int index)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      */
removeViews(int start, int count)3809     public void removeViews(int start, int count) {
3810         removeViewsInternal(start, count);
3811         requestLayout();
3812         invalidate(true);
3813     }
3814 
removeViewInternal(View view)3815     private void removeViewInternal(View view) {
3816         final int index = indexOfChild(view);
3817         if (index >= 0) {
3818             removeViewInternal(index, view);
3819         }
3820     }
3821 
removeViewInternal(int index, View view)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      */
setLayoutTransition(LayoutTransition transition)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      */
getLayoutTransition()3905     public LayoutTransition getLayoutTransition() {
3906         return mTransition;
3907     }
3908 
removeViewsInternal(int start, int count)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      */
removeAllViews()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      */
removeAllViewsInLayout()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      */
removeDetachedView(View child, boolean animate)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      */
attachViewToParent(View child, int index, LayoutParams params)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      */
detachViewFromParent(View child)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      */
detachViewFromParent(int index)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      */
detachViewsFromParent(int start, int count)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      */
detachAllViewsFromParent()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      */
invalidateChild(View child, final Rect dirty)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      */
invalidateChildInParent(final int[] location, final Rect dirty)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      */
invalidateChildFast(View child, final Rect dirty)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      */
invalidateChildInParentFast(int left, int top, final Rect dirty)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      */
offsetDescendantRectToMyCoords(View descendant, Rect rect)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      */
offsetRectIntoDescendantCoords(View descendant, Rect rect)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      */
offsetRectBetweenParentAndChild(View descendant, Rect rect, boolean offsetFromChildToParent, boolean clipToBounds)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      */
offsetChildrenTopAndBottom(int offset)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      */
getChildVisibleRect(View child, Rect r, android.graphics.Point offset)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
layout(int l, int t, int r, int b)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
onLayout(boolean changed, int l, int t, int r, int b)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      */
canAnimate()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      */
startLayoutAnimation()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      */
scheduleLayoutAnimation()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      */
setLayoutAnimation(LayoutAnimationController controller)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      */
getLayoutAnimation()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
isAnimationCacheEnabled()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      */
setAnimationCacheEnabled(boolean enabled)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")
isAlwaysDrawnWithCacheEnabled()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      */
setAlwaysDrawnWithCacheEnabled(boolean always)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")
isChildrenDrawnWithCacheEnabled()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      */
setChildrenDrawnWithCacheEnabled(boolean enabled)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")
isChildrenDrawingOrderEnabled()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      */
setChildrenDrawingOrderEnabled(boolean enabled)4822     protected void setChildrenDrawingOrderEnabled(boolean enabled) {
4823         setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
4824     }
4825 
hasBooleanFlag(int flag)4826     private boolean hasBooleanFlag(int flag) {
4827         return (mGroupFlags & flag) == flag;
4828     }
4829 
setBooleanFlag(int flag, boolean value)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     })
getPersistentDrawingCache()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      */
setPersistentDrawingCache(int drawingCacheToKeep)4869     public void setPersistentDrawingCache(int drawingCacheToKeep) {
4870         mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
4871     }
4872 
setLayoutMode(int layoutMode, boolean explicitly)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
invalidateInheritedLayoutMode(int layoutModeOfRoot)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      */
getLayoutMode()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      */
setLayoutMode(int layoutMode)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      */
generateLayoutParams(AttributeSet attrs)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      */
generateLayoutParams(ViewGroup.LayoutParams p)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      */
generateDefaultLayoutParams()4981     protected LayoutParams generateDefaultLayoutParams() {
4982         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
4983     }
4984 
4985     /**
4986      * {@inheritDoc}
4987      */
4988     @Override
debug(int depth)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      */
indexOfChild(View child)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      */
getChildCount()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      */
getChildAt(int index)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      */
measureChildren(int widthMeasureSpec, int heightMeasureSpec)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      */
measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)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      */
measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)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      */
getChildMeasureSpec(int spec, int padding, int childDimension)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      */
clearDisappearingChildren()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      */
addDisappearingView(View v)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      */
finishAnimatingView(final View view, Animation animation)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      */
isViewTransitioning(View view)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      */
startViewTransition(View view)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      */
endViewTransition(View view)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      */
suppressLayout(boolean suppress)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      */
isLayoutSuppressed()5391     public boolean isLayoutSuppressed() {
5392         return mSuppressLayout;
5393     }
5394 
5395     /**
5396      * {@inheritDoc}
5397      */
5398     @Override
gatherTransparentRegion(Region region)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      */
requestTransparentRegion(View child)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
fitSystemWindows(Rect insets)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      */
getLayoutAnimationListener()5456     public Animation.AnimationListener getLayoutAnimationListener() {
5457         return mAnimationListener;
5458     }
5459 
5460     @Override
drawableStateChanged()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
jumpDrawablesToCurrentState()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
onCreateDrawableState(int extraSpace)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      */
setAddStatesFromChildren(boolean addsStates)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      */
addStatesFromChildren()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      */
childDrawableStateChanged(View child)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      */
setLayoutAnimationListener(Animation.AnimationListener animationListener)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      */
requestTransitionStart(LayoutTransition transition)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
resolveRtlPropertiesIfNeeded()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
resolveLayoutDirection()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
resolveTextDirection()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
resolveTextAlignment()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
resolvePadding()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
resolveDrawables()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
resolveLayoutParams()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
resetResolvedLayoutDirection()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
resetResolvedTextDirection()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
resetResolvedTextAlignment()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
resetResolvedPadding()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
resetResolvedDrawables()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      */
shouldDelayChildPressedState()5792     public boolean shouldDelayChildPressedState() {
5793         return true;
5794     }
5795 
5796     /** @hide */
onSetLayoutParams(View child, LayoutParams layoutParams)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          */
LayoutParams(Context c, AttributeSet attrs)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          */
LayoutParams(int width, int height)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          */
LayoutParams(LayoutParams source)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          */
LayoutParams()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          */
setBaseAttributes(TypedArray a, int widthAttr, int heightAttr)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          */
resolveLayoutDirection(int layoutDirection)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          */
debug(String output)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          */
onDebugDraw(View view, Canvas canvas, Paint paint)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          */
sizeToString(int size)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          */
MarginLayoutParams(Context c, AttributeSet attrs)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          */
MarginLayoutParams(int width, int height)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          */
MarginLayoutParams(MarginLayoutParams source)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          */
MarginLayoutParams(LayoutParams source)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          */
setMargins(int left, int top, int right, int bottom)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          */
setMarginsRelative(int start, int top, int end, int bottom)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          */
setMarginStart(int start)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          */
getMarginStart()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          */
setMarginEnd(int end)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          */
getMarginEnd()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          */
isMarginRelative()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          */
setLayoutDirection(int layoutDirection)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          */
getLayoutDirection()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
resolveLayoutDirection(int layoutDirection)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 
doResolveMargins()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          */
isLayoutRtl()6438         public boolean isLayoutRtl() {
6439             return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
6440         }
6441 
6442         /**
6443          * @hide
6444          */
6445         @Override
onDebugDraw(View view, Canvas canvas, Paint paint)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 
TouchTarget()6486         private TouchTarget() {
6487         }
6488 
obtain(View child, int pointerIdBits)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 
recycle()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 
HoverTarget()6533         private HoverTarget() {
6534         }
6535 
obtain(View child)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 
recycle()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 
obtain(ViewGroup parent, boolean sort)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 
recycle()6590         public void recycle() {
6591             clear();
6592             sPool.release(this);
6593         }
6594 
getChildCount()6595         public int getChildCount() {
6596             return mChildren.size();
6597         }
6598 
getChildAt(int index)6599         public View getChildAt(int index) {
6600             return mChildren.get(index);
6601         }
6602 
getChildIndex(View child)6603         public int getChildIndex(View child) {
6604             return mChildren.indexOf(child);
6605         }
6606 
init(ViewGroup parent, boolean sort)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 
clear()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 
obtain(ViewGroup root, View view)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 
recycle()6664         public void recycle() {
6665             clear();
6666             sPool.release(this);
6667         }
6668 
6669         @Override
compareTo(ViewLocationHolder another)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 
init(ViewGroup root, View view)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 
clear()6728         private void clear() {
6729             mView = null;
6730             mLocation.set(0, 0, 0, 0);
6731         }
6732     }
6733 
getDebugPaint()6734     private static Paint getDebugPaint() {
6735         if (sDebugPaint == null) {
6736             sDebugPaint = new Paint();
6737             sDebugPaint.setAntiAlias(false);
6738         }
6739         return sDebugPaint;
6740     }
6741 
drawRect(Canvas canvas, Paint paint, int x1, int y1, int x2, int y2)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