Home | History | Annotate | Download | only in qs
      1 package com.android.systemui.qs;
      2 
      3 import android.content.Context;
      4 import android.content.res.Resources;
      5 import android.util.AttributeSet;
      6 import android.view.View;
      7 import android.view.ViewGroup;
      8 
      9 import com.android.systemui.R;
     10 import com.android.systemui.qs.QSPanel.QSTileLayout;
     11 import com.android.systemui.qs.QSPanel.TileRecord;
     12 
     13 import java.util.ArrayList;
     14 
     15 public class TileLayout extends ViewGroup implements QSTileLayout {
     16 
     17     private static final float TILE_ASPECT = 1.2f;
     18 
     19     private static final String TAG = "TileLayout";
     20 
     21     protected int mColumns;
     22     protected int mCellWidth;
     23     protected int mCellHeight;
     24     protected int mCellMarginHorizontal;
     25     protected int mCellMarginVertical;
     26     protected int mSidePadding;
     27     protected int mRows = 1;
     28 
     29     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
     30     private int mCellMarginTop;
     31     private boolean mListening;
     32     protected int mMaxAllowedRows = 3;
     33 
     34     public TileLayout(Context context) {
     35         this(context, null);
     36     }
     37 
     38     public TileLayout(Context context, AttributeSet attrs) {
     39         super(context, attrs);
     40         setFocusableInTouchMode(true);
     41         updateResources();
     42     }
     43 
     44     @Override
     45     public int getOffsetTop(TileRecord tile) {
     46         return getTop();
     47     }
     48 
     49     @Override
     50     public void setListening(boolean listening) {
     51         if (mListening == listening) return;
     52         mListening = listening;
     53         for (TileRecord record : mRecords) {
     54             record.tile.setListening(this, mListening);
     55         }
     56     }
     57 
     58     public void addTile(TileRecord tile) {
     59         mRecords.add(tile);
     60         tile.tile.setListening(this, mListening);
     61         addTileView(tile);
     62     }
     63 
     64     protected void addTileView(TileRecord tile) {
     65         addView(tile.tileView);
     66     }
     67 
     68     @Override
     69     public void removeTile(TileRecord tile) {
     70         mRecords.remove(tile);
     71         tile.tile.setListening(this, false);
     72         removeView(tile.tileView);
     73     }
     74 
     75     public void removeAllViews() {
     76         for (TileRecord record : mRecords) {
     77             record.tile.setListening(this, false);
     78         }
     79         mRecords.clear();
     80         super.removeAllViews();
     81     }
     82 
     83     public boolean updateResources() {
     84         final Resources res = mContext.getResources();
     85         final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
     86         mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
     87         mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
     88         mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
     89         mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
     90         mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
     91         mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
     92         if (mColumns != columns) {
     93             mColumns = columns;
     94             requestLayout();
     95             return true;
     96         }
     97         return false;
     98     }
     99 
    100     @Override
    101     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    102         // If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
    103         // it will show all its tiles. In this case, the tiles have to be entered before the
    104         // container is measured. Any change in the tiles, should trigger a remeasure.
    105         final int numTiles = mRecords.size();
    106         final int width = MeasureSpec.getSize(widthMeasureSpec);
    107         final int availableWidth = width - getPaddingStart() - getPaddingEnd();
    108         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    109         if (heightMode == MeasureSpec.UNSPECIFIED) {
    110             mRows = (numTiles + mColumns - 1) / mColumns;
    111         }
    112         mCellWidth =
    113                 (availableWidth - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
    114 
    115         // Measure each QS tile.
    116         View previousView = this;
    117         for (TileRecord record : mRecords) {
    118             if (record.tileView.getVisibility() == GONE) continue;
    119             record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
    120             previousView = record.tileView.updateAccessibilityOrder(previousView);
    121         }
    122 
    123         // Only include the top margin in our measurement if we have more than 1 row to show.
    124         // Otherwise, don't add the extra margin buffer at top.
    125         int height = (mCellHeight + mCellMarginVertical) * mRows +
    126                 (mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
    127         if (height < 0) height = 0;
    128 
    129         setMeasuredDimension(width, height);
    130     }
    131 
    132     /**
    133      * Determines the maximum number of rows that can be shown based on height. Clips at a minimum
    134      * of 1 and a maximum of mMaxAllowedRows.
    135      *
    136      * @param heightMeasureSpec Available height.
    137      * @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
    138      */
    139     public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
    140         final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop
    141                 + mCellMarginVertical;
    142         final int previousRows = mRows;
    143         mRows = availableHeight / (mCellHeight + mCellMarginVertical);
    144         if (mRows >= mMaxAllowedRows) {
    145             mRows = mMaxAllowedRows;
    146         } else if (mRows <= 1) {
    147             mRows = 1;
    148         }
    149         if (mRows > (tilesCount + mColumns - 1) / mColumns) {
    150             mRows = (tilesCount + mColumns - 1) / mColumns;
    151         }
    152         return previousRows != mRows;
    153     }
    154 
    155     @Override
    156     public boolean hasOverlappingRendering() {
    157         return false;
    158     }
    159 
    160     protected static int exactly(int size) {
    161         return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
    162     }
    163 
    164 
    165     protected void layoutTileRecords(int numRecords) {
    166         final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
    167         int row = 0;
    168         int column = 0;
    169 
    170         // Layout each QS tile.
    171         final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
    172         for (int i = 0; i < tilesToLayout; i++, column++) {
    173             // If we reached the last column available to layout a tile, wrap back to the next row.
    174             if (column == mColumns) {
    175                 column = 0;
    176                 row++;
    177             }
    178 
    179             final TileRecord record = mRecords.get(i);
    180             final int top = getRowTop(row);
    181             final int left = getColumnStart(isRtl ? mColumns - column - 1 : column);
    182             final int right = left + mCellWidth;
    183             record.tileView.layout(left, top, right, top + record.tileView.getMeasuredHeight());
    184         }
    185     }
    186 
    187     @Override
    188     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    189         layoutTileRecords(mRecords.size());
    190     }
    191 
    192     private int getRowTop(int row) {
    193         return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop;
    194     }
    195 
    196     protected int getColumnStart(int column) {
    197         return getPaddingStart() + mSidePadding + mCellMarginHorizontal / 2 +
    198                 column *  (mCellWidth + mCellMarginHorizontal);
    199     }
    200 
    201     @Override
    202     public int getNumVisibleTiles() {
    203         return mRecords.size();
    204     }
    205 }
    206