blob: a231e791e3b00802796ad2c0e1236d0d9276c251 [file] [log] [blame]
Jason Monkcaf37622015-08-18 12:33:50 -04001package com.android.systemui.qs;
2
3import android.content.Context;
Jason Monk6573ef22016-04-06 12:37:18 -04004import android.content.res.Configuration;
5import android.content.res.Resources;
Jason Monk51fb85a2016-03-28 14:06:04 -04006import android.support.v4.view.PagerAdapter;
7import android.support.v4.view.ViewPager;
Jason Monkcaf37622015-08-18 12:33:50 -04008import android.util.AttributeSet;
9import android.util.Log;
10import android.view.LayoutInflater;
11import android.view.View;
12import android.view.ViewGroup;
Jason Monkdf36aed2016-07-25 11:21:56 -040013
Jason Monkcaf37622015-08-18 12:33:50 -040014import com.android.systemui.R;
15import com.android.systemui.qs.QSPanel.QSTileLayout;
16import com.android.systemui.qs.QSPanel.TileRecord;
17
18import java.util.ArrayList;
19
20public class PagedTileLayout extends ViewPager implements QSTileLayout {
21
22 private static final boolean DEBUG = false;
23
24 private static final String TAG = "PagedTileLayout";
25
26 private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
27 private final ArrayList<TilePage> mPages = new ArrayList<TilePage>();
28
Jason Monkcaf37622015-08-18 12:33:50 -040029 private PageIndicator mPageIndicator;
30
31 private int mNumPages;
Jason Monk162011e2016-02-19 08:11:55 -050032 private PageListener mPageListener;
Jason Monkcaf37622015-08-18 12:33:50 -040033
Jason Monke5107a32016-05-31 15:40:58 -040034 private int mPosition;
35 private boolean mOffPage;
36 private boolean mListening;
37
Jason Monkcaf37622015-08-18 12:33:50 -040038 public PagedTileLayout(Context context, AttributeSet attrs) {
39 super(context, attrs);
40 setAdapter(mAdapter);
41 setOnPageChangeListener(new OnPageChangeListener() {
42 @Override
43 public void onPageSelected(int position) {
44 if (mPageIndicator == null) return;
Jason Monk162011e2016-02-19 08:11:55 -050045 if (mPageListener != null) {
Jason Monk51fb85a2016-03-28 14:06:04 -040046 mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
47 : position == 0);
Jason Monk162011e2016-02-19 08:11:55 -050048 }
Jason Monkcaf37622015-08-18 12:33:50 -040049 }
50
51 @Override
Jason Monk162011e2016-02-19 08:11:55 -050052 public void onPageScrolled(int position, float positionOffset,
53 int positionOffsetPixels) {
Jason Monkcaf37622015-08-18 12:33:50 -040054 if (mPageIndicator == null) return;
Jason Monke5107a32016-05-31 15:40:58 -040055 setCurrentPage(position, positionOffset != 0);
Jason Monkcaf37622015-08-18 12:33:50 -040056 mPageIndicator.setLocation(position + positionOffset);
Jason Monk66eaf312016-02-25 12:29:29 -050057 if (mPageListener != null) {
Jason Monk51fb85a2016-03-28 14:06:04 -040058 mPageListener.onPageChanged(positionOffsetPixels == 0 &&
59 (isLayoutRtl() ? position == mPages.size() - 1 : position == 0));
Jason Monk66eaf312016-02-25 12:29:29 -050060 }
Jason Monkcaf37622015-08-18 12:33:50 -040061 }
62
63 @Override
64 public void onPageScrollStateChanged(int state) {
65 }
66 });
67 setCurrentItem(0);
68 }
69
70 @Override
Jason Monk51fb85a2016-03-28 14:06:04 -040071 public void onRtlPropertiesChanged(int layoutDirection) {
72 super.onRtlPropertiesChanged(layoutDirection);
73 setAdapter(mAdapter);
74 setCurrentItem(0, false);
75 }
76
77 @Override
78 public void setCurrentItem(int item, boolean smoothScroll) {
79 if (isLayoutRtl()) {
80 item = mPages.size() - 1 - item;
81 }
82 super.setCurrentItem(item, smoothScroll);
83 }
84
85 @Override
Jason Monke5107a32016-05-31 15:40:58 -040086 public void setListening(boolean listening) {
87 if (mListening == listening) return;
88 mListening = listening;
89 if (mListening) {
Jason Monk69dac2b2016-09-30 10:15:16 -040090 setPageListening(mPosition, true);
Jason Monke5107a32016-05-31 15:40:58 -040091 if (mOffPage) {
Jason Monk69dac2b2016-09-30 10:15:16 -040092 setPageListening(mPosition + 1, true);
Jason Monke5107a32016-05-31 15:40:58 -040093 }
94 } else {
95 // Make sure no pages are listening.
96 for (int i = 0; i < mPages.size(); i++) {
97 mPages.get(i).setListening(false);
98 }
99 }
100 }
101
102 /**
103 * Sets individual pages to listening or not. If offPage it will set
104 * the next page after position to listening as well since we are in between
105 * pages.
106 */
107 private void setCurrentPage(int position, boolean offPage) {
108 if (mPosition == position && mOffPage == offPage) return;
109 if (mListening) {
110 if (mPosition != position) {
111 // Clear out the last pages from listening.
Jason Monk45233d62016-06-09 09:59:56 -0400112 setPageListening(mPosition, false);
Jason Monke5107a32016-05-31 15:40:58 -0400113 if (mOffPage) {
Jason Monk45233d62016-06-09 09:59:56 -0400114 setPageListening(mPosition + 1, false);
Jason Monke5107a32016-05-31 15:40:58 -0400115 }
116 // Set the new pages to listening
Jason Monk45233d62016-06-09 09:59:56 -0400117 setPageListening(position, true);
Jason Monke5107a32016-05-31 15:40:58 -0400118 if (offPage) {
Jason Monk45233d62016-06-09 09:59:56 -0400119 setPageListening(position + 1, true);
Jason Monke5107a32016-05-31 15:40:58 -0400120 }
121 } else if (mOffPage != offPage) {
122 // Whether we are showing position + 1 has changed.
Jason Monk45233d62016-06-09 09:59:56 -0400123 setPageListening(mPosition + 1, offPage);
Jason Monke5107a32016-05-31 15:40:58 -0400124 }
125 }
126 // Save the current state.
127 mPosition = position;
128 mOffPage = offPage;
129 }
130
Jason Monk45233d62016-06-09 09:59:56 -0400131 private void setPageListening(int position, boolean listening) {
132 if (position >= mPages.size()) return;
Jason Monk69dac2b2016-09-30 10:15:16 -0400133 if (isLayoutRtl()) {
134 position = mPages.size() - 1 - position;
135 }
Jason Monk45233d62016-06-09 09:59:56 -0400136 mPages.get(position).setListening(listening);
137 }
138
Jason Monke5107a32016-05-31 15:40:58 -0400139 @Override
Jason Monkc5bdafb2016-02-25 16:24:21 -0500140 public boolean hasOverlappingRendering() {
141 return false;
142 }
143
144 @Override
Jason Monkcaf37622015-08-18 12:33:50 -0400145 protected void onFinishInflate() {
146 super.onFinishInflate();
Jason Monk32508852017-01-18 09:17:13 -0500147 mPages.add((TilePage) LayoutInflater.from(getContext())
Jason Monke4e69302016-01-20 11:27:15 -0500148 .inflate(R.layout.qs_paged_page, this, false));
Jason Monkcaf37622015-08-18 12:33:50 -0400149 }
150
Jason Monk32508852017-01-18 09:17:13 -0500151 public void setPageIndicator(PageIndicator indicator) {
152 mPageIndicator = indicator;
153 }
154
Jason Monkcaf37622015-08-18 12:33:50 -0400155 @Override
156 public int getOffsetTop(TileRecord tile) {
Jason Monkae5bd032016-03-02 14:38:31 -0500157 final ViewGroup parent = (ViewGroup) tile.tileView.getParent();
158 if (parent == null) return 0;
159 return parent.getTop() + getTop();
Jason Monkcaf37622015-08-18 12:33:50 -0400160 }
161
162 @Override
Jason Monkcaf37622015-08-18 12:33:50 -0400163 public void addTile(TileRecord tile) {
164 mTiles.add(tile);
Jason Monk9d02a432016-01-20 16:33:46 -0500165 postDistributeTiles();
Jason Monkcaf37622015-08-18 12:33:50 -0400166 }
167
168 @Override
169 public void removeTile(TileRecord tile) {
170 if (mTiles.remove(tile)) {
Jason Monk9d02a432016-01-20 16:33:46 -0500171 postDistributeTiles();
Jason Monkcaf37622015-08-18 12:33:50 -0400172 }
173 }
174
Jason Monk162011e2016-02-19 08:11:55 -0500175 public void setPageListener(PageListener listener) {
176 mPageListener = listener;
177 }
178
Jason Monk9d02a432016-01-20 16:33:46 -0500179 private void postDistributeTiles() {
180 removeCallbacks(mDistribute);
181 post(mDistribute);
182 }
183
Jason Monkcaf37622015-08-18 12:33:50 -0400184 private void distributeTiles() {
185 if (DEBUG) Log.d(TAG, "Distributing tiles");
Jason Monkcaf37622015-08-18 12:33:50 -0400186 final int NP = mPages.size();
187 for (int i = 0; i < NP; i++) {
Jason Monk9d02a432016-01-20 16:33:46 -0500188 mPages.get(i).removeAllViews();
Jason Monkcaf37622015-08-18 12:33:50 -0400189 }
190 int index = 0;
191 final int NT = mTiles.size();
192 for (int i = 0; i < NT; i++) {
193 TileRecord tile = mTiles.get(i);
Jason Monkcaf37622015-08-18 12:33:50 -0400194 if (mPages.get(index).isFull()) {
195 if (++index == mPages.size()) {
Jason Monk8e73ef32016-05-04 11:36:46 -0400196 if (DEBUG) Log.d(TAG, "Adding page for "
197 + tile.tile.getClass().getSimpleName());
Jason Monk32508852017-01-18 09:17:13 -0500198 mPages.add((TilePage) LayoutInflater.from(getContext())
Jason Monkcaf37622015-08-18 12:33:50 -0400199 .inflate(R.layout.qs_paged_page, this, false));
200 }
201 }
202 if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
203 + index);
204 mPages.get(index).addTile(tile);
205 }
206 if (mNumPages != index + 1) {
207 mNumPages = index + 1;
Jason Monk8e73ef32016-05-04 11:36:46 -0400208 while (mPages.size() > mNumPages) {
209 mPages.remove(mPages.size() - 1);
210 }
211 if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
Jason Monkcaf37622015-08-18 12:33:50 -0400212 mPageIndicator.setNumPages(mNumPages);
Jason Monk32508852017-01-18 09:17:13 -0500213 mPageIndicator.setVisibility(mNumPages > 1 ? View.VISIBLE : View.GONE);
Jason Monk8e73ef32016-05-04 11:36:46 -0400214 setAdapter(mAdapter);
Jason Monkcaf37622015-08-18 12:33:50 -0400215 mAdapter.notifyDataSetChanged();
Jason Monk51fb85a2016-03-28 14:06:04 -0400216 setCurrentItem(0, false);
Jason Monkcaf37622015-08-18 12:33:50 -0400217 }
218 }
219
220 @Override
Jason Monk9d02a432016-01-20 16:33:46 -0500221 public boolean updateResources() {
222 boolean changed = false;
Jason Monkcaf37622015-08-18 12:33:50 -0400223 for (int i = 0; i < mPages.size(); i++) {
Jason Monk9d02a432016-01-20 16:33:46 -0500224 changed |= mPages.get(i).updateResources();
Jason Monkcaf37622015-08-18 12:33:50 -0400225 }
Jason Monk9d02a432016-01-20 16:33:46 -0500226 if (changed) {
227 distributeTiles();
228 }
229 return changed;
Jason Monkcaf37622015-08-18 12:33:50 -0400230 }
231
232 @Override
233 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
234 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
235 // The ViewPager likes to eat all of the space, instead force it to wrap to the max height
236 // of the pages.
237 int maxHeight = 0;
238 final int N = getChildCount();
239 for (int i = 0; i < N; i++) {
240 int height = getChildAt(i).getMeasuredHeight();
241 if (height > maxHeight) {
242 maxHeight = height;
243 }
244 }
Jason Monk32508852017-01-18 09:17:13 -0500245 setMeasuredDimension(getMeasuredWidth(), maxHeight);
Jason Monkcaf37622015-08-18 12:33:50 -0400246 }
247
Jason Monk9d02a432016-01-20 16:33:46 -0500248 private final Runnable mDistribute = new Runnable() {
249 @Override
250 public void run() {
251 distributeTiles();
252 }
253 };
254
Jason Monk8fb77872016-03-03 16:39:42 -0500255 public int getColumnCount() {
256 if (mPages.size() == 0) return 0;
257 return mPages.get(0).mColumns;
258 }
259
Jason Monkcaf37622015-08-18 12:33:50 -0400260 public static class TilePage extends TileLayout {
Jason Monk94a1bf62015-10-20 08:43:36 -0700261 private int mMaxRows = 3;
Jason Monkcaf37622015-08-18 12:33:50 -0400262
263 public TilePage(Context context, AttributeSet attrs) {
264 super(context, attrs);
Jason Monkb9c00192015-10-07 11:45:33 -0400265 updateResources();
Jason Monk32508852017-01-18 09:17:13 -0500266 setContentDescription(getContext().getString(R.string.accessibility_desc_quick_settings));
Jason Monkcaf37622015-08-18 12:33:50 -0400267 }
268
Jason Monk9d02a432016-01-20 16:33:46 -0500269 @Override
270 public boolean updateResources() {
Jason Monk6573ef22016-04-06 12:37:18 -0400271 final int rows = getRows();
272 boolean changed = rows != mMaxRows;
273 if (changed) {
274 mMaxRows = rows;
275 requestLayout();
Jason Monk9d02a432016-01-20 16:33:46 -0500276 }
Jason Monk6573ef22016-04-06 12:37:18 -0400277 return super.updateResources() || changed;
278 }
279
280 private int getRows() {
281 final Resources res = getContext().getResources();
282 if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
283 // Always have 3 rows in portrait.
284 return 3;
285 }
286 return Math.max(1, res.getInteger(R.integer.quick_settings_num_rows));
Jason Monk9d02a432016-01-20 16:33:46 -0500287 }
288
Jason Monkbd6dbb02015-09-03 15:46:25 -0400289 public void setMaxRows(int maxRows) {
290 mMaxRows = maxRows;
291 }
292
Jason Monkbd6dbb02015-09-03 15:46:25 -0400293 public boolean isFull() {
Jason Monkcaf37622015-08-18 12:33:50 -0400294 return mRecords.size() >= mColumns * mMaxRows;
295 }
296 }
297
298 private final PagerAdapter mAdapter = new PagerAdapter() {
299 public void destroyItem(ViewGroup container, int position, Object object) {
300 if (DEBUG) Log.d(TAG, "Destantiating " + position);
Jason Monkcaf37622015-08-18 12:33:50 -0400301 container.removeView((View) object);
302 }
303
304 public Object instantiateItem(ViewGroup container, int position) {
305 if (DEBUG) Log.d(TAG, "Instantiating " + position);
Jason Monk51fb85a2016-03-28 14:06:04 -0400306 if (isLayoutRtl()) {
307 position = mPages.size() - 1 - position;
308 }
Jason Monke4e69302016-01-20 11:27:15 -0500309 ViewGroup view = mPages.get(position);
Jason Monkcaf37622015-08-18 12:33:50 -0400310 container.addView(view);
311 return view;
312 }
313
314 @Override
315 public int getCount() {
316 return mNumPages;
317 }
318
319 @Override
320 public boolean isViewFromObject(View view, Object object) {
321 return view == object;
322 }
323 };
Jason Monk162011e2016-02-19 08:11:55 -0500324
325 public interface PageListener {
Jason Monk66eaf312016-02-25 12:29:29 -0500326 void onPageChanged(boolean isFirst);
Jason Monk162011e2016-02-19 08:11:55 -0500327 }
Jason Monkcaf37622015-08-18 12:33:50 -0400328}