blob: 098431658e6a1b8c685ce15d464df9950843adfd [file] [log] [blame]
package com.android.systemui.qs;
import static com.android.systemui.util.Utils.useQsMediaPlayer;
import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
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;
protected boolean mListening;
protected int mMaxAllowedRows = 3;
// Prototyping with less rows
private final boolean mLessRows;
public TileLayout(Context context) {
this(context, null);
}
public TileLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setFocusableInTouchMode(true);
mLessRows = (Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0)
|| useQsMediaPlayer(context);
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 (mLessRows) mMaxAllowedRows = Math.max(1, mMaxAllowedRows - 1);
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();
}
}