177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette/*
277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * Copyright (C) 2015 The Android Open Source Project
377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette *
477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * Licensed under the Apache License, Version 2.0 (the "License");
577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * you may not use this file except in compliance with the License.
677bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * You may obtain a copy of the License at
777bb6f179a5495c90470f910d46667d8306ae010Alan Viverette *
877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette *      http://www.apache.org/licenses/LICENSE-2.0
977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette *
1077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * Unless required by applicable law or agreed to in writing, software
1177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * distributed under the License is distributed on an "AS IS" BASIS,
1277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * See the License for the specific language governing permissions and
1477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * limitations under the License.
1577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette */
1677bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
1777bb6f179a5495c90470f910d46667d8306ae010Alan Viverettepackage com.android.internal.widget;
1877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
1977bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.content.Context;
20922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viveretteimport android.content.res.TypedArray;
2177bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.util.AttributeSet;
2277bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.view.Gravity;
2377bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.view.View;
2477bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport android.widget.LinearLayout;
2577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
2677bb6f179a5495c90470f910d46667d8306ae010Alan Viveretteimport com.android.internal.R;
2777bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
2877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette/**
2977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * An extension of LinearLayout that automatically switches to vertical
3077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette * orientation when it can't fit its child views horizontally.
3177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette */
3277bb6f179a5495c90470f910d46667d8306ae010Alan Viverettepublic class ButtonBarLayout extends LinearLayout {
3377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette    /** Whether the current configuration allows stacking. */
345658328d8d4684640b65c24980885060f56af14dJason Monk    private boolean mAllowStacking;
3577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
36922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette    private int mLastWidthSize = -1;
37922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette
3877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette    public ButtonBarLayout(Context context, AttributeSet attrs) {
3977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        super(context, attrs);
4077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
41922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette        final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ButtonBarLayout);
42922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette        mAllowStacking = ta.getBoolean(R.styleable.ButtonBarLayout_allowStacking, false);
43922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette        ta.recycle();
4477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette    }
4577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
465658328d8d4684640b65c24980885060f56af14dJason Monk    public void setAllowStacking(boolean allowStacking) {
475658328d8d4684640b65c24980885060f56af14dJason Monk        if (mAllowStacking != allowStacking) {
485658328d8d4684640b65c24980885060f56af14dJason Monk            mAllowStacking = allowStacking;
495658328d8d4684640b65c24980885060f56af14dJason Monk            if (!mAllowStacking && getOrientation() == LinearLayout.VERTICAL) {
505658328d8d4684640b65c24980885060f56af14dJason Monk                setStacked(false);
515658328d8d4684640b65c24980885060f56af14dJason Monk            }
525658328d8d4684640b65c24980885060f56af14dJason Monk            requestLayout();
535658328d8d4684640b65c24980885060f56af14dJason Monk        }
545658328d8d4684640b65c24980885060f56af14dJason Monk    }
555658328d8d4684640b65c24980885060f56af14dJason Monk
5677bb6f179a5495c90470f910d46667d8306ae010Alan Viverette    @Override
57922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
58cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
59cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette
60922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette        if (mAllowStacking) {
61cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette            if (widthSize > mLastWidthSize && isStacked()) {
62922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette                // We're being measured wider this time, try un-stacking.
63922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette                setStacked(false);
64922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette            }
6577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
66922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette            mLastWidthSize = widthSize;
6777bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        }
6877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
69cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        boolean needsRemeasure = false;
70cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette
71cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        // If we're not stacked, make sure the measure spec is AT_MOST rather
72cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        // than EXACTLY. This ensures that we'll still get TOO_SMALL so that we
73cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        // know to stack the buttons.
74cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        final int initialWidthMeasureSpec;
75cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        if (!isStacked() && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
76cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette            initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
77cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette
78cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette            // We'll need to remeasure again to fill excess space.
79cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette            needsRemeasure = true;
80cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        } else {
81cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette            initialWidthMeasureSpec = widthMeasureSpec;
82cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        }
83cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette
84cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
8577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
86cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        if (mAllowStacking && !isStacked()) {
8777bb6f179a5495c90470f910d46667d8306ae010Alan Viverette            final int measuredWidth = getMeasuredWidthAndState();
8877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette            final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK;
8977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette            if (measuredWidthState == MEASURED_STATE_TOO_SMALL) {
9077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette                setStacked(true);
9177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
9277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette                // Measure again in the new orientation.
93cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette                needsRemeasure = true;
9477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette            }
9577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        }
96cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette
97cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        if (needsRemeasure) {
98cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
99cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        }
10077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette    }
10177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
10277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette    private void setStacked(boolean stacked) {
10377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
10477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        setGravity(stacked ? Gravity.RIGHT : Gravity.BOTTOM);
10577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
106922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette        final View spacer = findViewById(R.id.spacer);
107922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette        if (spacer != null) {
108922e1c6ed28da4c5b7ff6b1d4448fe3e8c11652fAlan Viverette            spacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
10977bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        }
11077bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
11177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        // Reverse the child order. This is specific to the Material button
11277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        // bar's layout XML and will probably not generalize.
11377bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        final int childCount = getChildCount();
11477bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        for (int i = childCount - 2; i >= 0; i--) {
11577bb6f179a5495c90470f910d46667d8306ae010Alan Viverette            bringChildToFront(getChildAt(i));
11677bb6f179a5495c90470f910d46667d8306ae010Alan Viverette        }
117cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette    }
11877bb6f179a5495c90470f910d46667d8306ae010Alan Viverette
119cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette    private boolean isStacked() {
120cffaf7e7e4d77fc7f8136051c548f9e0e7318101Alan Viverette        return getOrientation() == LinearLayout.VERTICAL;
12177bb6f179a5495c90470f910d46667d8306ae010Alan Viverette    }
12277bb6f179a5495c90470f910d46667d8306ae010Alan Viverette}
123