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 28 protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); 29 private int mCellMarginTop; 30 private boolean mListening; 31 TileLayout(Context context)32 public TileLayout(Context context) { 33 this(context, null); 34 } 35 TileLayout(Context context, AttributeSet attrs)36 public TileLayout(Context context, AttributeSet attrs) { 37 super(context, attrs); 38 setFocusableInTouchMode(true); 39 updateResources(); 40 } 41 42 @Override getOffsetTop(TileRecord tile)43 public int getOffsetTop(TileRecord tile) { 44 return getTop(); 45 } 46 47 @Override setListening(boolean listening)48 public void setListening(boolean listening) { 49 if (mListening == listening) return; 50 mListening = listening; 51 for (TileRecord record : mRecords) { 52 record.tile.setListening(this, mListening); 53 } 54 } 55 addTile(TileRecord tile)56 public void addTile(TileRecord tile) { 57 mRecords.add(tile); 58 tile.tile.setListening(this, mListening); 59 addView(tile.tileView); 60 } 61 62 @Override removeTile(TileRecord tile)63 public void removeTile(TileRecord tile) { 64 mRecords.remove(tile); 65 tile.tile.setListening(this, false); 66 removeView(tile.tileView); 67 } 68 removeAllViews()69 public void removeAllViews() { 70 for (TileRecord record : mRecords) { 71 record.tile.setListening(this, false); 72 } 73 mRecords.clear(); 74 super.removeAllViews(); 75 } 76 updateResources()77 public boolean updateResources() { 78 final Resources res = mContext.getResources(); 79 final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns)); 80 mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height); 81 mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal); 82 mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical); 83 mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top); 84 mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side); 85 if (mColumns != columns) { 86 mColumns = columns; 87 requestLayout(); 88 return true; 89 } 90 return false; 91 } 92 93 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)94 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 95 final int numTiles = mRecords.size(); 96 final int width = MeasureSpec.getSize(widthMeasureSpec) 97 - getPaddingStart() - getPaddingEnd(); 98 final int numRows = (numTiles + mColumns - 1) / mColumns; 99 mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns; 100 101 // Measure each QS tile. 102 View previousView = this; 103 for (TileRecord record : mRecords) { 104 if (record.tileView.getVisibility() == GONE) continue; 105 record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight)); 106 previousView = record.tileView.updateAccessibilityOrder(previousView); 107 } 108 109 // Only include the top margin in our measurement if we have more than 1 row to show. 110 // Otherwise, don't add the extra margin buffer at top. 111 int height = (mCellHeight + mCellMarginVertical) * numRows + 112 (numRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0); 113 if (height < 0) height = 0; 114 115 setMeasuredDimension(width, height); 116 } 117 118 @Override hasOverlappingRendering()119 public boolean hasOverlappingRendering() { 120 return false; 121 } 122 exactly(int size)123 private static int exactly(int size) { 124 return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); 125 } 126 127 @Override onLayout(boolean changed, int l, int t, int r, int b)128 protected void onLayout(boolean changed, int l, int t, int r, int b) { 129 final int w = getWidth(); 130 final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL; 131 int row = 0; 132 int column = 0; 133 134 // Layout each QS tile. 135 for (int i = 0; i < mRecords.size(); i++, column++) { 136 // If we reached the last column available to layout a tile, wrap back to the next row. 137 if (column == mColumns) { 138 column = 0; 139 row++; 140 } 141 142 final TileRecord record = mRecords.get(i); 143 final int top = getRowTop(row); 144 final int left = getColumnStart(isRtl ? mColumns - column - 1 : column); 145 final int right = left + mCellWidth; 146 record.tileView.layout(left, top, right, top + record.tileView.getMeasuredHeight()); 147 } 148 } 149 getRowTop(int row)150 private int getRowTop(int row) { 151 return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop; 152 } 153 getColumnStart(int column)154 private int getColumnStart(int column) { 155 return getPaddingStart() + mSidePadding + mCellMarginHorizontal / 2 + 156 column * (mCellWidth + mCellMarginHorizontal); 157 } 158 } 159