1/*
2 * Copyright (C) 2015 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 com.android.settings.widget;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.Nullable;
22import android.content.Context;
23import android.content.res.TypedArray;
24import android.graphics.Canvas;
25import android.graphics.drawable.Drawable;
26import android.util.AttributeSet;
27import android.view.Gravity;
28import android.view.View;
29import android.view.ViewDebug;
30import android.view.ViewGroup;
31import android.view.ViewHierarchyEncoder;
32
33import com.android.internal.R;
34
35import java.lang.annotation.Retention;
36import java.lang.annotation.RetentionPolicy;
37
38
39/**
40 * A LinearLayout with a twist: if the contents don't fit, it takes space away from the
41 * MATCH_PARENT children, instead of taking it from the weighted ones.
42 *
43 * TODO: Remove once we redesign the ChooseLockPattern screen with a sane layout.
44 */
45public class MatchParentShrinkingLinearLayout extends ViewGroup {
46    /** @hide */
47    @IntDef({HORIZONTAL, VERTICAL})
48    @Retention(RetentionPolicy.SOURCE)
49    public @interface OrientationMode {}
50
51    public static final int HORIZONTAL = 0;
52    public static final int VERTICAL = 1;
53
54    /** @hide */
55    @IntDef(flag = true,
56            value = {
57                SHOW_DIVIDER_NONE,
58                SHOW_DIVIDER_BEGINNING,
59                SHOW_DIVIDER_MIDDLE,
60                SHOW_DIVIDER_END
61            })
62    @Retention(RetentionPolicy.SOURCE)
63    public @interface DividerMode {}
64
65    /**
66     * Don't show any dividers.
67     */
68    public static final int SHOW_DIVIDER_NONE = 0;
69    /**
70     * Show a divider at the beginning of the group.
71     */
72    public static final int SHOW_DIVIDER_BEGINNING = 1;
73    /**
74     * Show dividers between each item in the group.
75     */
76    public static final int SHOW_DIVIDER_MIDDLE = 2;
77    /**
78     * Show a divider at the end of the group.
79     */
80    public static final int SHOW_DIVIDER_END = 4;
81
82    /**
83     * Whether the children of this layout are baseline aligned.  Only applicable
84     * if {@link #mOrientation} is horizontal.
85     */
86    @ViewDebug.ExportedProperty(category = "layout")
87    private boolean mBaselineAligned = true;
88
89    /**
90     * If this layout is part of another layout that is baseline aligned,
91     * use the child at this index as the baseline.
92     *
93     * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned
94     * with whether the children of this layout are baseline aligned.
95     */
96    @ViewDebug.ExportedProperty(category = "layout")
97    private int mBaselineAlignedChildIndex = -1;
98
99    /**
100     * The additional offset to the child's baseline.
101     * We'll calculate the baseline of this layout as we measure vertically; for
102     * horizontal linear layouts, the offset of 0 is appropriate.
103     */
104    @ViewDebug.ExportedProperty(category = "measurement")
105    private int mBaselineChildTop = 0;
106
107    @ViewDebug.ExportedProperty(category = "measurement")
108    private int mOrientation;
109
110    @ViewDebug.ExportedProperty(category = "measurement", flagMapping = {
111            @ViewDebug.FlagToString(mask = -1,
112                equals = -1, name = "NONE"),
113            @ViewDebug.FlagToString(mask = Gravity.NO_GRAVITY,
114                equals = Gravity.NO_GRAVITY,name = "NONE"),
115            @ViewDebug.FlagToString(mask = Gravity.TOP,
116                equals = Gravity.TOP, name = "TOP"),
117            @ViewDebug.FlagToString(mask = Gravity.BOTTOM,
118                equals = Gravity.BOTTOM, name = "BOTTOM"),
119            @ViewDebug.FlagToString(mask = Gravity.LEFT,
120                equals = Gravity.LEFT, name = "LEFT"),
121            @ViewDebug.FlagToString(mask = Gravity.RIGHT,
122                equals = Gravity.RIGHT, name = "RIGHT"),
123            @ViewDebug.FlagToString(mask = Gravity.START,
124                equals = Gravity.START, name = "START"),
125            @ViewDebug.FlagToString(mask = Gravity.END,
126                equals = Gravity.END, name = "END"),
127            @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL,
128                equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"),
129            @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL,
130                equals = Gravity.FILL_VERTICAL, name = "FILL_VERTICAL"),
131            @ViewDebug.FlagToString(mask = Gravity.CENTER_HORIZONTAL,
132                equals = Gravity.CENTER_HORIZONTAL, name = "CENTER_HORIZONTAL"),
133            @ViewDebug.FlagToString(mask = Gravity.FILL_HORIZONTAL,
134                equals = Gravity.FILL_HORIZONTAL, name = "FILL_HORIZONTAL"),
135            @ViewDebug.FlagToString(mask = Gravity.CENTER,
136                equals = Gravity.CENTER, name = "CENTER"),
137            @ViewDebug.FlagToString(mask = Gravity.FILL,
138                equals = Gravity.FILL, name = "FILL"),
139            @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
140                equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
141        }, formatToHexString = true)
142    private int mGravity = Gravity.START | Gravity.TOP;
143
144    @ViewDebug.ExportedProperty(category = "measurement")
145    private int mTotalLength;
146
147    @ViewDebug.ExportedProperty(category = "layout")
148    private float mWeightSum;
149
150    @ViewDebug.ExportedProperty(category = "layout")
151    private boolean mUseLargestChild;
152
153    private int[] mMaxAscent;
154    private int[] mMaxDescent;
155
156    private static final int VERTICAL_GRAVITY_COUNT = 4;
157
158    private static final int INDEX_CENTER_VERTICAL = 0;
159    private static final int INDEX_TOP = 1;
160    private static final int INDEX_BOTTOM = 2;
161    private static final int INDEX_FILL = 3;
162
163    private Drawable mDivider;
164    private int mDividerWidth;
165    private int mDividerHeight;
166    private int mShowDividers;
167    private int mDividerPadding;
168
169    private int mLayoutDirection = View.LAYOUT_DIRECTION_UNDEFINED;
170
171    public MatchParentShrinkingLinearLayout(Context context) {
172        this(context, null);
173    }
174
175    public MatchParentShrinkingLinearLayout(Context context, @Nullable AttributeSet attrs) {
176        this(context, attrs, 0);
177    }
178
179    public MatchParentShrinkingLinearLayout(Context context, @Nullable AttributeSet attrs,
180            int defStyleAttr) {
181        this(context, attrs, defStyleAttr, 0);
182    }
183
184    public MatchParentShrinkingLinearLayout(Context context, AttributeSet attrs, int defStyleAttr,
185            int defStyleRes) {
186        super(context, attrs, defStyleAttr, defStyleRes);
187
188        final TypedArray a = context.obtainStyledAttributes(
189                attrs, com.android.internal.R.styleable.LinearLayout, defStyleAttr, defStyleRes);
190
191        int index = a.getInt(com.android.internal.R.styleable.LinearLayout_orientation, -1);
192        if (index >= 0) {
193            setOrientation(index);
194        }
195
196        index = a.getInt(com.android.internal.R.styleable.LinearLayout_gravity, -1);
197        if (index >= 0) {
198            setGravity(index);
199        }
200
201        boolean baselineAligned = a.getBoolean(R.styleable.LinearLayout_baselineAligned, true);
202        if (!baselineAligned) {
203            setBaselineAligned(baselineAligned);
204        }
205
206        mWeightSum = a.getFloat(R.styleable.LinearLayout_weightSum, -1.0f);
207
208        mBaselineAlignedChildIndex = a.getInt(
209                com.android.internal.R.styleable.LinearLayout_baselineAlignedChildIndex, -1);
210
211        mUseLargestChild = a.getBoolean(R.styleable.LinearLayout_measureWithLargestChild, false);
212
213        setDividerDrawable(a.getDrawable(R.styleable.LinearLayout_divider));
214        mShowDividers = a.getInt(R.styleable.LinearLayout_showDividers, SHOW_DIVIDER_NONE);
215        mDividerPadding = a.getDimensionPixelSize(R.styleable.LinearLayout_dividerPadding, 0);
216
217        a.recycle();
218    }
219
220    /**
221     * Set how dividers should be shown between items in this layout
222     *
223     * @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
224     *                     {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
225     *                     or {@link #SHOW_DIVIDER_NONE} to show no dividers.
226     */
227    public void setShowDividers(@DividerMode int showDividers) {
228        if (showDividers != mShowDividers) {
229            requestLayout();
230        }
231        mShowDividers = showDividers;
232    }
233
234    @Override
235    public boolean shouldDelayChildPressedState() {
236        return false;
237    }
238
239    /**
240     * @return A flag set indicating how dividers should be shown around items.
241     * @see #setShowDividers(int)
242     */
243    @DividerMode
244    public int getShowDividers() {
245        return mShowDividers;
246    }
247
248    /**
249     * @return the divider Drawable that will divide each item.
250     *
251     * @see #setDividerDrawable(android.graphics.drawable.Drawable)
252     *
253     * @attr ref android.R.styleable#LinearLayout_divider
254     */
255    public Drawable getDividerDrawable() {
256        return mDivider;
257    }
258
259    /**
260     * Set a drawable to be used as a divider between items.
261     *
262     * @param divider Drawable that will divide each item.
263     *
264     * @see #setShowDividers(int)
265     *
266     * @attr ref android.R.styleable#LinearLayout_divider
267     */
268    public void setDividerDrawable(Drawable divider) {
269        if (divider == mDivider) {
270            return;
271        }
272        mDivider = divider;
273        if (divider != null) {
274            mDividerWidth = divider.getIntrinsicWidth();
275            mDividerHeight = divider.getIntrinsicHeight();
276        } else {
277            mDividerWidth = 0;
278            mDividerHeight = 0;
279        }
280        setWillNotDraw(divider == null);
281        requestLayout();
282    }
283
284    /**
285     * Set padding displayed on both ends of dividers.
286     *
287     * @param padding Padding value in pixels that will be applied to each end
288     *
289     * @see #setShowDividers(int)
290     * @see #setDividerDrawable(android.graphics.drawable.Drawable)
291     * @see #getDividerPadding()
292     */
293    public void setDividerPadding(int padding) {
294        mDividerPadding = padding;
295    }
296
297    /**
298     * Get the padding size used to inset dividers in pixels
299     *
300     * @see #setShowDividers(int)
301     * @see #setDividerDrawable(android.graphics.drawable.Drawable)
302     * @see #setDividerPadding(int)
303     */
304    public int getDividerPadding() {
305        return mDividerPadding;
306    }
307
308    /**
309     * Get the width of the current divider drawable.
310     *
311     * @hide Used internally by framework.
312     */
313    public int getDividerWidth() {
314        return mDividerWidth;
315    }
316
317    @Override
318    protected void onDraw(Canvas canvas) {
319        if (mDivider == null) {
320            return;
321        }
322
323        if (mOrientation == VERTICAL) {
324            drawDividersVertical(canvas);
325        } else {
326            drawDividersHorizontal(canvas);
327        }
328    }
329
330    void drawDividersVertical(Canvas canvas) {
331        final int count = getVirtualChildCount();
332        for (int i = 0; i < count; i++) {
333            final View child = getVirtualChildAt(i);
334
335            if (child != null && child.getVisibility() != GONE) {
336                if (hasDividerBeforeChildAt(i)) {
337                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
338                    final int top = child.getTop() - lp.topMargin - mDividerHeight;
339                    drawHorizontalDivider(canvas, top);
340                }
341            }
342        }
343
344        if (hasDividerBeforeChildAt(count)) {
345            final View child = getVirtualChildAt(count - 1);
346            int bottom = 0;
347            if (child == null) {
348                bottom = getHeight() - getPaddingBottom() - mDividerHeight;
349            } else {
350                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
351                bottom = child.getBottom() + lp.bottomMargin;
352            }
353            drawHorizontalDivider(canvas, bottom);
354        }
355    }
356
357    void drawDividersHorizontal(Canvas canvas) {
358        final int count = getVirtualChildCount();
359        final boolean isLayoutRtl = isLayoutRtl();
360        for (int i = 0; i < count; i++) {
361            final View child = getVirtualChildAt(i);
362
363            if (child != null && child.getVisibility() != GONE) {
364                if (hasDividerBeforeChildAt(i)) {
365                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
366                    final int position;
367                    if (isLayoutRtl) {
368                        position = child.getRight() + lp.rightMargin;
369                    } else {
370                        position = child.getLeft() - lp.leftMargin - mDividerWidth;
371                    }
372                    drawVerticalDivider(canvas, position);
373                }
374            }
375        }
376
377        if (hasDividerBeforeChildAt(count)) {
378            final View child = getVirtualChildAt(count - 1);
379            int position;
380            if (child == null) {
381                if (isLayoutRtl) {
382                    position = getPaddingLeft();
383                } else {
384                    position = getWidth() - getPaddingRight() - mDividerWidth;
385                }
386            } else {
387                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
388                if (isLayoutRtl) {
389                    position = child.getLeft() - lp.leftMargin - mDividerWidth;
390                } else {
391                    position = child.getRight() + lp.rightMargin;
392                }
393            }
394            drawVerticalDivider(canvas, position);
395        }
396    }
397
398    void drawHorizontalDivider(Canvas canvas, int top) {
399        mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
400                getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
401        mDivider.draw(canvas);
402    }
403
404    void drawVerticalDivider(Canvas canvas, int left) {
405        mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
406                left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
407        mDivider.draw(canvas);
408    }
409
410    /**
411     * <p>Indicates whether widgets contained within this layout are aligned
412     * on their baseline or not.</p>
413     *
414     * @return true when widgets are baseline-aligned, false otherwise
415     */
416    public boolean isBaselineAligned() {
417        return mBaselineAligned;
418    }
419
420    /**
421     * <p>Defines whether widgets contained in this layout are
422     * baseline-aligned or not.</p>
423     *
424     * @param baselineAligned true to align widgets on their baseline,
425     *         false otherwise
426     *
427     * @attr ref android.R.styleable#LinearLayout_baselineAligned
428     */
429    @android.view.RemotableViewMethod
430    public void setBaselineAligned(boolean baselineAligned) {
431        mBaselineAligned = baselineAligned;
432    }
433
434    /**
435     * When true, all children with a weight will be considered having
436     * the minimum size of the largest child. If false, all children are
437     * measured normally.
438     *
439     * @return True to measure children with a weight using the minimum
440     *         size of the largest child, false otherwise.
441     *
442     * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
443     */
444    public boolean isMeasureWithLargestChildEnabled() {
445        return mUseLargestChild;
446    }
447
448    /**
449     * When set to true, all children with a weight will be considered having
450     * the minimum size of the largest child. If false, all children are
451     * measured normally.
452     *
453     * Disabled by default.
454     *
455     * @param enabled True to measure children with a weight using the
456     *        minimum size of the largest child, false otherwise.
457     *
458     * @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
459     */
460    @android.view.RemotableViewMethod
461    public void setMeasureWithLargestChildEnabled(boolean enabled) {
462        mUseLargestChild = enabled;
463    }
464
465    @Override
466    public int getBaseline() {
467        if (mBaselineAlignedChildIndex < 0) {
468            return super.getBaseline();
469        }
470
471        if (getChildCount() <= mBaselineAlignedChildIndex) {
472            throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
473                    + "set to an index that is out of bounds.");
474        }
475
476        final View child = getChildAt(mBaselineAlignedChildIndex);
477        final int childBaseline = child.getBaseline();
478
479        if (childBaseline == -1) {
480            if (mBaselineAlignedChildIndex == 0) {
481                // this is just the default case, safe to return -1
482                return -1;
483            }
484            // the user picked an index that points to something that doesn't
485            // know how to calculate its baseline.
486            throw new RuntimeException("mBaselineAlignedChildIndex of LinearLayout "
487                    + "points to a View that doesn't know how to get its baseline.");
488        }
489
490        // TODO: This should try to take into account the virtual offsets
491        // (See getNextLocationOffset and getLocationOffset)
492        // We should add to childTop:
493        // sum([getNextLocationOffset(getChildAt(i)) / i < mBaselineAlignedChildIndex])
494        // and also add:
495        // getLocationOffset(child)
496        int childTop = mBaselineChildTop;
497
498        if (mOrientation == VERTICAL) {
499            final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
500            if (majorGravity != Gravity.TOP) {
501               switch (majorGravity) {
502                   case Gravity.BOTTOM:
503                       childTop = mBottom - mTop - mPaddingBottom - mTotalLength;
504                       break;
505
506                   case Gravity.CENTER_VERTICAL:
507                       childTop += ((mBottom - mTop - mPaddingTop - mPaddingBottom) -
508                               mTotalLength) / 2;
509                       break;
510               }
511            }
512        }
513
514        LayoutParams lp = (LayoutParams) child.getLayoutParams();
515        return childTop + lp.topMargin + childBaseline;
516    }
517
518    /**
519     * @return The index of the child that will be used if this layout is
520     *   part of a larger layout that is baseline aligned, or -1 if none has
521     *   been set.
522     */
523    public int getBaselineAlignedChildIndex() {
524        return mBaselineAlignedChildIndex;
525    }
526
527    /**
528     * @param i The index of the child that will be used if this layout is
529     *          part of a larger layout that is baseline aligned.
530     *
531     * @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
532     */
533    @android.view.RemotableViewMethod
534    public void setBaselineAlignedChildIndex(int i) {
535        if ((i < 0) || (i >= getChildCount())) {
536            throw new IllegalArgumentException("base aligned child index out "
537                    + "of range (0, " + getChildCount() + ")");
538        }
539        mBaselineAlignedChildIndex = i;
540    }
541
542    /**
543     * <p>Returns the view at the specified index. This method can be overriden
544     * to take into account virtual children. Refer to
545     * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
546     * for an example.</p>
547     *
548     * @param index the child's index
549     * @return the child at the specified index
550     */
551    View getVirtualChildAt(int index) {
552        return getChildAt(index);
553    }
554
555    /**
556     * <p>Returns the virtual number of children. This number might be different
557     * than the actual number of children if the layout can hold virtual
558     * children. Refer to
559     * {@link android.widget.TableLayout} and {@link android.widget.TableRow}
560     * for an example.</p>
561     *
562     * @return the virtual number of children
563     */
564    int getVirtualChildCount() {
565        return getChildCount();
566    }
567
568    /**
569     * Returns the desired weights sum.
570     *
571     * @return A number greater than 0.0f if the weight sum is defined, or
572     *         a number lower than or equals to 0.0f if not weight sum is
573     *         to be used.
574     */
575    public float getWeightSum() {
576        return mWeightSum;
577    }
578
579    /**
580     * Defines the desired weights sum. If unspecified the weights sum is computed
581     * at layout time by adding the layout_weight of each child.
582     *
583     * This can be used for instance to give a single child 50% of the total
584     * available space by giving it a layout_weight of 0.5 and setting the
585     * weightSum to 1.0.
586     *
587     * @param weightSum a number greater than 0.0f, or a number lower than or equals
588     *        to 0.0f if the weight sum should be computed from the children's
589     *        layout_weight
590     */
591    @android.view.RemotableViewMethod
592    public void setWeightSum(float weightSum) {
593        mWeightSum = Math.max(0.0f, weightSum);
594    }
595
596    @Override
597    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
598        if (mOrientation == VERTICAL) {
599            measureVertical(widthMeasureSpec, heightMeasureSpec);
600        } else {
601            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
602        }
603    }
604
605    /**
606     * Determines where to position dividers between children.
607     *
608     * @param childIndex Index of child to check for preceding divider
609     * @return true if there should be a divider before the child at childIndex
610     * @hide Pending API consideration. Currently only used internally by the system.
611     */
612    protected boolean hasDividerBeforeChildAt(int childIndex) {
613        if (childIndex == 0) {
614            return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
615        } else if (childIndex == getChildCount()) {
616            return (mShowDividers & SHOW_DIVIDER_END) != 0;
617        } else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
618            boolean hasVisibleViewBefore = false;
619            for (int i = childIndex - 1; i >= 0; i--) {
620                if (getChildAt(i).getVisibility() != GONE) {
621                    hasVisibleViewBefore = true;
622                    break;
623                }
624            }
625            return hasVisibleViewBefore;
626        }
627        return false;
628    }
629
630    /**
631     * Measures the children when the orientation of this LinearLayout is set
632     * to {@link #VERTICAL}.
633     *
634     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
635     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
636     *
637     * @see #getOrientation()
638     * @see #setOrientation(int)
639     * @see #onMeasure(int, int)
640     */
641    void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
642        mTotalLength = 0;
643        int maxWidth = 0;
644        int childState = 0;
645        int alternativeMaxWidth = 0;
646        int weightedMaxWidth = 0;
647        boolean allFillParent = true;
648        float totalWeight = 0;
649
650        final int count = getVirtualChildCount();
651
652        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
653        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
654
655        boolean matchWidth = false;
656        boolean skippedMeasure = false;
657
658        final int baselineChildIndex = mBaselineAlignedChildIndex;
659        final boolean useLargestChild = mUseLargestChild;
660
661        int largestChildHeight = Integer.MIN_VALUE;
662
663        // See how tall everyone is. Also remember max width.
664        for (int i = 0; i < count; ++i) {
665            final View child = getVirtualChildAt(i);
666
667            if (child == null) {
668                mTotalLength += measureNullChild(i);
669                continue;
670            }
671
672            if (child.getVisibility() == View.GONE) {
673               i += getChildrenSkipCount(child, i);
674               continue;
675            }
676
677            if (hasDividerBeforeChildAt(i)) {
678                mTotalLength += mDividerHeight;
679            }
680
681            LayoutParams lp = (LayoutParams) child.getLayoutParams();
682
683            totalWeight += lp.weight;
684
685            if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
686                // Optimization: don't bother measuring children who are going to use
687                // leftover space. These views will get measured again down below if
688                // there is any leftover space.
689                final int totalLength = mTotalLength;
690                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
691                skippedMeasure = true;
692            } else {
693                int oldHeight = Integer.MIN_VALUE;
694
695                if (lp.height == 0 && lp.weight > 0) {
696                    // heightMode is either UNSPECIFIED or AT_MOST, and this
697                    // child wanted to stretch to fill available space.
698                    // Translate that to WRAP_CONTENT so that it does not end up
699                    // with a height of 0
700                    oldHeight = 0;
701                    lp.height = LayoutParams.WRAP_CONTENT;
702                }
703
704                // Determine how big this child would like to be. If this or
705                // previous children have given a weight, then we allow it to
706                // use all available space (and we will shrink things later
707                // if needed).
708                measureChildBeforeLayout(
709                       child, i, widthMeasureSpec, 0, heightMeasureSpec,
710                       totalWeight == 0 ? mTotalLength : 0);
711
712                if (oldHeight != Integer.MIN_VALUE) {
713                   lp.height = oldHeight;
714                }
715
716                final int childHeight = child.getMeasuredHeight();
717                final int totalLength = mTotalLength;
718                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
719                       lp.bottomMargin + getNextLocationOffset(child));
720
721                if (useLargestChild) {
722                    largestChildHeight = Math.max(childHeight, largestChildHeight);
723                }
724            }
725
726            /**
727             * If applicable, compute the additional offset to the child's baseline
728             * we'll need later when asked {@link #getBaseline}.
729             */
730            if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
731               mBaselineChildTop = mTotalLength;
732            }
733
734            // if we are trying to use a child index for our baseline, the above
735            // book keeping only works if there are no children above it with
736            // weight.  fail fast to aid the developer.
737            if (i < baselineChildIndex && lp.weight > 0) {
738                throw new RuntimeException("A child of LinearLayout with index "
739                        + "less than mBaselineAlignedChildIndex has weight > 0, which "
740                        + "won't work.  Either remove the weight, or don't set "
741                        + "mBaselineAlignedChildIndex.");
742            }
743
744            boolean matchWidthLocally = false;
745            if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
746                // The width of the linear layout will scale, and at least one
747                // child said it wanted to match our width. Set a flag
748                // indicating that we need to remeasure at least that view when
749                // we know our width.
750                matchWidth = true;
751                matchWidthLocally = true;
752            }
753
754            final int margin = lp.leftMargin + lp.rightMargin;
755            final int measuredWidth = child.getMeasuredWidth() + margin;
756            maxWidth = Math.max(maxWidth, measuredWidth);
757            childState = combineMeasuredStates(childState, child.getMeasuredState());
758
759            allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
760            if (lp.weight > 0) {
761                /*
762                 * Widths of weighted Views are bogus if we end up
763                 * remeasuring, so keep them separate.
764                 */
765                weightedMaxWidth = Math.max(weightedMaxWidth,
766                        matchWidthLocally ? margin : measuredWidth);
767            } else {
768                alternativeMaxWidth = Math.max(alternativeMaxWidth,
769                        matchWidthLocally ? margin : measuredWidth);
770            }
771
772            i += getChildrenSkipCount(child, i);
773        }
774
775        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
776            mTotalLength += mDividerHeight;
777        }
778
779        if (useLargestChild &&
780                (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
781            mTotalLength = 0;
782
783            for (int i = 0; i < count; ++i) {
784                final View child = getVirtualChildAt(i);
785
786                if (child == null) {
787                    mTotalLength += measureNullChild(i);
788                    continue;
789                }
790
791                if (child.getVisibility() == GONE) {
792                    i += getChildrenSkipCount(child, i);
793                    continue;
794                }
795
796                final LayoutParams lp = (LayoutParams)
797                        child.getLayoutParams();
798                // Account for negative margins
799                final int totalLength = mTotalLength;
800                mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
801                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
802            }
803        }
804
805        // Add in our padding
806        mTotalLength += mPaddingTop + mPaddingBottom;
807
808        int heightSize = mTotalLength;
809
810        // Check against our minimum height
811        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
812
813        // Reconcile our calculated size with the heightMeasureSpec
814        int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
815        heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
816
817        // Either expand children with weight to take up available space or
818        // shrink them if they extend beyond our current bounds. If we skipped
819        // measurement on any children, we need to measure them now.
820        int delta = heightSize - mTotalLength;
821        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
822            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
823
824            mTotalLength = 0;
825
826            for (int i = 0; i < count; ++i) {
827                final View child = getVirtualChildAt(i);
828
829                if (child.getVisibility() == View.GONE) {
830                    continue;
831                }
832
833                LayoutParams lp = (LayoutParams) child.getLayoutParams();
834
835                float childExtra = lp.weight;
836
837                // MatchParentShrinkingLinearLayout custom code starts here.
838                if (childExtra > 0 && delta > 0) {
839                    // Child said it could absorb extra space -- give him his share
840                    int share = (int) (childExtra * delta / weightSum);
841                    weightSum -= childExtra;
842                    delta -= share;
843
844                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
845                            mPaddingLeft + mPaddingRight +
846                                    lp.leftMargin + lp.rightMargin, lp.width);
847
848                    // TODO: Use a field like lp.isMeasured to figure out if this
849                    // child has been previously measured
850                    if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
851                        // child was measured once already above...
852                        // base new measurement on stored values
853                        int childHeight = child.getMeasuredHeight() + share;
854                        if (childHeight < 0) {
855                            childHeight = 0;
856                        }
857
858                        child.measure(childWidthMeasureSpec,
859                                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
860                    } else {
861                        // child was skipped in the loop above.
862                        // Measure for this first time here
863                        child.measure(childWidthMeasureSpec,
864                                MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
865                                        MeasureSpec.EXACTLY));
866                    }
867
868                    // Child may now not fit in vertical dimension.
869                    childState = combineMeasuredStates(childState, child.getMeasuredState()
870                            & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
871                } else if (delta < 0 && lp.height == LayoutParams.MATCH_PARENT) {
872                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
873                            mPaddingLeft + mPaddingRight +
874                                    lp.leftMargin + lp.rightMargin, lp.width);
875
876                    int childHeight = child.getMeasuredHeight() + delta;
877                    if (childHeight < 0) {
878                        childHeight = 0;
879                    }
880                    delta -= childHeight - child.getMeasuredHeight();
881
882                    child.measure(childWidthMeasureSpec,
883                            MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
884
885                    // Child may now not fit in vertical dimension.
886                    childState = combineMeasuredStates(childState, child.getMeasuredState()
887                            & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
888                }
889                // MatchParentShrinkingLinearLayout custom code ends here.
890
891                final int margin =  lp.leftMargin + lp.rightMargin;
892                final int measuredWidth = child.getMeasuredWidth() + margin;
893                maxWidth = Math.max(maxWidth, measuredWidth);
894
895                boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
896                        lp.width == LayoutParams.MATCH_PARENT;
897
898                alternativeMaxWidth = Math.max(alternativeMaxWidth,
899                        matchWidthLocally ? margin : measuredWidth);
900
901                allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
902
903                final int totalLength = mTotalLength;
904                mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
905                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
906            }
907
908            // Add in our padding
909            mTotalLength += mPaddingTop + mPaddingBottom;
910            // TODO: Should we recompute the heightSpec based on the new total length?
911        } else {
912            alternativeMaxWidth = Math.max(alternativeMaxWidth,
913                                           weightedMaxWidth);
914
915
916            // We have no limit, so make all weighted views as tall as the largest child.
917            // Children will have already been measured once.
918            if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
919                for (int i = 0; i < count; i++) {
920                    final View child = getVirtualChildAt(i);
921
922                    if (child == null || child.getVisibility() == View.GONE) {
923                        continue;
924                    }
925
926                    final LayoutParams lp = (LayoutParams) child.getLayoutParams();
927
928                    float childExtra = lp.weight;
929                    if (childExtra > 0) {
930                        child.measure(
931                                MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
932                                        MeasureSpec.EXACTLY),
933                                MeasureSpec.makeMeasureSpec(largestChildHeight,
934                                        MeasureSpec.EXACTLY));
935                    }
936                }
937            }
938        }
939
940        if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
941            maxWidth = alternativeMaxWidth;
942        }
943
944        maxWidth += mPaddingLeft + mPaddingRight;
945
946        // Check against our minimum width
947        maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
948
949        setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
950                heightSizeAndState);
951
952        if (matchWidth) {
953            forceUniformWidth(count, heightMeasureSpec);
954        }
955    }
956
957    private void forceUniformWidth(int count, int heightMeasureSpec) {
958        // Pretend that the linear layout has an exact size.
959        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(),
960                MeasureSpec.EXACTLY);
961        for (int i = 0; i< count; ++i) {
962           final View child = getVirtualChildAt(i);
963           if (child.getVisibility() != GONE) {
964               LayoutParams lp =
965                       ((LayoutParams)child.getLayoutParams());
966
967               if (lp.width == LayoutParams.MATCH_PARENT) {
968                   // Temporarily force children to reuse their old measured height
969                   // FIXME: this may not be right for something like wrapping text?
970                   int oldHeight = lp.height;
971                   lp.height = child.getMeasuredHeight();
972
973                   // Remeasue with new dimensions
974                   measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
975                   lp.height = oldHeight;
976               }
977           }
978        }
979    }
980
981    /**
982     * Measures the children when the orientation of this LinearLayout is set
983     * to {@link #HORIZONTAL}.
984     *
985     * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
986     * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
987     *
988     * @see #getOrientation()
989     * @see #setOrientation(int)
990     * @see #onMeasure(int, int)
991     */
992    void measureHorizontal(int widthMeasureSpec, int heightMeasureSpec) {
993        // MatchParentShrinkingLinearLayout custom code starts here.
994        throw new IllegalStateException("horizontal mode not supported.");
995        // MatchParentShrinkingLinearLayout custom code ends here.
996    }
997
998    private void forceUniformHeight(int count, int widthMeasureSpec) {
999        // Pretend that the linear layout has an exact size. This is the measured height of
1000        // ourselves. The measured height should be the max height of the children, changed
1001        // to accommodate the heightMeasureSpec from the parent
1002        int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight(),
1003                MeasureSpec.EXACTLY);
1004        for (int i = 0; i < count; ++i) {
1005           final View child = getVirtualChildAt(i);
1006           if (child.getVisibility() != GONE) {
1007               LayoutParams lp = (LayoutParams) child.getLayoutParams();
1008
1009               if (lp.height == LayoutParams.MATCH_PARENT) {
1010                   // Temporarily force children to reuse their old measured width
1011                   // FIXME: this may not be right for something like wrapping text?
1012                   int oldWidth = lp.width;
1013                   lp.width = child.getMeasuredWidth();
1014
1015                   // Remeasure with new dimensions
1016                   measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0);
1017                   lp.width = oldWidth;
1018               }
1019           }
1020        }
1021    }
1022
1023    /**
1024     * <p>Returns the number of children to skip after measuring/laying out
1025     * the specified child.</p>
1026     *
1027     * @param child the child after which we want to skip children
1028     * @param index the index of the child after which we want to skip children
1029     * @return the number of children to skip, 0 by default
1030     */
1031    int getChildrenSkipCount(View child, int index) {
1032        return 0;
1033    }
1034
1035    /**
1036     * <p>Returns the size (width or height) that should be occupied by a null
1037     * child.</p>
1038     *
1039     * @param childIndex the index of the null child
1040     * @return the width or height of the child depending on the orientation
1041     */
1042    int measureNullChild(int childIndex) {
1043        return 0;
1044    }
1045
1046    /**
1047     * <p>Measure the child according to the parent's measure specs. This
1048     * method should be overriden by subclasses to force the sizing of
1049     * children. This method is called by {@link #measureVertical(int, int)} and
1050     * {@link #measureHorizontal(int, int)}.</p>
1051     *
1052     * @param child the child to measure
1053     * @param childIndex the index of the child in this view
1054     * @param widthMeasureSpec horizontal space requirements as imposed by the parent
1055     * @param totalWidth extra space that has been used up by the parent horizontally
1056     * @param heightMeasureSpec vertical space requirements as imposed by the parent
1057     * @param totalHeight extra space that has been used up by the parent vertically
1058     */
1059    void measureChildBeforeLayout(View child, int childIndex,
1060            int widthMeasureSpec, int totalWidth, int heightMeasureSpec,
1061            int totalHeight) {
1062        measureChildWithMargins(child, widthMeasureSpec, totalWidth,
1063                heightMeasureSpec, totalHeight);
1064    }
1065
1066    /**
1067     * <p>Return the location offset of the specified child. This can be used
1068     * by subclasses to change the location of a given widget.</p>
1069     *
1070     * @param child the child for which to obtain the location offset
1071     * @return the location offset in pixels
1072     */
1073    int getLocationOffset(View child) {
1074        return 0;
1075    }
1076
1077    /**
1078     * <p>Return the size offset of the next sibling of the specified child.
1079     * This can be used by subclasses to change the location of the widget
1080     * following <code>child</code>.</p>
1081     *
1082     * @param child the child whose next sibling will be moved
1083     * @return the location offset of the next child in pixels
1084     */
1085    int getNextLocationOffset(View child) {
1086        return 0;
1087    }
1088
1089    @Override
1090    protected void onLayout(boolean changed, int l, int t, int r, int b) {
1091        if (mOrientation == VERTICAL) {
1092            layoutVertical(l, t, r, b);
1093        } else {
1094            layoutHorizontal(l, t, r, b);
1095        }
1096    }
1097
1098    /**
1099     * Position the children during a layout pass if the orientation of this
1100     * LinearLayout is set to {@link #VERTICAL}.
1101     *
1102     * @see #getOrientation()
1103     * @see #setOrientation(int)
1104     * @see #onLayout(boolean, int, int, int, int)
1105     * @param left
1106     * @param top
1107     * @param right
1108     * @param bottom
1109     */
1110    void layoutVertical(int left, int top, int right, int bottom) {
1111        final int paddingLeft = mPaddingLeft;
1112
1113        int childTop;
1114        int childLeft;
1115
1116        // Where right end of child should go
1117        final int width = right - left;
1118        int childRight = width - mPaddingRight;
1119
1120        // Space available for child
1121        int childSpace = width - paddingLeft - mPaddingRight;
1122
1123        final int count = getVirtualChildCount();
1124
1125        final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1126        final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1127
1128        switch (majorGravity) {
1129           case Gravity.BOTTOM:
1130               // mTotalLength contains the padding already
1131               childTop = mPaddingTop + bottom - top - mTotalLength;
1132               break;
1133
1134               // mTotalLength contains the padding already
1135           case Gravity.CENTER_VERTICAL:
1136               childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
1137               break;
1138
1139           case Gravity.TOP:
1140           default:
1141               childTop = mPaddingTop;
1142               break;
1143        }
1144
1145        for (int i = 0; i < count; i++) {
1146            final View child = getVirtualChildAt(i);
1147            if (child == null) {
1148                childTop += measureNullChild(i);
1149            } else if (child.getVisibility() != GONE) {
1150                final int childWidth = child.getMeasuredWidth();
1151                final int childHeight = child.getMeasuredHeight();
1152
1153                final LayoutParams lp =
1154                        (LayoutParams) child.getLayoutParams();
1155
1156                int gravity = lp.gravity;
1157                if (gravity < 0) {
1158                    gravity = minorGravity;
1159                }
1160                final int layoutDirection = getLayoutDirection();
1161                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
1162                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
1163                    case Gravity.CENTER_HORIZONTAL:
1164                        childLeft = paddingLeft + ((childSpace - childWidth) / 2)
1165                                + lp.leftMargin - lp.rightMargin;
1166                        break;
1167
1168                    case Gravity.RIGHT:
1169                        childLeft = childRight - childWidth - lp.rightMargin;
1170                        break;
1171
1172                    case Gravity.LEFT:
1173                    default:
1174                        childLeft = paddingLeft + lp.leftMargin;
1175                        break;
1176                }
1177
1178                if (hasDividerBeforeChildAt(i)) {
1179                    childTop += mDividerHeight;
1180                }
1181
1182                childTop += lp.topMargin;
1183                setChildFrame(child, childLeft, childTop + getLocationOffset(child),
1184                        childWidth, childHeight);
1185                childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);
1186
1187                i += getChildrenSkipCount(child, i);
1188            }
1189        }
1190    }
1191
1192    @Override
1193    public void onRtlPropertiesChanged(int layoutDirection) {
1194        super.onRtlPropertiesChanged(layoutDirection);
1195        if (layoutDirection != mLayoutDirection) {
1196            mLayoutDirection = layoutDirection;
1197            if (mOrientation == HORIZONTAL) {
1198                requestLayout();
1199            }
1200        }
1201    }
1202
1203    /**
1204     * Position the children during a layout pass if the orientation of this
1205     * LinearLayout is set to {@link #HORIZONTAL}.
1206     *
1207     * @see #getOrientation()
1208     * @see #setOrientation(int)
1209     * @see #onLayout(boolean, int, int, int, int)
1210     * @param left
1211     * @param top
1212     * @param right
1213     * @param bottom
1214     */
1215    void layoutHorizontal(int left, int top, int right, int bottom) {
1216        final boolean isLayoutRtl = isLayoutRtl();
1217        final int paddingTop = mPaddingTop;
1218
1219        int childTop;
1220        int childLeft;
1221
1222        // Where bottom of child should go
1223        final int height = bottom - top;
1224        int childBottom = height - mPaddingBottom;
1225
1226        // Space available for child
1227        int childSpace = height - paddingTop - mPaddingBottom;
1228
1229        final int count = getVirtualChildCount();
1230
1231        final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1232        final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
1233
1234        final boolean baselineAligned = mBaselineAligned;
1235
1236        final int[] maxAscent = mMaxAscent;
1237        final int[] maxDescent = mMaxDescent;
1238
1239        final int layoutDirection = getLayoutDirection();
1240        switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {
1241            case Gravity.RIGHT:
1242                // mTotalLength contains the padding already
1243                childLeft = mPaddingLeft + right - left - mTotalLength;
1244                break;
1245
1246            case Gravity.CENTER_HORIZONTAL:
1247                // mTotalLength contains the padding already
1248                childLeft = mPaddingLeft + (right - left - mTotalLength) / 2;
1249                break;
1250
1251            case Gravity.LEFT:
1252            default:
1253                childLeft = mPaddingLeft;
1254                break;
1255        }
1256
1257        int start = 0;
1258        int dir = 1;
1259        //In case of RTL, start drawing from the last child.
1260        if (isLayoutRtl) {
1261            start = count - 1;
1262            dir = -1;
1263        }
1264
1265        for (int i = 0; i < count; i++) {
1266            int childIndex = start + dir * i;
1267            final View child = getVirtualChildAt(childIndex);
1268
1269            if (child == null) {
1270                childLeft += measureNullChild(childIndex);
1271            } else if (child.getVisibility() != GONE) {
1272                final int childWidth = child.getMeasuredWidth();
1273                final int childHeight = child.getMeasuredHeight();
1274                int childBaseline = -1;
1275
1276                final LayoutParams lp =
1277                        (LayoutParams) child.getLayoutParams();
1278
1279                if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {
1280                    childBaseline = child.getBaseline();
1281                }
1282
1283                int gravity = lp.gravity;
1284                if (gravity < 0) {
1285                    gravity = minorGravity;
1286                }
1287
1288                switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {
1289                    case Gravity.TOP:
1290                        childTop = paddingTop + lp.topMargin;
1291                        if (childBaseline != -1) {
1292                            childTop += maxAscent[INDEX_TOP] - childBaseline;
1293                        }
1294                        break;
1295
1296                    case Gravity.CENTER_VERTICAL:
1297                        // Removed support for baseline alignment when layout_gravity or
1298                        // gravity == center_vertical. See bug #1038483.
1299                        // Keep the code around if we need to re-enable this feature
1300                        // if (childBaseline != -1) {
1301                        //     // Align baselines vertically only if the child is smaller than us
1302                        //     if (childSpace - childHeight > 0) {
1303                        //         childTop = paddingTop + (childSpace / 2) - childBaseline;
1304                        //     } else {
1305                        //         childTop = paddingTop + (childSpace - childHeight) / 2;
1306                        //     }
1307                        // } else {
1308                        childTop = paddingTop + ((childSpace - childHeight) / 2)
1309                                + lp.topMargin - lp.bottomMargin;
1310                        break;
1311
1312                    case Gravity.BOTTOM:
1313                        childTop = childBottom - childHeight - lp.bottomMargin;
1314                        if (childBaseline != -1) {
1315                            int descent = child.getMeasuredHeight() - childBaseline;
1316                            childTop -= (maxDescent[INDEX_BOTTOM] - descent);
1317                        }
1318                        break;
1319                    default:
1320                        childTop = paddingTop;
1321                        break;
1322                }
1323
1324                if (hasDividerBeforeChildAt(childIndex)) {
1325                    childLeft += mDividerWidth;
1326                }
1327
1328                childLeft += lp.leftMargin;
1329                setChildFrame(child, childLeft + getLocationOffset(child), childTop,
1330                        childWidth, childHeight);
1331                childLeft += childWidth + lp.rightMargin +
1332                        getNextLocationOffset(child);
1333
1334                i += getChildrenSkipCount(child, childIndex);
1335            }
1336        }
1337    }
1338
1339    private void setChildFrame(View child, int left, int top, int width, int height) {
1340        child.layout(left, top, left + width, top + height);
1341    }
1342
1343    /**
1344     * Should the layout be a column or a row.
1345     * @param orientation Pass {@link #HORIZONTAL} or {@link #VERTICAL}. Default
1346     * value is {@link #HORIZONTAL}.
1347     *
1348     * @attr ref android.R.styleable#LinearLayout_orientation
1349     */
1350    public void setOrientation(@OrientationMode int orientation) {
1351        if (mOrientation != orientation) {
1352            mOrientation = orientation;
1353            requestLayout();
1354        }
1355    }
1356
1357    /**
1358     * Returns the current orientation.
1359     *
1360     * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
1361     */
1362    @OrientationMode
1363    public int getOrientation() {
1364        return mOrientation;
1365    }
1366
1367    /**
1368     * Describes how the child views are positioned. Defaults to GRAVITY_TOP. If
1369     * this layout has a VERTICAL orientation, this controls where all the child
1370     * views are placed if there is extra vertical space. If this layout has a
1371     * HORIZONTAL orientation, this controls the alignment of the children.
1372     *
1373     * @param gravity See {@link android.view.Gravity}
1374     *
1375     * @attr ref android.R.styleable#LinearLayout_gravity
1376     */
1377    @android.view.RemotableViewMethod
1378    public void setGravity(int gravity) {
1379        if (mGravity != gravity) {
1380            if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
1381                gravity |= Gravity.START;
1382            }
1383
1384            if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
1385                gravity |= Gravity.TOP;
1386            }
1387
1388            mGravity = gravity;
1389            requestLayout();
1390        }
1391    }
1392
1393    @android.view.RemotableViewMethod
1394    public void setHorizontalGravity(int horizontalGravity) {
1395        final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
1396        if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
1397            mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
1398            requestLayout();
1399        }
1400    }
1401
1402    @android.view.RemotableViewMethod
1403    public void setVerticalGravity(int verticalGravity) {
1404        final int gravity = verticalGravity & Gravity.VERTICAL_GRAVITY_MASK;
1405        if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != gravity) {
1406            mGravity = (mGravity & ~Gravity.VERTICAL_GRAVITY_MASK) | gravity;
1407            requestLayout();
1408        }
1409    }
1410
1411    @Override
1412    public LayoutParams generateLayoutParams(AttributeSet attrs) {
1413        return new LayoutParams(getContext(), attrs);
1414    }
1415
1416    /**
1417     * Returns a set of layout parameters with a width of
1418     * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
1419     * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
1420     * when the layout's orientation is {@link #VERTICAL}. When the orientation is
1421     * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
1422     * and the height to {@link LayoutParams#WRAP_CONTENT}.
1423     */
1424    @Override
1425    protected LayoutParams generateDefaultLayoutParams() {
1426        if (mOrientation == HORIZONTAL) {
1427            return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
1428        } else if (mOrientation == VERTICAL) {
1429            return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
1430        }
1431        return null;
1432    }
1433
1434    @Override
1435    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1436        return new LayoutParams(p);
1437    }
1438
1439
1440    // Override to allow type-checking of LayoutParams.
1441    @Override
1442    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1443        return p instanceof LayoutParams;
1444    }
1445
1446    @Override
1447    public CharSequence getAccessibilityClassName() {
1448        return MatchParentShrinkingLinearLayout.class.getName();
1449    }
1450
1451    /** @hide */
1452    @Override
1453    protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
1454        super.encodeProperties(encoder);
1455        encoder.addProperty("layout:baselineAligned", mBaselineAligned);
1456        encoder.addProperty("layout:baselineAlignedChildIndex", mBaselineAlignedChildIndex);
1457        encoder.addProperty("measurement:baselineChildTop", mBaselineChildTop);
1458        encoder.addProperty("measurement:orientation", mOrientation);
1459        encoder.addProperty("measurement:gravity", mGravity);
1460        encoder.addProperty("measurement:totalLength", mTotalLength);
1461        encoder.addProperty("layout:totalLength", mTotalLength);
1462        encoder.addProperty("layout:useLargestChild", mUseLargestChild);
1463    }
1464
1465    /**
1466     * Per-child layout information associated with ViewLinearLayout.
1467     *
1468     * @attr ref android.R.styleable#LinearLayout_Layout_layout_weight
1469     * @attr ref android.R.styleable#LinearLayout_Layout_layout_gravity
1470     */
1471    public static class LayoutParams extends MarginLayoutParams {
1472        /**
1473         * Indicates how much of the extra space in the LinearLayout will be
1474         * allocated to the view associated with these LayoutParams. Specify
1475         * 0 if the view should not be stretched. Otherwise the extra pixels
1476         * will be pro-rated among all views whose weight is greater than 0.
1477         */
1478        @ViewDebug.ExportedProperty(category = "layout")
1479        public float weight;
1480
1481        /**
1482         * Gravity for the view associated with these LayoutParams.
1483         *
1484         * @see android.view.Gravity
1485         */
1486        @ViewDebug.ExportedProperty(category = "layout", mapping = {
1487            @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
1488            @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
1489            @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
1490            @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
1491            @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
1492            @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
1493            @ViewDebug.IntToString(from = Gravity.START,            to = "START"),
1494            @ViewDebug.IntToString(from = Gravity.END,             to = "END"),
1495            @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
1496            @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
1497            @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
1498            @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
1499            @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
1500            @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
1501        })
1502        public int gravity = -1;
1503
1504        /**
1505         * {@inheritDoc}
1506         */
1507        public LayoutParams(Context c, AttributeSet attrs) {
1508            super(c, attrs);
1509            TypedArray a = c.obtainStyledAttributes(
1510                    attrs, com.android.internal.R.styleable.LinearLayout_Layout);
1511
1512            weight = a.getFloat(
1513                    com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
1514            gravity = a.getInt(
1515                    com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
1516
1517            a.recycle();
1518        }
1519
1520        /**
1521         * {@inheritDoc}
1522         */
1523        public LayoutParams(int width, int height) {
1524            super(width, height);
1525            weight = 0;
1526        }
1527
1528        /**
1529         * Creates a new set of layout parameters with the specified width, height
1530         * and weight.
1531         *
1532         * @param width the width, either {@link #MATCH_PARENT},
1533         *        {@link #WRAP_CONTENT} or a fixed size in pixels
1534         * @param height the height, either {@link #MATCH_PARENT},
1535         *        {@link #WRAP_CONTENT} or a fixed size in pixels
1536         * @param weight the weight
1537         */
1538        public LayoutParams(int width, int height, float weight) {
1539            super(width, height);
1540            this.weight = weight;
1541        }
1542
1543        /**
1544         * {@inheritDoc}
1545         */
1546        public LayoutParams(ViewGroup.LayoutParams p) {
1547            super(p);
1548        }
1549
1550        /**
1551         * {@inheritDoc}
1552         */
1553        public LayoutParams(MarginLayoutParams source) {
1554            super(source);
1555        }
1556
1557        /**
1558         * Copy constructor. Clones the width, height, margin values, weight,
1559         * and gravity of the source.
1560         *
1561         * @param source The layout params to copy from.
1562         */
1563        public LayoutParams(LayoutParams source) {
1564            super(source);
1565
1566            this.weight = source.weight;
1567            this.gravity = source.gravity;
1568        }
1569
1570        @Override
1571        public String debug(String output) {
1572            return output + "MatchParentShrinkingLinearLayout.LayoutParams={width="
1573                    + sizeToString(width) + ", height=" + sizeToString(height)
1574                    + " weight=" + weight +  "}";
1575        }
1576
1577        /** @hide */
1578        @Override
1579        protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
1580            super.encodeProperties(encoder);
1581
1582            encoder.addProperty("layout:weight", weight);
1583            encoder.addProperty("layout:gravity", gravity);
1584        }
1585    }
1586}
1587