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