1af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock/*
2af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Copyright (C) 2014 The Android Open Source Project
3af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
4af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Licensed under the Apache License, Version 2.0 (the "License");
5af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * you may not use this file except in compliance with the License.
6af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * You may obtain a copy of the License at
7af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
8af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *      http://www.apache.org/licenses/LICENSE-2.0
9af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock *
10af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Unless required by applicable law or agreed to in writing, software
11af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * distributed under the License is distributed on an "AS IS" BASIS,
12af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * See the License for the specific language governing permissions and
14af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * limitations under the License.
15af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock */
16af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
17af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockpackage com.android.systemui.qs;
18af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
19af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.animation.Animator;
20af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.animation.Animator.AnimatorListener;
21af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.animation.AnimatorListenerAdapter;
22af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.content.Context;
237f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlockimport android.content.Intent;
24e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggiimport android.content.res.Configuration;
254bf31983d247a76142937c01bc2800b7f98d0281John Spurlockimport android.content.res.Resources;
26af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.Handler;
27af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.os.Message;
28af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.util.AttributeSet;
29e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggiimport android.util.TypedValue;
307f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlockimport android.view.LayoutInflater;
31af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.view.View;
32af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.view.ViewGroup;
336783bef9b9b26df093b07b0044cf079a0b5011cdJason Monkimport android.view.accessibility.AccessibilityEvent;
343f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggiimport android.widget.ImageView;
35e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggiimport android.widget.TextView;
36af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
37e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggiimport com.android.systemui.FontSizeUtils;
38af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport com.android.systemui.R;
397f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlockimport com.android.systemui.qs.QSTile.DetailAdapter;
403f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggiimport com.android.systemui.settings.BrightnessController;
413f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggiimport com.android.systemui.settings.ToggleSlider;
421ef80fe5905a19cf92f073163c187a96287335e4Adrian Roosimport com.android.systemui.statusbar.phone.QSTileHost;
435fd872e8ea44ffcd4fce1d1196736dca4f270508Adrian Roosimport com.android.systemui.statusbar.policy.BrightnessMirrorController;
44af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
45af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport java.util.ArrayList;
46bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlockimport java.util.Collection;
47af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
48af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock/** View that represents the quick settings tile panel. **/
49af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockpublic class QSPanel extends ViewGroup {
504bf31983d247a76142937c01bc2800b7f98d0281John Spurlock    private static final float TILE_ASPECT = 1.2f;
51af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
52af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private final Context mContext;
53af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
547f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    private final View mDetail;
557f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    private final ViewGroup mDetailContent;
56e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi    private final TextView mDetailSettingsButton;
57e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi    private final TextView mDetailDoneButton;
583f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi    private final View mBrightnessView;
598af525dd7d1640175fda344301a8712725557caaJohn Spurlock    private final QSDetailClipper mClipper;
60af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private final H mHandler = new H();
61af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
62af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private int mColumns;
63af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private int mCellWidth;
64af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private int mCellHeight;
65af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private int mLargeCellWidth;
66af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private int mLargeCellHeight;
6792d9b19e1bc5f93ef31c4e6eb3395eaf78a5c84eJohn Spurlock    private int mPanelPaddingBottom;
6839076ed379c293399292d1c6b799866c1321b44bJohn Spurlock    private int mDualTileUnderlap;
69cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos    private int mBrightnessPaddingTop;
708bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi    private int mGridHeight;
715729d090ab44228ff342a10b4ec0552741371501John Spurlock    private boolean mExpanded;
72e65e310fc979fa708d7469d01e42188174e47cf8Jorim Jaggi    private boolean mListening;
738bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi    private boolean mClosingDetail;
74af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
751ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    private Record mDetailRecord;
765729d090ab44228ff342a10b4ec0552741371501John Spurlock    private Callback mCallback;
773f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi    private BrightnessController mBrightnessController;
781ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    private QSTileHost mHost;
79af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
803d5f551cd5905c835bb789406763764a8a9a73deJason Monk    private QSFooter mFooter;
81e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek    private boolean mGridContentVisible = true;
823d5f551cd5905c835bb789406763764a8a9a73deJason Monk
83af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public QSPanel(Context context) {
84af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        this(context, null);
85af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
86af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
87af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public QSPanel(Context context, AttributeSet attrs) {
88af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        super(context, attrs);
89af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mContext = context;
90af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
917f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        mDetail = LayoutInflater.from(context).inflate(R.layout.qs_detail, this, false);
927f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        mDetailContent = (ViewGroup) mDetail.findViewById(android.R.id.content);
93e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        mDetailSettingsButton = (TextView) mDetail.findViewById(android.R.id.button2);
94e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        mDetailDoneButton = (TextView) mDetail.findViewById(android.R.id.button1);
95e2f4771a0105ad6a94ff759353ca00c3a5fba72aJason Monk        updateDetailText();
96af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mDetail.setVisibility(GONE);
97af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mDetail.setClickable(true);
983f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi        mBrightnessView = LayoutInflater.from(context).inflate(
993f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi                R.layout.quick_settings_brightness_dialog, this, false);
1003d5f551cd5905c835bb789406763764a8a9a73deJason Monk        mFooter = new QSFooter(this, context);
101af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        addView(mDetail);
1023f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi        addView(mBrightnessView);
1033d5f551cd5905c835bb789406763764a8a9a73deJason Monk        addView(mFooter.getView());
1048af525dd7d1640175fda344301a8712725557caaJohn Spurlock        mClipper = new QSDetailClipper(mDetail);
105af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        updateResources();
1063f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi
1073f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi        mBrightnessController = new BrightnessController(getContext(),
1083f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi                (ImageView) findViewById(R.id.brightness_icon),
1093f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi                (ToggleSlider) findViewById(R.id.brightness_slider));
1101ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
1111ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        mDetailDoneButton.setOnClickListener(new OnClickListener() {
1121ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos            @Override
1131ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos            public void onClick(View v) {
114f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock                closeDetail();
1151ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos            }
1161ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        });
117af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
118af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
119e2f4771a0105ad6a94ff759353ca00c3a5fba72aJason Monk    private void updateDetailText() {
120e2f4771a0105ad6a94ff759353ca00c3a5fba72aJason Monk        mDetailDoneButton.setText(R.string.quick_settings_done);
121e2f4771a0105ad6a94ff759353ca00c3a5fba72aJason Monk        mDetailSettingsButton.setText(R.string.quick_settings_more_settings);
122e2f4771a0105ad6a94ff759353ca00c3a5fba72aJason Monk    }
123e2f4771a0105ad6a94ff759353ca00c3a5fba72aJason Monk
1245fd872e8ea44ffcd4fce1d1196736dca4f270508Adrian Roos    public void setBrightnessMirror(BrightnessMirrorController c) {
1255fd872e8ea44ffcd4fce1d1196736dca4f270508Adrian Roos        super.onFinishInflate();
1265fd872e8ea44ffcd4fce1d1196736dca4f270508Adrian Roos        ToggleSlider brightnessSlider = (ToggleSlider) findViewById(R.id.brightness_slider);
1275fd872e8ea44ffcd4fce1d1196736dca4f270508Adrian Roos        ToggleSlider mirror = (ToggleSlider) c.getMirror().findViewById(R.id.brightness_slider);
1285fd872e8ea44ffcd4fce1d1196736dca4f270508Adrian Roos        brightnessSlider.setMirror(mirror);
1295fd872e8ea44ffcd4fce1d1196736dca4f270508Adrian Roos        brightnessSlider.setMirrorController(c);
1305fd872e8ea44ffcd4fce1d1196736dca4f270508Adrian Roos    }
1315fd872e8ea44ffcd4fce1d1196736dca4f270508Adrian Roos
1325729d090ab44228ff342a10b4ec0552741371501John Spurlock    public void setCallback(Callback callback) {
1335729d090ab44228ff342a10b4ec0552741371501John Spurlock        mCallback = callback;
1345729d090ab44228ff342a10b4ec0552741371501John Spurlock    }
1355729d090ab44228ff342a10b4ec0552741371501John Spurlock
1361ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    public void setHost(QSTileHost host) {
1371ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        mHost = host;
1383d5f551cd5905c835bb789406763764a8a9a73deJason Monk        mFooter.setHost(host);
1391ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    }
1401ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
14100a0b1f397557790cf9ab55fe06e72a96ebc5353Adrian Roos    public QSTileHost getHost() {
14200a0b1f397557790cf9ab55fe06e72a96ebc5353Adrian Roos        return mHost;
14300a0b1f397557790cf9ab55fe06e72a96ebc5353Adrian Roos    }
1441ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
145af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void updateResources() {
1464bf31983d247a76142937c01bc2800b7f98d0281John Spurlock        final Resources res = mContext.getResources();
1474bf31983d247a76142937c01bc2800b7f98d0281John Spurlock        final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
1484bf31983d247a76142937c01bc2800b7f98d0281John Spurlock        mCellHeight = res.getDimensionPixelSize(R.dimen.qs_tile_height);
1494bf31983d247a76142937c01bc2800b7f98d0281John Spurlock        mCellWidth = (int)(mCellHeight * TILE_ASPECT);
1504bf31983d247a76142937c01bc2800b7f98d0281John Spurlock        mLargeCellHeight = res.getDimensionPixelSize(R.dimen.qs_dual_tile_height);
1514bf31983d247a76142937c01bc2800b7f98d0281John Spurlock        mLargeCellWidth = (int)(mLargeCellHeight * TILE_ASPECT);
15292d9b19e1bc5f93ef31c4e6eb3395eaf78a5c84eJohn Spurlock        mPanelPaddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
15339076ed379c293399292d1c6b799866c1321b44bJohn Spurlock        mDualTileUnderlap = res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
154cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos        mBrightnessPaddingTop = res.getDimensionPixelSize(R.dimen.qs_brightness_padding_top);
155af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        if (mColumns != columns) {
156af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            mColumns = columns;
157af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            postInvalidate();
158af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
1591a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock        if (mListening) {
1601a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock            refreshAllTiles();
1611a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock        }
162e2f4771a0105ad6a94ff759353ca00c3a5fba72aJason Monk        updateDetailText();
163af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
164af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
165e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi    @Override
166e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi    protected void onConfigurationChanged(Configuration newConfig) {
167e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        super.onConfigurationChanged(newConfig);
168e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        FontSizeUtils.updateFontSize(mDetailDoneButton, R.dimen.qs_detail_button_text_size);
169e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        FontSizeUtils.updateFontSize(mDetailSettingsButton, R.dimen.qs_detail_button_text_size);
170e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi
171e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        // We need to poke the detail views as well as they might not be attached to the view
172e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        // hierarchy but reused at a later point.
173e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        int count = mRecords.size();
174e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        for (int i = 0; i < count; i++) {
175e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi            View detailView = mRecords.get(i).detailView;
176e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi            if (detailView != null) {
177e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi                detailView.dispatchConfigurationChanged(newConfig);
178e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi            }
179e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        }
180e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi        mFooter.onConfigurationChanged();
181e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi    }
182e17c4b49a41ec9de5c1d7f229273fc4677ec24b1Jorim Jaggi
183af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    public void setExpanded(boolean expanded) {
1845729d090ab44228ff342a10b4ec0552741371501John Spurlock        if (mExpanded == expanded) return;
1855729d090ab44228ff342a10b4ec0552741371501John Spurlock        mExpanded = expanded;
1865729d090ab44228ff342a10b4ec0552741371501John Spurlock        if (!mExpanded) {
187f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock            closeDetail();
188af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
189e65e310fc979fa708d7469d01e42188174e47cf8Jorim Jaggi    }
190e65e310fc979fa708d7469d01e42188174e47cf8Jorim Jaggi
191e65e310fc979fa708d7469d01e42188174e47cf8Jorim Jaggi    public void setListening(boolean listening) {
192e65e310fc979fa708d7469d01e42188174e47cf8Jorim Jaggi        if (mListening == listening) return;
193e65e310fc979fa708d7469d01e42188174e47cf8Jorim Jaggi        mListening = listening;
194af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        for (TileRecord r : mRecords) {
195e65e310fc979fa708d7469d01e42188174e47cf8Jorim Jaggi            r.tile.setListening(mListening);
1961a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock        }
1973d5f551cd5905c835bb789406763764a8a9a73deJason Monk        mFooter.setListening(mListening);
1981a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock        if (mListening) {
1991a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock            refreshAllTiles();
200af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
2013f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi        if (listening) {
2023f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi            mBrightnessController.registerCallbacks();
2033f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi        } else {
2043f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi            mBrightnessController.unregisterCallbacks();
2053f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi        }
206af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
207af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
2081ecd7cd58ed0f823610c7fdae56806d08339e7caJorim Jaggi    public void refreshAllTiles() {
2091a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock        for (TileRecord r : mRecords) {
2101a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock            r.tile.refreshState();
2111a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock        }
2123d5f551cd5905c835bb789406763764a8a9a73deJason Monk        mFooter.refreshState();
2131a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock    }
2141a462c1d2384608e0f546631d431ba39c7e6b27cJohn Spurlock
2151ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    public void showDetailAdapter(boolean show, DetailAdapter adapter) {
2161ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        Record r = new Record();
2171ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        r.detailAdapter = adapter;
2181ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        showDetail(show, r);
2191ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    }
2201ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
2211ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    private void showDetail(boolean show, Record r) {
222af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
223af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
224af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
225e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek    private void setTileVisibility(View v, int visibility) {
226e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        mHandler.obtainMessage(H.SET_TILE_VISIBILITY, visibility, 0, v).sendToTarget();
227af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
228af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
229e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek    private void handleSetTileVisibility(View v, int visibility) {
230e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        if (visibility == v.getVisibility()) return;
231e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        v.setVisibility(visibility);
232af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
233af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
234bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock    public void setTiles(Collection<QSTile<?>> tiles) {
235bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        for (TileRecord record : mRecords) {
236bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock            removeView(record.tileView);
237bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        }
238bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        mRecords.clear();
239bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        for (QSTile<?> tile : tiles) {
240bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock            addTile(tile);
241bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        }
242bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        if (isShowingDetail()) {
243bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock            mDetail.bringToFront();
244bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        }
245bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock    }
246bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock
247bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock    private void addTile(final QSTile<?> tile) {
248af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        final TileRecord r = new TileRecord();
249af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        r.tile = tile;
250af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        r.tileView = tile.createTileView(mContext);
251af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        r.tileView.setVisibility(View.GONE);
252bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        final QSTile.Callback callback = new QSTile.Callback() {
253af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            @Override
254af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            public void onStateChanged(QSTile.State state) {
255e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek                int visibility = state.visible ? VISIBLE : GONE;
256e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek                if (state.visible && !mGridContentVisible) {
257e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek
258e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek                    // We don't want to show it if the content is hidden,
259e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek                    // then we just set it to invisible, to ensure that it gets visible again
260e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek                    visibility = INVISIBLE;
261e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek                }
262e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek                setTileVisibility(r.tileView, visibility);
263af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                r.tileView.onStateChanged(state);
264af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
265af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            @Override
266af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            public void onShowDetail(boolean show) {
267af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                QSPanel.this.showDetail(show, r);
268af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
2697f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            @Override
2707f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            public void onToggleStateChanged(boolean state) {
2717f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock                if (mDetailRecord == r) {
2727f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock                    fireToggleStateChanged(state);
2737f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock                }
2747f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            }
275486b78e42652466f6241eb87d5bed60040db7a25John Spurlock            @Override
276486b78e42652466f6241eb87d5bed60040db7a25John Spurlock            public void onScanStateChanged(boolean state) {
277465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock                r.scanState = state;
278486b78e42652466f6241eb87d5bed60040db7a25John Spurlock                if (mDetailRecord == r) {
279465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock                    fireScanStateChanged(r.scanState);
280486b78e42652466f6241eb87d5bed60040db7a25John Spurlock                }
281486b78e42652466f6241eb87d5bed60040db7a25John Spurlock            }
2824fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek
2834fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek            @Override
2844fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek            public void onAnnouncementRequested(CharSequence announcement) {
2854fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek                announceForAccessibility(announcement);
2864fda7b2d97c77c64bac0b44dc7a44597ad2c48b0Selim Cinek            }
287bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        };
288bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        r.tile.setCallback(callback);
289af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        final View.OnClickListener click = new View.OnClickListener() {
290af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            @Override
291af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            public void onClick(View v) {
292af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                r.tile.click();
293af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
294af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        };
295af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        final View.OnClickListener clickSecondary = new View.OnClickListener() {
296af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            @Override
297af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            public void onClick(View v) {
298af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock                r.tile.secondaryClick();
299af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
300af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        };
301c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        final View.OnLongClickListener longClick = new View.OnLongClickListener() {
302c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock            @Override
303c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock            public boolean onLongClick(View v) {
304c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock                r.tile.longClick();
305c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock                return true;
306c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock            }
307c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        };
308c247b8f7270dd70ecc36977fcc0530217879f665John Spurlock        r.tileView.init(click, clickSecondary, longClick);
309bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        r.tile.setListening(mListening);
310bceed060f0090a4f86418c4515128d5ec8ebdd4aJohn Spurlock        callback.onStateChanged(r.tile.getState());
311ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock        r.tile.refreshState();
312af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mRecords.add(r);
313af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
314af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        addView(r.tileView);
315af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
316af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
317f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock    public boolean isShowingDetail() {
318f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock        return mDetailRecord != null;
319f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock    }
320f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock
321f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock    public void closeDetail() {
322f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock        showDetail(false, mDetailRecord);
323f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock    }
324f7ae44258fdcb452119936dff449c2d99fd8ece8John Spurlock
3258bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi    public boolean isClosingDetail() {
3268bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi        return mClosingDetail;
3278bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi    }
3288bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi
3298bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi    public int getGridHeight() {
3308bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi        return mGridHeight;
3318bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi    }
3328bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi
3331ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    private void handleShowDetail(Record r, boolean show) {
3341ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        if (r instanceof TileRecord) {
3351ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos            handleShowDetailTile((TileRecord) r, show);
3361ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        } else {
3371ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos            handleShowDetailImpl(r, show, getWidth() /* x */, 0/* y */);
3381ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        }
3391ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    }
3401ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
3411ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    private void handleShowDetailTile(TileRecord r, boolean show) {
3421ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        if ((mDetailRecord != null) == show) return;
3431ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
344af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        if (show) {
3457f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            r.detailAdapter = r.tile.getDetailAdapter();
3467f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            if (r.detailAdapter == null) return;
3471ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        }
3481ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
3491ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        int y = r.tileView.getTop() + r.tileView.getHeight() / 2;
3501ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        handleShowDetailImpl(r, show, x, y);
3511ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    }
3521ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
3531ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    private void handleShowDetailImpl(Record r, boolean show, int x, int y) {
3541ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        if ((mDetailRecord != null) == show) return;  // already in right state
3551ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        DetailAdapter detailAdapter = null;
3561ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        AnimatorListener listener = null;
3571ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        if (show) {
3581ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos            detailAdapter = r.detailAdapter;
3591ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos            r.detailView = detailAdapter.createDetailView(mContext, r.detailView, mDetailContent);
3607f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            if (r.detailView == null) throw new IllegalStateException("Must return detail view");
3611ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
3621ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos            final Intent settingsIntent = detailAdapter.getSettingsIntent();
3637f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
3647f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            mDetailSettingsButton.setOnClickListener(new OnClickListener() {
3657f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock                @Override
3667f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock                public void onClick(View v) {
3671ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos                    mHost.startSettingsActivity(settingsIntent);
3687f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock                }
3697f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            });
3701ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
3717f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            mDetailContent.removeAllViews();
372af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            mDetail.bringToFront();
3737f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            mDetailContent.addView(r.detailView);
374465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock            setDetailRecord(r);
375e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek            listener = mHideGridContentWhenDone;
376af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        } else {
3778bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi            mClosingDetail = true;
378e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek            setGridContentVisibility(true);
379af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            listener = mTeardownDetailWhenDone;
380465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock            fireScanStateChanged(false);
381af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
3826783bef9b9b26df093b07b0044cf079a0b5011cdJason Monk        sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
3831ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        fireShowingDetail(show ? detailAdapter : null);
384af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        mClipper.animateCircularClip(x, y, show, listener);
385af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
386af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
387e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek    private void setGridContentVisibility(boolean visible) {
388e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        int newVis = visible ? VISIBLE : INVISIBLE;
389e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        for (int i = 0; i < mRecords.size(); i++) {
390e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek            TileRecord tileRecord = mRecords.get(i);
391e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek            if (tileRecord.tileView.getVisibility() != GONE) {
392e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek                tileRecord.tileView.setVisibility(newVis);
393e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek            }
394e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        }
395e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        mBrightnessView.setVisibility(newVis);
396e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        mGridContentVisible = visible;
397e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek    }
398e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek
399af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    @Override
400af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
401af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        final int width = MeasureSpec.getSize(widthMeasureSpec);
4023f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi        mBrightnessView.measure(exactly(width), MeasureSpec.UNSPECIFIED);
403cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos        final int brightnessHeight = mBrightnessView.getMeasuredHeight() + mBrightnessPaddingTop;
4043d5f551cd5905c835bb789406763764a8a9a73deJason Monk        mFooter.getView().measure(exactly(width), MeasureSpec.UNSPECIFIED);
405ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock        int r = -1;
406ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock        int c = -1;
407af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        int rows = 0;
408ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock        boolean rowIsDual = false;
409af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        for (TileRecord record : mRecords) {
410af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            if (record.tileView.getVisibility() == GONE) continue;
411ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock            // wrap to next column if we've reached the max # of columns
412ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock            // also don't allow dual + single tiles on the same row
413ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock            if (r == -1 || c == (mColumns - 1) || rowIsDual != record.tile.supportsDualTargets()) {
414ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock                r++;
415ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock                c = 0;
416ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock                rowIsDual = record.tile.supportsDualTargets();
417ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock            } else {
418ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock                c++;
419ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock            }
420af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            record.row = r;
421af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            record.col = c;
422af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            rows = r + 1;
423af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
424af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
425af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        for (TileRecord record : mRecords) {
4268ba572a3277f0c729a7fcce67bbf6e101667d5f9Jason Monk            if (record.tileView.setDual(record.tile.supportsDualTargets())) {
4278ba572a3277f0c729a7fcce67bbf6e101667d5f9Jason Monk                record.tileView.handleStateChanged(record.tile.getState());
4288ba572a3277f0c729a7fcce67bbf6e101667d5f9Jason Monk            }
429e5557a972ca190cb82026a5dd0c53f4d119fa05aSelim Cinek            if (record.tileView.getVisibility() == GONE) continue;
430af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
431af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
432af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            record.tileView.measure(exactly(cw), exactly(ch));
433af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
434cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos        int h = rows == 0 ? brightnessHeight : (getRowTop(rows) + mPanelPaddingBottom);
4353d5f551cd5905c835bb789406763764a8a9a73deJason Monk        if (mFooter.hasFooter()) {
436cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos            h += mFooter.getView().getMeasuredHeight();
4373d5f551cd5905c835bb789406763764a8a9a73deJason Monk        }
4381940892d891c1d2538f51608b6618af646ab7481Adrian Roos        mDetail.measure(exactly(width), MeasureSpec.UNSPECIFIED);
4391940892d891c1d2538f51608b6618af646ab7481Adrian Roos        if (mDetail.getMeasuredHeight() < h) {
4401940892d891c1d2538f51608b6618af646ab7481Adrian Roos            mDetail.measure(exactly(width), exactly(h));
4411940892d891c1d2538f51608b6618af646ab7481Adrian Roos        }
4428bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi        mGridHeight = h;
4431940892d891c1d2538f51608b6618af646ab7481Adrian Roos        setMeasuredDimension(width, Math.max(h, mDetail.getMeasuredHeight()));
444af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
445af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
446af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private static int exactly(int size) {
447af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
448af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
449af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
450af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    @Override
451af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    protected void onLayout(boolean changed, int l, int t, int r, int b) {
4524bf31983d247a76142937c01bc2800b7f98d0281John Spurlock        final int w = getWidth();
453cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos        mBrightnessView.layout(0, mBrightnessPaddingTop,
454cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos                mBrightnessView.getMeasuredWidth(),
455cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos                mBrightnessPaddingTop + mBrightnessView.getMeasuredHeight());
45606d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek        boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
457af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        for (TileRecord record : mRecords) {
458af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            if (record.tileView.getVisibility() == GONE) continue;
459af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final int cols = getColumnCount(record.row);
460af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
461af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final int extra = (w - cw * cols) / (cols + 1);
46206d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek            int left = record.col * cw + (record.col + 1) * extra;
463af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            final int top = getRowTop(record.row);
46406d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek            int right;
46506d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek            int tileWith = record.tileView.getMeasuredWidth();
46606d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek            if (isRtl) {
46706d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek                right = w - left;
46806d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek                left = right - tileWith;
46906d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek            } else {
47006d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek                right = left + tileWith;
47106d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek            }
47206d3bca095aecbb7542ebf4bdaa56b368261dd9dSelim Cinek            record.tileView.layout(left, top, right, top + record.tileView.getMeasuredHeight());
473af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
4743e04cc8c9bcb6daa8f5336934a88dc6c4fdd5221John Spurlock        final int dh = Math.max(mDetail.getMeasuredHeight(), getMeasuredHeight());
4753e04cc8c9bcb6daa8f5336934a88dc6c4fdd5221John Spurlock        mDetail.layout(0, 0, mDetail.getMeasuredWidth(), dh);
4763d5f551cd5905c835bb789406763764a8a9a73deJason Monk        if (mFooter.hasFooter()) {
4773d5f551cd5905c835bb789406763764a8a9a73deJason Monk            View footer = mFooter.getView();
4783d5f551cd5905c835bb789406763764a8a9a73deJason Monk            footer.layout(0, getMeasuredHeight() - footer.getMeasuredHeight(),
4793d5f551cd5905c835bb789406763764a8a9a73deJason Monk                    footer.getMeasuredWidth(), getMeasuredHeight());
4803d5f551cd5905c835bb789406763764a8a9a73deJason Monk        }
481af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
482af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
483af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private int getRowTop(int row) {
484cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos        if (row <= 0) return mBrightnessView.getMeasuredHeight() + mBrightnessPaddingTop;
485cd542b8a98ea9c648622c9360c251de4be4223e4Adrian Roos        return mBrightnessView.getMeasuredHeight() + mBrightnessPaddingTop
4863f48f46d7e9073edbc4d8613a6dd4d68a37a11b6Jorim Jaggi                + mLargeCellHeight - mDualTileUnderlap + (row - 1) * mCellHeight;
487af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
488af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
489af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private int getColumnCount(int row) {
490af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        int cols = 0;
491af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        for (TileRecord record : mRecords) {
492af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            if (record.tileView.getVisibility() == GONE) continue;
493af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            if (record.row == row) cols++;
494af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
495af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        return cols;
496af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
497af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
4987f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    private void fireShowingDetail(QSTile.DetailAdapter detail) {
4997f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        if (mCallback != null) {
5007f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            mCallback.onShowingDetail(detail);
5017f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        }
5027f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    }
5037f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock
5047f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock    private void fireToggleStateChanged(boolean state) {
5055729d090ab44228ff342a10b4ec0552741371501John Spurlock        if (mCallback != null) {
5067f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            mCallback.onToggleStateChanged(state);
5075729d090ab44228ff342a10b4ec0552741371501John Spurlock        }
5085729d090ab44228ff342a10b4ec0552741371501John Spurlock    }
5095729d090ab44228ff342a10b4ec0552741371501John Spurlock
510486b78e42652466f6241eb87d5bed60040db7a25John Spurlock    private void fireScanStateChanged(boolean state) {
511486b78e42652466f6241eb87d5bed60040db7a25John Spurlock        if (mCallback != null) {
512486b78e42652466f6241eb87d5bed60040db7a25John Spurlock            mCallback.onScanStateChanged(state);
513486b78e42652466f6241eb87d5bed60040db7a25John Spurlock        }
514486b78e42652466f6241eb87d5bed60040db7a25John Spurlock    }
515486b78e42652466f6241eb87d5bed60040db7a25John Spurlock
516465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock    private void setDetailRecord(Record r) {
517465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock        if (r == mDetailRecord) return;
518465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock        mDetailRecord = r;
519465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock        final boolean scanState = mDetailRecord instanceof TileRecord
520465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock                && ((TileRecord) mDetailRecord).scanState;
521465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock        fireScanStateChanged(scanState);
522465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock    }
523465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock
524af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private class H extends Handler {
525af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        private static final int SHOW_DETAIL = 1;
526af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        private static final int SET_TILE_VISIBILITY = 2;
527af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        @Override
528af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public void handleMessage(Message msg) {
529af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            if (msg.what == SHOW_DETAIL) {
5301ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos                handleShowDetail((Record)msg.obj, msg.arg1 != 0);
531af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            } else if (msg.what == SET_TILE_VISIBILITY) {
532e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek                handleSetTileVisibility((View)msg.obj, msg.arg1);
533af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock            }
534af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        }
535af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
536af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
5371ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    private static class Record {
538856edebad73560e9b1cce021a7de9a0470d07176John Spurlock        View detailView;
5397f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        DetailAdapter detailAdapter;
5401ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    }
5411ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos
5421ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos    private static final class TileRecord extends Record {
5431ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        QSTile<?> tile;
5441ef80fe5905a19cf92f073163c187a96287335e4Adrian Roos        QSTileView tileView;
545af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        int row;
546af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        int col;
547465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock        boolean scanState;
548af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    }
549af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock
550af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() {
551af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        public void onAnimationEnd(Animator animation) {
5527f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock            mDetailContent.removeAllViews();
553465cefa1a26b256c9b7073d17a57cf82a17d6b87John Spurlock            setDetailRecord(null);
5548bc983efc439c321c8f548d384d807c31daf9180Jorim Jaggi            mClosingDetail = false;
555af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock        };
556af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock    };
5575729d090ab44228ff342a10b4ec0552741371501John Spurlock
558e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek    private final AnimatorListenerAdapter mHideGridContentWhenDone = new AnimatorListenerAdapter() {
5598a2d4fc5be174fb200c788659d0a422564ae6f97Jason Monk        public void onAnimationCancel(Animator animation) {
5608a2d4fc5be174fb200c788659d0a422564ae6f97Jason Monk            // If we have been cancelled, remove the listener so that onAnimationEnd doesn't get
5618a2d4fc5be174fb200c788659d0a422564ae6f97Jason Monk            // called, this will avoid accidentally turning off the grid when we don't want to.
5628a2d4fc5be174fb200c788659d0a422564ae6f97Jason Monk            animation.removeListener(this);
5638a2d4fc5be174fb200c788659d0a422564ae6f97Jason Monk        };
5648a2d4fc5be174fb200c788659d0a422564ae6f97Jason Monk
565e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        @Override
566e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        public void onAnimationEnd(Animator animation) {
56798fa70c98f61fe1589b81deb260422b92d4c443aJason Monk            // Only hide content if still in detail state.
56898fa70c98f61fe1589b81deb260422b92d4c443aJason Monk            if (mDetailRecord != null) {
56998fa70c98f61fe1589b81deb260422b92d4c443aJason Monk                setGridContentVisibility(false);
57098fa70c98f61fe1589b81deb260422b92d4c443aJason Monk            }
571e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek        }
572e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek    };
573e32010ac6120278fea41e49b9832af79b1b5463eSelim Cinek
5745729d090ab44228ff342a10b4ec0552741371501John Spurlock    public interface Callback {
5757f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        void onShowingDetail(QSTile.DetailAdapter detail);
5767f8f22a53005f776c8ee4d299e748e74b077ffd9John Spurlock        void onToggleStateChanged(boolean state);
577486b78e42652466f6241eb87d5bed60040db7a25John Spurlock        void onScanStateChanged(boolean state);
5785729d090ab44228ff342a10b4ec0552741371501John Spurlock    }
579af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock}
580