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