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