12c96011dee46e09d6ab766a42a98b299927d633bChris Banes/*
22c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Copyright (C) 2016 The Android Open Source Project
32c96011dee46e09d6ab766a42a98b299927d633bChris Banes *
42c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
52c96011dee46e09d6ab766a42a98b299927d633bChris Banes * you may not use this file except in compliance with the License.
62c96011dee46e09d6ab766a42a98b299927d633bChris Banes * You may obtain a copy of the License at
72c96011dee46e09d6ab766a42a98b299927d633bChris Banes *
82c96011dee46e09d6ab766a42a98b299927d633bChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
92c96011dee46e09d6ab766a42a98b299927d633bChris Banes *
102c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Unless required by applicable law or agreed to in writing, software
112c96011dee46e09d6ab766a42a98b299927d633bChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
122c96011dee46e09d6ab766a42a98b299927d633bChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132c96011dee46e09d6ab766a42a98b299927d633bChris Banes * See the License for the specific language governing permissions and
142c96011dee46e09d6ab766a42a98b299927d633bChris Banes * limitations under the License.
152c96011dee46e09d6ab766a42a98b299927d633bChris Banes */
162c96011dee46e09d6ab766a42a98b299927d633bChris Banes
172c96011dee46e09d6ab766a42a98b299927d633bChris Banespackage android.support.v7.widget;
182c96011dee46e09d6ab766a42a98b299927d633bChris Banes
198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
202c96011dee46e09d6ab766a42a98b299927d633bChris Banes
212c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.content.Context;
222c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.graphics.drawable.Drawable;
232c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.annotation.Nullable;
242c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.annotation.RestrictTo;
252c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.v4.view.GravityCompat;
262c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.v4.view.ViewCompat;
272c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.support.v7.appcompat.R;
282c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.util.AttributeSet;
292c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.view.Gravity;
302c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.view.View;
312c96011dee46e09d6ab766a42a98b299927d633bChris Banesimport android.view.ViewGroup;
322c96011dee46e09d6ab766a42a98b299927d633bChris Banes
332c96011dee46e09d6ab766a42a98b299927d633bChris Banes/**
342c96011dee46e09d6ab766a42a98b299927d633bChris Banes * Special implementation of linear layout that's capable of laying out alert
352c96011dee46e09d6ab766a42a98b299927d633bChris Banes * dialog components.
362c96011dee46e09d6ab766a42a98b299927d633bChris Banes * <p>
372c96011dee46e09d6ab766a42a98b299927d633bChris Banes * A dialog consists of up to three panels. All panels are optional, and a
382c96011dee46e09d6ab766a42a98b299927d633bChris Banes * dialog may contain only a single panel. The panels are laid out according
392c96011dee46e09d6ab766a42a98b299927d633bChris Banes * to the following guidelines:
402c96011dee46e09d6ab766a42a98b299927d633bChris Banes * <ul>
412c96011dee46e09d6ab766a42a98b299927d633bChris Banes *     <li>topPanel: exactly wrap_content</li>
422c96011dee46e09d6ab766a42a98b299927d633bChris Banes *     <li>contentPanel OR customPanel: at most fill_parent, first priority for
432c96011dee46e09d6ab766a42a98b299927d633bChris Banes *         extra space</li>
442c96011dee46e09d6ab766a42a98b299927d633bChris Banes *     <li>buttonPanel: at least minHeight, at most wrap_content, second
452c96011dee46e09d6ab766a42a98b299927d633bChris Banes *         priority for extra space</li>
462c96011dee46e09d6ab766a42a98b299927d633bChris Banes * </ul>
472c96011dee46e09d6ab766a42a98b299927d633bChris Banes *
482c96011dee46e09d6ab766a42a98b299927d633bChris Banes * @hide
492c96011dee46e09d6ab766a42a98b299927d633bChris Banes */
508e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas@RestrictTo(LIBRARY_GROUP)
512c96011dee46e09d6ab766a42a98b299927d633bChris Banespublic class AlertDialogLayout extends LinearLayoutCompat {
522c96011dee46e09d6ab766a42a98b299927d633bChris Banes
532c96011dee46e09d6ab766a42a98b299927d633bChris Banes    public AlertDialogLayout(@Nullable Context context) {
542c96011dee46e09d6ab766a42a98b299927d633bChris Banes        super(context);
552c96011dee46e09d6ab766a42a98b299927d633bChris Banes    }
562c96011dee46e09d6ab766a42a98b299927d633bChris Banes
572c96011dee46e09d6ab766a42a98b299927d633bChris Banes    public AlertDialogLayout(@Nullable Context context, @Nullable AttributeSet attrs) {
582c96011dee46e09d6ab766a42a98b299927d633bChris Banes        super(context, attrs);
592c96011dee46e09d6ab766a42a98b299927d633bChris Banes    }
602c96011dee46e09d6ab766a42a98b299927d633bChris Banes
612c96011dee46e09d6ab766a42a98b299927d633bChris Banes    @Override
622c96011dee46e09d6ab766a42a98b299927d633bChris Banes    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
632c96011dee46e09d6ab766a42a98b299927d633bChris Banes        if (!tryOnMeasure(widthMeasureSpec, heightMeasureSpec)) {
642c96011dee46e09d6ab766a42a98b299927d633bChris Banes            // Failed to perform custom measurement, let superclass handle it.
652c96011dee46e09d6ab766a42a98b299927d633bChris Banes            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
662c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
672c96011dee46e09d6ab766a42a98b299927d633bChris Banes    }
682c96011dee46e09d6ab766a42a98b299927d633bChris Banes
692c96011dee46e09d6ab766a42a98b299927d633bChris Banes    private boolean tryOnMeasure(int widthMeasureSpec, int heightMeasureSpec) {
702c96011dee46e09d6ab766a42a98b299927d633bChris Banes        View topPanel = null;
712c96011dee46e09d6ab766a42a98b299927d633bChris Banes        View buttonPanel = null;
722c96011dee46e09d6ab766a42a98b299927d633bChris Banes        View middlePanel = null;
732c96011dee46e09d6ab766a42a98b299927d633bChris Banes
742c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int count = getChildCount();
752c96011dee46e09d6ab766a42a98b299927d633bChris Banes        for (int i = 0; i < count; i++) {
762c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final View child = getChildAt(i);
772c96011dee46e09d6ab766a42a98b299927d633bChris Banes            if (child.getVisibility() == View.GONE) {
782c96011dee46e09d6ab766a42a98b299927d633bChris Banes                continue;
792c96011dee46e09d6ab766a42a98b299927d633bChris Banes            }
802c96011dee46e09d6ab766a42a98b299927d633bChris Banes
812c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final int id = child.getId();
822c96011dee46e09d6ab766a42a98b299927d633bChris Banes            if (id == R.id.topPanel) {
832c96011dee46e09d6ab766a42a98b299927d633bChris Banes                topPanel = child;
842c96011dee46e09d6ab766a42a98b299927d633bChris Banes            } else if (id == R.id.buttonPanel) {
852c96011dee46e09d6ab766a42a98b299927d633bChris Banes                buttonPanel = child;
862c96011dee46e09d6ab766a42a98b299927d633bChris Banes            } else if (id == R.id.contentPanel || id == R.id.customPanel) {
872c96011dee46e09d6ab766a42a98b299927d633bChris Banes                if (middlePanel != null) {
882c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    // Both the content and custom are visible. Abort!
892c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    return false;
902c96011dee46e09d6ab766a42a98b299927d633bChris Banes                }
912c96011dee46e09d6ab766a42a98b299927d633bChris Banes                middlePanel = child;
922c96011dee46e09d6ab766a42a98b299927d633bChris Banes            } else {
932c96011dee46e09d6ab766a42a98b299927d633bChris Banes                // Unknown top-level child. Abort!
942c96011dee46e09d6ab766a42a98b299927d633bChris Banes                return false;
952c96011dee46e09d6ab766a42a98b299927d633bChris Banes            }
962c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
972c96011dee46e09d6ab766a42a98b299927d633bChris Banes
982c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
992c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
1002c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
1012c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1022c96011dee46e09d6ab766a42a98b299927d633bChris Banes        int childState = 0;
1032c96011dee46e09d6ab766a42a98b299927d633bChris Banes        int usedHeight = getPaddingTop() + getPaddingBottom();
1042c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1052c96011dee46e09d6ab766a42a98b299927d633bChris Banes        if (topPanel != null) {
1062c96011dee46e09d6ab766a42a98b299927d633bChris Banes            topPanel.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
1072c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1082c96011dee46e09d6ab766a42a98b299927d633bChris Banes            usedHeight += topPanel.getMeasuredHeight();
109fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas            childState = View.combineMeasuredStates(childState, topPanel.getMeasuredState());
1102c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
1112c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1122c96011dee46e09d6ab766a42a98b299927d633bChris Banes        int buttonHeight = 0;
1132c96011dee46e09d6ab766a42a98b299927d633bChris Banes        int buttonWantsHeight = 0;
1142c96011dee46e09d6ab766a42a98b299927d633bChris Banes        if (buttonPanel != null) {
1152c96011dee46e09d6ab766a42a98b299927d633bChris Banes            buttonPanel.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
1162c96011dee46e09d6ab766a42a98b299927d633bChris Banes            buttonHeight = resolveMinimumHeight(buttonPanel);
1172c96011dee46e09d6ab766a42a98b299927d633bChris Banes            buttonWantsHeight = buttonPanel.getMeasuredHeight() - buttonHeight;
1182c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1192c96011dee46e09d6ab766a42a98b299927d633bChris Banes            usedHeight += buttonHeight;
120fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas            childState = View.combineMeasuredStates(childState, buttonPanel.getMeasuredState());
1212c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
1222c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1232c96011dee46e09d6ab766a42a98b299927d633bChris Banes        int middleHeight = 0;
1242c96011dee46e09d6ab766a42a98b299927d633bChris Banes        if (middlePanel != null) {
1252c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final int childHeightSpec;
1262c96011dee46e09d6ab766a42a98b299927d633bChris Banes            if (heightMode == MeasureSpec.UNSPECIFIED) {
1272c96011dee46e09d6ab766a42a98b299927d633bChris Banes                childHeightSpec = MeasureSpec.UNSPECIFIED;
1282c96011dee46e09d6ab766a42a98b299927d633bChris Banes            } else {
1292c96011dee46e09d6ab766a42a98b299927d633bChris Banes                childHeightSpec = MeasureSpec.makeMeasureSpec(
1302c96011dee46e09d6ab766a42a98b299927d633bChris Banes                        Math.max(0, heightSize - usedHeight), heightMode);
1312c96011dee46e09d6ab766a42a98b299927d633bChris Banes            }
1322c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1332c96011dee46e09d6ab766a42a98b299927d633bChris Banes            middlePanel.measure(widthMeasureSpec, childHeightSpec);
1342c96011dee46e09d6ab766a42a98b299927d633bChris Banes            middleHeight = middlePanel.getMeasuredHeight();
1352c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1362c96011dee46e09d6ab766a42a98b299927d633bChris Banes            usedHeight += middleHeight;
137fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas            childState = View.combineMeasuredStates(childState, middlePanel.getMeasuredState());
1382c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
1392c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1402c96011dee46e09d6ab766a42a98b299927d633bChris Banes        int remainingHeight = heightSize - usedHeight;
1412c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1422c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // Time for the "real" button measure pass. If we have remaining space,
1432c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // make the button pane bigger up to its target height. Otherwise,
1442c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // just remeasure the button at whatever height it needs.
1452c96011dee46e09d6ab766a42a98b299927d633bChris Banes        if (buttonPanel != null) {
1462c96011dee46e09d6ab766a42a98b299927d633bChris Banes            usedHeight -= buttonHeight;
1472c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1482c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final int heightToGive = Math.min(remainingHeight, buttonWantsHeight);
1492c96011dee46e09d6ab766a42a98b299927d633bChris Banes            if (heightToGive > 0) {
1502c96011dee46e09d6ab766a42a98b299927d633bChris Banes                remainingHeight -= heightToGive;
1512c96011dee46e09d6ab766a42a98b299927d633bChris Banes                buttonHeight += heightToGive;
1522c96011dee46e09d6ab766a42a98b299927d633bChris Banes            }
1532c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1542c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final int childHeightSpec = MeasureSpec.makeMeasureSpec(
1552c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    buttonHeight, MeasureSpec.EXACTLY);
1562c96011dee46e09d6ab766a42a98b299927d633bChris Banes            buttonPanel.measure(widthMeasureSpec, childHeightSpec);
1572c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1582c96011dee46e09d6ab766a42a98b299927d633bChris Banes            usedHeight += buttonPanel.getMeasuredHeight();
159fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas            childState = View.combineMeasuredStates(childState, buttonPanel.getMeasuredState());
1602c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
1612c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1622c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // If we still have remaining space, make the middle pane bigger up
1632c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // to the maximum height.
1642c96011dee46e09d6ab766a42a98b299927d633bChris Banes        if (middlePanel != null && remainingHeight > 0) {
1652c96011dee46e09d6ab766a42a98b299927d633bChris Banes            usedHeight -= middleHeight;
1662c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1672c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final int heightToGive = remainingHeight;
1682c96011dee46e09d6ab766a42a98b299927d633bChris Banes            remainingHeight -= heightToGive;
1692c96011dee46e09d6ab766a42a98b299927d633bChris Banes            middleHeight += heightToGive;
1702c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1712c96011dee46e09d6ab766a42a98b299927d633bChris Banes            // Pass the same height mode as we're using for the dialog itself.
1722c96011dee46e09d6ab766a42a98b299927d633bChris Banes            // If it's EXACTLY, then the middle pane MUST use the entire
1732c96011dee46e09d6ab766a42a98b299927d633bChris Banes            // height.
1742c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final int childHeightSpec = MeasureSpec.makeMeasureSpec(
1752c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    middleHeight, heightMode);
1762c96011dee46e09d6ab766a42a98b299927d633bChris Banes            middlePanel.measure(widthMeasureSpec, childHeightSpec);
1772c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1782c96011dee46e09d6ab766a42a98b299927d633bChris Banes            usedHeight += middlePanel.getMeasuredHeight();
179fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas            childState = View.combineMeasuredStates(childState, middlePanel.getMeasuredState());
1802c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
1812c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1822c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // Compute desired width as maximum child width.
1832c96011dee46e09d6ab766a42a98b299927d633bChris Banes        int maxWidth = 0;
1842c96011dee46e09d6ab766a42a98b299927d633bChris Banes        for (int i = 0; i < count; i++) {
1852c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final View child = getChildAt(i);
1862c96011dee46e09d6ab766a42a98b299927d633bChris Banes            if (child.getVisibility() != View.GONE) {
1872c96011dee46e09d6ab766a42a98b299927d633bChris Banes                maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
1882c96011dee46e09d6ab766a42a98b299927d633bChris Banes            }
1892c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
1902c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1912c96011dee46e09d6ab766a42a98b299927d633bChris Banes        maxWidth += getPaddingLeft() + getPaddingRight();
1922c96011dee46e09d6ab766a42a98b299927d633bChris Banes
193fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas        final int widthSizeAndState = View.resolveSizeAndState(
1942c96011dee46e09d6ab766a42a98b299927d633bChris Banes                maxWidth, widthMeasureSpec, childState);
195fa0f82f629bf95681c14ed559922f77a3030aa18Aurimas Liutikas        final int heightSizeAndState = View.resolveSizeAndState(
1962c96011dee46e09d6ab766a42a98b299927d633bChris Banes                usedHeight, heightMeasureSpec, 0);
1972c96011dee46e09d6ab766a42a98b299927d633bChris Banes        setMeasuredDimension(widthSizeAndState, heightSizeAndState);
1982c96011dee46e09d6ab766a42a98b299927d633bChris Banes
1992c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // If the children weren't already measured EXACTLY, we need to run
2002c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // another measure pass to for MATCH_PARENT widths.
2012c96011dee46e09d6ab766a42a98b299927d633bChris Banes        if (widthMode != MeasureSpec.EXACTLY) {
2022c96011dee46e09d6ab766a42a98b299927d633bChris Banes            forceUniformWidth(count, heightMeasureSpec);
2032c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
2042c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2052c96011dee46e09d6ab766a42a98b299927d633bChris Banes        return true;
2062c96011dee46e09d6ab766a42a98b299927d633bChris Banes    }
2072c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2082c96011dee46e09d6ab766a42a98b299927d633bChris Banes    /**
2092c96011dee46e09d6ab766a42a98b299927d633bChris Banes     * Remeasures child views to exactly match the layout's measured width.
2102c96011dee46e09d6ab766a42a98b299927d633bChris Banes     *
2112c96011dee46e09d6ab766a42a98b299927d633bChris Banes     * @param count the number of child views
2122c96011dee46e09d6ab766a42a98b299927d633bChris Banes     * @param heightMeasureSpec the original height measure spec
2132c96011dee46e09d6ab766a42a98b299927d633bChris Banes     */
2142c96011dee46e09d6ab766a42a98b299927d633bChris Banes    private void forceUniformWidth(int count, int heightMeasureSpec) {
2152c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // Pretend that the linear layout has an exact size.
2162c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int uniformMeasureSpec = MeasureSpec.makeMeasureSpec(
2172c96011dee46e09d6ab766a42a98b299927d633bChris Banes                getMeasuredWidth(), MeasureSpec.EXACTLY);
2182c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2192c96011dee46e09d6ab766a42a98b299927d633bChris Banes        for (int i = 0; i < count; i++) {
2202c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final View child = getChildAt(i);
2212c96011dee46e09d6ab766a42a98b299927d633bChris Banes            if (child.getVisibility() != GONE) {
2222c96011dee46e09d6ab766a42a98b299927d633bChris Banes                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
2232c96011dee46e09d6ab766a42a98b299927d633bChris Banes                if (lp.width == LayoutParams.MATCH_PARENT) {
2242c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    // Temporarily force children to reuse their old measured
2252c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    // height.
2262c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    final int oldHeight = lp.height;
2272c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    lp.height = child.getMeasuredHeight();
2282c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2292c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    // Remeasure with new dimensions.
2302c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    measureChildWithMargins(child, uniformMeasureSpec, 0, heightMeasureSpec, 0);
2312c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    lp.height = oldHeight;
2322c96011dee46e09d6ab766a42a98b299927d633bChris Banes                }
2332c96011dee46e09d6ab766a42a98b299927d633bChris Banes            }
2342c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
2352c96011dee46e09d6ab766a42a98b299927d633bChris Banes    }
2362c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2372c96011dee46e09d6ab766a42a98b299927d633bChris Banes    /**
2382c96011dee46e09d6ab766a42a98b299927d633bChris Banes     * Attempts to resolve the minimum height of a view.
2392c96011dee46e09d6ab766a42a98b299927d633bChris Banes     * <p>
2402c96011dee46e09d6ab766a42a98b299927d633bChris Banes     * If the view doesn't have a minimum height set and only contains a single
2412c96011dee46e09d6ab766a42a98b299927d633bChris Banes     * child, attempts to resolve the minimum height of the child view.
2422c96011dee46e09d6ab766a42a98b299927d633bChris Banes     *
2432c96011dee46e09d6ab766a42a98b299927d633bChris Banes     * @param v the view whose minimum height to resolve
2442c96011dee46e09d6ab766a42a98b299927d633bChris Banes     * @return the minimum height
2452c96011dee46e09d6ab766a42a98b299927d633bChris Banes     */
2462c96011dee46e09d6ab766a42a98b299927d633bChris Banes    private static int resolveMinimumHeight(View v) {
2472c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int minHeight = ViewCompat.getMinimumHeight(v);
2482c96011dee46e09d6ab766a42a98b299927d633bChris Banes        if (minHeight > 0) {
2492c96011dee46e09d6ab766a42a98b299927d633bChris Banes            return minHeight;
2502c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
2512c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2522c96011dee46e09d6ab766a42a98b299927d633bChris Banes        if (v instanceof ViewGroup) {
2532c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final ViewGroup vg = (ViewGroup) v;
2542c96011dee46e09d6ab766a42a98b299927d633bChris Banes            if (vg.getChildCount() == 1) {
2552c96011dee46e09d6ab766a42a98b299927d633bChris Banes                return resolveMinimumHeight(vg.getChildAt(0));
2562c96011dee46e09d6ab766a42a98b299927d633bChris Banes            }
2572c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
2582c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2592c96011dee46e09d6ab766a42a98b299927d633bChris Banes        return 0;
2602c96011dee46e09d6ab766a42a98b299927d633bChris Banes    }
2612c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2622c96011dee46e09d6ab766a42a98b299927d633bChris Banes    @Override
2632c96011dee46e09d6ab766a42a98b299927d633bChris Banes    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
2642c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int paddingLeft = getPaddingLeft();
2652c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2662c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // Where right end of child should go
2672c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int width = right - left;
2682c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int childRight = width - getPaddingRight();
2692c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2702c96011dee46e09d6ab766a42a98b299927d633bChris Banes        // Space available for child
2712c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int childSpace = width - paddingLeft - getPaddingRight();
2722c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2732c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int totalLength = getMeasuredHeight();
2742c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int count = getChildCount();
2752c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int gravity = getGravity();
2762c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int majorGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
2772c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int minorGravity = gravity & GravityCompat.RELATIVE_HORIZONTAL_GRAVITY_MASK;
2782c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2792c96011dee46e09d6ab766a42a98b299927d633bChris Banes        int childTop;
2802c96011dee46e09d6ab766a42a98b299927d633bChris Banes        switch (majorGravity) {
2812c96011dee46e09d6ab766a42a98b299927d633bChris Banes            case Gravity.BOTTOM:
2822c96011dee46e09d6ab766a42a98b299927d633bChris Banes                // totalLength contains the padding already
2832c96011dee46e09d6ab766a42a98b299927d633bChris Banes                childTop = getPaddingTop() + bottom - top - totalLength;
2842c96011dee46e09d6ab766a42a98b299927d633bChris Banes                break;
2852c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2862c96011dee46e09d6ab766a42a98b299927d633bChris Banes            // totalLength contains the padding already
2872c96011dee46e09d6ab766a42a98b299927d633bChris Banes            case Gravity.CENTER_VERTICAL:
2882c96011dee46e09d6ab766a42a98b299927d633bChris Banes                childTop = getPaddingTop() + (bottom - top - totalLength) / 2;
2892c96011dee46e09d6ab766a42a98b299927d633bChris Banes                break;
2902c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2912c96011dee46e09d6ab766a42a98b299927d633bChris Banes            case Gravity.TOP:
2922c96011dee46e09d6ab766a42a98b299927d633bChris Banes            default:
2932c96011dee46e09d6ab766a42a98b299927d633bChris Banes                childTop = getPaddingTop();
2942c96011dee46e09d6ab766a42a98b299927d633bChris Banes                break;
2952c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
2962c96011dee46e09d6ab766a42a98b299927d633bChris Banes
2972c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final Drawable dividerDrawable = getDividerDrawable();
2982c96011dee46e09d6ab766a42a98b299927d633bChris Banes        final int dividerHeight = dividerDrawable == null ?
2992c96011dee46e09d6ab766a42a98b299927d633bChris Banes                0 : dividerDrawable.getIntrinsicHeight();
3002c96011dee46e09d6ab766a42a98b299927d633bChris Banes
3012c96011dee46e09d6ab766a42a98b299927d633bChris Banes        for (int i = 0; i < count; i++) {
3022c96011dee46e09d6ab766a42a98b299927d633bChris Banes            final View child = getChildAt(i);
3032c96011dee46e09d6ab766a42a98b299927d633bChris Banes            if (child != null && child.getVisibility() != GONE) {
3042c96011dee46e09d6ab766a42a98b299927d633bChris Banes                final int childWidth = child.getMeasuredWidth();
3052c96011dee46e09d6ab766a42a98b299927d633bChris Banes                final int childHeight = child.getMeasuredHeight();
3062c96011dee46e09d6ab766a42a98b299927d633bChris Banes
3072c96011dee46e09d6ab766a42a98b299927d633bChris Banes                final LinearLayoutCompat.LayoutParams lp =
3082c96011dee46e09d6ab766a42a98b299927d633bChris Banes                        (LinearLayoutCompat.LayoutParams) child.getLayoutParams();
3092c96011dee46e09d6ab766a42a98b299927d633bChris Banes
3102c96011dee46e09d6ab766a42a98b299927d633bChris Banes                int layoutGravity = lp.gravity;
3112c96011dee46e09d6ab766a42a98b299927d633bChris Banes                if (layoutGravity < 0) {
3122c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    layoutGravity = minorGravity;
3132c96011dee46e09d6ab766a42a98b299927d633bChris Banes                }
3142c96011dee46e09d6ab766a42a98b299927d633bChris Banes                final int layoutDirection = ViewCompat.getLayoutDirection(this);
3152c96011dee46e09d6ab766a42a98b299927d633bChris Banes                final int absoluteGravity = GravityCompat.getAbsoluteGravity(
3162c96011dee46e09d6ab766a42a98b299927d633bChris Banes                        layoutGravity, layoutDirection);
3172c96011dee46e09d6ab766a42a98b299927d633bChris Banes
3182c96011dee46e09d6ab766a42a98b299927d633bChris Banes                final int childLeft;
3192c96011dee46e09d6ab766a42a98b299927d633bChris Banes                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
3202c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    case Gravity.CENTER_HORIZONTAL:
3212c96011dee46e09d6ab766a42a98b299927d633bChris Banes                        childLeft = paddingLeft + ((childSpace - childWidth) / 2)
3222c96011dee46e09d6ab766a42a98b299927d633bChris Banes                                + lp.leftMargin - lp.rightMargin;
3232c96011dee46e09d6ab766a42a98b299927d633bChris Banes                        break;
3242c96011dee46e09d6ab766a42a98b299927d633bChris Banes
3252c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    case Gravity.RIGHT:
3262c96011dee46e09d6ab766a42a98b299927d633bChris Banes                        childLeft = childRight - childWidth - lp.rightMargin;
3272c96011dee46e09d6ab766a42a98b299927d633bChris Banes                        break;
3282c96011dee46e09d6ab766a42a98b299927d633bChris Banes
3292c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    case Gravity.LEFT:
3302c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    default:
3312c96011dee46e09d6ab766a42a98b299927d633bChris Banes                        childLeft = paddingLeft + lp.leftMargin;
3322c96011dee46e09d6ab766a42a98b299927d633bChris Banes                        break;
3332c96011dee46e09d6ab766a42a98b299927d633bChris Banes                }
3342c96011dee46e09d6ab766a42a98b299927d633bChris Banes
3352c96011dee46e09d6ab766a42a98b299927d633bChris Banes                if (hasDividerBeforeChildAt(i)) {
3362c96011dee46e09d6ab766a42a98b299927d633bChris Banes                    childTop += dividerHeight;
3372c96011dee46e09d6ab766a42a98b299927d633bChris Banes                }
3382c96011dee46e09d6ab766a42a98b299927d633bChris Banes
3392c96011dee46e09d6ab766a42a98b299927d633bChris Banes                childTop += lp.topMargin;
3402c96011dee46e09d6ab766a42a98b299927d633bChris Banes                setChildFrame(child, childLeft, childTop, childWidth, childHeight);
3412c96011dee46e09d6ab766a42a98b299927d633bChris Banes                childTop += childHeight + lp.bottomMargin;
3422c96011dee46e09d6ab766a42a98b299927d633bChris Banes            }
3432c96011dee46e09d6ab766a42a98b299927d633bChris Banes        }
3442c96011dee46e09d6ab766a42a98b299927d633bChris Banes    }
3452c96011dee46e09d6ab766a42a98b299927d633bChris Banes
3462c96011dee46e09d6ab766a42a98b299927d633bChris Banes    private void setChildFrame(View child, int left, int top, int width, int height) {
3472c96011dee46e09d6ab766a42a98b299927d633bChris Banes        child.layout(left, top, left + width, top + height);
3482c96011dee46e09d6ab766a42a98b299927d633bChris Banes    }
3492c96011dee46e09d6ab766a42a98b299927d633bChris Banes}