1 /* 2 * Copyright (C) 2016 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.systemui.statusbar; 18 19 import android.content.Context; 20 import android.util.AttributeSet; 21 import android.util.DisplayMetrics; 22 import android.util.TypedValue; 23 import android.view.View; 24 import android.view.ViewGroup; 25 26 /** 27 * Layout used as a container for keyboard shortcut keys. It's children are wrapped and right 28 * aligned. 29 */ 30 public final class KeyboardShortcutKeysLayout extends ViewGroup { 31 private int mLineHeight; 32 private final Context mContext; 33 KeyboardShortcutKeysLayout(Context context)34 public KeyboardShortcutKeysLayout(Context context) { 35 super(context); 36 this.mContext = context; 37 } 38 KeyboardShortcutKeysLayout(Context context, AttributeSet attrs)39 public KeyboardShortcutKeysLayout(Context context, AttributeSet attrs) { 40 super(context, attrs); 41 this.mContext = context; 42 } 43 44 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)45 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 46 int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); 47 int childCount = getChildCount(); 48 int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); 49 int lineHeight = 0; 50 int xPos = getPaddingLeft(); 51 int yPos = getPaddingTop(); 52 53 int childHeightMeasureSpec; 54 if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { 55 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); 56 } else { 57 childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 58 } 59 60 for (int i = 0; i < childCount; i++) { 61 View child = getChildAt(i); 62 if (child.getVisibility() != GONE) { 63 LayoutParams layoutParams = (LayoutParams) child.getLayoutParams(); 64 child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), 65 childHeightMeasureSpec); 66 int childWidth = child.getMeasuredWidth(); 67 lineHeight = Math.max(lineHeight, 68 child.getMeasuredHeight() + layoutParams.mVerticalSpacing); 69 70 if (xPos + childWidth > width) { 71 xPos = getPaddingLeft(); 72 yPos += lineHeight; 73 } 74 xPos += childWidth + layoutParams.mHorizontalSpacing; 75 } 76 } 77 this.mLineHeight = lineHeight; 78 79 if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { 80 height = yPos + lineHeight; 81 } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { 82 if (yPos + lineHeight < height) { 83 height = yPos + lineHeight; 84 } 85 } 86 setMeasuredDimension(width, height); 87 } 88 89 @Override generateDefaultLayoutParams()90 protected LayoutParams generateDefaultLayoutParams() { 91 int spacing = getHorizontalVerticalSpacing(); 92 return new LayoutParams(spacing, spacing); 93 } 94 95 @Override generateLayoutParams(ViewGroup.LayoutParams layoutParams)96 protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams layoutParams) { 97 int spacing = getHorizontalVerticalSpacing(); 98 return new LayoutParams(spacing, spacing, layoutParams); 99 } 100 101 @Override checkLayoutParams(ViewGroup.LayoutParams p)102 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 103 return (p instanceof LayoutParams); 104 } 105 106 @Override onLayout(boolean changed, int l, int t, int r, int b)107 protected void onLayout(boolean changed, int l, int t, int r, int b) { 108 int childCount = getChildCount(); 109 int fullRowWidth = r - l; 110 int xPos = isRTL() 111 ? fullRowWidth - getPaddingRight() 112 : getPaddingLeft(); 113 int yPos = getPaddingTop(); 114 int lastHorizontalSpacing = 0; 115 // The index of the child which starts the current row. 116 int rowStartIdx = 0; 117 118 // Go through all the children. 119 for (int i = 0; i < childCount; i++) { 120 View currentChild = getChildAt(i); 121 if (currentChild.getVisibility() != GONE) { 122 int currentChildWidth = currentChild.getMeasuredWidth(); 123 LayoutParams lp = (LayoutParams) currentChild.getLayoutParams(); 124 125 boolean childDoesNotFitOnRow = isRTL() 126 ? xPos - getPaddingLeft() - currentChildWidth < 0 127 : xPos + currentChildWidth > fullRowWidth; 128 129 if (childDoesNotFitOnRow) { 130 // Layout all the children on this row but the current one. 131 layoutChildrenOnRow(rowStartIdx, i, fullRowWidth, xPos, yPos, 132 lastHorizontalSpacing); 133 // Update the positions for starting on the new row. 134 xPos = isRTL() 135 ? fullRowWidth - getPaddingRight() 136 : getPaddingLeft(); 137 yPos += mLineHeight; 138 rowStartIdx = i; 139 } 140 141 xPos = isRTL() 142 ? xPos - currentChildWidth - lp.mHorizontalSpacing 143 : xPos + currentChildWidth + lp.mHorizontalSpacing; 144 lastHorizontalSpacing = lp.mHorizontalSpacing; 145 } 146 } 147 148 // Lay out the children on the last row. 149 if (rowStartIdx < childCount) { 150 layoutChildrenOnRow(rowStartIdx, childCount, fullRowWidth, xPos, yPos, 151 lastHorizontalSpacing); 152 } 153 } 154 getHorizontalVerticalSpacing()155 private int getHorizontalVerticalSpacing() { 156 DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); 157 return (int) TypedValue.applyDimension( 158 TypedValue.COMPLEX_UNIT_DIP, 4, displayMetrics); 159 } 160 layoutChildrenOnRow(int startIndex, int endIndex, int fullRowWidth, int xPos, int yPos, int lastHorizontalSpacing)161 private void layoutChildrenOnRow(int startIndex, int endIndex, int fullRowWidth, int xPos, 162 int yPos, int lastHorizontalSpacing) { 163 if (!isRTL()) { 164 xPos = getPaddingLeft() + fullRowWidth - xPos + lastHorizontalSpacing; 165 } 166 167 for (int j = startIndex; j < endIndex; ++j) { 168 View currentChild = getChildAt(j); 169 int currentChildWidth = currentChild.getMeasuredWidth(); 170 LayoutParams lp = (LayoutParams) currentChild.getLayoutParams(); 171 if (isRTL() && j == startIndex) { 172 xPos = fullRowWidth - xPos - getPaddingRight() - currentChildWidth 173 - lp.mHorizontalSpacing; 174 } 175 176 currentChild.layout( 177 xPos, 178 yPos, 179 xPos + currentChildWidth, 180 yPos + currentChild.getMeasuredHeight()); 181 182 if (isRTL()) { 183 int nextChildWidth = j < endIndex - 1 184 ? getChildAt(j + 1).getMeasuredWidth() 185 : 0; 186 xPos -= nextChildWidth + lp.mHorizontalSpacing; 187 } else { 188 xPos += currentChildWidth + lp.mHorizontalSpacing; 189 } 190 } 191 } 192 193 private boolean isRTL() { 194 return mContext.getResources().getConfiguration().getLayoutDirection() 195 == View.LAYOUT_DIRECTION_RTL; 196 } 197 198 public static class LayoutParams extends ViewGroup.LayoutParams { 199 public final int mHorizontalSpacing; 200 public final int mVerticalSpacing; 201 202 public LayoutParams(int horizontalSpacing, int verticalSpacing, 203 ViewGroup.LayoutParams viewGroupLayout) { 204 super(viewGroupLayout); 205 this.mHorizontalSpacing = horizontalSpacing; 206 this.mVerticalSpacing = verticalSpacing; 207 } 208 209 public LayoutParams(int mHorizontalSpacing, int verticalSpacing) { 210 super(0, 0); 211 this.mHorizontalSpacing = mHorizontalSpacing; 212 this.mVerticalSpacing = verticalSpacing; 213 } 214 } 215 } 216