1ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav/*
2ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * Copyright (C) 2015 The Android Open Source Project
3ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav *
4ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * Licensed under the Apache License, Version 2.0 (the "License");
5ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * you may not use this file except in compliance with the License.
6ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * You may obtain a copy of the License at
7ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav *
8ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav *      http://www.apache.org/licenses/LICENSE-2.0
9ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav *
10ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * Unless required by applicable law or agreed to in writing, software
11ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * distributed under the License is distributed on an "AS IS" BASIS,
12ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * See the License for the specific language governing permissions and
14ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * limitations under the License.
15ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav */
16ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
17ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavpackage com.android.packageinstaller.permission.ui;
18ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
19ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.content.Context;
20ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.util.AttributeSet;
21ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.view.Gravity;
22ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.view.View;
23ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport android.widget.LinearLayout;
24ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavimport com.android.packageinstaller.R;
25ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
26ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav/**
27ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * An extension of LinearLayout that automatically switches to vertical
28ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav * orientation when it can't fit its child views horizontally.
29ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav */
30ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslavpublic class ButtonBarLayout extends LinearLayout {
31ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    /** Whether the current configuration allows stacking. */
32ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private boolean mAllowStacking;
33ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
34ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private int mLastWidthSize = -1;
35ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
36ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    public ButtonBarLayout(Context context, AttributeSet attrs) {
37ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        super(context, attrs);
38ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        mAllowStacking = true;
39ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
40ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
41ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    public void setAllowStacking(boolean allowStacking) {
42ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (mAllowStacking != allowStacking) {
43ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            mAllowStacking = allowStacking;
44ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            if (!mAllowStacking && getOrientation() == LinearLayout.VERTICAL) {
45ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                setStacked(false);
46ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            }
47ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            requestLayout();
48ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
49ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
50ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
51ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    @Override
52ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
53ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
54ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
55ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (mAllowStacking) {
56ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            if (widthSize > mLastWidthSize && isStacked()) {
57ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                // We're being measured wider this time, try un-stacking.
58ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                setStacked(false);
59ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            }
60ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
61ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            mLastWidthSize = widthSize;
62ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
63ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
64ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        boolean needsRemeasure = false;
65ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
66ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        // If we're not stacked, make sure the measure spec is AT_MOST rather
67ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        // than EXACTLY. This ensures that we'll still get TOO_SMALL so that we
68ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        // know to stack the buttons.
69ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        final int initialWidthMeasureSpec;
70ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (!isStacked() && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
71ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
72ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
73ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            // We'll need to remeasure again to fill excess space.
74ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            needsRemeasure = true;
75ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        } else {
76ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            initialWidthMeasureSpec = widthMeasureSpec;
77ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
78ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
79ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
80ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
81ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (mAllowStacking && !isStacked()) {
82ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            final int measuredWidth = getMeasuredWidthAndState();
83ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK;
84ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            if (measuredWidthState == MEASURED_STATE_TOO_SMALL) {
85ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                setStacked(true);
86ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
87ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                // Measure again in the new orientation.
88ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav                needsRemeasure = true;
89ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            }
90ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
91ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
92ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (needsRemeasure) {
93ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
94ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
95ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
96ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
97ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private void setStacked(boolean stacked) {
98ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
995e3cc5335e208fff39b0983a88f37e92b3da8c69Svet Ganov        setGravity(stacked ? Gravity.END : Gravity.BOTTOM);
100ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
101ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        final View spacer = findViewById(R.id.spacer);
102ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        if (spacer != null) {
103ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            spacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
104ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
105ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
106ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        // Reverse the child order. This is specific to the Material button
107ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        // bar's layout XML and will probably not generalize.
108ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        final int childCount = getChildCount();
109ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        for (int i = childCount - 2; i >= 0; i--) {
110ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav            bringChildToFront(getChildAt(i));
111ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        }
112ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
113ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav
114ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    private boolean isStacked() {
115ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav        return getOrientation() == LinearLayout.VERTICAL;
116ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav    }
117ef861375eebd9ac6cce7c0bb163380ab1c951063Svetoslav}
118