Home | History | Annotate | Download | only in statusbar
      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 
     34     public KeyboardShortcutKeysLayout(Context context) {
     35         super(context);
     36         this.mContext = context;
     37     }
     38 
     39     public KeyboardShortcutKeysLayout(Context context, AttributeSet attrs) {
     40         super(context, attrs);
     41         this.mContext = context;
     42     }
     43 
     44     @Override
     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
     90     protected LayoutParams generateDefaultLayoutParams() {
     91         int spacing = getHorizontalVerticalSpacing();
     92         return new LayoutParams(spacing, spacing);
     93     }
     94 
     95     @Override
     96     protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams layoutParams) {
     97         int spacing = getHorizontalVerticalSpacing();
     98         return new LayoutParams(spacing, spacing, layoutParams);
     99     }
    100 
    101     @Override
    102     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    103         return (p instanceof LayoutParams);
    104     }
    105 
    106     @Override
    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 
    155     private int getHorizontalVerticalSpacing() {
    156         DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
    157         return (int) TypedValue.applyDimension(
    158                 TypedValue.COMPLEX_UNIT_DIP, 4, displayMetrics);
    159     }
    160 
    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