blob: 37743ec55517693e619b0a4940667a67957a3cc2 [file] [log] [blame]
Jason Monk520ea062015-08-18 14:53:06 -04001package com.android.systemui.qs;
2
3import android.content.Context;
4import android.content.res.Resources;
Fabian Kozynskif92b8812019-10-10 14:50:59 -04005import android.provider.Settings;
Jason Monkcaf37622015-08-18 12:33:50 -04006import android.util.AttributeSet;
Jason Monk520ea062015-08-18 14:53:06 -04007import android.view.View;
8import android.view.ViewGroup;
Winsonc0d70582016-01-29 10:24:39 -08009
Jason Monk520ea062015-08-18 14:53:06 -040010import com.android.systemui.R;
Jason Monkcaf37622015-08-18 12:33:50 -040011import com.android.systemui.qs.QSPanel.QSTileLayout;
Jason Monk520ea062015-08-18 14:53:06 -040012import com.android.systemui.qs.QSPanel.TileRecord;
13
14import java.util.ArrayList;
15
Jason Monkcaf37622015-08-18 12:33:50 -040016public class TileLayout extends ViewGroup implements QSTileLayout {
Jason Monk520ea062015-08-18 14:53:06 -040017
18 private static final float TILE_ASPECT = 1.2f;
19
20 private static final String TAG = "TileLayout";
21
Jason Monkcaf37622015-08-18 12:33:50 -040022 protected int mColumns;
Xiaohui Chen2f3551b2016-04-07 10:37:25 -070023 protected int mCellWidth;
24 protected int mCellHeight;
Amin Shaikhd620def2018-02-27 16:52:53 -050025 protected int mCellMarginHorizontal;
26 protected int mCellMarginVertical;
Amin Shaikh9a6fa082018-03-29 17:27:26 -040027 protected int mSidePadding;
Fabian Kozynski712ae392018-09-12 09:11:16 -040028 protected int mRows = 1;
Jason Monk520ea062015-08-18 14:53:06 -040029
Jason Monkcaf37622015-08-18 12:33:50 -040030 protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
Jason Monk6573ef22016-04-06 12:37:18 -040031 private int mCellMarginTop;
Jason Monk1bec6af2016-05-31 15:40:58 -040032 private boolean mListening;
Fabian Kozynski712ae392018-09-12 09:11:16 -040033 protected int mMaxAllowedRows = 3;
Jason Monkcaf37622015-08-18 12:33:50 -040034
Fabian Kozynskif92b8812019-10-10 14:50:59 -040035 // Prototyping with less rows
36 private final boolean mLessRows;
37
Jason Monkcaf37622015-08-18 12:33:50 -040038 public TileLayout(Context context) {
39 this(context, null);
40 }
41
42 public TileLayout(Context context, AttributeSet attrs) {
43 super(context, attrs);
Julia Reynolds20aef8a2016-05-04 16:44:08 -040044 setFocusableInTouchMode(true);
Fabian Kozynskif92b8812019-10-10 14:50:59 -040045 mLessRows = Settings.System.getInt(context.getContentResolver(), "qs_less_rows", 0) != 0;
Jason Monk520ea062015-08-18 14:53:06 -040046 updateResources();
Fabian Kozynskif92b8812019-10-10 14:50:59 -040047
Jason Monk520ea062015-08-18 14:53:06 -040048 }
49
Jason Monkcaf37622015-08-18 12:33:50 -040050 @Override
51 public int getOffsetTop(TileRecord tile) {
52 return getTop();
53 }
54
Jason Monk1bec6af2016-05-31 15:40:58 -040055 @Override
56 public void setListening(boolean listening) {
57 if (mListening == listening) return;
58 mListening = listening;
59 for (TileRecord record : mRecords) {
60 record.tile.setListening(this, mListening);
61 }
62 }
63
Jason Monkcaf37622015-08-18 12:33:50 -040064 public void addTile(TileRecord tile) {
65 mRecords.add(tile);
Jason Monk1bec6af2016-05-31 15:40:58 -040066 tile.tile.setListening(this, mListening);
Fabian Kozynski802279f2018-09-07 13:44:54 -040067 addTileView(tile);
68 }
69
70 protected void addTileView(TileRecord tile) {
Jason Monkcaf37622015-08-18 12:33:50 -040071 addView(tile.tileView);
72 }
73
74 @Override
75 public void removeTile(TileRecord tile) {
76 mRecords.remove(tile);
Jason Monk1bec6af2016-05-31 15:40:58 -040077 tile.tile.setListening(this, false);
Jason Monkcaf37622015-08-18 12:33:50 -040078 removeView(tile.tileView);
79 }
80
Jason Monkbd6dbb02015-09-03 15:46:25 -040081 public void removeAllViews() {
Jason Monk1bec6af2016-05-31 15:40:58 -040082 for (TileRecord record : mRecords) {
83 record.tile.setListening(this, false);
84 }
Jason Monkbd6dbb02015-09-03 15:46:25 -040085 mRecords.clear();
86 super.removeAllViews();
87 }
88
Jason Monk9d02a432016-01-20 16:33:46 -050089 public boolean updateResources() {
Jason Monk520ea062015-08-18 14:53:06 -040090 final Resources res = mContext.getResources();
91 final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
Jason Monk9d02a432016-01-20 16:33:46 -050092 mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
Amin Shaikhd620def2018-02-27 16:52:53 -050093 mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
94 mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
Jason Monk6573ef22016-04-06 12:37:18 -040095 mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
Amin Shaikh9a6fa082018-03-29 17:27:26 -040096 mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
Fabian Kozynski712ae392018-09-12 09:11:16 -040097 mMaxAllowedRows = Math.max(1, getResources().getInteger(R.integer.quick_settings_max_rows));
Fabian Kozynskif92b8812019-10-10 14:50:59 -040098 if (mLessRows) mMaxAllowedRows = Math.max(1, mMaxAllowedRows - 1);
Jason Monk520ea062015-08-18 14:53:06 -040099 if (mColumns != columns) {
100 mColumns = columns;
Jason Monk6573ef22016-04-06 12:37:18 -0400101 requestLayout();
Jason Monk9d02a432016-01-20 16:33:46 -0500102 return true;
Jason Monk520ea062015-08-18 14:53:06 -0400103 }
Jason Monk9d02a432016-01-20 16:33:46 -0500104 return false;
Jason Monk94a1bf62015-10-20 08:43:36 -0700105 }
106
Jason Monk520ea062015-08-18 14:53:06 -0400107 @Override
108 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Fabian Kozynski712ae392018-09-12 09:11:16 -0400109 // If called with AT_MOST, it will limit the number of rows. If called with UNSPECIFIED
110 // it will show all its tiles. In this case, the tiles have to be entered before the
111 // container is measured. Any change in the tiles, should trigger a remeasure.
Jason Monk9d02a432016-01-20 16:33:46 -0500112 final int numTiles = mRecords.size();
Fabian Kozynski6eaf3ac2018-10-01 15:59:05 -0400113 final int width = MeasureSpec.getSize(widthMeasureSpec);
Fabian Kozynski52bd8c22018-10-08 12:52:51 -0400114 final int availableWidth = width - getPaddingStart() - getPaddingEnd();
Fabian Kozynski712ae392018-09-12 09:11:16 -0400115 final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
116 if (heightMode == MeasureSpec.UNSPECIFIED) {
117 mRows = (numTiles + mColumns - 1) / mColumns;
118 }
Fabian Kozynski52bd8c22018-10-08 12:52:51 -0400119 mCellWidth =
120 (availableWidth - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
Jason Monk520ea062015-08-18 14:53:06 -0400121
Rohan Shah853cd8f62018-03-09 15:14:59 -0800122 // Measure each QS tile.
Jason Monk520ea062015-08-18 14:53:06 -0400123 View previousView = this;
124 for (TileRecord record : mRecords) {
Jason Monk520ea062015-08-18 14:53:06 -0400125 if (record.tileView.getVisibility() == GONE) continue;
Jason Monk9d02a432016-01-20 16:33:46 -0500126 record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
Jason Monk520ea062015-08-18 14:53:06 -0400127 previousView = record.tileView.updateAccessibilityOrder(previousView);
128 }
Rohan Shahdb2cfa32018-02-20 11:27:22 -0800129
130 // Only include the top margin in our measurement if we have more than 1 row to show.
131 // Otherwise, don't add the extra margin buffer at top.
Fabian Kozynski712ae392018-09-12 09:11:16 -0400132 int height = (mCellHeight + mCellMarginVertical) * mRows +
133 (mRows != 0 ? (mCellMarginTop - mCellMarginVertical) : 0);
Jason Monkcedcb942017-02-06 14:37:29 -0800134 if (height < 0) height = 0;
Rohan Shah853cd8f62018-03-09 15:14:59 -0800135
Fabian Kozynski30b4d182019-05-08 09:19:52 -0400136 setMeasuredDimension(width, height);
Jason Monk520ea062015-08-18 14:53:06 -0400137 }
138
Fabian Kozynski712ae392018-09-12 09:11:16 -0400139 /**
140 * Determines the maximum number of rows that can be shown based on height. Clips at a minimum
141 * of 1 and a maximum of mMaxAllowedRows.
142 *
143 * @param heightMeasureSpec Available height.
144 * @param tilesCount Upper limit on the number of tiles to show. to prevent empty rows.
145 */
146 public boolean updateMaxRows(int heightMeasureSpec, int tilesCount) {
Fabian Kozynski72cd84a92019-04-15 14:09:43 -0400147 final int availableHeight = MeasureSpec.getSize(heightMeasureSpec) - mCellMarginTop
148 + mCellMarginVertical;
Fabian Kozynski712ae392018-09-12 09:11:16 -0400149 final int previousRows = mRows;
150 mRows = availableHeight / (mCellHeight + mCellMarginVertical);
151 if (mRows >= mMaxAllowedRows) {
152 mRows = mMaxAllowedRows;
153 } else if (mRows <= 1) {
154 mRows = 1;
155 }
156 if (mRows > (tilesCount + mColumns - 1) / mColumns) {
157 mRows = (tilesCount + mColumns - 1) / mColumns;
158 }
159 return previousRows != mRows;
160 }
161
Amin Shaikh7e6e64c2018-02-07 16:07:24 -0500162 @Override
163 public boolean hasOverlappingRendering() {
164 return false;
165 }
166
Fabian Kozynski802279f2018-09-07 13:44:54 -0400167 protected static int exactly(int size) {
Jason Monk520ea062015-08-18 14:53:06 -0400168 return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
169 }
170
Fabian Kozynski802279f2018-09-07 13:44:54 -0400171
172 protected void layoutTileRecords(int numRecords) {
Rohan Shah853cd8f62018-03-09 15:14:59 -0800173 final boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
Jason Monk9d02a432016-01-20 16:33:46 -0500174 int row = 0;
175 int column = 0;
Rohan Shah853cd8f62018-03-09 15:14:59 -0800176
177 // Layout each QS tile.
Fabian Kozynski712ae392018-09-12 09:11:16 -0400178 final int tilesToLayout = Math.min(numRecords, mRows * mColumns);
179 for (int i = 0; i < tilesToLayout; i++, column++) {
Rohan Shah853cd8f62018-03-09 15:14:59 -0800180 // If we reached the last column available to layout a tile, wrap back to the next row.
Jason Monk9d02a432016-01-20 16:33:46 -0500181 if (column == mColumns) {
Rohan Shah853cd8f62018-03-09 15:14:59 -0800182 column = 0;
Jason Monk9d02a432016-01-20 16:33:46 -0500183 row++;
Jason Monk9d02a432016-01-20 16:33:46 -0500184 }
Rohan Shah853cd8f62018-03-09 15:14:59 -0800185
186 final TileRecord record = mRecords.get(i);
Jason Monk9d02a432016-01-20 16:33:46 -0500187 final int top = getRowTop(row);
Amin Shaikha64bb522018-05-01 17:39:47 -0400188 final int left = getColumnStart(isRtl ? mColumns - column - 1 : column);
189 final int right = left + mCellWidth;
Jason Monk520ea062015-08-18 14:53:06 -0400190 record.tileView.layout(left, top, right, top + record.tileView.getMeasuredHeight());
191 }
192 }
193
Fabian Kozynski802279f2018-09-07 13:44:54 -0400194 @Override
195 protected void onLayout(boolean changed, int l, int t, int r, int b) {
196 layoutTileRecords(mRecords.size());
197 }
198
Jason Monk520ea062015-08-18 14:53:06 -0400199 private int getRowTop(int row) {
Amin Shaikhd620def2018-02-27 16:52:53 -0500200 return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop;
Jason Monk520ea062015-08-18 14:53:06 -0400201 }
202
Fabian Kozynski802279f2018-09-07 13:44:54 -0400203 protected int getColumnStart(int column) {
Amin Shaikha64bb522018-05-01 17:39:47 -0400204 return getPaddingStart() + mSidePadding + mCellMarginHorizontal / 2 +
205 column * (mCellWidth + mCellMarginHorizontal);
Jason Monk520ea062015-08-18 14:53:06 -0400206 }
Fabian Kozynski802279f2018-09-07 13:44:54 -0400207
208 @Override
209 public int getNumVisibleTiles() {
210 return mRecords.size();
211 }
Jason Monk520ea062015-08-18 14:53:06 -0400212}