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 17package com.android.packageinstaller.permission.ui; 18 19import android.content.Context; 20import android.util.AttributeSet; 21import android.view.Gravity; 22import android.view.View; 23import android.widget.LinearLayout; 24import 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 */ 30public class ButtonBarLayout extends LinearLayout { 31 /** Whether the current configuration allows stacking. */ 32 private boolean mAllowStacking; 33 34 private int mLastWidthSize = -1; 35 36 public ButtonBarLayout(Context context, AttributeSet attrs) { 37 super(context, attrs); 38 mAllowStacking = true; 39 } 40 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 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 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 114 private boolean isStacked() { 115 return getOrientation() == LinearLayout.VERTICAL; 116 } 117} 118