11940892d891c1d2538f51608b6618af646ab7481Adrian Roos/* 21940892d891c1d2538f51608b6618af646ab7481Adrian Roos * Copyright (C) 2014 The Android Open Source Project 31940892d891c1d2538f51608b6618af646ab7481Adrian Roos * 41940892d891c1d2538f51608b6618af646ab7481Adrian Roos * Licensed under the Apache License, Version 2.0 (the "License"); 51940892d891c1d2538f51608b6618af646ab7481Adrian Roos * you may not use this file except in compliance with the License. 61940892d891c1d2538f51608b6618af646ab7481Adrian Roos * You may obtain a copy of the License at 71940892d891c1d2538f51608b6618af646ab7481Adrian Roos * 81940892d891c1d2538f51608b6618af646ab7481Adrian Roos * http://www.apache.org/licenses/LICENSE-2.0 91940892d891c1d2538f51608b6618af646ab7481Adrian Roos * 101940892d891c1d2538f51608b6618af646ab7481Adrian Roos * Unless required by applicable law or agreed to in writing, software 111940892d891c1d2538f51608b6618af646ab7481Adrian Roos * distributed under the License is distributed on an "AS IS" BASIS, 121940892d891c1d2538f51608b6618af646ab7481Adrian Roos * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131940892d891c1d2538f51608b6618af646ab7481Adrian Roos * See the License for the specific language governing permissions and 141940892d891c1d2538f51608b6618af646ab7481Adrian Roos * limitations under the License 151940892d891c1d2538f51608b6618af646ab7481Adrian Roos */ 161940892d891c1d2538f51608b6618af646ab7481Adrian Roos 171940892d891c1d2538f51608b6618af646ab7481Adrian Roospackage com.android.systemui.qs; 181940892d891c1d2538f51608b6618af646ab7481Adrian Roos 191940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport com.android.systemui.R; 201940892d891c1d2538f51608b6618af646ab7481Adrian Roos 211940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.content.Context; 221940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.content.res.TypedArray; 231940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.database.DataSetObserver; 241940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.util.AttributeSet; 251940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.view.View; 261940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.view.ViewGroup; 271940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.widget.BaseAdapter; 281940892d891c1d2538f51608b6618af646ab7481Adrian Roos 291940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport java.lang.ref.WeakReference; 301940892d891c1d2538f51608b6618af646ab7481Adrian Roos 311940892d891c1d2538f51608b6618af646ab7481Adrian Roos/** 321940892d891c1d2538f51608b6618af646ab7481Adrian Roos * A view that arranges it's children in a grid with a fixed number of evenly spaced columns. 331940892d891c1d2538f51608b6618af646ab7481Adrian Roos * 341940892d891c1d2538f51608b6618af646ab7481Adrian Roos * {@see android.widget.GridView} 351940892d891c1d2538f51608b6618af646ab7481Adrian Roos */ 361940892d891c1d2538f51608b6618af646ab7481Adrian Roospublic class PseudoGridView extends ViewGroup { 371940892d891c1d2538f51608b6618af646ab7481Adrian Roos 381940892d891c1d2538f51608b6618af646ab7481Adrian Roos private int mNumColumns = 3; 391940892d891c1d2538f51608b6618af646ab7481Adrian Roos private int mVerticalSpacing; 401940892d891c1d2538f51608b6618af646ab7481Adrian Roos private int mHorizontalSpacing; 411940892d891c1d2538f51608b6618af646ab7481Adrian Roos 421940892d891c1d2538f51608b6618af646ab7481Adrian Roos public PseudoGridView(Context context, AttributeSet attrs) { 431940892d891c1d2538f51608b6618af646ab7481Adrian Roos super(context, attrs); 441940892d891c1d2538f51608b6618af646ab7481Adrian Roos 451940892d891c1d2538f51608b6618af646ab7481Adrian Roos final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PseudoGridView); 461940892d891c1d2538f51608b6618af646ab7481Adrian Roos 471940892d891c1d2538f51608b6618af646ab7481Adrian Roos final int N = a.getIndexCount(); 481940892d891c1d2538f51608b6618af646ab7481Adrian Roos for (int i = 0; i < N; i++) { 491940892d891c1d2538f51608b6618af646ab7481Adrian Roos int attr = a.getIndex(i); 501940892d891c1d2538f51608b6618af646ab7481Adrian Roos switch (attr) { 511940892d891c1d2538f51608b6618af646ab7481Adrian Roos case R.styleable.PseudoGridView_numColumns: 521940892d891c1d2538f51608b6618af646ab7481Adrian Roos mNumColumns = a.getInt(attr, 3); 531940892d891c1d2538f51608b6618af646ab7481Adrian Roos break; 541940892d891c1d2538f51608b6618af646ab7481Adrian Roos case R.styleable.PseudoGridView_verticalSpacing: 551940892d891c1d2538f51608b6618af646ab7481Adrian Roos mVerticalSpacing = a.getDimensionPixelSize(attr, 0); 561940892d891c1d2538f51608b6618af646ab7481Adrian Roos break; 571940892d891c1d2538f51608b6618af646ab7481Adrian Roos case R.styleable.PseudoGridView_horizontalSpacing: 581940892d891c1d2538f51608b6618af646ab7481Adrian Roos mHorizontalSpacing = a.getDimensionPixelSize(attr, 0); 591940892d891c1d2538f51608b6618af646ab7481Adrian Roos break; 601940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 611940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 621940892d891c1d2538f51608b6618af646ab7481Adrian Roos 631940892d891c1d2538f51608b6618af646ab7481Adrian Roos a.recycle(); 641940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 651940892d891c1d2538f51608b6618af646ab7481Adrian Roos 661940892d891c1d2538f51608b6618af646ab7481Adrian Roos @Override 671940892d891c1d2538f51608b6618af646ab7481Adrian Roos protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 680832b482214fc21d5458a792978ef117318e7b3fAdrian Roos if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) { 691940892d891c1d2538f51608b6618af646ab7481Adrian Roos throw new UnsupportedOperationException("Needs a maximum width"); 701940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 711940892d891c1d2538f51608b6618af646ab7481Adrian Roos int width = MeasureSpec.getSize(widthMeasureSpec); 721940892d891c1d2538f51608b6618af646ab7481Adrian Roos 731940892d891c1d2538f51608b6618af646ab7481Adrian Roos int childWidth = (width - (mNumColumns - 1) * mHorizontalSpacing) / mNumColumns; 741940892d891c1d2538f51608b6618af646ab7481Adrian Roos int childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY); 750832b482214fc21d5458a792978ef117318e7b3fAdrian Roos int childHeightSpec = MeasureSpec.UNSPECIFIED; 761940892d891c1d2538f51608b6618af646ab7481Adrian Roos int totalHeight = 0; 771940892d891c1d2538f51608b6618af646ab7481Adrian Roos int children = getChildCount(); 781940892d891c1d2538f51608b6618af646ab7481Adrian Roos int rows = (children + mNumColumns - 1) / mNumColumns; 791940892d891c1d2538f51608b6618af646ab7481Adrian Roos for (int row = 0; row < rows; row++) { 801940892d891c1d2538f51608b6618af646ab7481Adrian Roos int startOfRow = row * mNumColumns; 811940892d891c1d2538f51608b6618af646ab7481Adrian Roos int endOfRow = Math.min(startOfRow + mNumColumns, children); 821940892d891c1d2538f51608b6618af646ab7481Adrian Roos int maxHeight = 0; 831940892d891c1d2538f51608b6618af646ab7481Adrian Roos for (int i = startOfRow; i < endOfRow; i++) { 841940892d891c1d2538f51608b6618af646ab7481Adrian Roos View child = getChildAt(i); 851940892d891c1d2538f51608b6618af646ab7481Adrian Roos child.measure(childWidthSpec, childHeightSpec); 861940892d891c1d2538f51608b6618af646ab7481Adrian Roos maxHeight = Math.max(maxHeight, child.getMeasuredHeight()); 871940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 881940892d891c1d2538f51608b6618af646ab7481Adrian Roos int maxHeightSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY); 891940892d891c1d2538f51608b6618af646ab7481Adrian Roos for (int i = startOfRow; i < endOfRow; i++) { 901940892d891c1d2538f51608b6618af646ab7481Adrian Roos View child = getChildAt(i); 910832b482214fc21d5458a792978ef117318e7b3fAdrian Roos if (child.getMeasuredHeight() != maxHeight) { 920832b482214fc21d5458a792978ef117318e7b3fAdrian Roos child.measure(childWidthSpec, maxHeightSpec); 930832b482214fc21d5458a792978ef117318e7b3fAdrian Roos } 941940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 951940892d891c1d2538f51608b6618af646ab7481Adrian Roos totalHeight += maxHeight; 961940892d891c1d2538f51608b6618af646ab7481Adrian Roos if (row > 0) { 971940892d891c1d2538f51608b6618af646ab7481Adrian Roos totalHeight += mVerticalSpacing; 981940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 991940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1001940892d891c1d2538f51608b6618af646ab7481Adrian Roos 1011940892d891c1d2538f51608b6618af646ab7481Adrian Roos setMeasuredDimension(width, getDefaultSize(totalHeight, heightMeasureSpec)); 1021940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1031940892d891c1d2538f51608b6618af646ab7481Adrian Roos 1041940892d891c1d2538f51608b6618af646ab7481Adrian Roos @Override 1051940892d891c1d2538f51608b6618af646ab7481Adrian Roos protected void onLayout(boolean changed, int l, int t, int r, int b) { 1060832b482214fc21d5458a792978ef117318e7b3fAdrian Roos boolean isRtl = isLayoutRtl(); 1071940892d891c1d2538f51608b6618af646ab7481Adrian Roos int children = getChildCount(); 1081940892d891c1d2538f51608b6618af646ab7481Adrian Roos int rows = (children + mNumColumns - 1) / mNumColumns; 1091940892d891c1d2538f51608b6618af646ab7481Adrian Roos int y = 0; 1101940892d891c1d2538f51608b6618af646ab7481Adrian Roos for (int row = 0; row < rows; row++) { 1110832b482214fc21d5458a792978ef117318e7b3fAdrian Roos int x = isRtl ? getWidth() : 0; 1121940892d891c1d2538f51608b6618af646ab7481Adrian Roos int maxHeight = 0; 1131940892d891c1d2538f51608b6618af646ab7481Adrian Roos int startOfRow = row * mNumColumns; 1141940892d891c1d2538f51608b6618af646ab7481Adrian Roos int endOfRow = Math.min(startOfRow + mNumColumns, children); 1151940892d891c1d2538f51608b6618af646ab7481Adrian Roos for (int i = startOfRow; i < endOfRow; i++) { 1161940892d891c1d2538f51608b6618af646ab7481Adrian Roos View child = getChildAt(i); 1171940892d891c1d2538f51608b6618af646ab7481Adrian Roos int width = child.getMeasuredWidth(); 1181940892d891c1d2538f51608b6618af646ab7481Adrian Roos int height = child.getMeasuredHeight(); 1190832b482214fc21d5458a792978ef117318e7b3fAdrian Roos if (isRtl) { 1200832b482214fc21d5458a792978ef117318e7b3fAdrian Roos x -= width; 1210832b482214fc21d5458a792978ef117318e7b3fAdrian Roos } 1221940892d891c1d2538f51608b6618af646ab7481Adrian Roos child.layout(x, y, x + width, y + height); 1231940892d891c1d2538f51608b6618af646ab7481Adrian Roos maxHeight = Math.max(maxHeight, height); 1240832b482214fc21d5458a792978ef117318e7b3fAdrian Roos if (isRtl) { 1250832b482214fc21d5458a792978ef117318e7b3fAdrian Roos x -= mHorizontalSpacing; 1260832b482214fc21d5458a792978ef117318e7b3fAdrian Roos } else { 1270832b482214fc21d5458a792978ef117318e7b3fAdrian Roos x += width + mHorizontalSpacing; 1280832b482214fc21d5458a792978ef117318e7b3fAdrian Roos } 1291940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1301940892d891c1d2538f51608b6618af646ab7481Adrian Roos y += maxHeight; 1311940892d891c1d2538f51608b6618af646ab7481Adrian Roos if (row > 0) { 1321940892d891c1d2538f51608b6618af646ab7481Adrian Roos y += mVerticalSpacing; 1331940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1341940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1351940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1361940892d891c1d2538f51608b6618af646ab7481Adrian Roos 1371940892d891c1d2538f51608b6618af646ab7481Adrian Roos /** 1381940892d891c1d2538f51608b6618af646ab7481Adrian Roos * Bridges between a ViewGroup and a BaseAdapter. 1391940892d891c1d2538f51608b6618af646ab7481Adrian Roos * <p> 1401940892d891c1d2538f51608b6618af646ab7481Adrian Roos * Usage: {@code ViewGroupAdapterBridge.link(viewGroup, adapter)} 1411940892d891c1d2538f51608b6618af646ab7481Adrian Roos * <br /> 1421940892d891c1d2538f51608b6618af646ab7481Adrian Roos * After this call, the ViewGroup's children will be provided by the adapter. 1431940892d891c1d2538f51608b6618af646ab7481Adrian Roos */ 1441940892d891c1d2538f51608b6618af646ab7481Adrian Roos public static class ViewGroupAdapterBridge extends DataSetObserver { 1451940892d891c1d2538f51608b6618af646ab7481Adrian Roos 1461940892d891c1d2538f51608b6618af646ab7481Adrian Roos private final WeakReference<ViewGroup> mViewGroup; 1471940892d891c1d2538f51608b6618af646ab7481Adrian Roos private final BaseAdapter mAdapter; 1481940892d891c1d2538f51608b6618af646ab7481Adrian Roos private boolean mReleased; 1491940892d891c1d2538f51608b6618af646ab7481Adrian Roos 1501940892d891c1d2538f51608b6618af646ab7481Adrian Roos public static void link(ViewGroup viewGroup, BaseAdapter adapter) { 1511940892d891c1d2538f51608b6618af646ab7481Adrian Roos new ViewGroupAdapterBridge(viewGroup, adapter); 1521940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1531940892d891c1d2538f51608b6618af646ab7481Adrian Roos 1541940892d891c1d2538f51608b6618af646ab7481Adrian Roos private ViewGroupAdapterBridge(ViewGroup viewGroup, BaseAdapter adapter) { 1551940892d891c1d2538f51608b6618af646ab7481Adrian Roos mViewGroup = new WeakReference<>(viewGroup); 1561940892d891c1d2538f51608b6618af646ab7481Adrian Roos mAdapter = adapter; 1571940892d891c1d2538f51608b6618af646ab7481Adrian Roos mReleased = false; 1581940892d891c1d2538f51608b6618af646ab7481Adrian Roos mAdapter.registerDataSetObserver(this); 1591940892d891c1d2538f51608b6618af646ab7481Adrian Roos refresh(); 1601940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1611940892d891c1d2538f51608b6618af646ab7481Adrian Roos 1621940892d891c1d2538f51608b6618af646ab7481Adrian Roos private void refresh() { 1631940892d891c1d2538f51608b6618af646ab7481Adrian Roos if (mReleased) { 1641940892d891c1d2538f51608b6618af646ab7481Adrian Roos return; 1651940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1661940892d891c1d2538f51608b6618af646ab7481Adrian Roos ViewGroup viewGroup = mViewGroup.get(); 1671940892d891c1d2538f51608b6618af646ab7481Adrian Roos if (viewGroup == null) { 1681940892d891c1d2538f51608b6618af646ab7481Adrian Roos release(); 1691940892d891c1d2538f51608b6618af646ab7481Adrian Roos return; 1701940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1711940892d891c1d2538f51608b6618af646ab7481Adrian Roos final int childCount = viewGroup.getChildCount(); 1721940892d891c1d2538f51608b6618af646ab7481Adrian Roos final int adapterCount = mAdapter.getCount(); 1731940892d891c1d2538f51608b6618af646ab7481Adrian Roos final int N = Math.max(childCount, adapterCount); 1741940892d891c1d2538f51608b6618af646ab7481Adrian Roos for (int i = 0; i < N; i++) { 1751940892d891c1d2538f51608b6618af646ab7481Adrian Roos if (i < adapterCount) { 1761940892d891c1d2538f51608b6618af646ab7481Adrian Roos View oldView = null; 1771940892d891c1d2538f51608b6618af646ab7481Adrian Roos if (i < childCount) { 1781940892d891c1d2538f51608b6618af646ab7481Adrian Roos oldView = viewGroup.getChildAt(i); 1791940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1801940892d891c1d2538f51608b6618af646ab7481Adrian Roos View newView = mAdapter.getView(i, oldView, viewGroup); 1811940892d891c1d2538f51608b6618af646ab7481Adrian Roos if (oldView == null) { 1821940892d891c1d2538f51608b6618af646ab7481Adrian Roos // We ran out of existing views. Add it at the end. 1831940892d891c1d2538f51608b6618af646ab7481Adrian Roos viewGroup.addView(newView); 1841940892d891c1d2538f51608b6618af646ab7481Adrian Roos } else if (oldView != newView) { 1851940892d891c1d2538f51608b6618af646ab7481Adrian Roos // We couldn't rebind the view. Replace it. 1861940892d891c1d2538f51608b6618af646ab7481Adrian Roos viewGroup.removeViewAt(i); 1871940892d891c1d2538f51608b6618af646ab7481Adrian Roos viewGroup.addView(newView, i); 1881940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1891940892d891c1d2538f51608b6618af646ab7481Adrian Roos } else { 1901940892d891c1d2538f51608b6618af646ab7481Adrian Roos int lastIndex = viewGroup.getChildCount() - 1; 1911940892d891c1d2538f51608b6618af646ab7481Adrian Roos viewGroup.removeViewAt(lastIndex); 1921940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1931940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1941940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 1951940892d891c1d2538f51608b6618af646ab7481Adrian Roos 1961940892d891c1d2538f51608b6618af646ab7481Adrian Roos @Override 1971940892d891c1d2538f51608b6618af646ab7481Adrian Roos public void onChanged() { 1981940892d891c1d2538f51608b6618af646ab7481Adrian Roos refresh(); 1991940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 2001940892d891c1d2538f51608b6618af646ab7481Adrian Roos 2011940892d891c1d2538f51608b6618af646ab7481Adrian Roos @Override 2021940892d891c1d2538f51608b6618af646ab7481Adrian Roos public void onInvalidated() { 2031940892d891c1d2538f51608b6618af646ab7481Adrian Roos release(); 2041940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 2051940892d891c1d2538f51608b6618af646ab7481Adrian Roos 2061940892d891c1d2538f51608b6618af646ab7481Adrian Roos private void release() { 2071940892d891c1d2538f51608b6618af646ab7481Adrian Roos if (!mReleased) { 2081940892d891c1d2538f51608b6618af646ab7481Adrian Roos mReleased = true; 2091940892d891c1d2538f51608b6618af646ab7481Adrian Roos mAdapter.unregisterDataSetObserver(this); 2101940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 2111940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 2121940892d891c1d2538f51608b6618af646ab7481Adrian Roos } 2131940892d891c1d2538f51608b6618af646ab7481Adrian Roos} 214