PagedTileLayout.java revision 45233d6b102083880808abe65698c160f71f30fb
1package com.android.systemui.qs;
2
3import android.content.Context;
4import android.content.res.Configuration;
5import android.content.res.Resources;
6import android.support.v4.view.PagerAdapter;
7import android.support.v4.view.ViewPager;
8import android.util.AttributeSet;
9import android.util.Log;
10import android.view.LayoutInflater;
11import android.view.View;
12import android.view.ViewGroup;
13import com.android.systemui.R;
14import com.android.systemui.qs.QSPanel.QSTileLayout;
15import com.android.systemui.qs.QSPanel.TileRecord;
16
17import java.util.ArrayList;
18
19public class PagedTileLayout extends ViewPager implements QSTileLayout {
20
21    private static final boolean DEBUG = false;
22
23    private static final String TAG = "PagedTileLayout";
24
25    private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>();
26    private final ArrayList<TilePage> mPages = new ArrayList<TilePage>();
27
28    private PageIndicator mPageIndicator;
29
30    private int mNumPages;
31    private View mDecorGroup;
32    private PageListener mPageListener;
33
34    private int mPosition;
35    private boolean mOffPage;
36    private boolean mListening;
37
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
71    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
86    public void setListening(boolean listening) {
87        if (mListening == listening) return;
88        mListening = listening;
89        if (mListening) {
90            mPages.get(mPosition).setListening(listening);
91            if (mOffPage) {
92                mPages.get(mPosition + 1).setListening(listening);
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     */
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
131    private void setPageListening(int position, boolean listening) {
132        if (position >= mPages.size()) return;
133        mPages.get(position).setListening(listening);
134    }
135
136    @Override
137    public boolean hasOverlappingRendering() {
138        return false;
139    }
140
141    @Override
142    protected void onFinishInflate() {
143        super.onFinishInflate();
144        mPageIndicator = (PageIndicator) findViewById(R.id.page_indicator);
145        mDecorGroup = findViewById(R.id.page_decor);
146        ((LayoutParams) mDecorGroup.getLayoutParams()).isDecor = true;
147
148        mPages.add((TilePage) LayoutInflater.from(mContext)
149                .inflate(R.layout.qs_paged_page, this, false));
150    }
151
152    @Override
153    public int getOffsetTop(TileRecord tile) {
154        final ViewGroup parent = (ViewGroup) tile.tileView.getParent();
155        if (parent == null) return 0;
156        return parent.getTop() + getTop();
157    }
158
159    @Override
160    public void addTile(TileRecord tile) {
161        mTiles.add(tile);
162        postDistributeTiles();
163    }
164
165    @Override
166    public void removeTile(TileRecord tile) {
167        if (mTiles.remove(tile)) {
168            postDistributeTiles();
169        }
170    }
171
172    public void setPageListener(PageListener listener) {
173        mPageListener = listener;
174    }
175
176    private void postDistributeTiles() {
177        removeCallbacks(mDistribute);
178        post(mDistribute);
179    }
180
181    private void distributeTiles() {
182        if (DEBUG) Log.d(TAG, "Distributing tiles");
183        final int NP = mPages.size();
184        for (int i = 0; i < NP; i++) {
185            mPages.get(i).removeAllViews();
186        }
187        int index = 0;
188        final int NT = mTiles.size();
189        for (int i = 0; i < NT; i++) {
190            TileRecord tile = mTiles.get(i);
191            if (mPages.get(index).isFull()) {
192                if (++index == mPages.size()) {
193                    if (DEBUG) Log.d(TAG, "Adding page for "
194                            + tile.tile.getClass().getSimpleName());
195                    mPages.add((TilePage) LayoutInflater.from(mContext)
196                            .inflate(R.layout.qs_paged_page, this, false));
197                }
198            }
199            if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to "
200                    + index);
201            mPages.get(index).addTile(tile);
202        }
203        if (mNumPages != index + 1) {
204            mNumPages = index + 1;
205            while (mPages.size() > mNumPages) {
206                mPages.remove(mPages.size() - 1);
207            }
208            if (DEBUG) Log.d(TAG, "Size: " + mNumPages);
209            mPageIndicator.setNumPages(mNumPages);
210            setAdapter(mAdapter);
211            mAdapter.notifyDataSetChanged();
212            setCurrentItem(0, false);
213        }
214    }
215
216    @Override
217    public boolean updateResources() {
218        boolean changed = false;
219        for (int i = 0; i < mPages.size(); i++) {
220            changed |= mPages.get(i).updateResources();
221        }
222        if (changed) {
223            distributeTiles();
224        }
225        return changed;
226    }
227
228    @Override
229    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
230        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
231        // The ViewPager likes to eat all of the space, instead force it to wrap to the max height
232        // of the pages.
233        int maxHeight = 0;
234        final int N = getChildCount();
235        for (int i = 0; i < N; i++) {
236            int height = getChildAt(i).getMeasuredHeight();
237            if (height > maxHeight) {
238                maxHeight = height;
239            }
240        }
241        setMeasuredDimension(getMeasuredWidth(), maxHeight + mDecorGroup.getMeasuredHeight());
242    }
243
244    private final Runnable mDistribute = new Runnable() {
245        @Override
246        public void run() {
247            distributeTiles();
248        }
249    };
250
251    public int getColumnCount() {
252        if (mPages.size() == 0) return 0;
253        return mPages.get(0).mColumns;
254    }
255
256    public static class TilePage extends TileLayout {
257        private int mMaxRows = 3;
258
259        public TilePage(Context context, AttributeSet attrs) {
260            super(context, attrs);
261            updateResources();
262            setContentDescription(mContext.getString(R.string.accessibility_desc_quick_settings));
263        }
264
265        @Override
266        public boolean updateResources() {
267            final int rows = getRows();
268            boolean changed = rows != mMaxRows;
269            if (changed) {
270                mMaxRows = rows;
271                requestLayout();
272            }
273            return super.updateResources() || changed;
274        }
275
276        private int getRows() {
277            final Resources res = getContext().getResources();
278            if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
279                // Always have 3 rows in portrait.
280                return 3;
281            }
282            return Math.max(1, res.getInteger(R.integer.quick_settings_num_rows));
283        }
284
285        public void setMaxRows(int maxRows) {
286            mMaxRows = maxRows;
287        }
288
289        public boolean isFull() {
290            return mRecords.size() >= mColumns * mMaxRows;
291        }
292    }
293
294    private final PagerAdapter mAdapter = new PagerAdapter() {
295        public void destroyItem(ViewGroup container, int position, Object object) {
296            if (DEBUG) Log.d(TAG, "Destantiating " + position);
297            container.removeView((View) object);
298        }
299
300        public Object instantiateItem(ViewGroup container, int position) {
301            if (DEBUG) Log.d(TAG, "Instantiating " + position);
302            if (isLayoutRtl()) {
303                position = mPages.size() - 1 - position;
304            }
305            ViewGroup view = mPages.get(position);
306            container.addView(view);
307            return view;
308        }
309
310        @Override
311        public int getCount() {
312            return mNumPages;
313        }
314
315        @Override
316        public boolean isViewFromObject(View view, Object object) {
317            return view == object;
318        }
319    };
320
321    public interface PageListener {
322        void onPageChanged(boolean isFirst);
323    }
324}
325