1 /*
2  * Copyright (C) 2017 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.settings.widget;
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 
25 import androidx.annotation.Nullable;
26 import androidx.annotation.VisibleForTesting;
27 
28 /**
29  * An extension of LinearLayout that automatically switches to vertical
30  * orientation when it can't fit its child views horizontally.
31  *
32  * Main logic in this class comes from {@link androidx.appcompat.widget.ButtonBarLayout}.
33  * Compared with {@link androidx.appcompat.widget.ButtonBarLayout}, this layout won't reverse
34  * children's order and won't update the minimum height
35  */
36 public class BottomLabelLayout extends LinearLayout {
37     private static final String TAG = "BottomLabelLayout";
38 
BottomLabelLayout(Context context, @Nullable AttributeSet attrs)39     public BottomLabelLayout(Context context,
40             @Nullable AttributeSet attrs) {
41         super(context, attrs);
42     }
43 
44     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)45     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
46         final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
47         final boolean isStacked = isStacked();
48         boolean needsRemeasure = false;
49 
50         // If we're not stacked, make sure the measure spec is AT_MOST rather
51         // than EXACTLY. This ensures that we'll still get TOO_SMALL so that we
52         // know to stack the buttons.
53         final int initialWidthMeasureSpec;
54         if (!isStacked && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
55             initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
56 
57             // We'll need to remeasure again to fill excess space.
58             needsRemeasure = true;
59         } else {
60             initialWidthMeasureSpec = widthMeasureSpec;
61         }
62 
63         super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
64         if (!isStacked) {
65             final int measuredWidth = getMeasuredWidthAndState();
66             final int measuredWidthState = measuredWidth & View.MEASURED_STATE_MASK;
67 
68             if (measuredWidthState == View.MEASURED_STATE_TOO_SMALL) {
69                 setStacked(true);
70                 // Measure again in the new orientation.
71                 needsRemeasure = true;
72             }
73         }
74 
75         if (needsRemeasure) {
76             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
77         }
78 
79     }
80 
81     @VisibleForTesting
setStacked(boolean stacked)82     void setStacked(boolean stacked) {
83         setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
84         setGravity(stacked ? Gravity.START : Gravity.BOTTOM);
85 
86         final View spacer = findViewById(com.android.settings.R.id.spacer);
87         if (spacer != null) {
88             spacer.setVisibility(stacked ? View.GONE : View.VISIBLE);
89         }
90     }
91 
isStacked()92     private boolean isStacked() {
93         return getOrientation() == LinearLayout.VERTICAL;
94     }
95 }
96