1 package com.android.systemui.qs;
2 
3 import android.content.Context;
4 import android.content.res.Configuration;
5 import android.content.res.Resources;
6 import android.support.v4.view.PagerAdapter;
7 import android.support.v4.view.ViewPager;
8 import android.util.AttributeSet;
9 import android.util.Log;
10 import android.view.LayoutInflater;
11 import android.view.View;
12 import android.view.ViewGroup;
13 
14 import com.android.systemui.R;
15 import com.android.systemui.qs.QSPanel.QSTileLayout;
16 import com.android.systemui.qs.QSPanel.TileRecord;
17 
18 import java.util.ArrayList;
19 
20 public 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 
29     private PageIndicator mPageIndicator;
30 
31     private int mNumPages;
32     private PageListener mPageListener;
33 
34     private int mPosition;
35     private boolean mOffPage;
36     private boolean mListening;
37 
PagedTileLayout(Context context, AttributeSet attrs)38     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;
45                 if (mPageListener != null) {
46                     mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1
47                             : position == 0);
48                 }
49             }
50 
51             @Override
52             public void onPageScrolled(int position, float positionOffset,
53                     int positionOffsetPixels) {
54                 if (mPageIndicator == null) return;
55                 setCurrentPage(position, positionOffset != 0);
56                 mPageIndicator.setLocation(position + positionOffset);
57                 if (mPageListener != null) {
58                     mPageListener.onPageChanged(positionOffsetPixels == 0 &&
59                             (isLayoutRtl() ? position == mPages.size() - 1 : position == 0));
60                 }
61             }
62 
63             @Override
64             public void onPageScrollStateChanged(int state) {
65             }
66         });
67         setCurrentItem(0);
68     }
69 
70     @Override
onRtlPropertiesChanged(int layoutDirection)71     public void onRtlPropertiesChanged(int layoutDirection) {
72         super.onRtlPropertiesChanged(layoutDirection);
73         setAdapter(mAdapter);
74         setCurrentItem(0, false);
75     }
76 
77     @Override
setCurrentItem(int item, boolean smoothScroll)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
setListening(boolean listening)86     public void setListening(boolean listening) {
87         if (mListening == listening) return;
88         mListening = listening;
89         if (mListening) {
90             setPageListening(mPosition, true);
91             if (mOffPage) {
92                 setPageListening(mPosition + 1, true);
93             }
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      */
setCurrentPage(int position, boolean offPage)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.
112                 setPageListening(mPosition, false);
113                 if (mOffPage) {
114                     setPageListening(mPosition + 1, false);
115                 }
116                 // Set the new pages to listening
117                 setPageListening(position, true);
118                 if (offPage) {
119                     setPageListening(position + 1, true);
120                 }
121             } else if (mOffPage != offPage) {
122                 // Whether we are showing position + 1 has changed.
123                 setPageListening(mPosition + 1, offPage);
124             }
125         }
126         // Save the current state.
127         mPosition = position;
128         mOffPage = offPage;
129     }
130 
setPageListening(int position, boolean listening)131     private void setPageListening(int position, boolean listening) {
132         if (position >= mPages.size()) return;
133         if (isLayoutRtl()) {
134             position = mPages.size() - 1 - position;
135         }
136         mPages.get(position).setListening(listening);
137     }
138 
139     @Override
hasOverlappingRendering()140     public boolean hasOverlappingRendering() {
141         return false;
142     }
143 
144     @Override
onFinishInflate()145     protected void onFinishInflate() {
146         super.onFinishInflate();
147         mPages.add((TilePage) LayoutInflater.from(getContext())
148                 .inflate(R.layout.qs_paged_page, this, false));
149     }
150 
setPageIndicator(PageIndicator indicator)151     public void setPageIndicator(PageIndicator indicator) {
152         mPageIndicator = indicator;
153     }
154 
155     @Override
getOffsetTop(TileRecord tile)156     public int getOffsetTop(TileRecord tile) {
157         final ViewGroup parent = (ViewGroup) tile.tileView.getParent();
158         if (parent == null) return 0;
159         return parent.getTop() + getTop();
160     }
161 
162     @Override
addTile(TileRecord tile)163     public void addTile(TileRecord tile) {
164         mTiles.add(tile);
165         postDistributeTiles();
166     }
167 
168     @Override
removeTile(TileRecord tile)169     public void removeTile(TileRecord tile) {
170         if (mTiles.remove(tile)) {
171             postDistributeTiles();
172         }
173     }
174 
setPageListener(PageListener listener)175     public void setPageListener(PageListener listener) {
176         mPageListener = listener;
177     }
178 
postDistributeTiles()179     private void postDistributeTiles() {
180         removeCallbacks(mDistribute);
181         post(mDistribute);
182     }
183 
distributeTiles()184     private void distributeTiles() {
185         if (DEBUG) Log.d(TAG, "Distributing tiles");
186         final int NP = mPages.size();
187         for (int i = 0; i < NP; i++) {
188             mPages.get(i).removeAllViews();
189         }
190         int index = 0;
191         final int NT = mTiles.size();
192         for (int i = 0; i < NT; i++) {
193             TileRecord tile = mTiles.get(i);
194             if (mPages.get(index).isFull()) {
195                 if (++index == mPages.size()) {
196                     if (DEBUG) Log.d(TAG, "Adding page for "
197                             + tile.tile.getClass().getSimpleName());
198                     mPages.add((TilePage) LayoutInflater.from(getContext())
199                             .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;
208             while (mPages.size() > mNumPages) {
209                 mPages.remove(mPages.size() - 1);
210             }
211             if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
212             mPageIndicator.setNumPages(mNumPages);
213             mPageIndicator.setVisibility(mNumPages > 1 ? View.VISIBLE : View.GONE);
214             setAdapter(mAdapter);
215             mAdapter.notifyDataSetChanged();
216             setCurrentItem(0, false);
217         }
218     }
219 
220     @Override
updateResources()221     public boolean updateResources() {
222         boolean changed = false;
223         for (int i = 0; i < mPages.size(); i++) {
224             changed |= mPages.get(i).updateResources();
225         }
226         if (changed) {
227             distributeTiles();
228         }
229         return changed;
230     }
231 
232     @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)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         }
245         setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom());
246     }
247 
248     private final Runnable mDistribute = new Runnable() {
249         @Override
250         public void run() {
251             distributeTiles();
252         }
253     };
254 
getColumnCount()255     public int getColumnCount() {
256         if (mPages.size() == 0) return 0;
257         return mPages.get(0).mColumns;
258     }
259 
260     public static class TilePage extends TileLayout {
261         private int mMaxRows = 3;
262 
TilePage(Context context, AttributeSet attrs)263         public TilePage(Context context, AttributeSet attrs) {
264             super(context, attrs);
265             updateResources();
266         }
267 
268         @Override
updateResources()269         public boolean updateResources() {
270             final int rows = getRows();
271             boolean changed = rows != mMaxRows;
272             if (changed) {
273                 mMaxRows = rows;
274                 requestLayout();
275             }
276             return super.updateResources() || changed;
277         }
278 
getRows()279         private int getRows() {
280             final Resources res = getContext().getResources();
281             if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
282                 return res.getInteger(R.integer.quick_settings_num_rows_portrait);
283             }
284             return Math.max(1, res.getInteger(R.integer.quick_settings_num_rows));
285         }
286 
setMaxRows(int maxRows)287         public void setMaxRows(int maxRows) {
288             mMaxRows = maxRows;
289         }
290 
isFull()291         public boolean isFull() {
292             return mRecords.size() >= mColumns * mMaxRows;
293         }
294     }
295 
296     private final PagerAdapter mAdapter = new PagerAdapter() {
297         public void destroyItem(ViewGroup container, int position, Object object) {
298             if (DEBUG) Log.d(TAG, "Destantiating " + position);
299             container.removeView((View) object);
300         }
301 
302         public Object instantiateItem(ViewGroup container, int position) {
303             if (DEBUG) Log.d(TAG, "Instantiating " + position);
304             if (isLayoutRtl()) {
305                 position = mPages.size() - 1 - position;
306             }
307             ViewGroup view = mPages.get(position);
308             container.addView(view);
309             return view;
310         }
311 
312         @Override
313         public int getCount() {
314             return mNumPages;
315         }
316 
317         @Override
318         public boolean isViewFromObject(View view, Object object) {
319             return view == object;
320         }
321     };
322 
323     public interface PageListener {
onPageChanged(boolean isFirst)324         void onPageChanged(boolean isFirst);
325     }
326 }
327