package com.android.systemui.qs;
|
|
import android.content.Context;
|
import android.content.res.Resources;
|
import android.util.AttributeSet;
|
import android.view.View;
|
import android.view.ViewGroup;
|
|
import com.android.systemui.R;
|
import com.android.systemui.qs.QSPanel.QSTileLayout;
|
import com.android.systemui.qs.QSPanel.TileRecord;
|
|
import java.util.ArrayList;
|
|
public class TileLayout extends ViewGroup implements QSTileLayout {
|
|
private static final float TILE_ASPECT = 1.2f;
|
|
private static final String TAG = "TileLayout";
|
|
protected int mColumns;
|
protected int mCellWidth;
|
protected int mCellHeight;
|
protected int mCellMarginHorizontal;
|
protected int mCellMarginVertical;
|
protected int mSidePadding;
|
protected int mRows = 1;
|
|
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
|
private int mCellMarginTop;
|
private boolean mListening;
|
protected int mMaxAllowedRows = 3;
|
|
public TileLayout(Context context) {
|
this(context, null);
|
}
|
|
public TileLayout(Context context, AttributeSet attrs) {
|
super(context, attrs);
|
setFocusableInTouchMode(true);
|
updateResources();
|
}
|
|
@Override
|
public int getOffsetTop(TileRecord tile) {
|
return getTop();
|
}
|
|
@Override
|
public void setListening(boolean listening) {
|
if (mListening == listening) return;
|
mListening = listening;
|
for (TileRecord record : mRecords) {
|
record.tile.setListening(this, mListening);
|
}
|
}
|
|
public void addTile(TileRecord tile) {
|
mRecords.add(tile);
|
tile.tile.setListening(this, mListening);
|
addTileView(tile);
|
}
|
|
protected void addTileView(TileRecord tile) {
|
addView(tile.tileView);
|
}
|
|
@Override
|
public void removeTile(TileRecord tile) {
|
mRecords.remove(tile);
|
tile.tile.setListening(this, false);
|
removeView(tile.tileView);
|
}
|
|
public void removeAllViews() {
|
for (TileRecord record : mRecords) {
|
record.tile.setListening(this, false);
|
}
|
mRecords.clear();
|
super.removeAllViews();
|
}
|
|
public boolean updateResources() {
|
final Resources res = mContext.getResources();
|
final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
|
mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
|
mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
|
mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
|
mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
|
mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
|
mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
|
if (mColumns != columns) {
|
mColumns = columns;
|
requestLayout();
|
return true;
|
}
|
return false;
|
}
|
|
@Override
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
// If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
|
// it will show all its tiles. In this case, the tiles have to be entered before the
|
// container is measured. Any change in the tiles, should trigger a remeasure.
|
final int numTiles = mRecords.size();
|
final int width = MeasureSpec.getSize(widthMeasureSpec);
|
final int availableWidth = width - getPaddingStart() - getPaddingEnd();
|
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
if (heightMode == MeasureSpec.UNSPECIFIED) {
|
mRows = (numTiles + mColumns - 1) / mColumns;
|
}
|
mCellWidth =
|
(availableWidth - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
|
|
// Measure each QS tile.
|
View previousView = this;
|
for (TileRecord record : mRecords) {
|
if (record.tileView.getVisibility() == GONE) continue;
|
record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
|
previousView = record.tileView.updateAccessibilityOrder(previousView);
|
}
|
|
// Only include the top margin in our measurement if we have more than 1 row to show.
|
// Otherwise, don't add the extra margin buffer at top.
|
int height = (mCellHeight + mCellMarginVertical) * mRows +
|
(mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
|
if (height < 0) height = 0;
|
|
setMeasuredDimension(width, height);
|
}
|
|
/**
|
* Determines the maximum number of rows that can be shown based on height. Clips at a minimum
|
* of 1 and a maximum of mMaxAllowedRows.
|
*
|
* @param heightMeasureSpec Available height.
|
* @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
|
*/
|
public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
|
final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop
|
+ mCellMarginVertical;
|
final int previousRows = mRows;
|
mRows = availableHeight / (mCellHeight + mCellMarginVertical);
|
if (mRows >= mMaxAllowedRows) {
|
mRows = mMaxAllowedRows;
|
} else if (mRows <= 1) {
|
mRows = 1;
|
}
|
if (mRows > (tilesCount + mColumns - 1) / mColumns) {
|
mRows = (tilesCount + mColumns - 1) / mColumns;
|
}
|
return previousRows != mRows;
|
}
|
|
@Override
|
public boolean hasOverlappingRendering() {
|
return false;
|
}
|
|
protected static int exactly(int size) {
|
return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
|
}
|
|
|
protected void layoutTileRecords(int numRecords) {
|
final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
|
int row = 0;
|
int column = 0;
|
|
// Layout each QS tile.
|
final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
|
for (int i = 0; i < tilesToLayout; i++, column++) {
|
// If we reached the last column available to layout a tile, wrap back to the next row.
|
if (column == mColumns) {
|
column = 0;
|
row++;
|
}
|
|
final TileRecord record = mRecords.get(i);
|
final int top = getRowTop(row);
|
final int left = getColumnStart(isRtl ? mColumns - column - 1 : column);
|
final int right = left + mCellWidth;
|
record.tileView.layout(left, top, right, top + record.tileView.getMeasuredHeight());
|
}
|
}
|
|
@Override
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
layoutTileRecords(mRecords.size());
|
}
|
|
private int getRowTop(int row) {
|
return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop;
|
}
|
|
protected int getColumnStart(int column) {
|
return getPaddingStart() + mSidePadding + mCellMarginHorizontal / 2 +
|
column * (mCellWidth + mCellMarginHorizontal);
|
}
|
|
@Override
|
public int getNumVisibleTiles() {
|
return mRecords.size();
|
}
|
}
|