blob: afedbe3ad21f7e7f5777175f3df5d41119890e72 [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 Monk21428432016-02-10 16:42:38 -050032 private View mDecorGroup;
Jason Monk162011e2016-02-19 08:11:55 -050033 private PageListener mPageListener;
Jason Monkcaf37622015-08-18 12:33:50 -040034
Jason Monke5107a32016-05-31 15:40:58 -040035 private int mPosition;
36 private boolean mOffPage;
37 private boolean mListening;
38
Jason Monkcaf37622015-08-18 12:33:50 -040039 public PagedTileLayout(Context context, AttributeSet attrs) {
40 super(context, attrs);
41 setAdapter(mAdapter);
42 setOnPageChangeListener(new OnPageChangeListener() {
43 @Override
44 public void onPageSelected(int position) {
45 if (mPageIndicator == null) return;
Jason Monk162011e2016-02-19 08:11:55 -050046 if (mPageListener != null) {
Jason Monk51fb85a2016-03-28 14:06:04 -040047 mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
48 : position == 0);
Jason Monk162011e2016-02-19 08:11:55 -050049 }
Jason Monkcaf37622015-08-18 12:33:50 -040050 }
51
52 @Override
Jason Monk162011e2016-02-19 08:11:55 -050053 public void onPageScrolled(int position, float positionOffset,
54 int positionOffsetPixels) {
Jason Monkcaf37622015-08-18 12:33:50 -040055 if (mPageIndicator == null) return;
Jason Monke5107a32016-05-31 15:40:58 -040056 setCurrentPage(position, positionOffset != 0);
Jason Monkcaf37622015-08-18 12:33:50 -040057 mPageIndicator.setLocation(position + positionOffset);
Jason Monk66eaf312016-02-25 12:29:29 -050058 if (mPageListener != null) {
Jason Monk51fb85a2016-03-28 14:06:04 -040059 mPageListener.onPageChanged(positionOffsetPixels == 0 &&
60 (isLayoutRtl() ? position == mPages.size() - 1 : position == 0));
Jason Monk66eaf312016-02-25 12:29:29 -050061 }
Jason Monkcaf37622015-08-18 12:33:50 -040062 }
63
64 @Override
65 public void onPageScrollStateChanged(int state) {
66 }
67 });
68 setCurrentItem(0);
69 }
70
71 @Override
Jason Monk51fb85a2016-03-28 14:06:04 -040072 public void onRtlPropertiesChanged(int layoutDirection) {
73 super.onRtlPropertiesChanged(layoutDirection);
74 setAdapter(mAdapter);
75 setCurrentItem(0, false);
76 }
77
78 @Override
79 public void setCurrentItem(int item, boolean smoothScroll) {
80 if (isLayoutRtl()) {
81 item = mPages.size() - 1 - item;
82 }
83 super.setCurrentItem(item, smoothScroll);
84 }
85
86 @Override
Jason Monke5107a32016-05-31 15:40:58 -040087 public void setListening(boolean listening) {
88 if (mListening == listening) return;
89 mListening = listening;
90 if (mListening) {
91 mPages.get(mPosition).setListening(listening);
92 if (mOffPage) {
93 mPages.get(mPosition + 1).setListening(listening);
94 }
95 } else {
96 // Make sure no pages are listening.
97 for (int i = 0; i < mPages.size(); i++) {
98 mPages.get(i).setListening(false);
99 }
100 }
101 }
102
103 /**
104 * Sets individual pages to listening or not. If offPage it will set
105 * the next page after position to listening as well since we are in between
106 * pages.
107 */
108 private void setCurrentPage(int position, boolean offPage) {
109 if (mPosition == position && mOffPage == offPage) return;
110 if (mListening) {
111 if (mPosition != position) {
112 // Clear out the last pages from listening.
Jason Monk45233d62016-06-09 09:59:56 -0400113 setPageListening(mPosition, false);
Jason Monke5107a32016-05-31 15:40:58 -0400114 if (mOffPage) {
Jason Monk45233d62016-06-09 09:59:56 -0400115 setPageListening(mPosition + 1, false);
Jason Monke5107a32016-05-31 15:40:58 -0400116 }
117 // Set the new pages to listening
Jason Monk45233d62016-06-09 09:59:56 -0400118 setPageListening(position, true);
Jason Monke5107a32016-05-31 15:40:58 -0400119 if (offPage) {
Jason Monk45233d62016-06-09 09:59:56 -0400120 setPageListening(position + 1, true);
Jason Monke5107a32016-05-31 15:40:58 -0400121 }
122 } else if (mOffPage != offPage) {
123 // Whether we are showing position + 1 has changed.
Jason Monk45233d62016-06-09 09:59:56 -0400124 setPageListening(mPosition + 1, offPage);
Jason Monke5107a32016-05-31 15:40:58 -0400125 }
126 }
127 // Save the current state.
128 mPosition = position;
129 mOffPage = offPage;
130 }
131
Jason Monk45233d62016-06-09 09:59:56 -0400132 private void setPageListening(int position, boolean listening) {
133 if (position >= mPages.size()) return;
134 mPages.get(position).setListening(listening);
135 }
136
Jason Monke5107a32016-05-31 15:40:58 -0400137 @Override
Jason Monkc5bdafb2016-02-25 16:24:21 -0500138 public boolean hasOverlappingRendering() {
139 return false;
140 }
141
142 @Override
Jason Monkcaf37622015-08-18 12:33:50 -0400143 protected void onFinishInflate() {
144 super.onFinishInflate();
145 mPageIndicator = (PageIndicator) findViewById(R.id.page_indicator);
Jason Monk21428432016-02-10 16:42:38 -0500146 mDecorGroup = findViewById(R.id.page_decor);
147 ((LayoutParams) mDecorGroup.getLayoutParams()).isDecor = true;
Jason Monkcaf37622015-08-18 12:33:50 -0400148
Jason Monke4e69302016-01-20 11:27:15 -0500149 mPages.add((TilePage) LayoutInflater.from(mContext)
150 .inflate(R.layout.qs_paged_page, this, false));
Jason Monkcaf37622015-08-18 12:33:50 -0400151 }
152
153 @Override
154 public int getOffsetTop(TileRecord tile) {
Jason Monkae5bd032016-03-02 14:38:31 -0500155 final ViewGroup parent = (ViewGroup) tile.tileView.getParent();
156 if (parent == null) return 0;
157 return parent.getTop() + getTop();
Jason Monkcaf37622015-08-18 12:33:50 -0400158 }
159
160 @Override
Jason Monkcaf37622015-08-18 12:33:50 -0400161 public void addTile(TileRecord tile) {
162 mTiles.add(tile);
Jason Monk9d02a432016-01-20 16:33:46 -0500163 postDistributeTiles();
Jason Monkcaf37622015-08-18 12:33:50 -0400164 }
165
166 @Override
167 public void removeTile(TileRecord tile) {
168 if (mTiles.remove(tile)) {
Jason Monk9d02a432016-01-20 16:33:46 -0500169 postDistributeTiles();
Jason Monkcaf37622015-08-18 12:33:50 -0400170 }
171 }
172
Jason Monk162011e2016-02-19 08:11:55 -0500173 public void setPageListener(PageListener listener) {
174 mPageListener = listener;
175 }
176
Jason Monk9d02a432016-01-20 16:33:46 -0500177 private void postDistributeTiles() {
178 removeCallbacks(mDistribute);
179 post(mDistribute);
180 }
181
Jason Monkcaf37622015-08-18 12:33:50 -0400182 private void distributeTiles() {
183 if (DEBUG) Log.d(TAG, "Distributing tiles");
Jason Monkcaf37622015-08-18 12:33:50 -0400184 final int NP = mPages.size();
185 for (int i = 0; i < NP; i++) {
Jason Monk9d02a432016-01-20 16:33:46 -0500186 mPages.get(i).removeAllViews();
Jason Monkcaf37622015-08-18 12:33:50 -0400187 }
188 int index = 0;
189 final int NT = mTiles.size();
190 for (int i = 0; i < NT; i++) {
191 TileRecord tile = mTiles.get(i);
Jason Monkcaf37622015-08-18 12:33:50 -0400192 if (mPages.get(index).isFull()) {
193 if (++index == mPages.size()) {
Jason Monk8e73ef32016-05-04 11:36:46 -0400194 if (DEBUG) Log.d(TAG, "Adding page for "
195 + tile.tile.getClass().getSimpleName());
Jason Monkcaf37622015-08-18 12:33:50 -0400196 mPages.add((TilePage) LayoutInflater.from(mContext)
197 .inflate(R.layout.qs_paged_page, this, false));
198 }
199 }
200 if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
201 + index);
202 mPages.get(index).addTile(tile);
203 }
204 if (mNumPages != index + 1) {
205 mNumPages = index + 1;
Jason Monk8e73ef32016-05-04 11:36:46 -0400206 while (mPages.size() > mNumPages) {
207 mPages.remove(mPages.size() - 1);
208 }
209 if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
Jason Monkcaf37622015-08-18 12:33:50 -0400210 mPageIndicator.setNumPages(mNumPages);
Jason Monkdf36aed2016-07-25 11:21:56 -0400211 mDecorGroup.setVisibility(mNumPages > 1 ? View.VISIBLE : View.GONE);
Jason Monk8e73ef32016-05-04 11:36:46 -0400212 setAdapter(mAdapter);
Jason Monkcaf37622015-08-18 12:33:50 -0400213 mAdapter.notifyDataSetChanged();
Jason Monk51fb85a2016-03-28 14:06:04 -0400214 setCurrentItem(0, false);
Jason Monkcaf37622015-08-18 12:33:50 -0400215 }
216 }
217
218 @Override
Jason Monk9d02a432016-01-20 16:33:46 -0500219 public boolean updateResources() {
220 boolean changed = false;
Jason Monkcaf37622015-08-18 12:33:50 -0400221 for (int i = 0; i < mPages.size(); i++) {
Jason Monk9d02a432016-01-20 16:33:46 -0500222 changed |= mPages.get(i).updateResources();
Jason Monkcaf37622015-08-18 12:33:50 -0400223 }
Jason Monk9d02a432016-01-20 16:33:46 -0500224 if (changed) {
225 distributeTiles();
226 }
227 return changed;
Jason Monkcaf37622015-08-18 12:33:50 -0400228 }
229
230 @Override
231 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
232 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
233 // The ViewPager likes to eat all of the space, instead force it to wrap to the max height
234 // of the pages.
235 int maxHeight = 0;
236 final int N = getChildCount();
237 for (int i = 0; i < N; i++) {
238 int height = getChildAt(i).getMeasuredHeight();
239 if (height > maxHeight) {
240 maxHeight = height;
241 }
242 }
Jason Monkdf36aed2016-07-25 11:21:56 -0400243 setMeasuredDimension(getMeasuredWidth(), maxHeight
244 + (mDecorGroup.getVisibility() != View.GONE ? mDecorGroup.getMeasuredHeight() : 0));
Jason Monkcaf37622015-08-18 12:33:50 -0400245 }
246
Jason Monk9d02a432016-01-20 16:33:46 -0500247 private final Runnable mDistribute = new Runnable() {
248 @Override
249 public void run() {
250 distributeTiles();
251 }
252 };
253
Jason Monk8fb77872016-03-03 16:39:42 -0500254 public int getColumnCount() {
255 if (mPages.size() == 0) return 0;
256 return mPages.get(0).mColumns;
257 }
258
Jason Monkcaf37622015-08-18 12:33:50 -0400259 public static class TilePage extends TileLayout {
Jason Monk94a1bf62015-10-20 08:43:36 -0700260 private int mMaxRows = 3;
Jason Monkcaf37622015-08-18 12:33:50 -0400261
262 public TilePage(Context context, AttributeSet attrs) {
263 super(context, attrs);
Jason Monkb9c00192015-10-07 11:45:33 -0400264 updateResources();
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400265 setContentDescription(mContext.getString(R.string.accessibility_desc_quick_settings));
Jason Monkcaf37622015-08-18 12:33:50 -0400266 }
267
Jason Monk9d02a432016-01-20 16:33:46 -0500268 @Override
269 public boolean updateResources() {
Jason Monk6573ef22016-04-06 12:37:18 -0400270 final int rows = getRows();
271 boolean changed = rows != mMaxRows;
272 if (changed) {
273 mMaxRows = rows;
274 requestLayout();
Jason Monk9d02a432016-01-20 16:33:46 -0500275 }
Jason Monk6573ef22016-04-06 12:37:18 -0400276 return super.updateResources() || changed;
277 }
278
279 private int getRows() {
280 final Resources res = getContext().getResources();
281 if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
282 // Always have 3 rows in portrait.
283 return 3;
284 }
285 return Math.max(1, res.getInteger(R.integer.quick_settings_num_rows));
Jason Monk9d02a432016-01-20 16:33:46 -0500286 }
287
Jason Monkbd6dbb02015-09-03 15:46:25 -0400288 public void setMaxRows(int maxRows) {
289 mMaxRows = maxRows;
290 }
291
Jason Monkbd6dbb02015-09-03 15:46:25 -0400292 public boolean isFull() {
Jason Monkcaf37622015-08-18 12:33:50 -0400293 return mRecords.size() >= mColumns * mMaxRows;
294 }
295 }
296
297 private final PagerAdapter mAdapter = new PagerAdapter() {
298 public void destroyItem(ViewGroup container, int position, Object object) {
299 if (DEBUG) Log.d(TAG, "Destantiating " + position);
Jason Monkcaf37622015-08-18 12:33:50 -0400300 container.removeView((View) object);
301 }
302
303 public Object instantiateItem(ViewGroup container, int position) {
304 if (DEBUG) Log.d(TAG, "Instantiating " + position);
Jason Monk51fb85a2016-03-28 14:06:04 -0400305 if (isLayoutRtl()) {
306 position = mPages.size() - 1 - position;
307 }
Jason Monke4e69302016-01-20 11:27:15 -0500308 ViewGroup view = mPages.get(position);
Jason Monkcaf37622015-08-18 12:33:50 -0400309 container.addView(view);
310 return view;
311 }
312
313 @Override
314 public int getCount() {
315 return mNumPages;
316 }
317
318 @Override
319 public boolean isViewFromObject(View view, Object object) {
320 return view == object;
321 }
322 };
Jason Monk162011e2016-02-19 08:11:55 -0500323
324 public interface PageListener {
Jason Monk66eaf312016-02-25 12:29:29 -0500325 void onPageChanged(boolean isFirst);
Jason Monk162011e2016-02-19 08:11:55 -0500326 }
Jason Monkcaf37622015-08-18 12:33:50 -0400327}