ViewGroup.java revision 2b342f0a76f3237e97f15dc2f4e8a0b72dd7c023
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import com.android.internal.R;
20
21import android.content.Context;
22import android.content.res.Configuration;
23import android.content.res.TypedArray;
24import android.graphics.Bitmap;
25import android.graphics.Canvas;
26import android.graphics.Matrix;
27import android.graphics.Paint;
28import android.graphics.Rect;
29import android.graphics.RectF;
30import android.graphics.Region;
31import android.os.Parcelable;
32import android.os.SystemClock;
33import android.util.AttributeSet;
34import android.util.Config;
35import android.util.EventLog;
36import android.util.Log;
37import android.util.SparseArray;
38import android.view.ViewGroup.LayoutParams;
39import android.view.accessibility.AccessibilityEvent;
40import android.view.animation.Animation;
41import android.view.animation.AnimationUtils;
42import android.view.animation.LayoutAnimationController;
43import android.view.animation.Transformation;
44
45import java.util.ArrayList;
46import java.util.Arrays;
47
48/**
49 * <p>
50 * A <code>ViewGroup</code> is a special view that can contain other views
51 * (called children.) The view group is the base class for layouts and views
52 * containers. This class also defines the
53 * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
54 * class for layouts parameters.
55 * </p>
56 *
57 * <p>
58 * Also see {@link LayoutParams} for layout attributes.
59 * </p>
60 *
61 * @attr ref android.R.styleable#ViewGroup_clipChildren
62 * @attr ref android.R.styleable#ViewGroup_clipToPadding
63 * @attr ref android.R.styleable#ViewGroup_layoutAnimation
64 * @attr ref android.R.styleable#ViewGroup_animationCache
65 * @attr ref android.R.styleable#ViewGroup_persistentDrawingCache
66 * @attr ref android.R.styleable#ViewGroup_alwaysDrawnWithCache
67 * @attr ref android.R.styleable#ViewGroup_addStatesFromChildren
68 * @attr ref android.R.styleable#ViewGroup_descendantFocusability
69 */
70public abstract class ViewGroup extends View implements ViewParent, ViewManager {
71    private static final boolean DBG = false;
72
73    /**
74     * Views which have been hidden or removed which need to be animated on
75     * their way out.
76     * This field should be made private, so it is hidden from the SDK.
77     * {@hide}
78     */
79    protected ArrayList<View> mDisappearingChildren;
80
81    /**
82     * Listener used to propagate events indicating when children are added
83     * and/or removed from a view group.
84     * This field should be made private, so it is hidden from the SDK.
85     * {@hide}
86     */
87    protected OnHierarchyChangeListener mOnHierarchyChangeListener;
88
89    // The view contained within this ViewGroup that has or contains focus.
90    private View mFocused;
91
92    /**
93     * A Transformation used when drawing children, to
94     * apply on the child being drawn.
95     */
96    private final Transformation mChildTransformation = new Transformation();
97
98    /**
99     * Used to track the current invalidation region.
100     */
101    private RectF mInvalidateRegion;
102
103    /**
104     * A Transformation used to calculate a correct
105     * invalidation area when the application is autoscaled.
106     */
107    private Transformation mInvalidationTransformation;
108
109    // Target of Motion events
110    private View mMotionTarget;
111
112    // Targets of MotionEvents in split mode
113    private SplitMotionTargets mSplitMotionTargets;
114
115    // Layout animation
116    private LayoutAnimationController mLayoutAnimationController;
117    private Animation.AnimationListener mAnimationListener;
118
119    /**
120     * Internal flags.
121     *
122     * This field should be made private, so it is hidden from the SDK.
123     * {@hide}
124     */
125    protected int mGroupFlags;
126
127    // When set, ViewGroup invalidates only the child's rectangle
128    // Set by default
129    private static final int FLAG_CLIP_CHILDREN = 0x1;
130
131    // When set, ViewGroup excludes the padding area from the invalidate rectangle
132    // Set by default
133    private static final int FLAG_CLIP_TO_PADDING = 0x2;
134
135    // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
136    // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
137    private static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
138
139    // When set, dispatchDraw() will run the layout animation and unset the flag
140    private static final int FLAG_RUN_ANIMATION = 0x8;
141
142    // When set, there is either no layout animation on the ViewGroup or the layout
143    // animation is over
144    // Set by default
145    private static final int FLAG_ANIMATION_DONE = 0x10;
146
147    // If set, this ViewGroup has padding; if unset there is no padding and we don't need
148    // to clip it, even if FLAG_CLIP_TO_PADDING is set
149    private static final int FLAG_PADDING_NOT_NULL = 0x20;
150
151    // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
152    // Set by default
153    private static final int FLAG_ANIMATION_CACHE = 0x40;
154
155    // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
156    // layout animation; this avoid clobbering the hierarchy
157    // Automatically set when the layout animation starts, depending on the animation's
158    // characteristics
159    private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
160
161    // When set, the next call to drawChild() will clear mChildTransformation's matrix
162    private static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
163
164    // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
165    // the children's Bitmap caches if necessary
166    // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
167    private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
168
169    /**
170     * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
171     * to get the index of the child to draw for that iteration.
172     *
173     * @hide
174     */
175    protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
176
177    /**
178     * When set, this ViewGroup supports static transformations on children; this causes
179     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
180     * invoked when a child is drawn.
181     *
182     * Any subclass overriding
183     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
184     * set this flags in {@link #mGroupFlags}.
185     *
186     * {@hide}
187     */
188    protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
189
190    // When the previous drawChild() invocation used an alpha value that was lower than
191    // 1.0 and set it in mCachePaint
192    private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
193
194    /**
195     * When set, this ViewGroup's drawable states also include those
196     * of its children.
197     */
198    private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
199
200    /**
201     * When set, this ViewGroup tries to always draw its children using their drawing cache.
202     */
203    private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
204
205    /**
206     * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
207     * draw its children with their drawing cache.
208     */
209    private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
210
211    /**
212     * When set, this group will go through its list of children to notify them of
213     * any drawable state change.
214     */
215    private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
216
217    private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
218
219    /**
220     * This view will get focus before any of its descendants.
221     */
222    public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
223
224    /**
225     * This view will get focus only if none of its descendants want it.
226     */
227    public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
228
229    /**
230     * This view will block any of its descendants from getting focus, even
231     * if they are focusable.
232     */
233    public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
234
235    /**
236     * Used to map between enum in attrubutes and flag values.
237     */
238    private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
239            {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
240                    FOCUS_BLOCK_DESCENDANTS};
241
242    /**
243     * When set, this ViewGroup should not intercept touch events.
244     * {@hide}
245     */
246    protected static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
247
248    /**
249     * When set, this ViewGroup will split MotionEvents to multiple child Views when appropriate.
250     */
251    private static final int FLAG_SPLIT_MOTION_EVENTS = 0x100000;
252
253    /**
254     * Indicates which types of drawing caches are to be kept in memory.
255     * This field should be made private, so it is hidden from the SDK.
256     * {@hide}
257     */
258    protected int mPersistentDrawingCache;
259
260    /**
261     * Used to indicate that no drawing cache should be kept in memory.
262     */
263    public static final int PERSISTENT_NO_CACHE = 0x0;
264
265    /**
266     * Used to indicate that the animation drawing cache should be kept in memory.
267     */
268    public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
269
270    /**
271     * Used to indicate that the scrolling drawing cache should be kept in memory.
272     */
273    public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
274
275    /**
276     * Used to indicate that all drawing caches should be kept in memory.
277     */
278    public static final int PERSISTENT_ALL_CACHES = 0x3;
279
280    /**
281     * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
282     * are set at the same time.
283     */
284    protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
285
286    // Index of the child's left position in the mLocation array
287    private static final int CHILD_LEFT_INDEX = 0;
288    // Index of the child's top position in the mLocation array
289    private static final int CHILD_TOP_INDEX = 1;
290
291    // Child views of this ViewGroup
292    private View[] mChildren;
293    // Number of valid children in the mChildren array, the rest should be null or not
294    // considered as children
295    private int mChildrenCount;
296
297    private static final int ARRAY_INITIAL_CAPACITY = 12;
298    private static final int ARRAY_CAPACITY_INCREMENT = 12;
299
300    // Used to draw cached views
301    private final Paint mCachePaint = new Paint();
302
303    public ViewGroup(Context context) {
304        super(context);
305        initViewGroup();
306    }
307
308    public ViewGroup(Context context, AttributeSet attrs) {
309        super(context, attrs);
310        initViewGroup();
311        initFromAttributes(context, attrs);
312    }
313
314    public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
315        super(context, attrs, defStyle);
316        initViewGroup();
317        initFromAttributes(context, attrs);
318    }
319
320    private void initViewGroup() {
321        // ViewGroup doesn't draw by default
322        setFlags(WILL_NOT_DRAW, DRAW_MASK);
323        mGroupFlags |= FLAG_CLIP_CHILDREN;
324        mGroupFlags |= FLAG_CLIP_TO_PADDING;
325        mGroupFlags |= FLAG_ANIMATION_DONE;
326        mGroupFlags |= FLAG_ANIMATION_CACHE;
327        mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
328
329        setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
330
331        mChildren = new View[ARRAY_INITIAL_CAPACITY];
332        mChildrenCount = 0;
333
334        mCachePaint.setDither(false);
335
336        mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
337    }
338
339    private void initFromAttributes(Context context, AttributeSet attrs) {
340        TypedArray a = context.obtainStyledAttributes(attrs,
341                R.styleable.ViewGroup);
342
343        final int N = a.getIndexCount();
344        for (int i = 0; i < N; i++) {
345            int attr = a.getIndex(i);
346            switch (attr) {
347                case R.styleable.ViewGroup_clipChildren:
348                    setClipChildren(a.getBoolean(attr, true));
349                    break;
350                case R.styleable.ViewGroup_clipToPadding:
351                    setClipToPadding(a.getBoolean(attr, true));
352                    break;
353                case R.styleable.ViewGroup_animationCache:
354                    setAnimationCacheEnabled(a.getBoolean(attr, true));
355                    break;
356                case R.styleable.ViewGroup_persistentDrawingCache:
357                    setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
358                    break;
359                case R.styleable.ViewGroup_addStatesFromChildren:
360                    setAddStatesFromChildren(a.getBoolean(attr, false));
361                    break;
362                case R.styleable.ViewGroup_alwaysDrawnWithCache:
363                    setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
364                    break;
365                case R.styleable.ViewGroup_layoutAnimation:
366                    int id = a.getResourceId(attr, -1);
367                    if (id > 0) {
368                        setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
369                    }
370                    break;
371                case R.styleable.ViewGroup_descendantFocusability:
372                    setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
373                    break;
374                case R.styleable.ViewGroup_splitMotionEvents:
375                    setMotionEventSplittingEnabled(a.getBoolean(attr, false));
376                    break;
377            }
378        }
379
380        a.recycle();
381    }
382
383    /**
384     * Gets the descendant focusability of this view group.  The descendant
385     * focusability defines the relationship between this view group and its
386     * descendants when looking for a view to take focus in
387     * {@link #requestFocus(int, android.graphics.Rect)}.
388     *
389     * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
390     *   {@link #FOCUS_BLOCK_DESCENDANTS}.
391     */
392    @ViewDebug.ExportedProperty(category = "focus", mapping = {
393        @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
394        @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
395        @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
396    })
397    public int getDescendantFocusability() {
398        return mGroupFlags & FLAG_MASK_FOCUSABILITY;
399    }
400
401    /**
402     * Set the descendant focusability of this view group. This defines the relationship
403     * between this view group and its descendants when looking for a view to
404     * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
405     *
406     * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
407     *   {@link #FOCUS_BLOCK_DESCENDANTS}.
408     */
409    public void setDescendantFocusability(int focusability) {
410        switch (focusability) {
411            case FOCUS_BEFORE_DESCENDANTS:
412            case FOCUS_AFTER_DESCENDANTS:
413            case FOCUS_BLOCK_DESCENDANTS:
414                break;
415            default:
416                throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
417                        + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
418        }
419        mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
420        mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
421    }
422
423    /**
424     * {@inheritDoc}
425     */
426    @Override
427    void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
428        if (mFocused != null) {
429            mFocused.unFocus();
430            mFocused = null;
431        }
432        super.handleFocusGainInternal(direction, previouslyFocusedRect);
433    }
434
435    /**
436     * {@inheritDoc}
437     */
438    public void requestChildFocus(View child, View focused) {
439        if (DBG) {
440            System.out.println(this + " requestChildFocus()");
441        }
442        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
443            return;
444        }
445
446        // Unfocus us, if necessary
447        super.unFocus();
448
449        // We had a previous notion of who had focus. Clear it.
450        if (mFocused != child) {
451            if (mFocused != null) {
452                mFocused.unFocus();
453            }
454
455            mFocused = child;
456        }
457        if (mParent != null) {
458            mParent.requestChildFocus(this, focused);
459        }
460    }
461
462    /**
463     * {@inheritDoc}
464     */
465    public void focusableViewAvailable(View v) {
466        if (mParent != null
467                // shortcut: don't report a new focusable view if we block our descendants from
468                // getting focus
469                && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
470                // shortcut: don't report a new focusable view if we already are focused
471                // (and we don't prefer our descendants)
472                //
473                // note: knowing that mFocused is non-null is not a good enough reason
474                // to break the traversal since in that case we'd actually have to find
475                // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
476                // an ancestor of v; this will get checked for at ViewRoot
477                && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
478            mParent.focusableViewAvailable(v);
479        }
480    }
481
482    /**
483     * {@inheritDoc}
484     */
485    public boolean showContextMenuForChild(View originalView) {
486        return mParent != null && mParent.showContextMenuForChild(originalView);
487    }
488
489    /**
490     * {@inheritDoc}
491     */
492    public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
493        return mParent != null ? mParent.startActionModeForChild(originalView, callback) : null;
494    }
495
496    /**
497     * Find the nearest view in the specified direction that wants to take
498     * focus.
499     *
500     * @param focused The view that currently has focus
501     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
502     *        FOCUS_RIGHT, or 0 for not applicable.
503     */
504    public View focusSearch(View focused, int direction) {
505        if (isRootNamespace()) {
506            // root namespace means we should consider ourselves the top of the
507            // tree for focus searching; otherwise we could be focus searching
508            // into other tabs.  see LocalActivityManager and TabHost for more info
509            return FocusFinder.getInstance().findNextFocus(this, focused, direction);
510        } else if (mParent != null) {
511            return mParent.focusSearch(focused, direction);
512        }
513        return null;
514    }
515
516    /**
517     * {@inheritDoc}
518     */
519    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
520        return false;
521    }
522
523    /**
524     * {@inheritDoc}
525     */
526    @Override
527    public boolean dispatchUnhandledMove(View focused, int direction) {
528        return mFocused != null &&
529                mFocused.dispatchUnhandledMove(focused, direction);
530    }
531
532    /**
533     * {@inheritDoc}
534     */
535    public void clearChildFocus(View child) {
536        if (DBG) {
537            System.out.println(this + " clearChildFocus()");
538        }
539
540        mFocused = null;
541        if (mParent != null) {
542            mParent.clearChildFocus(this);
543        }
544    }
545
546    /**
547     * {@inheritDoc}
548     */
549    @Override
550    public void clearFocus() {
551        super.clearFocus();
552
553        // clear any child focus if it exists
554        if (mFocused != null) {
555            mFocused.clearFocus();
556        }
557    }
558
559    /**
560     * {@inheritDoc}
561     */
562    @Override
563    void unFocus() {
564        if (DBG) {
565            System.out.println(this + " unFocus()");
566        }
567
568        super.unFocus();
569        if (mFocused != null) {
570            mFocused.unFocus();
571        }
572        mFocused = null;
573    }
574
575    /**
576     * Returns the focused child of this view, if any. The child may have focus
577     * or contain focus.
578     *
579     * @return the focused child or null.
580     */
581    public View getFocusedChild() {
582        return mFocused;
583    }
584
585    /**
586     * Returns true if this view has or contains focus
587     *
588     * @return true if this view has or contains focus
589     */
590    @Override
591    public boolean hasFocus() {
592        return (mPrivateFlags & FOCUSED) != 0 || mFocused != null;
593    }
594
595    /*
596     * (non-Javadoc)
597     *
598     * @see android.view.View#findFocus()
599     */
600    @Override
601    public View findFocus() {
602        if (DBG) {
603            System.out.println("Find focus in " + this + ": flags="
604                    + isFocused() + ", child=" + mFocused);
605        }
606
607        if (isFocused()) {
608            return this;
609        }
610
611        if (mFocused != null) {
612            return mFocused.findFocus();
613        }
614        return null;
615    }
616
617    /**
618     * {@inheritDoc}
619     */
620    @Override
621    public boolean hasFocusable() {
622        if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
623            return false;
624        }
625
626        if (isFocusable()) {
627            return true;
628        }
629
630        final int descendantFocusability = getDescendantFocusability();
631        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
632            final int count = mChildrenCount;
633            final View[] children = mChildren;
634
635            for (int i = 0; i < count; i++) {
636                final View child = children[i];
637                if (child.hasFocusable()) {
638                    return true;
639                }
640            }
641        }
642
643        return false;
644    }
645
646    /**
647     * {@inheritDoc}
648     */
649    @Override
650    public void addFocusables(ArrayList<View> views, int direction) {
651        addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
652    }
653
654    /**
655     * {@inheritDoc}
656     */
657    @Override
658    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
659        final int focusableCount = views.size();
660
661        final int descendantFocusability = getDescendantFocusability();
662
663        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
664            final int count = mChildrenCount;
665            final View[] children = mChildren;
666
667            for (int i = 0; i < count; i++) {
668                final View child = children[i];
669                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
670                    child.addFocusables(views, direction, focusableMode);
671                }
672            }
673        }
674
675        // we add ourselves (if focusable) in all cases except for when we are
676        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
677        // to avoid the focus search finding layouts when a more precise search
678        // among the focusable children would be more interesting.
679        if (
680            descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
681                // No focusable descendants
682                (focusableCount == views.size())) {
683            super.addFocusables(views, direction, focusableMode);
684        }
685    }
686
687    /**
688     * {@inheritDoc}
689     */
690    @Override
691    public void dispatchWindowFocusChanged(boolean hasFocus) {
692        super.dispatchWindowFocusChanged(hasFocus);
693        final int count = mChildrenCount;
694        final View[] children = mChildren;
695        for (int i = 0; i < count; i++) {
696            children[i].dispatchWindowFocusChanged(hasFocus);
697        }
698    }
699
700    /**
701     * {@inheritDoc}
702     */
703    @Override
704    public void addTouchables(ArrayList<View> views) {
705        super.addTouchables(views);
706
707        final int count = mChildrenCount;
708        final View[] children = mChildren;
709
710        for (int i = 0; i < count; i++) {
711            final View child = children[i];
712            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
713                child.addTouchables(views);
714            }
715        }
716    }
717
718    /**
719     * {@inheritDoc}
720     */
721    @Override
722    public void dispatchDisplayHint(int hint) {
723        super.dispatchDisplayHint(hint);
724        final int count = mChildrenCount;
725        final View[] children = mChildren;
726        for (int i = 0; i < count; i++) {
727            children[i].dispatchDisplayHint(hint);
728        }
729    }
730
731    /**
732     * {@inheritDoc}
733     */
734    @Override
735    protected void dispatchVisibilityChanged(View changedView, int visibility) {
736        super.dispatchVisibilityChanged(changedView, visibility);
737        final int count = mChildrenCount;
738        final View[] children = mChildren;
739        for (int i = 0; i < count; i++) {
740            children[i].dispatchVisibilityChanged(changedView, visibility);
741        }
742    }
743
744    /**
745     * {@inheritDoc}
746     */
747    @Override
748    public void dispatchWindowVisibilityChanged(int visibility) {
749        super.dispatchWindowVisibilityChanged(visibility);
750        final int count = mChildrenCount;
751        final View[] children = mChildren;
752        for (int i = 0; i < count; i++) {
753            children[i].dispatchWindowVisibilityChanged(visibility);
754        }
755    }
756
757    /**
758     * {@inheritDoc}
759     */
760    @Override
761    public void dispatchConfigurationChanged(Configuration newConfig) {
762        super.dispatchConfigurationChanged(newConfig);
763        final int count = mChildrenCount;
764        final View[] children = mChildren;
765        for (int i = 0; i < count; i++) {
766            children[i].dispatchConfigurationChanged(newConfig);
767        }
768    }
769
770    /**
771     * {@inheritDoc}
772     */
773    public void recomputeViewAttributes(View child) {
774        ViewParent parent = mParent;
775        if (parent != null) parent.recomputeViewAttributes(this);
776    }
777
778    @Override
779    void dispatchCollectViewAttributes(int visibility) {
780        visibility |= mViewFlags&VISIBILITY_MASK;
781        super.dispatchCollectViewAttributes(visibility);
782        final int count = mChildrenCount;
783        final View[] children = mChildren;
784        for (int i = 0; i < count; i++) {
785            children[i].dispatchCollectViewAttributes(visibility);
786        }
787    }
788
789    /**
790     * {@inheritDoc}
791     */
792    public void bringChildToFront(View child) {
793        int index = indexOfChild(child);
794        if (index >= 0) {
795            removeFromArray(index);
796            addInArray(child, mChildrenCount);
797            child.mParent = this;
798        }
799    }
800
801    /**
802     * {@inheritDoc}
803     */
804    @Override
805    public boolean dispatchKeyEventPreIme(KeyEvent event) {
806        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
807            return super.dispatchKeyEventPreIme(event);
808        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
809            return mFocused.dispatchKeyEventPreIme(event);
810        }
811        return false;
812    }
813
814    /**
815     * {@inheritDoc}
816     */
817    @Override
818    public boolean dispatchKeyEvent(KeyEvent event) {
819        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
820            return super.dispatchKeyEvent(event);
821        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
822            return mFocused.dispatchKeyEvent(event);
823        }
824        return false;
825    }
826
827    /**
828     * {@inheritDoc}
829     */
830    @Override
831    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
832        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
833            return super.dispatchKeyShortcutEvent(event);
834        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
835            return mFocused.dispatchKeyShortcutEvent(event);
836        }
837        return false;
838    }
839
840    /**
841     * {@inheritDoc}
842     */
843    @Override
844    public boolean dispatchTrackballEvent(MotionEvent event) {
845        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
846            return super.dispatchTrackballEvent(event);
847        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
848            return mFocused.dispatchTrackballEvent(event);
849        }
850        return false;
851    }
852
853    /**
854     * {@inheritDoc}
855     */
856    @Override
857    public boolean dispatchTouchEvent(MotionEvent ev) {
858        if ((mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS) {
859            return dispatchSplitTouchEvent(ev);
860        }
861
862        final int action = ev.getAction();
863        final float xf = ev.getX();
864        final float yf = ev.getY();
865        final float scrolledXFloat = xf + mScrollX;
866        final float scrolledYFloat = yf + mScrollY;
867
868        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
869
870        if (action == MotionEvent.ACTION_DOWN) {
871            if (mMotionTarget != null) {
872                // this is weird, we got a pen down, but we thought it was
873                // already down!
874                // XXX: We should probably send an ACTION_UP to the current
875                // target.
876                mMotionTarget = null;
877            }
878            // If we're disallowing intercept or if we're allowing and we didn't
879            // intercept
880            if (disallowIntercept || !onInterceptTouchEvent(ev)) {
881                // reset this event's action (just to protect ourselves)
882                ev.setAction(MotionEvent.ACTION_DOWN);
883                // We know we want to dispatch the event down, find a child
884                // who can handle it, start with the front-most child.
885                final View[] children = mChildren;
886                final int count = mChildrenCount;
887                for (int i = count - 1; i >= 0; i--) {
888                    final View child = children[i];
889                    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
890                            || child.getAnimation() != null) {
891                        // Single dispatch always picks its target based on the initial down
892                        // event's position - index 0
893                        if (dispatchTouchEventIfInView(child, ev, 0)) {
894                            mMotionTarget = child;
895                            return true;
896                        }
897                    }
898                }
899            }
900        }
901
902        boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
903                (action == MotionEvent.ACTION_CANCEL);
904
905        if (isUpOrCancel) {
906            // Note, we've already copied the previous state to our local
907            // variable, so this takes effect on the next event
908            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
909        }
910
911        // The event wasn't an ACTION_DOWN, dispatch it to our target if
912        // we have one.
913        final View target = mMotionTarget;
914        if (target == null) {
915            // We don't have a target, this means we're handling the
916            // event as a regular view.
917            ev.setLocation(xf, yf);
918            if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
919                ev.setAction(MotionEvent.ACTION_CANCEL);
920                mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
921            }
922            return super.dispatchTouchEvent(ev);
923        }
924
925        // Calculate the offset point into the target's local coordinates
926        float xc = scrolledXFloat - (float) target.mLeft;
927        float yc = scrolledYFloat - (float) target.mTop;
928        if (!target.hasIdentityMatrix() && mAttachInfo != null) {
929            // non-identity matrix: transform the point into the view's coordinates
930            final float[] localXY = mAttachInfo.mTmpTransformLocation;
931            localXY[0] = xc;
932            localXY[1] = yc;
933            target.getInverseMatrix().mapPoints(localXY);
934            xc = localXY[0];
935            yc = localXY[1];
936        }
937
938        // if have a target, see if we're allowed to and want to intercept its
939        // events
940        if (!disallowIntercept && onInterceptTouchEvent(ev)) {
941            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
942            ev.setAction(MotionEvent.ACTION_CANCEL);
943            ev.setLocation(xc, yc);
944            if (!target.dispatchTouchEvent(ev)) {
945                // target didn't handle ACTION_CANCEL. not much we can do
946                // but they should have.
947            }
948            // clear the target
949            mMotionTarget = null;
950            // Don't dispatch this event to our own view, because we already
951            // saw it when intercepting; we just want to give the following
952            // event to the normal onTouchEvent().
953            return true;
954        }
955
956        if (isUpOrCancel) {
957            mMotionTarget = null;
958        }
959
960        // finally offset the event to the target's coordinate system and
961        // dispatch the event.
962        ev.setLocation(xc, yc);
963
964        if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
965            ev.setAction(MotionEvent.ACTION_CANCEL);
966            target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
967            mMotionTarget = null;
968        }
969
970        return target.dispatchTouchEvent(ev);
971    }
972
973    /**
974     * This method detects whether the pointer location at <code>pointerIndex</code> within
975     * <code>ev</code> is inside the specified view. If so, the transformed event is dispatched to
976     * <code>child</code>.
977     *
978     * @param child View to hit test against
979     * @param ev MotionEvent to test
980     * @param pointerIndex Index of the pointer within <code>ev</code> to test
981     * @return <code>false</code> if the hit test failed, or the result of
982     *         <code>child.dispatchTouchEvent</code>
983     */
984    private boolean dispatchTouchEventIfInView(View child, MotionEvent ev, int pointerIndex) {
985        final float x = ev.getX(pointerIndex);
986        final float y = ev.getY(pointerIndex);
987        final float scrolledX = x + mScrollX;
988        final float scrolledY = y + mScrollY;
989        float localX = scrolledX - child.mLeft;
990        float localY = scrolledY - child.mTop;
991        if (!child.hasIdentityMatrix() && mAttachInfo != null) {
992            // non-identity matrix: transform the point into the view's coordinates
993            final float[] localXY = mAttachInfo.mTmpTransformLocation;
994            localXY[0] = localX;
995            localXY[1] = localY;
996            child.getInverseMatrix().mapPoints(localXY);
997            localX = localXY[0];
998            localY = localXY[1];
999        }
1000        if (localX >= 0 && localY >= 0 && localX < (child.mRight - child.mLeft) &&
1001                localY < (child.mBottom - child.mTop)) {
1002            // It would be safer to clone the event here but we don't for performance.
1003            // There are many subtle interactions in touch event dispatch; change at your own risk.
1004            child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
1005            ev.offsetLocation(localX - x, localY - y);
1006            return child.dispatchTouchEvent(ev);
1007        }
1008        return false;
1009    }
1010
1011    private boolean dispatchSplitTouchEvent(MotionEvent ev) {
1012        final SplitMotionTargets targets = mSplitMotionTargets;
1013        final int action = ev.getAction();
1014        final int maskedAction = ev.getActionMasked();
1015        float xf = ev.getX();
1016        float yf = ev.getY();
1017        float scrolledXFloat = xf + mScrollX;
1018        float scrolledYFloat = yf + mScrollY;
1019
1020        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
1021
1022        if (maskedAction == MotionEvent.ACTION_DOWN ||
1023                maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
1024            final int actionIndex = ev.getActionIndex();
1025            final int actionId = ev.getPointerId(actionIndex);
1026
1027            // Clear out any current target for this ID.
1028            // XXX: We should probably send an ACTION_UP to the current
1029            // target if present.
1030            targets.removeById(actionId);
1031
1032            // If we're disallowing intercept or if we're allowing and we didn't
1033            // intercept
1034            if (disallowIntercept || !onInterceptTouchEvent(ev)) {
1035                // reset this event's action (just to protect ourselves)
1036                ev.setAction(action);
1037                // We know we want to dispatch the event down, try to find a child
1038                // who can handle it, start with the front-most child.
1039                final View[] children = mChildren;
1040                final int count = mChildrenCount;
1041                for (int i = count - 1; i >= 0; i--) {
1042                    final View child = children[i];
1043                    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
1044                            || child.getAnimation() != null) {
1045                        final MotionEvent childEvent = targets.filterMotionEventForChild(ev, child);
1046                        if (childEvent != null) {
1047                            try {
1048                                final int childActionIndex = childEvent.findPointerIndex(actionId);
1049                                if (dispatchTouchEventIfInView(child, childEvent,
1050                                        childActionIndex)) {
1051                                    targets.add(actionId, child);
1052
1053                                    return true;
1054                                }
1055                            } finally {
1056                                childEvent.recycle();
1057                            }
1058                        }
1059                    }
1060                }
1061
1062                // Didn't find a new target. Do we have a "primary" target to send to?
1063                final View primaryTarget = targets.getPrimaryTarget();
1064                if (primaryTarget != null) {
1065                    final MotionEvent childEvent =
1066                            targets.filterMotionEventForChild(ev, primaryTarget);
1067                    if (childEvent != null) {
1068                        try {
1069                            // Calculate the offset point into the target's local coordinates
1070                            float xc = scrolledXFloat - (float) primaryTarget.mLeft;
1071                            float yc = scrolledYFloat - (float) primaryTarget.mTop;
1072                            if (!primaryTarget.hasIdentityMatrix() && mAttachInfo != null) {
1073                                // non-identity matrix: transform the point into the view's
1074                                // coordinates
1075                                final float[] localXY = mAttachInfo.mTmpTransformLocation;
1076                                localXY[0] = xc;
1077                                localXY[1] = yc;
1078                                primaryTarget.getInverseMatrix().mapPoints(localXY);
1079                                xc = localXY[0];
1080                                yc = localXY[1];
1081                            }
1082                            childEvent.setLocation(xc, yc);
1083                            if (primaryTarget.dispatchTouchEvent(childEvent)) {
1084                                targets.add(actionId, primaryTarget);
1085                                return true;
1086                            }
1087                        } finally {
1088                            childEvent.recycle();
1089                        }
1090                    }
1091                }
1092            }
1093        }
1094
1095        boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
1096                (action == MotionEvent.ACTION_CANCEL);
1097
1098        if (isUpOrCancel) {
1099            // Note, we've already copied the previous state to our local
1100            // variable, so this takes effect on the next event
1101            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1102        }
1103
1104        if (targets.isEmpty()) {
1105            // We don't have any targets, this means we're handling the
1106            // event as a regular view.
1107            ev.setLocation(xf, yf);
1108            if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
1109                ev.setAction(MotionEvent.ACTION_CANCEL);
1110                mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
1111            }
1112            return super.dispatchTouchEvent(ev);
1113        }
1114
1115        // if we have targets, see if we're allowed to and want to intercept their
1116        // events
1117        int uniqueTargetCount = targets.getUniqueTargetCount();
1118        if (!disallowIntercept && onInterceptTouchEvent(ev)) {
1119            mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
1120
1121            for (int uniqueIndex = 0; uniqueIndex < uniqueTargetCount; uniqueIndex++) {
1122                final View target = targets.getUniqueTargetAt(uniqueIndex);
1123
1124                // Calculate the offset point into the target's local coordinates
1125                float xc = scrolledXFloat - (float) target.mLeft;
1126                float yc = scrolledYFloat - (float) target.mTop;
1127                if (!target.hasIdentityMatrix() && mAttachInfo != null) {
1128                    // non-identity matrix: transform the point into the view's coordinates
1129                    final float[] localXY = mAttachInfo.mTmpTransformLocation;
1130                    localXY[0] = xc;
1131                    localXY[1] = yc;
1132                    target.getInverseMatrix().mapPoints(localXY);
1133                    xc = localXY[0];
1134                    yc = localXY[1];
1135                }
1136
1137                ev.setAction(MotionEvent.ACTION_CANCEL);
1138                ev.setLocation(xc, yc);
1139                if (!target.dispatchTouchEvent(ev)) {
1140                    // target didn't handle ACTION_CANCEL. not much we can do
1141                    // but they should have.
1142                }
1143            }
1144            targets.clear();
1145            // Don't dispatch this event to our own view, because we already
1146            // saw it when intercepting; we just want to give the following
1147            // event to the normal onTouchEvent().
1148            return true;
1149        }
1150
1151        boolean handled = false;
1152        for (int uniqueIndex = 0; uniqueIndex < uniqueTargetCount; uniqueIndex++) {
1153            final View target = targets.getUniqueTargetAt(uniqueIndex);
1154
1155            final MotionEvent targetEvent = targets.filterMotionEventForChild(ev, target);
1156            if (targetEvent == null) {
1157                continue;
1158            }
1159
1160            try {
1161                // Calculate the offset point into the target's local coordinates
1162                xf = targetEvent.getX();
1163                yf = targetEvent.getY();
1164                scrolledXFloat = xf + mScrollX;
1165                scrolledYFloat = yf + mScrollY;
1166                float xc = scrolledXFloat - (float) target.mLeft;
1167                float yc = scrolledYFloat - (float) target.mTop;
1168                if (!target.hasIdentityMatrix() && mAttachInfo != null) {
1169                    // non-identity matrix: transform the point into the view's coordinates
1170                    final float[] localXY = mAttachInfo.mTmpTransformLocation;
1171                    localXY[0] = xc;
1172                    localXY[1] = yc;
1173                    target.getInverseMatrix().mapPoints(localXY);
1174                    xc = localXY[0];
1175                    yc = localXY[1];
1176                }
1177
1178                // finally offset the event to the target's coordinate system and
1179                // dispatch the event.
1180                targetEvent.setLocation(xc, yc);
1181
1182                if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
1183                    targetEvent.setAction(MotionEvent.ACTION_CANCEL);
1184                    target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
1185                    targets.removeView(target);
1186                    uniqueIndex--;
1187                    uniqueTargetCount--;
1188                }
1189
1190                handled |= target.dispatchTouchEvent(targetEvent);
1191            } finally {
1192                targetEvent.recycle();
1193            }
1194        }
1195
1196        if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
1197            final int removeId = ev.getPointerId(ev.getActionIndex());
1198            targets.removeById(removeId);
1199        }
1200
1201        if (isUpOrCancel) {
1202            targets.clear();
1203        }
1204
1205        return handled;
1206    }
1207
1208    /**
1209     * Enable or disable the splitting of MotionEvents to multiple children during touch event
1210     * dispatch. This behavior is disabled by default.
1211     *
1212     * <p>When this option is enabled MotionEvents may be split and dispatched to different child
1213     * views depending on where each pointer initially went down. This allows for user interactions
1214     * such as scrolling two panes of content independently, chording of buttons, and performing
1215     * independent gestures on different pieces of content.
1216     *
1217     * @param split <code>true</code> to allow MotionEvents to be split and dispatched to multiple
1218     *              child views. <code>false</code> to only allow one child view to be the target of
1219     *              any MotionEvent received by this ViewGroup.
1220     */
1221    public void setMotionEventSplittingEnabled(boolean split) {
1222        // TODO Applications really shouldn't change this setting mid-touch event,
1223        // but perhaps this should handle that case and send ACTION_CANCELs to any child views
1224        // with gestures in progress when this is changed.
1225        if (split) {
1226            if ((mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == 0) {
1227                mSplitMotionTargets = new SplitMotionTargets();
1228            }
1229            mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
1230        } else {
1231            mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
1232            mSplitMotionTargets = null;
1233        }
1234    }
1235
1236    /**
1237     * @return true if MotionEvents dispatched to this ViewGroup can be split to multiple children.
1238     */
1239    public boolean isMotionEventSplittingEnabled() {
1240        return (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) == FLAG_SPLIT_MOTION_EVENTS;
1241    }
1242
1243    /**
1244     * {@inheritDoc}
1245     */
1246    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
1247
1248        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
1249            // We're already in this state, assume our ancestors are too
1250            return;
1251        }
1252
1253        if (disallowIntercept) {
1254            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
1255        } else {
1256            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
1257        }
1258
1259        // Pass it up to our parent
1260        if (mParent != null) {
1261            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
1262        }
1263    }
1264
1265    /**
1266     * Implement this method to intercept all touch screen motion events.  This
1267     * allows you to watch events as they are dispatched to your children, and
1268     * take ownership of the current gesture at any point.
1269     *
1270     * <p>Using this function takes some care, as it has a fairly complicated
1271     * interaction with {@link View#onTouchEvent(MotionEvent)
1272     * View.onTouchEvent(MotionEvent)}, and using it requires implementing
1273     * that method as well as this one in the correct way.  Events will be
1274     * received in the following order:
1275     *
1276     * <ol>
1277     * <li> You will receive the down event here.
1278     * <li> The down event will be handled either by a child of this view
1279     * group, or given to your own onTouchEvent() method to handle; this means
1280     * you should implement onTouchEvent() to return true, so you will
1281     * continue to see the rest of the gesture (instead of looking for
1282     * a parent view to handle it).  Also, by returning true from
1283     * onTouchEvent(), you will not receive any following
1284     * events in onInterceptTouchEvent() and all touch processing must
1285     * happen in onTouchEvent() like normal.
1286     * <li> For as long as you return false from this function, each following
1287     * event (up to and including the final up) will be delivered first here
1288     * and then to the target's onTouchEvent().
1289     * <li> If you return true from here, you will not receive any
1290     * following events: the target view will receive the same event but
1291     * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
1292     * events will be delivered to your onTouchEvent() method and no longer
1293     * appear here.
1294     * </ol>
1295     *
1296     * @param ev The motion event being dispatched down the hierarchy.
1297     * @return Return true to steal motion events from the children and have
1298     * them dispatched to this ViewGroup through onTouchEvent().
1299     * The current target will receive an ACTION_CANCEL event, and no further
1300     * messages will be delivered here.
1301     */
1302    public boolean onInterceptTouchEvent(MotionEvent ev) {
1303        return false;
1304    }
1305
1306    /**
1307     * {@inheritDoc}
1308     *
1309     * Looks for a view to give focus to respecting the setting specified by
1310     * {@link #getDescendantFocusability()}.
1311     *
1312     * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
1313     * find focus within the children of this group when appropriate.
1314     *
1315     * @see #FOCUS_BEFORE_DESCENDANTS
1316     * @see #FOCUS_AFTER_DESCENDANTS
1317     * @see #FOCUS_BLOCK_DESCENDANTS
1318     * @see #onRequestFocusInDescendants
1319     */
1320    @Override
1321    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
1322        if (DBG) {
1323            System.out.println(this + " ViewGroup.requestFocus direction="
1324                    + direction);
1325        }
1326        int descendantFocusability = getDescendantFocusability();
1327
1328        switch (descendantFocusability) {
1329            case FOCUS_BLOCK_DESCENDANTS:
1330                return super.requestFocus(direction, previouslyFocusedRect);
1331            case FOCUS_BEFORE_DESCENDANTS: {
1332                final boolean took = super.requestFocus(direction, previouslyFocusedRect);
1333                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
1334            }
1335            case FOCUS_AFTER_DESCENDANTS: {
1336                final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
1337                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
1338            }
1339            default:
1340                throw new IllegalStateException("descendant focusability must be "
1341                        + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
1342                        + "but is " + descendantFocusability);
1343        }
1344    }
1345
1346    /**
1347     * Look for a descendant to call {@link View#requestFocus} on.
1348     * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
1349     * when it wants to request focus within its children.  Override this to
1350     * customize how your {@link ViewGroup} requests focus within its children.
1351     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
1352     * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
1353     *        to give a finer grained hint about where focus is coming from.  May be null
1354     *        if there is no hint.
1355     * @return Whether focus was taken.
1356     */
1357    @SuppressWarnings({"ConstantConditions"})
1358    protected boolean onRequestFocusInDescendants(int direction,
1359            Rect previouslyFocusedRect) {
1360        int index;
1361        int increment;
1362        int end;
1363        int count = mChildrenCount;
1364        if ((direction & FOCUS_FORWARD) != 0) {
1365            index = 0;
1366            increment = 1;
1367            end = count;
1368        } else {
1369            index = count - 1;
1370            increment = -1;
1371            end = -1;
1372        }
1373        final View[] children = mChildren;
1374        for (int i = index; i != end; i += increment) {
1375            View child = children[i];
1376            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1377                if (child.requestFocus(direction, previouslyFocusedRect)) {
1378                    return true;
1379                }
1380            }
1381        }
1382        return false;
1383    }
1384
1385    /**
1386     * {@inheritDoc}
1387     *
1388     * @hide
1389     */
1390    @Override
1391    public void dispatchStartTemporaryDetach() {
1392        super.dispatchStartTemporaryDetach();
1393        final int count = mChildrenCount;
1394        final View[] children = mChildren;
1395        for (int i = 0; i < count; i++) {
1396            children[i].dispatchStartTemporaryDetach();
1397        }
1398    }
1399
1400    /**
1401     * {@inheritDoc}
1402     *
1403     * @hide
1404     */
1405    @Override
1406    public void dispatchFinishTemporaryDetach() {
1407        super.dispatchFinishTemporaryDetach();
1408        final int count = mChildrenCount;
1409        final View[] children = mChildren;
1410        for (int i = 0; i < count; i++) {
1411            children[i].dispatchFinishTemporaryDetach();
1412        }
1413    }
1414
1415    /**
1416     * {@inheritDoc}
1417     */
1418    @Override
1419    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
1420        super.dispatchAttachedToWindow(info, visibility);
1421        visibility |= mViewFlags & VISIBILITY_MASK;
1422        final int count = mChildrenCount;
1423        final View[] children = mChildren;
1424        for (int i = 0; i < count; i++) {
1425            children[i].dispatchAttachedToWindow(info, visibility);
1426        }
1427    }
1428
1429    @Override
1430    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
1431        boolean populated = false;
1432        for (int i = 0, count = getChildCount(); i < count; i++) {
1433            populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event);
1434        }
1435        return populated;
1436    }
1437
1438    /**
1439     * {@inheritDoc}
1440     */
1441    @Override
1442    void dispatchDetachedFromWindow() {
1443        // If we still have a motion target, we are still in the process of
1444        // dispatching motion events to a child; we need to get rid of that
1445        // child to avoid dispatching events to it after the window is torn
1446        // down. To make sure we keep the child in a consistent state, we
1447        // first send it an ACTION_CANCEL motion event.
1448        if (mMotionTarget != null) {
1449            final long now = SystemClock.uptimeMillis();
1450            final MotionEvent event = MotionEvent.obtain(now, now,
1451                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
1452            mMotionTarget.dispatchTouchEvent(event);
1453            event.recycle();
1454            mMotionTarget = null;
1455        }
1456
1457        final int count = mChildrenCount;
1458        final View[] children = mChildren;
1459        for (int i = 0; i < count; i++) {
1460            children[i].dispatchDetachedFromWindow();
1461        }
1462        super.dispatchDetachedFromWindow();
1463    }
1464
1465    /**
1466     * {@inheritDoc}
1467     */
1468    @Override
1469    public void setPadding(int left, int top, int right, int bottom) {
1470        super.setPadding(left, top, right, bottom);
1471
1472        if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingRight) != 0) {
1473            mGroupFlags |= FLAG_PADDING_NOT_NULL;
1474        } else {
1475            mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
1476        }
1477    }
1478
1479    /**
1480     * {@inheritDoc}
1481     */
1482    @Override
1483    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
1484        super.dispatchSaveInstanceState(container);
1485        final int count = mChildrenCount;
1486        final View[] children = mChildren;
1487        for (int i = 0; i < count; i++) {
1488            View c = children[i];
1489            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
1490                c.dispatchSaveInstanceState(container);
1491            }
1492        }
1493    }
1494
1495    /**
1496     * Perform dispatching of a {@link #saveHierarchyState freeze()} to only this view,
1497     * not to its children.  For use when overriding
1498     * {@link #dispatchSaveInstanceState dispatchFreeze()} to allow subclasses to freeze
1499     * their own state but not the state of their children.
1500     *
1501     * @param container the container
1502     */
1503    protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
1504        super.dispatchSaveInstanceState(container);
1505    }
1506
1507    /**
1508     * {@inheritDoc}
1509     */
1510    @Override
1511    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
1512        super.dispatchRestoreInstanceState(container);
1513        final int count = mChildrenCount;
1514        final View[] children = mChildren;
1515        for (int i = 0; i < count; i++) {
1516            View c = children[i];
1517            if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
1518                c.dispatchRestoreInstanceState(container);
1519            }
1520        }
1521    }
1522
1523    /**
1524     * Perform dispatching of a {@link #restoreHierarchyState thaw()} to only this view,
1525     * not to its children.  For use when overriding
1526     * {@link #dispatchRestoreInstanceState dispatchThaw()} to allow subclasses to thaw
1527     * their own state but not the state of their children.
1528     *
1529     * @param container the container
1530     */
1531    protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
1532        super.dispatchRestoreInstanceState(container);
1533    }
1534
1535    /**
1536     * Enables or disables the drawing cache for each child of this view group.
1537     *
1538     * @param enabled true to enable the cache, false to dispose of it
1539     */
1540    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
1541        if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
1542            final View[] children = mChildren;
1543            final int count = mChildrenCount;
1544            for (int i = 0; i < count; i++) {
1545                children[i].setDrawingCacheEnabled(enabled);
1546            }
1547        }
1548    }
1549
1550    @Override
1551    protected void onAnimationStart() {
1552        super.onAnimationStart();
1553
1554        // When this ViewGroup's animation starts, build the cache for the children
1555        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
1556            final int count = mChildrenCount;
1557            final View[] children = mChildren;
1558
1559            for (int i = 0; i < count; i++) {
1560                final View child = children[i];
1561                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1562                    child.setDrawingCacheEnabled(true);
1563                    child.buildDrawingCache(true);
1564                }
1565            }
1566
1567            mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
1568        }
1569    }
1570
1571    @Override
1572    protected void onAnimationEnd() {
1573        super.onAnimationEnd();
1574
1575        // When this ViewGroup's animation ends, destroy the cache of the children
1576        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
1577            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
1578
1579            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
1580                setChildrenDrawingCacheEnabled(false);
1581            }
1582        }
1583    }
1584
1585    @Override
1586    Bitmap createSnapshot(Bitmap.Config quality, int backgroundColor, boolean skipChildren) {
1587        int count = mChildrenCount;
1588        int[] visibilities = null;
1589
1590        if (skipChildren) {
1591            visibilities = new int[count];
1592            for (int i = 0; i < count; i++) {
1593                View child = getChildAt(i);
1594                visibilities[i] = child.getVisibility();
1595                if (visibilities[i] == View.VISIBLE) {
1596                    child.setVisibility(INVISIBLE);
1597                }
1598            }
1599        }
1600
1601        Bitmap b = super.createSnapshot(quality, backgroundColor, skipChildren);
1602
1603        if (skipChildren) {
1604            for (int i = 0; i < count; i++) {
1605                getChildAt(i).setVisibility(visibilities[i]);
1606            }
1607        }
1608
1609        return b;
1610    }
1611
1612    /**
1613     * {@inheritDoc}
1614     */
1615    @Override
1616    protected void dispatchDraw(Canvas canvas) {
1617        final int count = mChildrenCount;
1618        final View[] children = mChildren;
1619        int flags = mGroupFlags;
1620
1621        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
1622            final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
1623
1624            for (int i = 0; i < count; i++) {
1625                final View child = children[i];
1626                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
1627                    final LayoutParams params = child.getLayoutParams();
1628                    attachLayoutAnimationParameters(child, params, i, count);
1629                    bindLayoutAnimation(child);
1630                    if (cache) {
1631                        child.setDrawingCacheEnabled(true);
1632                        child.buildDrawingCache(true);
1633                    }
1634                }
1635            }
1636
1637            final LayoutAnimationController controller = mLayoutAnimationController;
1638            if (controller.willOverlap()) {
1639                mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
1640            }
1641
1642            controller.start();
1643
1644            mGroupFlags &= ~FLAG_RUN_ANIMATION;
1645            mGroupFlags &= ~FLAG_ANIMATION_DONE;
1646
1647            if (cache) {
1648                mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
1649            }
1650
1651            if (mAnimationListener != null) {
1652                mAnimationListener.onAnimationStart(controller.getAnimation());
1653            }
1654        }
1655
1656        int saveCount = 0;
1657        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
1658        if (clipToPadding) {
1659            saveCount = canvas.save();
1660            canvas.clipRect(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
1661                    mScrollX + mRight - mLeft - mPaddingRight,
1662                    mScrollY + mBottom - mTop - mPaddingBottom);
1663
1664        }
1665
1666        // We will draw our child's animation, let's reset the flag
1667        mPrivateFlags &= ~DRAW_ANIMATION;
1668        mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
1669
1670        boolean more = false;
1671        final long drawingTime = getDrawingTime();
1672
1673        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
1674            for (int i = 0; i < count; i++) {
1675                final View child = children[i];
1676                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
1677                    more |= drawChild(canvas, child, drawingTime);
1678                }
1679            }
1680        } else {
1681            for (int i = 0; i < count; i++) {
1682                final View child = children[getChildDrawingOrder(count, i)];
1683                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
1684                    more |= drawChild(canvas, child, drawingTime);
1685                }
1686            }
1687        }
1688
1689        // Draw any disappearing views that have animations
1690        if (mDisappearingChildren != null) {
1691            final ArrayList<View> disappearingChildren = mDisappearingChildren;
1692            final int disappearingCount = disappearingChildren.size() - 1;
1693            // Go backwards -- we may delete as animations finish
1694            for (int i = disappearingCount; i >= 0; i--) {
1695                final View child = disappearingChildren.get(i);
1696                more |= drawChild(canvas, child, drawingTime);
1697            }
1698        }
1699
1700        if (clipToPadding) {
1701            canvas.restoreToCount(saveCount);
1702        }
1703
1704        // mGroupFlags might have been updated by drawChild()
1705        flags = mGroupFlags;
1706
1707        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
1708            invalidate();
1709        }
1710
1711        if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
1712                mLayoutAnimationController.isDone() && !more) {
1713            // We want to erase the drawing cache and notify the listener after the
1714            // next frame is drawn because one extra invalidate() is caused by
1715            // drawChild() after the animation is over
1716            mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
1717            final Runnable end = new Runnable() {
1718               public void run() {
1719                   notifyAnimationListener();
1720               }
1721            };
1722            post(end);
1723        }
1724    }
1725
1726    /**
1727     * Returns the index of the child to draw for this iteration. Override this
1728     * if you want to change the drawing order of children. By default, it
1729     * returns i.
1730     * <p>
1731     * NOTE: In order for this method to be called, you must enable child ordering
1732     * first by calling {@link #setChildrenDrawingOrderEnabled(boolean)}.
1733     *
1734     * @param i The current iteration.
1735     * @return The index of the child to draw this iteration.
1736     *
1737     * @see #setChildrenDrawingOrderEnabled(boolean)
1738     * @see #isChildrenDrawingOrderEnabled()
1739     */
1740    protected int getChildDrawingOrder(int childCount, int i) {
1741        return i;
1742    }
1743
1744    private void notifyAnimationListener() {
1745        mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
1746        mGroupFlags |= FLAG_ANIMATION_DONE;
1747
1748        if (mAnimationListener != null) {
1749           final Runnable end = new Runnable() {
1750               public void run() {
1751                   mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
1752               }
1753           };
1754           post(end);
1755        }
1756
1757        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
1758            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
1759            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
1760                setChildrenDrawingCacheEnabled(false);
1761            }
1762        }
1763
1764        invalidate();
1765    }
1766
1767    /**
1768     * Draw one child of this View Group. This method is responsible for getting
1769     * the canvas in the right state. This includes clipping, translating so
1770     * that the child's scrolled origin is at 0, 0, and applying any animation
1771     * transformations.
1772     *
1773     * @param canvas The canvas on which to draw the child
1774     * @param child Who to draw
1775     * @param drawingTime The time at which draw is occuring
1776     * @return True if an invalidate() was issued
1777     */
1778    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
1779        boolean more = false;
1780
1781        final int cl = child.mLeft;
1782        final int ct = child.mTop;
1783        final int cr = child.mRight;
1784        final int cb = child.mBottom;
1785
1786        final int flags = mGroupFlags;
1787
1788        if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
1789            mChildTransformation.clear();
1790            mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
1791        }
1792
1793        Transformation transformToApply = null;
1794        Transformation invalidationTransform;
1795        final Animation a = child.getAnimation();
1796        boolean concatMatrix = false;
1797
1798        boolean scalingRequired = false;
1799        boolean caching = false;
1800        if (!canvas.isHardwareAccelerated() &&
1801                (flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
1802                (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
1803            caching = true;
1804            if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired;
1805        }
1806
1807        if (a != null) {
1808            final boolean initialized = a.isInitialized();
1809            if (!initialized) {
1810                a.initialize(cr - cl, cb - ct, getWidth(), getHeight());
1811                a.initializeInvalidateRegion(0, 0, cr - cl, cb - ct);
1812                child.onAnimationStart();
1813            }
1814
1815            more = a.getTransformation(drawingTime, mChildTransformation,
1816                    scalingRequired ? mAttachInfo.mApplicationScale : 1f);
1817            if (scalingRequired && mAttachInfo.mApplicationScale != 1f) {
1818                if (mInvalidationTransformation == null) {
1819                    mInvalidationTransformation = new Transformation();
1820                }
1821                invalidationTransform = mInvalidationTransformation;
1822                a.getTransformation(drawingTime, invalidationTransform, 1f);
1823            } else {
1824                invalidationTransform = mChildTransformation;
1825            }
1826            transformToApply = mChildTransformation;
1827
1828            concatMatrix = a.willChangeTransformationMatrix();
1829
1830            if (more) {
1831                if (!a.willChangeBounds()) {
1832                    if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
1833                            FLAG_OPTIMIZE_INVALIDATE) {
1834                        mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
1835                    } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
1836                        // The child need to draw an animation, potentially offscreen, so
1837                        // make sure we do not cancel invalidate requests
1838                        mPrivateFlags |= DRAW_ANIMATION;
1839                        invalidate(cl, ct, cr, cb);
1840                    }
1841                } else {
1842                    if (mInvalidateRegion == null) {
1843                        mInvalidateRegion = new RectF();
1844                    }
1845                    final RectF region = mInvalidateRegion;
1846                    a.getInvalidateRegion(0, 0, cr - cl, cb - ct, region, invalidationTransform);
1847
1848                    // The child need to draw an animation, potentially offscreen, so
1849                    // make sure we do not cancel invalidate requests
1850                    mPrivateFlags |= DRAW_ANIMATION;
1851
1852                    final int left = cl + (int) region.left;
1853                    final int top = ct + (int) region.top;
1854                    invalidate(left, top, left + (int) region.width(), top + (int) region.height());
1855                }
1856            }
1857        } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
1858                FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
1859            final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
1860            if (hasTransform) {
1861                final int transformType = mChildTransformation.getTransformationType();
1862                transformToApply = transformType != Transformation.TYPE_IDENTITY ?
1863                        mChildTransformation : null;
1864                concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
1865            }
1866        }
1867
1868        concatMatrix |= !child.hasIdentityMatrix();
1869
1870        // Sets the flag as early as possible to allow draw() implementations
1871        // to call invalidate() successfully when doing animations
1872        child.mPrivateFlags |= DRAWN;
1873
1874        if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
1875                (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
1876            return more;
1877        }
1878
1879        child.computeScroll();
1880
1881        final int sx = child.mScrollX;
1882        final int sy = child.mScrollY;
1883
1884        Bitmap cache = null;
1885        if (caching) {
1886            cache = child.getDrawingCache(true);
1887        }
1888
1889        final boolean hasNoCache = cache == null;
1890
1891        final int restoreTo = canvas.save();
1892        if (hasNoCache) {
1893            canvas.translate(cl - sx, ct - sy);
1894        } else {
1895            canvas.translate(cl, ct);
1896            if (scalingRequired) {
1897                // mAttachInfo cannot be null, otherwise scalingRequired == false
1898                final float scale = 1.0f / mAttachInfo.mApplicationScale;
1899                canvas.scale(scale, scale);
1900            }
1901        }
1902
1903        float alpha = child.getAlpha();
1904
1905        if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
1906            int transX = 0;
1907            int transY = 0;
1908
1909            if (hasNoCache) {
1910                transX = -sx;
1911                transY = -sy;
1912            }
1913
1914            if (transformToApply != null) {
1915                if (concatMatrix) {
1916                    // Undo the scroll translation, apply the transformation matrix,
1917                    // then redo the scroll translate to get the correct result.
1918                    canvas.translate(-transX, -transY);
1919                    canvas.concat(transformToApply.getMatrix());
1920                    canvas.translate(transX, transY);
1921                    mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
1922                }
1923
1924                float transformAlpha = transformToApply.getAlpha();
1925                if (transformAlpha < 1.0f) {
1926                    alpha *= transformToApply.getAlpha();
1927                    mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
1928                }
1929            }
1930
1931            if (!child.hasIdentityMatrix()) {
1932                canvas.translate(-transX, -transY);
1933                canvas.concat(child.getMatrix());
1934                canvas.translate(transX, transY);
1935            }
1936
1937            if (alpha < 1.0f) {
1938                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
1939
1940                if (hasNoCache) {
1941                    final int multipliedAlpha = (int) (255 * alpha);
1942                    if (!child.onSetAlpha(multipliedAlpha)) {
1943                        canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha,
1944                                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
1945                    } else {
1946                        child.mPrivateFlags |= ALPHA_SET;
1947                    }
1948                }
1949            }
1950        } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
1951            child.onSetAlpha(255);
1952        }
1953
1954        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
1955            if (hasNoCache) {
1956                canvas.clipRect(sx, sy, sx + (cr - cl), sy + (cb - ct));
1957            } else {
1958                if (!scalingRequired) {
1959                    canvas.clipRect(0, 0, cr - cl, cb - ct);
1960                } else {
1961                    canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
1962                }
1963            }
1964        }
1965
1966        if (hasNoCache) {
1967            // Fast path for layouts with no backgrounds
1968            if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
1969                if (ViewDebug.TRACE_HIERARCHY) {
1970                    ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
1971                }
1972                child.mPrivateFlags &= ~DIRTY_MASK;
1973                child.dispatchDraw(canvas);
1974            } else {
1975                child.draw(canvas);
1976            }
1977        } else {
1978            final Paint cachePaint = mCachePaint;
1979            if (alpha < 1.0f) {
1980                cachePaint.setAlpha((int) (alpha * 255));
1981                mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
1982            } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
1983                cachePaint.setAlpha(255);
1984                mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
1985            }
1986            if (Config.DEBUG && ViewDebug.profileDrawing) {
1987                EventLog.writeEvent(60003, hashCode());
1988            }
1989            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
1990        }
1991
1992        canvas.restoreToCount(restoreTo);
1993
1994        if (a != null && !more) {
1995            child.onSetAlpha(255);
1996            finishAnimatingView(child, a);
1997        }
1998
1999        return more;
2000    }
2001
2002    /**
2003     * By default, children are clipped to their bounds before drawing. This
2004     * allows view groups to override this behavior for animations, etc.
2005     *
2006     * @param clipChildren true to clip children to their bounds,
2007     *        false otherwise
2008     * @attr ref android.R.styleable#ViewGroup_clipChildren
2009     */
2010    public void setClipChildren(boolean clipChildren) {
2011        setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
2012    }
2013
2014    /**
2015     * By default, children are clipped to the padding of the ViewGroup. This
2016     * allows view groups to override this behavior
2017     *
2018     * @param clipToPadding true to clip children to the padding of the
2019     *        group, false otherwise
2020     * @attr ref android.R.styleable#ViewGroup_clipToPadding
2021     */
2022    public void setClipToPadding(boolean clipToPadding) {
2023        setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
2024    }
2025
2026    /**
2027     * {@inheritDoc}
2028     */
2029    @Override
2030    public void dispatchSetSelected(boolean selected) {
2031        final View[] children = mChildren;
2032        final int count = mChildrenCount;
2033        for (int i = 0; i < count; i++) {
2034
2035            children[i].setSelected(selected);
2036        }
2037    }
2038
2039    @Override
2040    protected void dispatchSetPressed(boolean pressed) {
2041        final View[] children = mChildren;
2042        final int count = mChildrenCount;
2043        for (int i = 0; i < count; i++) {
2044            children[i].setPressed(pressed);
2045        }
2046    }
2047
2048    /**
2049     * When this property is set to true, this ViewGroup supports static transformations on
2050     * children; this causes
2051     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
2052     * invoked when a child is drawn.
2053     *
2054     * Any subclass overriding
2055     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
2056     * set this property to true.
2057     *
2058     * @param enabled True to enable static transformations on children, false otherwise.
2059     *
2060     * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
2061     */
2062    protected void setStaticTransformationsEnabled(boolean enabled) {
2063        setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
2064    }
2065
2066    /**
2067     * {@inheritDoc}
2068     *
2069     * @see #setStaticTransformationsEnabled(boolean)
2070     */
2071    protected boolean getChildStaticTransformation(View child, Transformation t) {
2072        return false;
2073    }
2074
2075    /**
2076     * {@hide}
2077     */
2078    @Override
2079    protected View findViewTraversal(int id) {
2080        if (id == mID) {
2081            return this;
2082        }
2083
2084        final View[] where = mChildren;
2085        final int len = mChildrenCount;
2086
2087        for (int i = 0; i < len; i++) {
2088            View v = where[i];
2089
2090            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
2091                v = v.findViewById(id);
2092
2093                if (v != null) {
2094                    return v;
2095                }
2096            }
2097        }
2098
2099        return null;
2100    }
2101
2102    /**
2103     * {@hide}
2104     */
2105    @Override
2106    protected View findViewWithTagTraversal(Object tag) {
2107        if (tag != null && tag.equals(mTag)) {
2108            return this;
2109        }
2110
2111        final View[] where = mChildren;
2112        final int len = mChildrenCount;
2113
2114        for (int i = 0; i < len; i++) {
2115            View v = where[i];
2116
2117            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
2118                v = v.findViewWithTag(tag);
2119
2120                if (v != null) {
2121                    return v;
2122                }
2123            }
2124        }
2125
2126        return null;
2127    }
2128
2129    /**
2130     * Adds a child view. If no layout parameters are already set on the child, the
2131     * default parameters for this ViewGroup are set on the child.
2132     *
2133     * @param child the child view to add
2134     *
2135     * @see #generateDefaultLayoutParams()
2136     */
2137    public void addView(View child) {
2138        addView(child, -1);
2139    }
2140
2141    /**
2142     * Adds a child view. If no layout parameters are already set on the child, the
2143     * default parameters for this ViewGroup are set on the child.
2144     *
2145     * @param child the child view to add
2146     * @param index the position at which to add the child
2147     *
2148     * @see #generateDefaultLayoutParams()
2149     */
2150    public void addView(View child, int index) {
2151        LayoutParams params = child.getLayoutParams();
2152        if (params == null) {
2153            params = generateDefaultLayoutParams();
2154            if (params == null) {
2155                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
2156            }
2157        }
2158        addView(child, index, params);
2159    }
2160
2161    /**
2162     * Adds a child view with this ViewGroup's default layout parameters and the
2163     * specified width and height.
2164     *
2165     * @param child the child view to add
2166     */
2167    public void addView(View child, int width, int height) {
2168        final LayoutParams params = generateDefaultLayoutParams();
2169        params.width = width;
2170        params.height = height;
2171        addView(child, -1, params);
2172    }
2173
2174    /**
2175     * Adds a child view with the specified layout parameters.
2176     *
2177     * @param child the child view to add
2178     * @param params the layout parameters to set on the child
2179     */
2180    public void addView(View child, LayoutParams params) {
2181        addView(child, -1, params);
2182    }
2183
2184    /**
2185     * Adds a child view with the specified layout parameters.
2186     *
2187     * @param child the child view to add
2188     * @param index the position at which to add the child
2189     * @param params the layout parameters to set on the child
2190     */
2191    public void addView(View child, int index, LayoutParams params) {
2192        if (DBG) {
2193            System.out.println(this + " addView");
2194        }
2195
2196        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
2197        // therefore, we call requestLayout() on ourselves before, so that the child's request
2198        // will be blocked at our level
2199        requestLayout();
2200        invalidate();
2201        addViewInner(child, index, params, false);
2202    }
2203
2204    /**
2205     * {@inheritDoc}
2206     */
2207    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
2208        if (!checkLayoutParams(params)) {
2209            throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
2210        }
2211        if (view.mParent != this) {
2212            throw new IllegalArgumentException("Given view not a child of " + this);
2213        }
2214        view.setLayoutParams(params);
2215    }
2216
2217    /**
2218     * {@inheritDoc}
2219     */
2220    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
2221        return  p != null;
2222    }
2223
2224    /**
2225     * Interface definition for a callback to be invoked when the hierarchy
2226     * within this view changed. The hierarchy changes whenever a child is added
2227     * to or removed from this view.
2228     */
2229    public interface OnHierarchyChangeListener {
2230        /**
2231         * Called when a new child is added to a parent view.
2232         *
2233         * @param parent the view in which a child was added
2234         * @param child the new child view added in the hierarchy
2235         */
2236        void onChildViewAdded(View parent, View child);
2237
2238        /**
2239         * Called when a child is removed from a parent view.
2240         *
2241         * @param parent the view from which the child was removed
2242         * @param child the child removed from the hierarchy
2243         */
2244        void onChildViewRemoved(View parent, View child);
2245    }
2246
2247    /**
2248     * Register a callback to be invoked when a child is added to or removed
2249     * from this view.
2250     *
2251     * @param listener the callback to invoke on hierarchy change
2252     */
2253    public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
2254        mOnHierarchyChangeListener = listener;
2255    }
2256
2257    /**
2258     * Adds a view during layout. This is useful if in your onLayout() method,
2259     * you need to add more views (as does the list view for example).
2260     *
2261     * If index is negative, it means put it at the end of the list.
2262     *
2263     * @param child the view to add to the group
2264     * @param index the index at which the child must be added
2265     * @param params the layout parameters to associate with the child
2266     * @return true if the child was added, false otherwise
2267     */
2268    protected boolean addViewInLayout(View child, int index, LayoutParams params) {
2269        return addViewInLayout(child, index, params, false);
2270    }
2271
2272    /**
2273     * Adds a view during layout. This is useful if in your onLayout() method,
2274     * you need to add more views (as does the list view for example).
2275     *
2276     * If index is negative, it means put it at the end of the list.
2277     *
2278     * @param child the view to add to the group
2279     * @param index the index at which the child must be added
2280     * @param params the layout parameters to associate with the child
2281     * @param preventRequestLayout if true, calling this method will not trigger a
2282     *        layout request on child
2283     * @return true if the child was added, false otherwise
2284     */
2285    protected boolean addViewInLayout(View child, int index, LayoutParams params,
2286            boolean preventRequestLayout) {
2287        child.mParent = null;
2288        addViewInner(child, index, params, preventRequestLayout);
2289        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
2290        return true;
2291    }
2292
2293    /**
2294     * Prevents the specified child to be laid out during the next layout pass.
2295     *
2296     * @param child the child on which to perform the cleanup
2297     */
2298    protected void cleanupLayoutState(View child) {
2299        child.mPrivateFlags &= ~View.FORCE_LAYOUT;
2300    }
2301
2302    private void addViewInner(View child, int index, LayoutParams params,
2303            boolean preventRequestLayout) {
2304
2305        if (child.getParent() != null) {
2306            throw new IllegalStateException("The specified child already has a parent. " +
2307                    "You must call removeView() on the child's parent first.");
2308        }
2309
2310        if (!checkLayoutParams(params)) {
2311            params = generateLayoutParams(params);
2312        }
2313
2314        if (preventRequestLayout) {
2315            child.mLayoutParams = params;
2316        } else {
2317            child.setLayoutParams(params);
2318        }
2319
2320        if (index < 0) {
2321            index = mChildrenCount;
2322        }
2323
2324        addInArray(child, index);
2325
2326        // tell our children
2327        if (preventRequestLayout) {
2328            child.assignParent(this);
2329        } else {
2330            child.mParent = this;
2331        }
2332
2333        if (child.hasFocus()) {
2334            requestChildFocus(child, child.findFocus());
2335        }
2336
2337        AttachInfo ai = mAttachInfo;
2338        if (ai != null) {
2339            boolean lastKeepOn = ai.mKeepScreenOn;
2340            ai.mKeepScreenOn = false;
2341            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
2342            if (ai.mKeepScreenOn) {
2343                needGlobalAttributesUpdate(true);
2344            }
2345            ai.mKeepScreenOn = lastKeepOn;
2346        }
2347
2348        if (mOnHierarchyChangeListener != null) {
2349            mOnHierarchyChangeListener.onChildViewAdded(this, child);
2350        }
2351
2352        if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
2353            mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
2354        }
2355    }
2356
2357    private void addInArray(View child, int index) {
2358        View[] children = mChildren;
2359        final int count = mChildrenCount;
2360        final int size = children.length;
2361        if (index == count) {
2362            if (size == count) {
2363                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
2364                System.arraycopy(children, 0, mChildren, 0, size);
2365                children = mChildren;
2366            }
2367            children[mChildrenCount++] = child;
2368        } else if (index < count) {
2369            if (size == count) {
2370                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
2371                System.arraycopy(children, 0, mChildren, 0, index);
2372                System.arraycopy(children, index, mChildren, index + 1, count - index);
2373                children = mChildren;
2374            } else {
2375                System.arraycopy(children, index, children, index + 1, count - index);
2376            }
2377            children[index] = child;
2378            mChildrenCount++;
2379        } else {
2380            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
2381        }
2382    }
2383
2384    // This method also sets the child's mParent to null
2385    private void removeFromArray(int index) {
2386        final View[] children = mChildren;
2387        children[index].mParent = null;
2388        final int count = mChildrenCount;
2389        if (index == count - 1) {
2390            children[--mChildrenCount] = null;
2391        } else if (index >= 0 && index < count) {
2392            System.arraycopy(children, index + 1, children, index, count - index - 1);
2393            children[--mChildrenCount] = null;
2394        } else {
2395            throw new IndexOutOfBoundsException();
2396        }
2397    }
2398
2399    // This method also sets the children's mParent to null
2400    private void removeFromArray(int start, int count) {
2401        final View[] children = mChildren;
2402        final int childrenCount = mChildrenCount;
2403
2404        start = Math.max(0, start);
2405        final int end = Math.min(childrenCount, start + count);
2406
2407        if (start == end) {
2408            return;
2409        }
2410
2411        if (end == childrenCount) {
2412            for (int i = start; i < end; i++) {
2413                children[i].mParent = null;
2414                children[i] = null;
2415            }
2416        } else {
2417            for (int i = start; i < end; i++) {
2418                children[i].mParent = null;
2419            }
2420
2421            // Since we're looping above, we might as well do the copy, but is arraycopy()
2422            // faster than the extra 2 bounds checks we would do in the loop?
2423            System.arraycopy(children, end, children, start, childrenCount - end);
2424
2425            for (int i = childrenCount - (end - start); i < childrenCount; i++) {
2426                children[i] = null;
2427            }
2428        }
2429
2430        mChildrenCount -= (end - start);
2431    }
2432
2433    private void bindLayoutAnimation(View child) {
2434        Animation a = mLayoutAnimationController.getAnimationForView(child);
2435        child.setAnimation(a);
2436    }
2437
2438    /**
2439     * Subclasses should override this method to set layout animation
2440     * parameters on the supplied child.
2441     *
2442     * @param child the child to associate with animation parameters
2443     * @param params the child's layout parameters which hold the animation
2444     *        parameters
2445     * @param index the index of the child in the view group
2446     * @param count the number of children in the view group
2447     */
2448    protected void attachLayoutAnimationParameters(View child,
2449            LayoutParams params, int index, int count) {
2450        LayoutAnimationController.AnimationParameters animationParams =
2451                    params.layoutAnimationParameters;
2452        if (animationParams == null) {
2453            animationParams = new LayoutAnimationController.AnimationParameters();
2454            params.layoutAnimationParameters = animationParams;
2455        }
2456
2457        animationParams.count = count;
2458        animationParams.index = index;
2459    }
2460
2461    /**
2462     * {@inheritDoc}
2463     */
2464    public void removeView(View view) {
2465        removeViewInternal(view);
2466        requestLayout();
2467        invalidate();
2468    }
2469
2470    /**
2471     * Removes a view during layout. This is useful if in your onLayout() method,
2472     * you need to remove more views.
2473     *
2474     * @param view the view to remove from the group
2475     */
2476    public void removeViewInLayout(View view) {
2477        removeViewInternal(view);
2478    }
2479
2480    /**
2481     * Removes a range of views during layout. This is useful if in your onLayout() method,
2482     * you need to remove more views.
2483     *
2484     * @param start the index of the first view to remove from the group
2485     * @param count the number of views to remove from the group
2486     */
2487    public void removeViewsInLayout(int start, int count) {
2488        removeViewsInternal(start, count);
2489    }
2490
2491    /**
2492     * Removes the view at the specified position in the group.
2493     *
2494     * @param index the position in the group of the view to remove
2495     */
2496    public void removeViewAt(int index) {
2497        removeViewInternal(index, getChildAt(index));
2498        requestLayout();
2499        invalidate();
2500    }
2501
2502    /**
2503     * Removes the specified range of views from the group.
2504     *
2505     * @param start the first position in the group of the range of views to remove
2506     * @param count the number of views to remove
2507     */
2508    public void removeViews(int start, int count) {
2509        removeViewsInternal(start, count);
2510        requestLayout();
2511        invalidate();
2512    }
2513
2514    private void removeViewInternal(View view) {
2515        final int index = indexOfChild(view);
2516        if (index >= 0) {
2517            removeViewInternal(index, view);
2518        }
2519    }
2520
2521    private void removeViewInternal(int index, View view) {
2522        boolean clearChildFocus = false;
2523        if (view == mFocused) {
2524            view.clearFocusForRemoval();
2525            clearChildFocus = true;
2526        }
2527
2528        if (view.getAnimation() != null) {
2529            addDisappearingView(view);
2530        } else if (view.mAttachInfo != null) {
2531           view.dispatchDetachedFromWindow();
2532        }
2533
2534        if (mOnHierarchyChangeListener != null) {
2535            mOnHierarchyChangeListener.onChildViewRemoved(this, view);
2536        }
2537
2538        needGlobalAttributesUpdate(false);
2539
2540        removeFromArray(index);
2541
2542        if (clearChildFocus) {
2543            clearChildFocus(view);
2544        }
2545    }
2546
2547    private void removeViewsInternal(int start, int count) {
2548        final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
2549        final boolean notifyListener = onHierarchyChangeListener != null;
2550        final View focused = mFocused;
2551        final boolean detach = mAttachInfo != null;
2552        View clearChildFocus = null;
2553
2554        final View[] children = mChildren;
2555        final int end = start + count;
2556
2557        for (int i = start; i < end; i++) {
2558            final View view = children[i];
2559
2560            if (view == focused) {
2561                view.clearFocusForRemoval();
2562                clearChildFocus = view;
2563            }
2564
2565            if (view.getAnimation() != null) {
2566                addDisappearingView(view);
2567            } else if (detach) {
2568               view.dispatchDetachedFromWindow();
2569            }
2570
2571            needGlobalAttributesUpdate(false);
2572
2573            if (notifyListener) {
2574                onHierarchyChangeListener.onChildViewRemoved(this, view);
2575            }
2576        }
2577
2578        removeFromArray(start, count);
2579
2580        if (clearChildFocus != null) {
2581            clearChildFocus(clearChildFocus);
2582        }
2583    }
2584
2585    /**
2586     * Call this method to remove all child views from the
2587     * ViewGroup.
2588     */
2589    public void removeAllViews() {
2590        removeAllViewsInLayout();
2591        requestLayout();
2592        invalidate();
2593    }
2594
2595    /**
2596     * Called by a ViewGroup subclass to remove child views from itself,
2597     * when it must first know its size on screen before it can calculate how many
2598     * child views it will render. An example is a Gallery or a ListView, which
2599     * may "have" 50 children, but actually only render the number of children
2600     * that can currently fit inside the object on screen. Do not call
2601     * this method unless you are extending ViewGroup and understand the
2602     * view measuring and layout pipeline.
2603     */
2604    public void removeAllViewsInLayout() {
2605        final int count = mChildrenCount;
2606        if (count <= 0) {
2607            return;
2608        }
2609
2610        final View[] children = mChildren;
2611        mChildrenCount = 0;
2612
2613        final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
2614        final boolean notify = listener != null;
2615        final View focused = mFocused;
2616        final boolean detach = mAttachInfo != null;
2617        View clearChildFocus = null;
2618
2619        needGlobalAttributesUpdate(false);
2620
2621        for (int i = count - 1; i >= 0; i--) {
2622            final View view = children[i];
2623
2624            if (view == focused) {
2625                view.clearFocusForRemoval();
2626                clearChildFocus = view;
2627            }
2628
2629            if (view.getAnimation() != null) {
2630                addDisappearingView(view);
2631            } else if (detach) {
2632               view.dispatchDetachedFromWindow();
2633            }
2634
2635            if (notify) {
2636                listener.onChildViewRemoved(this, view);
2637            }
2638
2639            view.mParent = null;
2640            children[i] = null;
2641        }
2642
2643        if (clearChildFocus != null) {
2644            clearChildFocus(clearChildFocus);
2645        }
2646    }
2647
2648    /**
2649     * Finishes the removal of a detached view. This method will dispatch the detached from
2650     * window event and notify the hierarchy change listener.
2651     *
2652     * @param child the child to be definitely removed from the view hierarchy
2653     * @param animate if true and the view has an animation, the view is placed in the
2654     *                disappearing views list, otherwise, it is detached from the window
2655     *
2656     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2657     * @see #detachAllViewsFromParent()
2658     * @see #detachViewFromParent(View)
2659     * @see #detachViewFromParent(int)
2660     */
2661    protected void removeDetachedView(View child, boolean animate) {
2662        if (child == mFocused) {
2663            child.clearFocus();
2664        }
2665
2666        if (animate && child.getAnimation() != null) {
2667            addDisappearingView(child);
2668        } else if (child.mAttachInfo != null) {
2669            child.dispatchDetachedFromWindow();
2670        }
2671
2672        if (mOnHierarchyChangeListener != null) {
2673            mOnHierarchyChangeListener.onChildViewRemoved(this, child);
2674        }
2675    }
2676
2677    /**
2678     * Attaches a view to this view group. Attaching a view assigns this group as the parent,
2679     * sets the layout parameters and puts the view in the list of children so it can be retrieved
2680     * by calling {@link #getChildAt(int)}.
2681     *
2682     * This method should be called only for view which were detached from their parent.
2683     *
2684     * @param child the child to attach
2685     * @param index the index at which the child should be attached
2686     * @param params the layout parameters of the child
2687     *
2688     * @see #removeDetachedView(View, boolean)
2689     * @see #detachAllViewsFromParent()
2690     * @see #detachViewFromParent(View)
2691     * @see #detachViewFromParent(int)
2692     */
2693    protected void attachViewToParent(View child, int index, LayoutParams params) {
2694        child.mLayoutParams = params;
2695
2696        if (index < 0) {
2697            index = mChildrenCount;
2698        }
2699
2700        addInArray(child, index);
2701
2702        child.mParent = this;
2703        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK & ~DRAWING_CACHE_VALID) | DRAWN;
2704
2705        if (child.hasFocus()) {
2706            requestChildFocus(child, child.findFocus());
2707        }
2708    }
2709
2710    /**
2711     * Detaches a view from its parent. Detaching a view should be temporary and followed
2712     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2713     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
2714     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2715     *
2716     * @param child the child to detach
2717     *
2718     * @see #detachViewFromParent(int)
2719     * @see #detachViewsFromParent(int, int)
2720     * @see #detachAllViewsFromParent()
2721     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2722     * @see #removeDetachedView(View, boolean)
2723     */
2724    protected void detachViewFromParent(View child) {
2725        removeFromArray(indexOfChild(child));
2726    }
2727
2728    /**
2729     * Detaches a view from its parent. Detaching a view should be temporary and followed
2730     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2731     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
2732     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2733     *
2734     * @param index the index of the child to detach
2735     *
2736     * @see #detachViewFromParent(View)
2737     * @see #detachAllViewsFromParent()
2738     * @see #detachViewsFromParent(int, int)
2739     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2740     * @see #removeDetachedView(View, boolean)
2741     */
2742    protected void detachViewFromParent(int index) {
2743        removeFromArray(index);
2744    }
2745
2746    /**
2747     * Detaches a range of view from their parent. Detaching a view should be temporary and followed
2748     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2749     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
2750     * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2751     *
2752     * @param start the first index of the childrend range to detach
2753     * @param count the number of children to detach
2754     *
2755     * @see #detachViewFromParent(View)
2756     * @see #detachViewFromParent(int)
2757     * @see #detachAllViewsFromParent()
2758     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2759     * @see #removeDetachedView(View, boolean)
2760     */
2761    protected void detachViewsFromParent(int start, int count) {
2762        removeFromArray(start, count);
2763    }
2764
2765    /**
2766     * Detaches all views from the parent. Detaching a view should be temporary and followed
2767     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
2768     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
2769     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
2770     *
2771     * @see #detachViewFromParent(View)
2772     * @see #detachViewFromParent(int)
2773     * @see #detachViewsFromParent(int, int)
2774     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
2775     * @see #removeDetachedView(View, boolean)
2776     */
2777    protected void detachAllViewsFromParent() {
2778        final int count = mChildrenCount;
2779        if (count <= 0) {
2780            return;
2781        }
2782
2783        final View[] children = mChildren;
2784        mChildrenCount = 0;
2785
2786        for (int i = count - 1; i >= 0; i--) {
2787            children[i].mParent = null;
2788            children[i] = null;
2789        }
2790    }
2791
2792    /**
2793     * Don't call or override this method. It is used for the implementation of
2794     * the view hierarchy.
2795     */
2796    public final void invalidateChild(View child, final Rect dirty) {
2797        if (ViewDebug.TRACE_HIERARCHY) {
2798            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
2799        }
2800
2801        ViewParent parent = this;
2802
2803        final AttachInfo attachInfo = mAttachInfo;
2804        if (attachInfo != null) {
2805            final int[] location = attachInfo.mInvalidateChildLocation;
2806            location[CHILD_LEFT_INDEX] = child.mLeft;
2807            location[CHILD_TOP_INDEX] = child.mTop;
2808            Matrix childMatrix = child.getMatrix();
2809            if (!childMatrix.isIdentity()) {
2810                RectF boundingRect = attachInfo.mTmpTransformRect;
2811                boundingRect.set(dirty);
2812                childMatrix.mapRect(boundingRect);
2813                dirty.set((int) boundingRect.left, (int) boundingRect.top,
2814                        (int) (boundingRect.right + 0.5f),
2815                        (int) (boundingRect.bottom + 0.5f));
2816            }
2817
2818            // If the child is drawing an animation, we want to copy this flag onto
2819            // ourselves and the parent to make sure the invalidate request goes
2820            // through
2821            final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
2822
2823            // Check whether the child that requests the invalidate is fully opaque
2824            final boolean isOpaque = child.isOpaque() && !drawAnimation &&
2825                    child.getAnimation() != null;
2826            // Mark the child as dirty, using the appropriate flag
2827            // Make sure we do not set both flags at the same time
2828            final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
2829
2830            do {
2831                View view = null;
2832                if (parent instanceof View) {
2833                    view = (View) parent;
2834                }
2835
2836                if (drawAnimation) {
2837                    if (view != null) {
2838                        view.mPrivateFlags |= DRAW_ANIMATION;
2839                    } else if (parent instanceof ViewRoot) {
2840                        ((ViewRoot) parent).mIsAnimating = true;
2841                    }
2842                }
2843
2844                // If the parent is dirty opaque or not dirty, mark it dirty with the opaque
2845                // flag coming from the child that initiated the invalidate
2846                if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
2847                    view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
2848                }
2849
2850                parent = parent.invalidateChildInParent(location, dirty);
2851                if (view != null) {
2852                    // Account for transform on current parent
2853                    Matrix m = view.getMatrix();
2854                    if (!m.isIdentity()) {
2855                        RectF boundingRect = attachInfo.mTmpTransformRect;
2856                        boundingRect.set(dirty);
2857                        m.mapRect(boundingRect);
2858                        dirty.set((int) boundingRect.left, (int) boundingRect.top,
2859                                (int) (boundingRect.right + 0.5f),
2860                                (int) (boundingRect.bottom + 0.5f));
2861                    }
2862                }
2863            } while (parent != null);
2864        }
2865    }
2866
2867    /**
2868     * Don't call or override this method. It is used for the implementation of
2869     * the view hierarchy.
2870     *
2871     * This implementation returns null if this ViewGroup does not have a parent,
2872     * if this ViewGroup is already fully invalidated or if the dirty rectangle
2873     * does not intersect with this ViewGroup's bounds.
2874     */
2875    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
2876        if (ViewDebug.TRACE_HIERARCHY) {
2877            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
2878        }
2879
2880        if ((mPrivateFlags & DRAWN) == DRAWN) {
2881            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
2882                        FLAG_OPTIMIZE_INVALIDATE) {
2883                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
2884                        location[CHILD_TOP_INDEX] - mScrollY);
2885
2886                final int left = mLeft;
2887                final int top = mTop;
2888
2889                if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
2890                        (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
2891                    mPrivateFlags &= ~DRAWING_CACHE_VALID;
2892
2893                    location[CHILD_LEFT_INDEX] = left;
2894                    location[CHILD_TOP_INDEX] = top;
2895
2896                    return mParent;
2897                }
2898            } else {
2899                mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
2900
2901                location[CHILD_LEFT_INDEX] = mLeft;
2902                location[CHILD_TOP_INDEX] = mTop;
2903
2904                dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX],
2905                        mBottom - location[CHILD_TOP_INDEX]);
2906
2907                return mParent;
2908            }
2909        }
2910
2911        return null;
2912    }
2913
2914    /**
2915     * Offset a rectangle that is in a descendant's coordinate
2916     * space into our coordinate space.
2917     * @param descendant A descendant of this view
2918     * @param rect A rectangle defined in descendant's coordinate space.
2919     */
2920    public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
2921        offsetRectBetweenParentAndChild(descendant, rect, true, false);
2922    }
2923
2924    /**
2925     * Offset a rectangle that is in our coordinate space into an ancestor's
2926     * coordinate space.
2927     * @param descendant A descendant of this view
2928     * @param rect A rectangle defined in descendant's coordinate space.
2929     */
2930    public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
2931        offsetRectBetweenParentAndChild(descendant, rect, false, false);
2932    }
2933
2934    /**
2935     * Helper method that offsets a rect either from parent to descendant or
2936     * descendant to parent.
2937     */
2938    void offsetRectBetweenParentAndChild(View descendant, Rect rect,
2939            boolean offsetFromChildToParent, boolean clipToBounds) {
2940
2941        // already in the same coord system :)
2942        if (descendant == this) {
2943            return;
2944        }
2945
2946        ViewParent theParent = descendant.mParent;
2947
2948        // search and offset up to the parent
2949        while ((theParent != null)
2950                && (theParent instanceof View)
2951                && (theParent != this)) {
2952
2953            if (offsetFromChildToParent) {
2954                rect.offset(descendant.mLeft - descendant.mScrollX,
2955                        descendant.mTop - descendant.mScrollY);
2956                if (clipToBounds) {
2957                    View p = (View) theParent;
2958                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
2959                }
2960            } else {
2961                if (clipToBounds) {
2962                    View p = (View) theParent;
2963                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
2964                }
2965                rect.offset(descendant.mScrollX - descendant.mLeft,
2966                        descendant.mScrollY - descendant.mTop);
2967            }
2968
2969            descendant = (View) theParent;
2970            theParent = descendant.mParent;
2971        }
2972
2973        // now that we are up to this view, need to offset one more time
2974        // to get into our coordinate space
2975        if (theParent == this) {
2976            if (offsetFromChildToParent) {
2977                rect.offset(descendant.mLeft - descendant.mScrollX,
2978                        descendant.mTop - descendant.mScrollY);
2979            } else {
2980                rect.offset(descendant.mScrollX - descendant.mLeft,
2981                        descendant.mScrollY - descendant.mTop);
2982            }
2983        } else {
2984            throw new IllegalArgumentException("parameter must be a descendant of this view");
2985        }
2986    }
2987
2988    /**
2989     * Offset the vertical location of all children of this view by the specified number of pixels.
2990     *
2991     * @param offset the number of pixels to offset
2992     *
2993     * @hide
2994     */
2995    public void offsetChildrenTopAndBottom(int offset) {
2996        final int count = mChildrenCount;
2997        final View[] children = mChildren;
2998
2999        for (int i = 0; i < count; i++) {
3000            final View v = children[i];
3001            v.mTop += offset;
3002            v.mBottom += offset;
3003        }
3004    }
3005
3006    /**
3007     * {@inheritDoc}
3008     */
3009    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
3010        int dx = child.mLeft - mScrollX;
3011        int dy = child.mTop - mScrollY;
3012        if (offset != null) {
3013            offset.x += dx;
3014            offset.y += dy;
3015        }
3016        r.offset(dx, dy);
3017        return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
3018               (mParent == null || mParent.getChildVisibleRect(this, r, offset));
3019    }
3020
3021    /**
3022     * {@inheritDoc}
3023     */
3024    @Override
3025    protected abstract void onLayout(boolean changed,
3026            int l, int t, int r, int b);
3027
3028    /**
3029     * Indicates whether the view group has the ability to animate its children
3030     * after the first layout.
3031     *
3032     * @return true if the children can be animated, false otherwise
3033     */
3034    protected boolean canAnimate() {
3035        return mLayoutAnimationController != null;
3036    }
3037
3038    /**
3039     * Runs the layout animation. Calling this method triggers a relayout of
3040     * this view group.
3041     */
3042    public void startLayoutAnimation() {
3043        if (mLayoutAnimationController != null) {
3044            mGroupFlags |= FLAG_RUN_ANIMATION;
3045            requestLayout();
3046        }
3047    }
3048
3049    /**
3050     * Schedules the layout animation to be played after the next layout pass
3051     * of this view group. This can be used to restart the layout animation
3052     * when the content of the view group changes or when the activity is
3053     * paused and resumed.
3054     */
3055    public void scheduleLayoutAnimation() {
3056        mGroupFlags |= FLAG_RUN_ANIMATION;
3057    }
3058
3059    /**
3060     * Sets the layout animation controller used to animate the group's
3061     * children after the first layout.
3062     *
3063     * @param controller the animation controller
3064     */
3065    public void setLayoutAnimation(LayoutAnimationController controller) {
3066        mLayoutAnimationController = controller;
3067        if (mLayoutAnimationController != null) {
3068            mGroupFlags |= FLAG_RUN_ANIMATION;
3069        }
3070    }
3071
3072    /**
3073     * Returns the layout animation controller used to animate the group's
3074     * children.
3075     *
3076     * @return the current animation controller
3077     */
3078    public LayoutAnimationController getLayoutAnimation() {
3079        return mLayoutAnimationController;
3080    }
3081
3082    /**
3083     * Indicates whether the children's drawing cache is used during a layout
3084     * animation. By default, the drawing cache is enabled but this will prevent
3085     * nested layout animations from working. To nest animations, you must disable
3086     * the cache.
3087     *
3088     * @return true if the animation cache is enabled, false otherwise
3089     *
3090     * @see #setAnimationCacheEnabled(boolean)
3091     * @see View#setDrawingCacheEnabled(boolean)
3092     */
3093    @ViewDebug.ExportedProperty
3094    public boolean isAnimationCacheEnabled() {
3095        return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
3096    }
3097
3098    /**
3099     * Enables or disables the children's drawing cache during a layout animation.
3100     * By default, the drawing cache is enabled but this will prevent nested
3101     * layout animations from working. To nest animations, you must disable the
3102     * cache.
3103     *
3104     * @param enabled true to enable the animation cache, false otherwise
3105     *
3106     * @see #isAnimationCacheEnabled()
3107     * @see View#setDrawingCacheEnabled(boolean)
3108     */
3109    public void setAnimationCacheEnabled(boolean enabled) {
3110        setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
3111    }
3112
3113    /**
3114     * Indicates whether this ViewGroup will always try to draw its children using their
3115     * drawing cache. By default this property is enabled.
3116     *
3117     * @return true if the animation cache is enabled, false otherwise
3118     *
3119     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
3120     * @see #setChildrenDrawnWithCacheEnabled(boolean)
3121     * @see View#setDrawingCacheEnabled(boolean)
3122     */
3123    @ViewDebug.ExportedProperty(category = "drawing")
3124    public boolean isAlwaysDrawnWithCacheEnabled() {
3125        return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
3126    }
3127
3128    /**
3129     * Indicates whether this ViewGroup will always try to draw its children using their
3130     * drawing cache. This property can be set to true when the cache rendering is
3131     * slightly different from the children's normal rendering. Renderings can be different,
3132     * for instance, when the cache's quality is set to low.
3133     *
3134     * When this property is disabled, the ViewGroup will use the drawing cache of its
3135     * children only when asked to. It's usually the task of subclasses to tell ViewGroup
3136     * when to start using the drawing cache and when to stop using it.
3137     *
3138     * @param always true to always draw with the drawing cache, false otherwise
3139     *
3140     * @see #isAlwaysDrawnWithCacheEnabled()
3141     * @see #setChildrenDrawnWithCacheEnabled(boolean)
3142     * @see View#setDrawingCacheEnabled(boolean)
3143     * @see View#setDrawingCacheQuality(int)
3144     */
3145    public void setAlwaysDrawnWithCacheEnabled(boolean always) {
3146        setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
3147    }
3148
3149    /**
3150     * Indicates whether the ViewGroup is currently drawing its children using
3151     * their drawing cache.
3152     *
3153     * @return true if children should be drawn with their cache, false otherwise
3154     *
3155     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
3156     * @see #setChildrenDrawnWithCacheEnabled(boolean)
3157     */
3158    @ViewDebug.ExportedProperty(category = "drawing")
3159    protected boolean isChildrenDrawnWithCacheEnabled() {
3160        return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
3161    }
3162
3163    /**
3164     * Tells the ViewGroup to draw its children using their drawing cache. This property
3165     * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
3166     * will be used only if it has been enabled.
3167     *
3168     * Subclasses should call this method to start and stop using the drawing cache when
3169     * they perform performance sensitive operations, like scrolling or animating.
3170     *
3171     * @param enabled true if children should be drawn with their cache, false otherwise
3172     *
3173     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
3174     * @see #isChildrenDrawnWithCacheEnabled()
3175     */
3176    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
3177        setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
3178    }
3179
3180    /**
3181     * Indicates whether the ViewGroup is drawing its children in the order defined by
3182     * {@link #getChildDrawingOrder(int, int)}.
3183     *
3184     * @return true if children drawing order is defined by {@link #getChildDrawingOrder(int, int)},
3185     *         false otherwise
3186     *
3187     * @see #setChildrenDrawingOrderEnabled(boolean)
3188     * @see #getChildDrawingOrder(int, int)
3189     */
3190    @ViewDebug.ExportedProperty(category = "drawing")
3191    protected boolean isChildrenDrawingOrderEnabled() {
3192        return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER;
3193    }
3194
3195    /**
3196     * Tells the ViewGroup whether to draw its children in the order defined by the method
3197     * {@link #getChildDrawingOrder(int, int)}.
3198     *
3199     * @param enabled true if the order of the children when drawing is determined by
3200     *        {@link #getChildDrawingOrder(int, int)}, false otherwise
3201     *
3202     * @see #isChildrenDrawingOrderEnabled()
3203     * @see #getChildDrawingOrder(int, int)
3204     */
3205    protected void setChildrenDrawingOrderEnabled(boolean enabled) {
3206        setBooleanFlag(FLAG_USE_CHILD_DRAWING_ORDER, enabled);
3207    }
3208
3209    private void setBooleanFlag(int flag, boolean value) {
3210        if (value) {
3211            mGroupFlags |= flag;
3212        } else {
3213            mGroupFlags &= ~flag;
3214        }
3215    }
3216
3217    /**
3218     * Returns an integer indicating what types of drawing caches are kept in memory.
3219     *
3220     * @see #setPersistentDrawingCache(int)
3221     * @see #setAnimationCacheEnabled(boolean)
3222     *
3223     * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
3224     *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
3225     *         and {@link #PERSISTENT_ALL_CACHES}
3226     */
3227    @ViewDebug.ExportedProperty(category = "drawing", mapping = {
3228        @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
3229        @ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
3230        @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
3231        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
3232    })
3233    public int getPersistentDrawingCache() {
3234        return mPersistentDrawingCache;
3235    }
3236
3237    /**
3238     * Indicates what types of drawing caches should be kept in memory after
3239     * they have been created.
3240     *
3241     * @see #getPersistentDrawingCache()
3242     * @see #setAnimationCacheEnabled(boolean)
3243     *
3244     * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
3245     *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
3246     *        and {@link #PERSISTENT_ALL_CACHES}
3247     */
3248    public void setPersistentDrawingCache(int drawingCacheToKeep) {
3249        mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
3250    }
3251
3252    /**
3253     * Returns a new set of layout parameters based on the supplied attributes set.
3254     *
3255     * @param attrs the attributes to build the layout parameters from
3256     *
3257     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
3258     *         of its descendants
3259     */
3260    public LayoutParams generateLayoutParams(AttributeSet attrs) {
3261        return new LayoutParams(getContext(), attrs);
3262    }
3263
3264    /**
3265     * Returns a safe set of layout parameters based on the supplied layout params.
3266     * When a ViewGroup is passed a View whose layout params do not pass the test of
3267     * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
3268     * is invoked. This method should return a new set of layout params suitable for
3269     * this ViewGroup, possibly by copying the appropriate attributes from the
3270     * specified set of layout params.
3271     *
3272     * @param p The layout parameters to convert into a suitable set of layout parameters
3273     *          for this ViewGroup.
3274     *
3275     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
3276     *         of its descendants
3277     */
3278    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
3279        return p;
3280    }
3281
3282    /**
3283     * Returns a set of default layout parameters. These parameters are requested
3284     * when the View passed to {@link #addView(View)} has no layout parameters
3285     * already set. If null is returned, an exception is thrown from addView.
3286     *
3287     * @return a set of default layout parameters or null
3288     */
3289    protected LayoutParams generateDefaultLayoutParams() {
3290        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
3291    }
3292
3293    /**
3294     * @hide
3295     */
3296    @Override
3297    protected boolean dispatchConsistencyCheck(int consistency) {
3298        boolean result = super.dispatchConsistencyCheck(consistency);
3299
3300        final int count = mChildrenCount;
3301        final View[] children = mChildren;
3302        for (int i = 0; i < count; i++) {
3303            if (!children[i].dispatchConsistencyCheck(consistency)) result = false;
3304        }
3305
3306        return result;
3307    }
3308
3309    /**
3310     * @hide
3311     */
3312    @Override
3313    protected boolean onConsistencyCheck(int consistency) {
3314        boolean result = super.onConsistencyCheck(consistency);
3315
3316        final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
3317        final boolean checkDrawing = (consistency & ViewDebug.CONSISTENCY_DRAWING) != 0;
3318
3319        if (checkLayout) {
3320            final int count = mChildrenCount;
3321            final View[] children = mChildren;
3322            for (int i = 0; i < count; i++) {
3323                if (children[i].getParent() != this) {
3324                    result = false;
3325                    android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
3326                            "View " + children[i] + " has no parent/a parent that is not " + this);
3327                }
3328            }
3329        }
3330
3331        if (checkDrawing) {
3332            // If this group is dirty, check that the parent is dirty as well
3333            if ((mPrivateFlags & DIRTY_MASK) != 0) {
3334                final ViewParent parent = getParent();
3335                if (parent != null && !(parent instanceof ViewRoot)) {
3336                    if ((((View) parent).mPrivateFlags & DIRTY_MASK) == 0) {
3337                        result = false;
3338                        android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
3339                                "ViewGroup " + this + " is dirty but its parent is not: " + this);
3340                    }
3341                }
3342            }
3343        }
3344
3345        return result;
3346    }
3347
3348    /**
3349     * {@inheritDoc}
3350     */
3351    @Override
3352    protected void debug(int depth) {
3353        super.debug(depth);
3354        String output;
3355
3356        if (mFocused != null) {
3357            output = debugIndent(depth);
3358            output += "mFocused";
3359            Log.d(VIEW_LOG_TAG, output);
3360        }
3361        if (mChildrenCount != 0) {
3362            output = debugIndent(depth);
3363            output += "{";
3364            Log.d(VIEW_LOG_TAG, output);
3365        }
3366        int count = mChildrenCount;
3367        for (int i = 0; i < count; i++) {
3368            View child = mChildren[i];
3369            child.debug(depth + 1);
3370        }
3371
3372        if (mChildrenCount != 0) {
3373            output = debugIndent(depth);
3374            output += "}";
3375            Log.d(VIEW_LOG_TAG, output);
3376        }
3377    }
3378
3379    /**
3380     * Returns the position in the group of the specified child view.
3381     *
3382     * @param child the view for which to get the position
3383     * @return a positive integer representing the position of the view in the
3384     *         group, or -1 if the view does not exist in the group
3385     */
3386    public int indexOfChild(View child) {
3387        final int count = mChildrenCount;
3388        final View[] children = mChildren;
3389        for (int i = 0; i < count; i++) {
3390            if (children[i] == child) {
3391                return i;
3392            }
3393        }
3394        return -1;
3395    }
3396
3397    /**
3398     * Returns the number of children in the group.
3399     *
3400     * @return a positive integer representing the number of children in
3401     *         the group
3402     */
3403    public int getChildCount() {
3404        return mChildrenCount;
3405    }
3406
3407    /**
3408     * Returns the view at the specified position in the group.
3409     *
3410     * @param index the position at which to get the view from
3411     * @return the view at the specified position or null if the position
3412     *         does not exist within the group
3413     */
3414    public View getChildAt(int index) {
3415        try {
3416            return mChildren[index];
3417        } catch (IndexOutOfBoundsException ex) {
3418            return null;
3419        }
3420    }
3421
3422    /**
3423     * Ask all of the children of this view to measure themselves, taking into
3424     * account both the MeasureSpec requirements for this view and its padding.
3425     * We skip children that are in the GONE state The heavy lifting is done in
3426     * getChildMeasureSpec.
3427     *
3428     * @param widthMeasureSpec The width requirements for this view
3429     * @param heightMeasureSpec The height requirements for this view
3430     */
3431    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
3432        final int size = mChildrenCount;
3433        final View[] children = mChildren;
3434        for (int i = 0; i < size; ++i) {
3435            final View child = children[i];
3436            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
3437                measureChild(child, widthMeasureSpec, heightMeasureSpec);
3438            }
3439        }
3440    }
3441
3442    /**
3443     * Ask one of the children of this view to measure itself, taking into
3444     * account both the MeasureSpec requirements for this view and its padding.
3445     * The heavy lifting is done in getChildMeasureSpec.
3446     *
3447     * @param child The child to measure
3448     * @param parentWidthMeasureSpec The width requirements for this view
3449     * @param parentHeightMeasureSpec The height requirements for this view
3450     */
3451    protected void measureChild(View child, int parentWidthMeasureSpec,
3452            int parentHeightMeasureSpec) {
3453        final LayoutParams lp = child.getLayoutParams();
3454
3455        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
3456                mPaddingLeft + mPaddingRight, lp.width);
3457        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
3458                mPaddingTop + mPaddingBottom, lp.height);
3459
3460        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
3461    }
3462
3463    /**
3464     * Ask one of the children of this view to measure itself, taking into
3465     * account both the MeasureSpec requirements for this view and its padding
3466     * and margins. The child must have MarginLayoutParams The heavy lifting is
3467     * done in getChildMeasureSpec.
3468     *
3469     * @param child The child to measure
3470     * @param parentWidthMeasureSpec The width requirements for this view
3471     * @param widthUsed Extra space that has been used up by the parent
3472     *        horizontally (possibly by other children of the parent)
3473     * @param parentHeightMeasureSpec The height requirements for this view
3474     * @param heightUsed Extra space that has been used up by the parent
3475     *        vertically (possibly by other children of the parent)
3476     */
3477    protected void measureChildWithMargins(View child,
3478            int parentWidthMeasureSpec, int widthUsed,
3479            int parentHeightMeasureSpec, int heightUsed) {
3480        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
3481
3482        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
3483                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
3484                        + widthUsed, lp.width);
3485        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
3486                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
3487                        + heightUsed, lp.height);
3488
3489        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
3490    }
3491
3492    /**
3493     * Does the hard part of measureChildren: figuring out the MeasureSpec to
3494     * pass to a particular child. This method figures out the right MeasureSpec
3495     * for one dimension (height or width) of one child view.
3496     *
3497     * The goal is to combine information from our MeasureSpec with the
3498     * LayoutParams of the child to get the best possible results. For example,
3499     * if the this view knows its size (because its MeasureSpec has a mode of
3500     * EXACTLY), and the child has indicated in its LayoutParams that it wants
3501     * to be the same size as the parent, the parent should ask the child to
3502     * layout given an exact size.
3503     *
3504     * @param spec The requirements for this view
3505     * @param padding The padding of this view for the current dimension and
3506     *        margins, if applicable
3507     * @param childDimension How big the child wants to be in the current
3508     *        dimension
3509     * @return a MeasureSpec integer for the child
3510     */
3511    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
3512        int specMode = MeasureSpec.getMode(spec);
3513        int specSize = MeasureSpec.getSize(spec);
3514
3515        int size = Math.max(0, specSize - padding);
3516
3517        int resultSize = 0;
3518        int resultMode = 0;
3519
3520        switch (specMode) {
3521        // Parent has imposed an exact size on us
3522        case MeasureSpec.EXACTLY:
3523            if (childDimension >= 0) {
3524                resultSize = childDimension;
3525                resultMode = MeasureSpec.EXACTLY;
3526            } else if (childDimension == LayoutParams.MATCH_PARENT) {
3527                // Child wants to be our size. So be it.
3528                resultSize = size;
3529                resultMode = MeasureSpec.EXACTLY;
3530            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
3531                // Child wants to determine its own size. It can't be
3532                // bigger than us.
3533                resultSize = size;
3534                resultMode = MeasureSpec.AT_MOST;
3535            }
3536            break;
3537
3538        // Parent has imposed a maximum size on us
3539        case MeasureSpec.AT_MOST:
3540            if (childDimension >= 0) {
3541                // Child wants a specific size... so be it
3542                resultSize = childDimension;
3543                resultMode = MeasureSpec.EXACTLY;
3544            } else if (childDimension == LayoutParams.MATCH_PARENT) {
3545                // Child wants to be our size, but our size is not fixed.
3546                // Constrain child to not be bigger than us.
3547                resultSize = size;
3548                resultMode = MeasureSpec.AT_MOST;
3549            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
3550                // Child wants to determine its own size. It can't be
3551                // bigger than us.
3552                resultSize = size;
3553                resultMode = MeasureSpec.AT_MOST;
3554            }
3555            break;
3556
3557        // Parent asked to see how big we want to be
3558        case MeasureSpec.UNSPECIFIED:
3559            if (childDimension >= 0) {
3560                // Child wants a specific size... let him have it
3561                resultSize = childDimension;
3562                resultMode = MeasureSpec.EXACTLY;
3563            } else if (childDimension == LayoutParams.MATCH_PARENT) {
3564                // Child wants to be our size... find out how big it should
3565                // be
3566                resultSize = 0;
3567                resultMode = MeasureSpec.UNSPECIFIED;
3568            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
3569                // Child wants to determine its own size.... find out how
3570                // big it should be
3571                resultSize = 0;
3572                resultMode = MeasureSpec.UNSPECIFIED;
3573            }
3574            break;
3575        }
3576        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
3577    }
3578
3579
3580    /**
3581     * Removes any pending animations for views that have been removed. Call
3582     * this if you don't want animations for exiting views to stack up.
3583     */
3584    public void clearDisappearingChildren() {
3585        if (mDisappearingChildren != null) {
3586            mDisappearingChildren.clear();
3587        }
3588    }
3589
3590    /**
3591     * Add a view which is removed from mChildren but still needs animation
3592     *
3593     * @param v View to add
3594     */
3595    private void addDisappearingView(View v) {
3596        ArrayList<View> disappearingChildren = mDisappearingChildren;
3597
3598        if (disappearingChildren == null) {
3599            disappearingChildren = mDisappearingChildren = new ArrayList<View>();
3600        }
3601
3602        disappearingChildren.add(v);
3603    }
3604
3605    /**
3606     * Cleanup a view when its animation is done. This may mean removing it from
3607     * the list of disappearing views.
3608     *
3609     * @param view The view whose animation has finished
3610     * @param animation The animation, cannot be null
3611     */
3612    private void finishAnimatingView(final View view, Animation animation) {
3613        final ArrayList<View> disappearingChildren = mDisappearingChildren;
3614        if (disappearingChildren != null) {
3615            if (disappearingChildren.contains(view)) {
3616                disappearingChildren.remove(view);
3617
3618                if (view.mAttachInfo != null) {
3619                    view.dispatchDetachedFromWindow();
3620                }
3621
3622                view.clearAnimation();
3623                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
3624            }
3625        }
3626
3627        if (animation != null && !animation.getFillAfter()) {
3628            view.clearAnimation();
3629        }
3630
3631        if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
3632            view.onAnimationEnd();
3633            // Should be performed by onAnimationEnd() but this avoid an infinite loop,
3634            // so we'd rather be safe than sorry
3635            view.mPrivateFlags &= ~ANIMATION_STARTED;
3636            // Draw one more frame after the animation is done
3637            mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
3638        }
3639    }
3640
3641    /**
3642     * {@inheritDoc}
3643     */
3644    @Override
3645    public boolean gatherTransparentRegion(Region region) {
3646        // If no transparent regions requested, we are always opaque.
3647        final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
3648        if (meOpaque && region == null) {
3649            // The caller doesn't care about the region, so stop now.
3650            return true;
3651        }
3652        super.gatherTransparentRegion(region);
3653        final View[] children = mChildren;
3654        final int count = mChildrenCount;
3655        boolean noneOfTheChildrenAreTransparent = true;
3656        for (int i = 0; i < count; i++) {
3657            final View child = children[i];
3658            if ((child.mViewFlags & VISIBILITY_MASK) != GONE || child.getAnimation() != null) {
3659                if (!child.gatherTransparentRegion(region)) {
3660                    noneOfTheChildrenAreTransparent = false;
3661                }
3662            }
3663        }
3664        return meOpaque || noneOfTheChildrenAreTransparent;
3665    }
3666
3667    /**
3668     * {@inheritDoc}
3669     */
3670    public void requestTransparentRegion(View child) {
3671        if (child != null) {
3672            child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
3673            if (mParent != null) {
3674                mParent.requestTransparentRegion(this);
3675            }
3676        }
3677    }
3678
3679
3680    @Override
3681    protected boolean fitSystemWindows(Rect insets) {
3682        boolean done = super.fitSystemWindows(insets);
3683        if (!done) {
3684            final int count = mChildrenCount;
3685            final View[] children = mChildren;
3686            for (int i = 0; i < count; i++) {
3687                done = children[i].fitSystemWindows(insets);
3688                if (done) {
3689                    break;
3690                }
3691            }
3692        }
3693        return done;
3694    }
3695
3696    /**
3697     * Returns the animation listener to which layout animation events are
3698     * sent.
3699     *
3700     * @return an {@link android.view.animation.Animation.AnimationListener}
3701     */
3702    public Animation.AnimationListener getLayoutAnimationListener() {
3703        return mAnimationListener;
3704    }
3705
3706    @Override
3707    protected void drawableStateChanged() {
3708        super.drawableStateChanged();
3709
3710        if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
3711            if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
3712                throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
3713                        + " child has duplicateParentState set to true");
3714            }
3715
3716            final View[] children = mChildren;
3717            final int count = mChildrenCount;
3718
3719            for (int i = 0; i < count; i++) {
3720                final View child = children[i];
3721                if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
3722                    child.refreshDrawableState();
3723                }
3724            }
3725        }
3726    }
3727
3728    @Override
3729    protected int[] onCreateDrawableState(int extraSpace) {
3730        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
3731            return super.onCreateDrawableState(extraSpace);
3732        }
3733
3734        int need = 0;
3735        int n = getChildCount();
3736        for (int i = 0; i < n; i++) {
3737            int[] childState = getChildAt(i).getDrawableState();
3738
3739            if (childState != null) {
3740                need += childState.length;
3741            }
3742        }
3743
3744        int[] state = super.onCreateDrawableState(extraSpace + need);
3745
3746        for (int i = 0; i < n; i++) {
3747            int[] childState = getChildAt(i).getDrawableState();
3748
3749            if (childState != null) {
3750                state = mergeDrawableStates(state, childState);
3751            }
3752        }
3753
3754        return state;
3755    }
3756
3757    /**
3758     * Sets whether this ViewGroup's drawable states also include
3759     * its children's drawable states.  This is used, for example, to
3760     * make a group appear to be focused when its child EditText or button
3761     * is focused.
3762     */
3763    public void setAddStatesFromChildren(boolean addsStates) {
3764        if (addsStates) {
3765            mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
3766        } else {
3767            mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
3768        }
3769
3770        refreshDrawableState();
3771    }
3772
3773    /**
3774     * Returns whether this ViewGroup's drawable states also include
3775     * its children's drawable states.  This is used, for example, to
3776     * make a group appear to be focused when its child EditText or button
3777     * is focused.
3778     */
3779    public boolean addStatesFromChildren() {
3780        return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
3781    }
3782
3783    /**
3784     * If {link #addStatesFromChildren} is true, refreshes this group's
3785     * drawable state (to include the states from its children).
3786     */
3787    public void childDrawableStateChanged(View child) {
3788        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
3789            refreshDrawableState();
3790        }
3791    }
3792
3793    /**
3794     * Specifies the animation listener to which layout animation events must
3795     * be sent. Only
3796     * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
3797     * and
3798     * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
3799     * are invoked.
3800     *
3801     * @param animationListener the layout animation listener
3802     */
3803    public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
3804        mAnimationListener = animationListener;
3805    }
3806
3807    /**
3808     * LayoutParams are used by views to tell their parents how they want to be
3809     * laid out. See
3810     * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
3811     * for a list of all child view attributes that this class supports.
3812     *
3813     * <p>
3814     * The base LayoutParams class just describes how big the view wants to be
3815     * for both width and height. For each dimension, it can specify one of:
3816     * <ul>
3817     * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
3818     * means that the view wants to be as big as its parent (minus padding)
3819     * <li> WRAP_CONTENT, which means that the view wants to be just big enough
3820     * to enclose its content (plus padding)
3821     * <li> an exact number
3822     * </ul>
3823     * There are subclasses of LayoutParams for different subclasses of
3824     * ViewGroup. For example, AbsoluteLayout has its own subclass of
3825     * LayoutParams which adds an X and Y value.
3826     *
3827     * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
3828     * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
3829     */
3830    public static class LayoutParams {
3831        /**
3832         * Special value for the height or width requested by a View.
3833         * FILL_PARENT means that the view wants to be as big as its parent,
3834         * minus the parent's padding, if any. This value is deprecated
3835         * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
3836         */
3837        @SuppressWarnings({"UnusedDeclaration"})
3838        @Deprecated
3839        public static final int FILL_PARENT = -1;
3840
3841        /**
3842         * Special value for the height or width requested by a View.
3843         * MATCH_PARENT means that the view wants to be as big as its parent,
3844         * minus the parent's padding, if any. Introduced in API Level 8.
3845         */
3846        public static final int MATCH_PARENT = -1;
3847
3848        /**
3849         * Special value for the height or width requested by a View.
3850         * WRAP_CONTENT means that the view wants to be just large enough to fit
3851         * its own internal content, taking its own padding into account.
3852         */
3853        public static final int WRAP_CONTENT = -2;
3854
3855        /**
3856         * Information about how wide the view wants to be. Can be one of the
3857         * constants FILL_PARENT (replaced by MATCH_PARENT ,
3858         * in API Level 8) or WRAP_CONTENT. or an exact size.
3859         */
3860        @ViewDebug.ExportedProperty(category = "layout", mapping = {
3861            @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
3862            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
3863        })
3864        public int width;
3865
3866        /**
3867         * Information about how tall the view wants to be. Can be one of the
3868         * constants FILL_PARENT (replaced by MATCH_PARENT ,
3869         * in API Level 8) or WRAP_CONTENT. or an exact size.
3870         */
3871        @ViewDebug.ExportedProperty(category = "layout", mapping = {
3872            @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
3873            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
3874        })
3875        public int height;
3876
3877        /**
3878         * Used to animate layouts.
3879         */
3880        public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
3881
3882        /**
3883         * Creates a new set of layout parameters. The values are extracted from
3884         * the supplied attributes set and context. The XML attributes mapped
3885         * to this set of layout parameters are:
3886         *
3887         * <ul>
3888         *   <li><code>layout_width</code>: the width, either an exact value,
3889         *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
3890         *   {@link #MATCH_PARENT} in API Level 8)</li>
3891         *   <li><code>layout_height</code>: the height, either an exact value,
3892         *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
3893         *   {@link #MATCH_PARENT} in API Level 8)</li>
3894         * </ul>
3895         *
3896         * @param c the application environment
3897         * @param attrs the set of attributes from which to extract the layout
3898         *              parameters' values
3899         */
3900        public LayoutParams(Context c, AttributeSet attrs) {
3901            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
3902            setBaseAttributes(a,
3903                    R.styleable.ViewGroup_Layout_layout_width,
3904                    R.styleable.ViewGroup_Layout_layout_height);
3905            a.recycle();
3906        }
3907
3908        /**
3909         * Creates a new set of layout parameters with the specified width
3910         * and height.
3911         *
3912         * @param width the width, either {@link #WRAP_CONTENT},
3913         *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
3914         *        API Level 8), or a fixed size in pixels
3915         * @param height the height, either {@link #WRAP_CONTENT},
3916         *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
3917         *        API Level 8), or a fixed size in pixels
3918         */
3919        public LayoutParams(int width, int height) {
3920            this.width = width;
3921            this.height = height;
3922        }
3923
3924        /**
3925         * Copy constructor. Clones the width and height values of the source.
3926         *
3927         * @param source The layout params to copy from.
3928         */
3929        public LayoutParams(LayoutParams source) {
3930            this.width = source.width;
3931            this.height = source.height;
3932        }
3933
3934        /**
3935         * Used internally by MarginLayoutParams.
3936         * @hide
3937         */
3938        LayoutParams() {
3939        }
3940
3941        /**
3942         * Extracts the layout parameters from the supplied attributes.
3943         *
3944         * @param a the style attributes to extract the parameters from
3945         * @param widthAttr the identifier of the width attribute
3946         * @param heightAttr the identifier of the height attribute
3947         */
3948        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
3949            width = a.getLayoutDimension(widthAttr, "layout_width");
3950            height = a.getLayoutDimension(heightAttr, "layout_height");
3951        }
3952
3953        /**
3954         * Returns a String representation of this set of layout parameters.
3955         *
3956         * @param output the String to prepend to the internal representation
3957         * @return a String with the following format: output +
3958         *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
3959         *
3960         * @hide
3961         */
3962        public String debug(String output) {
3963            return output + "ViewGroup.LayoutParams={ width="
3964                    + sizeToString(width) + ", height=" + sizeToString(height) + " }";
3965        }
3966
3967        /**
3968         * Converts the specified size to a readable String.
3969         *
3970         * @param size the size to convert
3971         * @return a String instance representing the supplied size
3972         *
3973         * @hide
3974         */
3975        protected static String sizeToString(int size) {
3976            if (size == WRAP_CONTENT) {
3977                return "wrap-content";
3978            }
3979            if (size == MATCH_PARENT) {
3980                return "match-parent";
3981            }
3982            return String.valueOf(size);
3983        }
3984    }
3985
3986    /**
3987     * Per-child layout information for layouts that support margins.
3988     * See
3989     * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
3990     * for a list of all child view attributes that this class supports.
3991     */
3992    public static class MarginLayoutParams extends ViewGroup.LayoutParams {
3993        /**
3994         * The left margin in pixels of the child.
3995         */
3996        @ViewDebug.ExportedProperty(category = "layout")
3997        public int leftMargin;
3998
3999        /**
4000         * The top margin in pixels of the child.
4001         */
4002        @ViewDebug.ExportedProperty(category = "layout")
4003        public int topMargin;
4004
4005        /**
4006         * The right margin in pixels of the child.
4007         */
4008        @ViewDebug.ExportedProperty(category = "layout")
4009        public int rightMargin;
4010
4011        /**
4012         * The bottom margin in pixels of the child.
4013         */
4014        @ViewDebug.ExportedProperty(category = "layout")
4015        public int bottomMargin;
4016
4017        /**
4018         * Creates a new set of layout parameters. The values are extracted from
4019         * the supplied attributes set and context.
4020         *
4021         * @param c the application environment
4022         * @param attrs the set of attributes from which to extract the layout
4023         *              parameters' values
4024         */
4025        public MarginLayoutParams(Context c, AttributeSet attrs) {
4026            super();
4027
4028            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
4029            setBaseAttributes(a,
4030                    R.styleable.ViewGroup_MarginLayout_layout_width,
4031                    R.styleable.ViewGroup_MarginLayout_layout_height);
4032
4033            int margin = a.getDimensionPixelSize(
4034                    com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
4035            if (margin >= 0) {
4036                leftMargin = margin;
4037                topMargin = margin;
4038                rightMargin= margin;
4039                bottomMargin = margin;
4040            } else {
4041                leftMargin = a.getDimensionPixelSize(
4042                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
4043                topMargin = a.getDimensionPixelSize(
4044                        R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
4045                rightMargin = a.getDimensionPixelSize(
4046                        R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
4047                bottomMargin = a.getDimensionPixelSize(
4048                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
4049            }
4050
4051            a.recycle();
4052        }
4053
4054        /**
4055         * {@inheritDoc}
4056         */
4057        public MarginLayoutParams(int width, int height) {
4058            super(width, height);
4059        }
4060
4061        /**
4062         * Copy constructor. Clones the width, height and margin values of the source.
4063         *
4064         * @param source The layout params to copy from.
4065         */
4066        public MarginLayoutParams(MarginLayoutParams source) {
4067            this.width = source.width;
4068            this.height = source.height;
4069
4070            this.leftMargin = source.leftMargin;
4071            this.topMargin = source.topMargin;
4072            this.rightMargin = source.rightMargin;
4073            this.bottomMargin = source.bottomMargin;
4074        }
4075
4076        /**
4077         * {@inheritDoc}
4078         */
4079        public MarginLayoutParams(LayoutParams source) {
4080            super(source);
4081        }
4082
4083        /**
4084         * Sets the margins, in pixels.
4085         *
4086         * @param left the left margin size
4087         * @param top the top margin size
4088         * @param right the right margin size
4089         * @param bottom the bottom margin size
4090         *
4091         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
4092         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
4093         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
4094         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
4095         */
4096        public void setMargins(int left, int top, int right, int bottom) {
4097            leftMargin = left;
4098            topMargin = top;
4099            rightMargin = right;
4100            bottomMargin = bottom;
4101        }
4102    }
4103
4104    private static class SplitMotionTargets {
4105        private SparseArray<View> mTargets;
4106        private View[] mUniqueTargets;
4107        private int mUniqueTargetCount;
4108        private long mDownTime;
4109        private MotionEvent.PointerCoords[] mPointerCoords;
4110        private int[] mPointerIds;
4111
4112        private static final int INITIAL_UNIQUE_MOTION_TARGETS_SIZE = 5;
4113        private static final int INITIAL_BUCKET_SIZE = 5;
4114
4115        public SplitMotionTargets() {
4116            mTargets = new SparseArray<View>();
4117            mUniqueTargets = new View[INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
4118            mPointerIds = new int[INITIAL_BUCKET_SIZE];
4119            mPointerCoords = new MotionEvent.PointerCoords[INITIAL_BUCKET_SIZE];
4120            for (int i = 0; i < INITIAL_BUCKET_SIZE; i++) {
4121                mPointerCoords[i] = new MotionEvent.PointerCoords();
4122            }
4123        }
4124
4125        public void clear() {
4126            mTargets.clear();
4127            Arrays.fill(mUniqueTargets, null);
4128            mUniqueTargetCount = 0;
4129        }
4130
4131        public void add(int pointerId, View target) {
4132            mTargets.put(pointerId, target);
4133
4134            final int uniqueCount = mUniqueTargetCount;
4135            boolean addUnique = true;
4136            for (int i = 0; i < uniqueCount; i++) {
4137                if (mUniqueTargets[i] == target) {
4138                    addUnique = false;
4139                }
4140            }
4141            if (addUnique) {
4142                if (mUniqueTargets == null) {
4143                    mUniqueTargets = new View[INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
4144                }
4145                if (mUniqueTargets.length == uniqueCount) {
4146                    View[] newTargets =
4147                        new View[uniqueCount + INITIAL_UNIQUE_MOTION_TARGETS_SIZE];
4148                    System.arraycopy(mUniqueTargets, 0, newTargets, 0, uniqueCount);
4149                    mUniqueTargets = newTargets;
4150                }
4151                mUniqueTargets[uniqueCount] = target;
4152                mUniqueTargetCount++;
4153            }
4154        }
4155
4156        public int getIdCount() {
4157            return mTargets.size();
4158        }
4159
4160        public int getUniqueTargetCount() {
4161            return mUniqueTargetCount;
4162        }
4163
4164        public View getUniqueTargetAt(int index) {
4165            return mUniqueTargets[index];
4166        }
4167
4168        public View get(int id) {
4169            return mTargets.get(id);
4170        }
4171
4172        public int indexOfId(int id) {
4173            return mTargets.indexOfKey(id);
4174        }
4175
4176        public int indexOfTarget(View target) {
4177            return mTargets.indexOfValue(target);
4178        }
4179
4180        public int idAt(int index) {
4181            return mTargets.keyAt(index);
4182        }
4183
4184        public View targetAt(int index) {
4185            return mTargets.valueAt(index);
4186        }
4187
4188        public View getPrimaryTarget() {
4189            if (!isEmpty()) {
4190                return mUniqueTargets[0];
4191            }
4192            return null;
4193        }
4194
4195        public boolean hasTarget(View target) {
4196            final View[] unique = mUniqueTargets;
4197            final int uniqueCount = mUniqueTargetCount;
4198            for (int i = 0; i < uniqueCount; i++) {
4199                if (unique[i] == target) {
4200                    return true;
4201                }
4202            }
4203            return false;
4204        }
4205
4206        public boolean isEmpty() {
4207            return mUniqueTargetCount == 0;
4208        }
4209
4210        public void removeById(int id) {
4211            final int index = mTargets.indexOfKey(id);
4212            removeAt(index);
4213        }
4214
4215        public void removeView(View view) {
4216            int i = 0;
4217            while (i < mTargets.size()) {
4218                if (mTargets.valueAt(i) == view) {
4219                    mTargets.removeAt(i);
4220                } else {
4221                    i++;
4222                }
4223            }
4224            removeUnique(view);
4225        }
4226
4227        public void removeAt(int index) {
4228            if (index < 0 || index >= mTargets.size()) {
4229                return;
4230            }
4231
4232            final View removeView = mTargets.valueAt(index);
4233            mTargets.removeAt(index);
4234            if (mTargets.indexOfValue(removeView) < 0) {
4235                removeUnique(removeView);
4236            }
4237        }
4238
4239        private void removeUnique(View removeView) {
4240            View[] unique = mUniqueTargets;
4241            int uniqueCount = mUniqueTargetCount;
4242            for (int i = 0; i < uniqueCount; i++) {
4243                if (unique[i] == removeView) {
4244                    unique[i] = unique[--uniqueCount];
4245                    unique[uniqueCount] = null;
4246                    break;
4247                }
4248            }
4249
4250            mUniqueTargetCount = uniqueCount;
4251        }
4252
4253        /**
4254         * Return a new (obtain()ed) MotionEvent containing only data for pointers that should
4255         * be dispatched to child. Don't forget to recycle it!
4256         */
4257        public MotionEvent filterMotionEventForChild(MotionEvent ev, View child) {
4258            int action = ev.getAction();
4259            final int maskedAction = action & MotionEvent.ACTION_MASK;
4260
4261            // Only send pointer up events if this child was the target. Drop it otherwise.
4262            if (maskedAction == MotionEvent.ACTION_POINTER_UP &&
4263                    get(ev.getPointerId(ev.getActionIndex())) != child) {
4264                return null;
4265            }
4266
4267            int pointerCount = 0;
4268            final int idCount = getIdCount();
4269            for (int i = 0; i < idCount; i++) {
4270                if (targetAt(i) == child) {
4271                    pointerCount++;
4272                }
4273            }
4274
4275            int actionId = -1;
4276            boolean needsNewIndex = false; // True if we should fill in the action's masked index
4277
4278            // If we have a down event, it wasn't counted above.
4279            if (maskedAction == MotionEvent.ACTION_DOWN) {
4280                pointerCount++;
4281                actionId = ev.getPointerId(0);
4282                mDownTime = ev.getDownTime();
4283            } else if (maskedAction == MotionEvent.ACTION_POINTER_DOWN) {
4284                pointerCount++;
4285
4286                actionId = ev.getPointerId(ev.getActionIndex());
4287
4288                if (indexOfTarget(child) < 0) {
4289                    // The new action should be ACTION_DOWN if this child isn't currently getting
4290                    // any events.
4291                    action = MotionEvent.ACTION_DOWN;
4292                } else {
4293                    // Fill in the index portion of the action later.
4294                    needsNewIndex = true;
4295                }
4296            } else if (maskedAction == MotionEvent.ACTION_POINTER_UP) {
4297                actionId = ev.getPointerId(ev.getActionIndex());
4298                if (pointerCount == 1) {
4299                    // The new action should be ACTION_UP if there's only one pointer left for
4300                    // this target.
4301                    action = MotionEvent.ACTION_UP;
4302                } else {
4303                    // Fill in the index portion of the action later.
4304                    needsNewIndex = true;
4305                }
4306            }
4307
4308            if (pointerCount == 0) {
4309                return null;
4310            }
4311
4312            // Fill the buckets with pointer data!
4313            final int eventPointerCount = ev.getPointerCount();
4314            int bucketIndex = 0;
4315            int newActionIndex = -1;
4316            for (int evp = 0; evp < eventPointerCount; evp++) {
4317                final int id = ev.getPointerId(evp);
4318
4319                // Add this pointer to the bucket if it is new or targeted at child
4320                if (id == actionId || get(id) == child) {
4321                    // Expand scratch arrays if needed
4322                    if (mPointerCoords.length <= bucketIndex) {
4323                        int[] pointerIds = new int[pointerCount];
4324                        MotionEvent.PointerCoords[] pointerCoords =
4325                                new MotionEvent.PointerCoords[pointerCount];
4326                        for (int i = mPointerCoords.length; i < pointerCoords.length; i++) {
4327                            pointerCoords[i] = new MotionEvent.PointerCoords();
4328                        }
4329
4330                        System.arraycopy(mPointerCoords, 0,
4331                                pointerCoords, 0, mPointerCoords.length);
4332                        System.arraycopy(mPointerIds, 0, pointerIds, 0, mPointerIds.length);
4333
4334                        mPointerCoords = pointerCoords;
4335                        mPointerIds = pointerIds;
4336                    }
4337
4338                    mPointerIds[bucketIndex] = id;
4339                    ev.getPointerCoords(evp, mPointerCoords[bucketIndex]);
4340
4341                    if (needsNewIndex && id == actionId) {
4342                        newActionIndex = bucketIndex;
4343                    }
4344
4345                    bucketIndex++;
4346                }
4347            }
4348
4349            // Encode the new action index if we have one
4350            if (newActionIndex >= 0) {
4351                action = (action & MotionEvent.ACTION_MASK) |
4352                        (newActionIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
4353            }
4354
4355            MotionEvent result = MotionEvent.obtain(mDownTime, ev.getEventTime(),
4356                    action, pointerCount, mPointerIds, mPointerCoords, ev.getMetaState(),
4357                    ev.getXPrecision(), ev.getYPrecision(), ev.getDeviceId(), ev.getEdgeFlags(),
4358                    ev.getSource());
4359            return result;
4360        }
4361    }
4362}
4363