| package com.android.systemui.qs; |
| |
| import android.content.Context; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.support.v4.view.PagerAdapter; |
| import android.support.v4.view.ViewPager; |
| import android.util.AttributeSet; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| |
| import com.android.systemui.R; |
| import com.android.systemui.qs.QSPanel.QSTileLayout; |
| import com.android.systemui.qs.QSPanel.TileRecord; |
| |
| import java.util.ArrayList; |
| |
| public class PagedTileLayout extends ViewPager implements QSTileLayout { |
| |
| private static final boolean DEBUG = false; |
| |
| private static final String TAG = "PagedTileLayout"; |
| |
| private final ArrayList<TileRecord> mTiles = new ArrayList<TileRecord>(); |
| private final ArrayList<TilePage> mPages = new ArrayList<TilePage>(); |
| |
| private PageIndicator mPageIndicator; |
| |
| private int mNumPages; |
| private PageListener mPageListener; |
| |
| private int mPosition; |
| private boolean mOffPage; |
| private boolean mListening; |
| |
| public PagedTileLayout(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| setAdapter(mAdapter); |
| setOnPageChangeListener(new OnPageChangeListener() { |
| @Override |
| public void onPageSelected(int position) { |
| if (mPageIndicator == null) return; |
| if (mPageListener != null) { |
| mPageListener.onPageChanged(isLayoutRtl() ? position == mPages.size() - 1 |
| : position == 0); |
| } |
| } |
| |
| @Override |
| public void onPageScrolled(int position, float positionOffset, |
| int positionOffsetPixels) { |
| if (mPageIndicator == null) return; |
| setCurrentPage(position, positionOffset != 0); |
| mPageIndicator.setLocation(position + positionOffset); |
| if (mPageListener != null) { |
| mPageListener.onPageChanged(positionOffsetPixels == 0 && |
| (isLayoutRtl() ? position == mPages.size() - 1 : position == 0)); |
| } |
| } |
| |
| @Override |
| public void onPageScrollStateChanged(int state) { |
| } |
| }); |
| setCurrentItem(0); |
| } |
| |
| @Override |
| public void onRtlPropertiesChanged(int layoutDirection) { |
| super.onRtlPropertiesChanged(layoutDirection); |
| setAdapter(mAdapter); |
| setCurrentItem(0, false); |
| } |
| |
| @Override |
| public void setCurrentItem(int item, boolean smoothScroll) { |
| if (isLayoutRtl()) { |
| item = mPages.size() - 1 - item; |
| } |
| super.setCurrentItem(item, smoothScroll); |
| } |
| |
| @Override |
| public void setListening(boolean listening) { |
| if (mListening == listening) return; |
| mListening = listening; |
| if (mListening) { |
| setPageListening(mPosition, true); |
| if (mOffPage) { |
| setPageListening(mPosition + 1, true); |
| } |
| } else { |
| // Make sure no pages are listening. |
| for (int i = 0; i < mPages.size(); i++) { |
| mPages.get(i).setListening(false); |
| } |
| } |
| } |
| |
| /** |
| * Sets individual pages to listening or not. If offPage it will set |
| * the next page after position to listening as well since we are in between |
| * pages. |
| */ |
| private void setCurrentPage(int position, boolean offPage) { |
| if (mPosition == position && mOffPage == offPage) return; |
| if (mListening) { |
| if (mPosition != position) { |
| // Clear out the last pages from listening. |
| setPageListening(mPosition, false); |
| if (mOffPage) { |
| setPageListening(mPosition + 1, false); |
| } |
| // Set the new pages to listening |
| setPageListening(position, true); |
| if (offPage) { |
| setPageListening(position + 1, true); |
| } |
| } else if (mOffPage != offPage) { |
| // Whether we are showing position + 1 has changed. |
| setPageListening(mPosition + 1, offPage); |
| } |
| } |
| // Save the current state. |
| mPosition = position; |
| mOffPage = offPage; |
| } |
| |
| private void setPageListening(int position, boolean listening) { |
| if (position >= mPages.size()) return; |
| if (isLayoutRtl()) { |
| position = mPages.size() - 1 - position; |
| } |
| mPages.get(position).setListening(listening); |
| } |
| |
| @Override |
| public boolean hasOverlappingRendering() { |
| return false; |
| } |
| |
| @Override |
| protected void onFinishInflate() { |
| super.onFinishInflate(); |
| mPages.add((TilePage) LayoutInflater.from(getContext()) |
| .inflate(R.layout.qs_paged_page, this, false)); |
| } |
| |
| public void setPageIndicator(PageIndicator indicator) { |
| mPageIndicator = indicator; |
| } |
| |
| @Override |
| public int getOffsetTop(TileRecord tile) { |
| final ViewGroup parent = (ViewGroup) tile.tileView.getParent(); |
| if (parent == null) return 0; |
| return parent.getTop() + getTop(); |
| } |
| |
| @Override |
| public void addTile(TileRecord tile) { |
| mTiles.add(tile); |
| postDistributeTiles(); |
| } |
| |
| @Override |
| public void removeTile(TileRecord tile) { |
| if (mTiles.remove(tile)) { |
| postDistributeTiles(); |
| } |
| } |
| |
| public void setPageListener(PageListener listener) { |
| mPageListener = listener; |
| } |
| |
| private void postDistributeTiles() { |
| removeCallbacks(mDistribute); |
| post(mDistribute); |
| } |
| |
| private void distributeTiles() { |
| if (DEBUG) Log.d(TAG, "Distributing tiles"); |
| final int NP = mPages.size(); |
| for (int i = 0; i < NP; i++) { |
| mPages.get(i).removeAllViews(); |
| } |
| int index = 0; |
| final int NT = mTiles.size(); |
| for (int i = 0; i < NT; i++) { |
| TileRecord tile = mTiles.get(i); |
| if (mPages.get(index).isFull()) { |
| if (++index == mPages.size()) { |
| if (DEBUG) Log.d(TAG, "Adding page for " |
| + tile.tile.getClass().getSimpleName()); |
| mPages.add((TilePage) LayoutInflater.from(getContext()) |
| .inflate(R.layout.qs_paged_page, this, false)); |
| } |
| } |
| if (DEBUG) Log.d(TAG, "Adding " + tile.tile.getClass().getSimpleName() + " to " |
| + index); |
| mPages.get(index).addTile(tile); |
| } |
| if (mNumPages != index + 1) { |
| mNumPages = index + 1; |
| while (mPages.size() > mNumPages) { |
| mPages.remove(mPages.size() - 1); |
| } |
| if (DEBUG) Log.d(TAG, "Size: " + mNumPages); |
| mPageIndicator.setNumPages(mNumPages); |
| mPageIndicator.setVisibility(mNumPages > 1 ? View.VISIBLE : View.GONE); |
| setAdapter(mAdapter); |
| mAdapter.notifyDataSetChanged(); |
| setCurrentItem(0, false); |
| } |
| } |
| |
| @Override |
| public boolean updateResources() { |
| boolean changed = false; |
| for (int i = 0; i < mPages.size(); i++) { |
| changed |= mPages.get(i).updateResources(); |
| } |
| if (changed) { |
| distributeTiles(); |
| } |
| return changed; |
| } |
| |
| @Override |
| protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
| super.onMeasure(widthMeasureSpec, heightMeasureSpec); |
| // The ViewPager likes to eat all of the space, instead force it to wrap to the max height |
| // of the pages. |
| int maxHeight = 0; |
| final int N = getChildCount(); |
| for (int i = 0; i < N; i++) { |
| int height = getChildAt(i).getMeasuredHeight(); |
| if (height > maxHeight) { |
| maxHeight = height; |
| } |
| } |
| setMeasuredDimension(getMeasuredWidth(), maxHeight + getPaddingBottom()); |
| } |
| |
| private final Runnable mDistribute = new Runnable() { |
| @Override |
| public void run() { |
| distributeTiles(); |
| } |
| }; |
| |
| public int getColumnCount() { |
| if (mPages.size() == 0) return 0; |
| return mPages.get(0).mColumns; |
| } |
| |
| public static class TilePage extends TileLayout { |
| private int mMaxRows = 3; |
| |
| public TilePage(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| updateResources(); |
| } |
| |
| @Override |
| public boolean updateResources() { |
| final int rows = getRows(); |
| boolean changed = rows != mMaxRows; |
| if (changed) { |
| mMaxRows = rows; |
| requestLayout(); |
| } |
| return super.updateResources() || changed; |
| } |
| |
| private int getRows() { |
| final Resources res = getContext().getResources(); |
| if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { |
| return res.getInteger(R.integer.quick_settings_num_rows_portrait); |
| } |
| return Math.max(1, res.getInteger(R.integer.quick_settings_num_rows)); |
| } |
| |
| public void setMaxRows(int maxRows) { |
| mMaxRows = maxRows; |
| } |
| |
| public boolean isFull() { |
| return mRecords.size() >= mColumns * mMaxRows; |
| } |
| } |
| |
| private final PagerAdapter mAdapter = new PagerAdapter() { |
| public void destroyItem(ViewGroup container, int position, Object object) { |
| if (DEBUG) Log.d(TAG, "Destantiating " + position); |
| container.removeView((View) object); |
| } |
| |
| public Object instantiateItem(ViewGroup container, int position) { |
| if (DEBUG) Log.d(TAG, "Instantiating " + position); |
| if (isLayoutRtl()) { |
| position = mPages.size() - 1 - position; |
| } |
| ViewGroup view = mPages.get(position); |
| container.addView(view); |
| return view; |
| } |
| |
| @Override |
| public int getCount() { |
| return mNumPages; |
| } |
| |
| @Override |
| public boolean isViewFromObject(View view, Object object) { |
| return view == object; |
| } |
| }; |
| |
| public interface PageListener { |
| void onPageChanged(boolean isFirst); |
| } |
| } |