• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.google.android.setupcompat.view;
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.google.android.setupcompat.R;
25 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
26 import com.google.android.setupcompat.template.FooterActionButton;
27 import com.google.android.setupcompat.util.Logger;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 
31 /**
32  * An extension of LinearLayout that automatically switches to vertical orientation when it can't
33  * fit its child views horizontally.
34  *
35  * <p>Modified from {@code com.android.internal.widget.ButtonBarLayout}
36  */
37 public class ButtonBarLayout extends LinearLayout {
38 
39   private static final Logger LOG = new Logger(ButtonBarLayout.class);
40 
41   private boolean stacked = false;
42   private int originalPaddingLeft;
43   private int originalPaddingRight;
44 
ButtonBarLayout(Context context)45   public ButtonBarLayout(Context context) {
46     super(context);
47   }
48 
ButtonBarLayout(Context context, AttributeSet attrs)49   public ButtonBarLayout(Context context, AttributeSet attrs) {
50     super(context, attrs);
51   }
52 
53   @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)54   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
55     final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
56 
57     setStacked(false);
58 
59     boolean needsRemeasure = false;
60 
61     int initialWidthMeasureSpec = widthMeasureSpec;
62     if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
63       // Measure with WRAP_CONTENT, so that we can compare the measured size with the
64       // available size to see if we need to stack.
65       initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
66 
67       // We'll need to remeasure again to fill excess space.
68       needsRemeasure = true;
69     }
70 
71     super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
72 
73     final boolean childrenLargerThanContainer = (widthSize > 0) && (getMeasuredWidth() > widthSize);
74     if (!isFooterButtonsEvenlyWeighted(getContext()) && childrenLargerThanContainer) {
75       setStacked(true);
76 
77       // Measure again in the new orientation.
78       needsRemeasure = true;
79     }
80 
81     if (needsRemeasure) {
82       super.onMeasure(widthMeasureSpec, heightMeasureSpec);
83     }
84   }
85 
setStacked(boolean stacked)86   private void setStacked(boolean stacked) {
87     if (this.stacked == stacked) {
88       return;
89     }
90     this.stacked = stacked;
91     boolean isUnstack = false;
92     int primaryStyleButtonCount = 0;
93     int childCount = getChildCount();
94     for (int i = 0; i < childCount; i++) {
95       View child = getChildAt(i);
96       LayoutParams childParams = (LayoutParams) child.getLayoutParams();
97       if (stacked) {
98         child.setTag(R.id.suc_customization_original_weight, childParams.weight);
99         childParams.weight = 0;
100         childParams.leftMargin = 0;
101       } else {
102         Float weight = (Float) child.getTag(R.id.suc_customization_original_weight);
103         if (weight != null) {
104           childParams.weight = weight;
105         } else {
106           // If the tag in the child is gone, it will be unstack and the child in the container will
107           // be disorder.
108           isUnstack = true;
109         }
110         if (isPrimaryButtonStyle(child)) {
111           primaryStyleButtonCount++;
112         }
113       }
114       child.setLayoutParams(childParams);
115     }
116 
117     setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
118     if (isUnstack) {
119       LOG.w("Reorder the FooterActionButtons in the container");
120       ArrayList<View> childViewsInContainerInOrder =
121           getChildViewsInContainerInOrder(
122               childCount, /* isOnePrimaryButton= */ (primaryStyleButtonCount <= 1));
123       for (int i = 0; i < childCount; i++) {
124         View view = childViewsInContainerInOrder.get(i);
125         if (view != null) {
126           bringChildToFront(view);
127         }
128       }
129     } else {
130       for (int i = childCount - 1; i >= 0; i--) {
131         bringChildToFront(getChildAt(i));
132       }
133     }
134 
135     if (stacked) {
136       // When stacked, the buttons need to be kept in the center of the button bar.
137       setHorizontalGravity(Gravity.CENTER);
138       // HACK: In the default button bar style, the left and right paddings are not
139       // balanced to compensate for different alignment for borderless (left) button and
140       // the raised (right) button. When it's stacked, we want the buttons to be centered,
141       // so we balance out the paddings here.
142       originalPaddingLeft = getPaddingLeft();
143       originalPaddingRight = getPaddingRight();
144       int paddingHorizontal = Math.max(originalPaddingLeft, originalPaddingRight);
145       setPadding(paddingHorizontal, getPaddingTop(), paddingHorizontal, getPaddingBottom());
146     } else {
147       setPadding(originalPaddingLeft, getPaddingTop(), originalPaddingRight, getPaddingBottom());
148     }
149   }
150 
isPrimaryButtonStyle(View child)151   private boolean isPrimaryButtonStyle(View child) {
152     return child instanceof FooterActionButton
153         && ((FooterActionButton) child).isPrimaryButtonStyle();
154   }
155 
156   /**
157    * Return a array which store child views in the container and in the order (secondary button,
158    * space view, primary button), if only one primary button, the child views will replace null
159    * value in specific proper position, if there are two primary buttons, expected get the original
160    * child by the order (space view, secondary button, primary button), so insert the space view to
161    * the middle in the array.
162    */
getChildViewsInContainerInOrder( int childCount, boolean isOnePrimaryButton)163   private ArrayList<View> getChildViewsInContainerInOrder(
164       int childCount, boolean isOnePrimaryButton) {
165     int childViewsInContainerCount = 3;
166     int secondaryButtonIndex = 0;
167     int spaceViewIndex = 1;
168     int primaryButtonIndex = 2;
169 
170     ArrayList<View> childFooterButtons = new ArrayList<>();
171 
172     if (isOnePrimaryButton) {
173       childFooterButtons.addAll(Collections.nCopies(childViewsInContainerCount, null));
174     }
175 
176     for (int i = 0; i < childCount; i++) {
177       View childAt = getChildAt(i);
178       if (isOnePrimaryButton) {
179         if (isPrimaryButtonStyle(childAt)) {
180           childFooterButtons.set(primaryButtonIndex, childAt);
181         } else if (!(childAt instanceof FooterActionButton)) {
182           childFooterButtons.set(spaceViewIndex, childAt);
183         } else {
184           childFooterButtons.set(secondaryButtonIndex, childAt);
185         }
186       } else {
187         if (!(childAt instanceof FooterActionButton)) {
188           childFooterButtons.add(spaceViewIndex, childAt);
189         } else {
190           childFooterButtons.add(getChildAt(i));
191         }
192       }
193     }
194     return childFooterButtons;
195   }
196 
isFooterButtonsEvenlyWeighted(Context context)197   private boolean isFooterButtonsEvenlyWeighted(Context context) {
198     int childCount = getChildCount();
199     int primaryButtonCount = 0;
200     for (int i = 0; i < childCount; i++) {
201       View child = getChildAt(i);
202       if (child instanceof FooterActionButton) {
203         if (((FooterActionButton) child).isPrimaryButtonStyle()) {
204           primaryButtonCount += 1;
205         }
206       }
207     }
208     if (primaryButtonCount != 2) {
209       return false;
210     }
211 
212     // TODO: Support neutral button style in glif layout for phone and tablet
213     if (context.getResources().getConfiguration().smallestScreenWidthDp >= 600
214         && PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context)) {
215       return true;
216     } else {
217       return false;
218     }
219   }
220 }
221