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 android.content.Context;
201940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.content.res.TypedArray;
211940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.database.DataSetObserver;
221940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.util.AttributeSet;
231940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.view.View;
241940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.view.ViewGroup;
251940892d891c1d2538f51608b6618af646ab7481Adrian Roosimport android.widget.BaseAdapter;
261940892d891c1d2538f51608b6618af646ab7481Adrian Roos
27c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport com.android.systemui.R;
28c0d7058b14c24cd07912f5629c26b39b7b4673d5Winson
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
101deba7a42ed9fdde9017f2b627fc5f63a31a82c4bJason Monk        setMeasuredDimension(width, resolveSizeAndState(totalHeight, heightMeasureSpec, 0));
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