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 
17 package com.android.packageinstaller.permission.ui;
18 
19 import android.content.Context;
20 import android.util.AttributeSet;
21 import android.view.Gravity;
22 import android.view.View;
23 import android.widget.LinearLayout;
24 import com.android.packageinstaller.R;
25 
26 /**
27  * An extension of LinearLayout that automatically switches to vertical
28  * orientation when it can't fit its child views horizontally.
29  */
30 public class ButtonBarLayout extends LinearLayout {
31     /** Whether the current configuration allows stacking. */
32     private boolean mAllowStacking;
33 
34     private int mLastWidthSize = -1;
35 
ButtonBarLayout(Context context, AttributeSet attrs)36     public ButtonBarLayout(Context context, AttributeSet attrs) {
37         super(context, attrs);
38         mAllowStacking = true;
39     }
40 
setAllowStacking(boolean allowStacking)41     public void setAllowStacking(boolean allowStacking) {
42         if (mAllowStacking != allowStacking) {
43             mAllowStacking = allowStacking;
44             if (!mAllowStacking && getOrientation() == LinearLayout.VERTICAL) {
45                 setStacked(false);
46             }
47             requestLayout();
48         }
49     }
50 
51     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)52     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
53         final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
54 
55         if (mAllowStacking) {
56             if (widthSize > mLastWidthSize && isStacked()) {
57                 // We're being measured wider this time, try un-stacking.
58                 setStacked(false);
59             }
60 
61             mLastWidthSize = widthSize;
62         }
63 
64         boolean needsRemeasure = false;
65 
66         // If we're not stacked, make sure the measure spec is AT_MOST rather
67         // than EXACTLY. This ensures that we'll still get TOO_SMALL so that we
68         // know to stack the buttons.
69         final int initialWidthMeasureSpec;
70         if (!isStacked() && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
71             initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
72 
73             // We'll need to remeasure again to fill excess space.
74             needsRemeasure = true;
75         } else {
76             initialWidthMeasureSpec = widthMeasureSpec;
77         }
78 
79         super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
80 
81         if (mAllowStacking && !isStacked()) {
82             final int measuredWidth = getMeasuredWidthAndState();
83             final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK;
84             if (measuredWidthState == MEASURED_STATE_TOO_SMALL) {
85                 setStacked(true);
86 
87                 // Measure again in the new orientation.
88                 needsRemeasure = true;
89             }
90         }
91 
92         if (needsRemeasure) {
93             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
94         }
95     }
96 
setStacked(boolean stacked)97     private void setStacked(boolean stacked) {
98         setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
99         setGravity(stacked ? Gravity.END : Gravity.BOTTOM);
100 
101         final View spacer = findViewById(R.id.spacer);
102         if (spacer != null) {
103             spacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
104         }
105 
106         // Reverse the child order. This is specific to the Material button
107         // bar's layout XML and will probably not generalize.
108         final int childCount = getChildCount();
109         for (int i = childCount - 2; i >= 0; i--) {
110             bringChildToFront(getChildAt(i));
111         }
112     }
113 
isStacked()114     private boolean isStacked() {
115         return getOrientation() == LinearLayout.VERTICAL;
116     }
117 }
118