Home | History | Annotate | Download | only in qs
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.systemui.qs;
     18 
     19 import android.animation.Animator;
     20 import android.animation.Animator.AnimatorListener;
     21 import android.animation.AnimatorListenerAdapter;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.res.Configuration;
     25 import android.content.res.Resources;
     26 import android.os.Handler;
     27 import android.os.Message;
     28 import android.util.AttributeSet;
     29 import android.view.LayoutInflater;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 import android.view.accessibility.AccessibilityEvent;
     33 import android.widget.ImageView;
     34 import android.widget.TextView;
     35 
     36 import com.android.internal.logging.MetricsLogger;
     37 import com.android.systemui.FontSizeUtils;
     38 import com.android.systemui.R;
     39 import com.android.systemui.qs.QSTile.DetailAdapter;
     40 import com.android.systemui.settings.BrightnessController;
     41 import com.android.systemui.settings.ToggleSlider;
     42 import com.android.systemui.statusbar.phone.QSTileHost;
     43 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
     44 
     45 import java.util.ArrayList;
     46 import java.util.Collection;
     47 
     48 /** View that represents the quick settings tile panel. **/
     49 public class QSPanel extends ViewGroup {
     50     private static final float TILE_ASPECT = 1.2f;
     51 
     52     private final Context mContext;
     53     protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
     54     private final View mDetail;
     55     private final ViewGroup mDetailContent;
     56     private final TextView mDetailSettingsButton;
     57     private final TextView mDetailDoneButton;
     58     protected final View mBrightnessView;
     59     private final QSDetailClipper mClipper;
     60     private final H mHandler = new H();
     61 
     62     private int mColumns;
     63     private int mCellWidth;
     64     private int mCellHeight;
     65     private int mLargeCellWidth;
     66     private int mLargeCellHeight;
     67     private int mPanelPaddingBottom;
     68     private int mDualTileUnderlap;
     69     private int mBrightnessPaddingTop;
     70     private int mGridHeight;
     71     private boolean mExpanded;
     72     private boolean mListening;
     73     private boolean mClosingDetail;
     74 
     75     private Record mDetailRecord;
     76     private Callback mCallback;
     77     private BrightnessController mBrightnessController;
     78     private QSTileHost mHost;
     79 
     80     private QSFooter mFooter;
     81     private boolean mGridContentVisible = true;
     82 
     83     public QSPanel(Context context) {
     84         this(context, null);
     85     }
     86 
     87     public QSPanel(Context context, AttributeSet attrs) {
     88         super(context, attrs);
     89         mContext = context;
     90 
     91         mDetail = LayoutInflater.from(context).inflate(R.layout.qs_detail, this, false);
     92         mDetailContent = (ViewGroup) mDetail.findViewById(android.R.id.content);
     93         mDetailSettingsButton = (TextView) mDetail.findViewById(android.R.id.button2);
     94         mDetailDoneButton = (TextView) mDetail.findViewById(android.R.id.button1);
     95         updateDetailText();
     96         mDetail.setVisibility(GONE);
     97         mDetail.setClickable(true);
     98         mBrightnessView = LayoutInflater.from(context).inflate(
     99                 R.layout.quick_settings_brightness_dialog, this, false);
    100         mFooter = new QSFooter(this, context);
    101         addView(mDetail);
    102         addView(mBrightnessView);
    103         addView(mFooter.getView());
    104         mClipper = new QSDetailClipper(mDetail);
    105         updateResources();
    106 
    107         mBrightnessController = new BrightnessController(getContext(),
    108                 (ImageView) findViewById(R.id.brightness_icon),
    109                 (ToggleSlider) findViewById(R.id.brightness_slider));
    110 
    111         mDetailDoneButton.setOnClickListener(new OnClickListener() {
    112             @Override
    113             public void onClick(View v) {
    114                 announceForAccessibility(
    115                         mContext.getString(R.string.accessibility_desc_quick_settings));
    116                 closeDetail();
    117             }
    118         });
    119     }
    120 
    121     private void updateDetailText() {
    122         mDetailDoneButton.setText(R.string.quick_settings_done);
    123         mDetailSettingsButton.setText(R.string.quick_settings_more_settings);
    124     }
    125 
    126     public void setBrightnessMirror(BrightnessMirrorController c) {
    127         super.onFinishInflate();
    128         ToggleSlider brightnessSlider = (ToggleSlider) findViewById(R.id.brightness_slider);
    129         ToggleSlider mirror = (ToggleSlider) c.getMirror().findViewById(R.id.brightness_slider);
    130         brightnessSlider.setMirror(mirror);
    131         brightnessSlider.setMirrorController(c);
    132     }
    133 
    134     public void setCallback(Callback callback) {
    135         mCallback = callback;
    136     }
    137 
    138     public void setHost(QSTileHost host) {
    139         mHost = host;
    140         mFooter.setHost(host);
    141     }
    142 
    143     public QSTileHost getHost() {
    144         return mHost;
    145     }
    146 
    147     public void updateResources() {
    148         final Resources res = mContext.getResources();
    149         final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
    150         mCellHeight = res.getDimensionPixelSize(R.dimen.qs_tile_height);
    151         mCellWidth = (int)(mCellHeight * TILE_ASPECT);
    152         mLargeCellHeight = res.getDimensionPixelSize(R.dimen.qs_dual_tile_height);
    153         mLargeCellWidth = (int)(mLargeCellHeight * TILE_ASPECT);
    154         mPanelPaddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
    155         mDualTileUnderlap = res.getDimensionPixelSize(R.dimen.qs_dual_tile_padding_vertical);
    156         mBrightnessPaddingTop = res.getDimensionPixelSize(R.dimen.qs_brightness_padding_top);
    157         if (mColumns != columns) {
    158             mColumns = columns;
    159             postInvalidate();
    160         }
    161         for (TileRecord r : mRecords) {
    162             r.tile.clearState();
    163         }
    164         if (mListening) {
    165             refreshAllTiles();
    166         }
    167         updateDetailText();
    168     }
    169 
    170     @Override
    171     protected void onConfigurationChanged(Configuration newConfig) {
    172         super.onConfigurationChanged(newConfig);
    173         FontSizeUtils.updateFontSize(mDetailDoneButton, R.dimen.qs_detail_button_text_size);
    174         FontSizeUtils.updateFontSize(mDetailSettingsButton, R.dimen.qs_detail_button_text_size);
    175 
    176         // We need to poke the detail views as well as they might not be attached to the view
    177         // hierarchy but reused at a later point.
    178         int count = mRecords.size();
    179         for (int i = 0; i < count; i++) {
    180             View detailView = mRecords.get(i).detailView;
    181             if (detailView != null) {
    182                 detailView.dispatchConfigurationChanged(newConfig);
    183             }
    184         }
    185         mFooter.onConfigurationChanged();
    186     }
    187 
    188     public void setExpanded(boolean expanded) {
    189         if (mExpanded == expanded) return;
    190         mExpanded = expanded;
    191         MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, mExpanded);
    192         if (!mExpanded) {
    193             closeDetail();
    194         } else {
    195             logTiles();
    196         }
    197     }
    198 
    199     public void setListening(boolean listening) {
    200         if (mListening == listening) return;
    201         mListening = listening;
    202         for (TileRecord r : mRecords) {
    203             r.tile.setListening(mListening);
    204         }
    205         mFooter.setListening(mListening);
    206         if (mListening) {
    207             refreshAllTiles();
    208         }
    209         if (listening) {
    210             mBrightnessController.registerCallbacks();
    211         } else {
    212             mBrightnessController.unregisterCallbacks();
    213         }
    214     }
    215 
    216     public void refreshAllTiles() {
    217         for (TileRecord r : mRecords) {
    218             r.tile.refreshState();
    219         }
    220         mFooter.refreshState();
    221     }
    222 
    223     public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) {
    224         int xInWindow = locationInWindow[0];
    225         int yInWindow = locationInWindow[1];
    226         mDetail.getLocationInWindow(locationInWindow);
    227 
    228         Record r = new Record();
    229         r.detailAdapter = adapter;
    230         r.x = xInWindow - locationInWindow[0];
    231         r.y = yInWindow - locationInWindow[1];
    232 
    233         locationInWindow[0] = xInWindow;
    234         locationInWindow[1] = yInWindow;
    235 
    236         showDetail(show, r);
    237     }
    238 
    239     private void showDetail(boolean show, Record r) {
    240         mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
    241     }
    242 
    243     private void setTileVisibility(View v, int visibility) {
    244         mHandler.obtainMessage(H.SET_TILE_VISIBILITY, visibility, 0, v).sendToTarget();
    245     }
    246 
    247     private void handleSetTileVisibility(View v, int visibility) {
    248         if (visibility == VISIBLE && !mGridContentVisible) {
    249             visibility = INVISIBLE;
    250         }
    251         if (visibility == v.getVisibility()) return;
    252         v.setVisibility(visibility);
    253     }
    254 
    255     public void setTiles(Collection<QSTile<?>> tiles) {
    256         for (TileRecord record : mRecords) {
    257             removeView(record.tileView);
    258         }
    259         mRecords.clear();
    260         for (QSTile<?> tile : tiles) {
    261             addTile(tile);
    262         }
    263         if (isShowingDetail()) {
    264             mDetail.bringToFront();
    265         }
    266     }
    267 
    268     private void drawTile(TileRecord r, QSTile.State state) {
    269         final int visibility = state.visible ? VISIBLE : GONE;
    270         setTileVisibility(r.tileView, visibility);
    271         r.tileView.onStateChanged(state);
    272     }
    273 
    274     private void addTile(final QSTile<?> tile) {
    275         final TileRecord r = new TileRecord();
    276         r.tile = tile;
    277         r.tileView = tile.createTileView(mContext);
    278         r.tileView.setVisibility(View.GONE);
    279         final QSTile.Callback callback = new QSTile.Callback() {
    280             @Override
    281             public void onStateChanged(QSTile.State state) {
    282                 if (!r.openingDetail) {
    283                     drawTile(r, state);
    284                 }
    285             }
    286             @Override
    287             public void onShowDetail(boolean show) {
    288                 QSPanel.this.showDetail(show, r);
    289             }
    290             @Override
    291             public void onToggleStateChanged(boolean state) {
    292                 if (mDetailRecord == r) {
    293                     fireToggleStateChanged(state);
    294                 }
    295             }
    296             @Override
    297             public void onScanStateChanged(boolean state) {
    298                 r.scanState = state;
    299                 if (mDetailRecord == r) {
    300                     fireScanStateChanged(r.scanState);
    301                 }
    302             }
    303 
    304             @Override
    305             public void onAnnouncementRequested(CharSequence announcement) {
    306                 announceForAccessibility(announcement);
    307             }
    308         };
    309         r.tile.setCallback(callback);
    310         final View.OnClickListener click = new View.OnClickListener() {
    311             @Override
    312             public void onClick(View v) {
    313                 r.tile.click();
    314             }
    315         };
    316         final View.OnClickListener clickSecondary = new View.OnClickListener() {
    317             @Override
    318             public void onClick(View v) {
    319                 r.tile.secondaryClick();
    320             }
    321         };
    322         final View.OnLongClickListener longClick = new View.OnLongClickListener() {
    323             @Override
    324             public boolean onLongClick(View v) {
    325                 r.tile.longClick();
    326                 return true;
    327             }
    328         };
    329         r.tileView.init(click, clickSecondary, longClick);
    330         r.tile.setListening(mListening);
    331         callback.onStateChanged(r.tile.getState());
    332         r.tile.refreshState();
    333         mRecords.add(r);
    334 
    335         addView(r.tileView);
    336     }
    337 
    338     public boolean isShowingDetail() {
    339         return mDetailRecord != null;
    340     }
    341 
    342     public void closeDetail() {
    343         showDetail(false, mDetailRecord);
    344     }
    345 
    346     public boolean isClosingDetail() {
    347         return mClosingDetail;
    348     }
    349 
    350     public int getGridHeight() {
    351         return mGridHeight;
    352     }
    353 
    354     private void handleShowDetail(Record r, boolean show) {
    355         if (r instanceof TileRecord) {
    356             handleShowDetailTile((TileRecord) r, show);
    357         } else {
    358             int x = 0;
    359             int y = 0;
    360             if (r != null) {
    361                 x = r.x;
    362                 y = r.y;
    363             }
    364             handleShowDetailImpl(r, show, x, y);
    365         }
    366     }
    367 
    368     private void handleShowDetailTile(TileRecord r, boolean show) {
    369         if ((mDetailRecord != null) == show && mDetailRecord == r) return;
    370 
    371         if (show) {
    372             r.detailAdapter = r.tile.getDetailAdapter();
    373             if (r.detailAdapter == null) return;
    374         }
    375         r.tile.setDetailListening(show);
    376         int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
    377         int y = r.tileView.getTop() + r.tileView.getHeight() / 2;
    378         handleShowDetailImpl(r, show, x, y);
    379     }
    380 
    381     private void handleShowDetailImpl(Record r, boolean show, int x, int y) {
    382         boolean visibleDiff = (mDetailRecord != null) != show;
    383         if (!visibleDiff && mDetailRecord == r) return;  // already in right state
    384         DetailAdapter detailAdapter = null;
    385         AnimatorListener listener = null;
    386         if (show) {
    387             detailAdapter = r.detailAdapter;
    388             r.detailView = detailAdapter.createDetailView(mContext, r.detailView, mDetailContent);
    389             if (r.detailView == null) throw new IllegalStateException("Must return detail view");
    390 
    391             final Intent settingsIntent = detailAdapter.getSettingsIntent();
    392             mDetailSettingsButton.setVisibility(settingsIntent != null ? VISIBLE : GONE);
    393             mDetailSettingsButton.setOnClickListener(new OnClickListener() {
    394                 @Override
    395                 public void onClick(View v) {
    396                     mHost.startActivityDismissingKeyguard(settingsIntent);
    397                 }
    398             });
    399 
    400             mDetailContent.removeAllViews();
    401             mDetail.bringToFront();
    402             mDetailContent.addView(r.detailView);
    403             MetricsLogger.visible(mContext, detailAdapter.getMetricsCategory());
    404             announceForAccessibility(mContext.getString(
    405                     R.string.accessibility_quick_settings_detail,
    406                     mContext.getString(detailAdapter.getTitle())));
    407             setDetailRecord(r);
    408             listener = mHideGridContentWhenDone;
    409             if (r instanceof TileRecord && visibleDiff) {
    410                 ((TileRecord) r).openingDetail = true;
    411             }
    412         } else {
    413             if (mDetailRecord != null) {
    414                 MetricsLogger.hidden(mContext, mDetailRecord.detailAdapter.getMetricsCategory());
    415             }
    416             mClosingDetail = true;
    417             setGridContentVisibility(true);
    418             listener = mTeardownDetailWhenDone;
    419             fireScanStateChanged(false);
    420         }
    421         sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
    422         fireShowingDetail(show ? detailAdapter : null);
    423         if (visibleDiff) {
    424             mClipper.animateCircularClip(x, y, show, listener);
    425         }
    426     }
    427 
    428     private void setGridContentVisibility(boolean visible) {
    429         int newVis = visible ? VISIBLE : INVISIBLE;
    430         for (int i = 0; i < mRecords.size(); i++) {
    431             TileRecord tileRecord = mRecords.get(i);
    432             if (tileRecord.tileView.getVisibility() != GONE) {
    433                 tileRecord.tileView.setVisibility(newVis);
    434             }
    435         }
    436         mBrightnessView.setVisibility(newVis);
    437         if (mGridContentVisible != visible) {
    438             MetricsLogger.visibility(mContext, MetricsLogger.QS_PANEL, newVis);
    439         }
    440         mGridContentVisible = visible;
    441     }
    442 
    443     private void logTiles() {
    444         for (int i = 0; i < mRecords.size(); i++) {
    445             TileRecord tileRecord = mRecords.get(i);
    446             if (tileRecord.tile.getState().visible) {
    447                 MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory());
    448             }
    449         }
    450     }
    451 
    452     @Override
    453     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    454         final int width = MeasureSpec.getSize(widthMeasureSpec);
    455         mBrightnessView.measure(exactly(width), MeasureSpec.UNSPECIFIED);
    456         final int brightnessHeight = mBrightnessView.getMeasuredHeight() + mBrightnessPaddingTop;
    457         mFooter.getView().measure(exactly(width), MeasureSpec.UNSPECIFIED);
    458         int r = -1;
    459         int c = -1;
    460         int rows = 0;
    461         boolean rowIsDual = false;
    462         for (TileRecord record : mRecords) {
    463             if (record.tileView.getVisibility() == GONE) continue;
    464             // wrap to next column if we've reached the max # of columns
    465             // also don't allow dual + single tiles on the same row
    466             if (r == -1 || c == (mColumns - 1) || rowIsDual != record.tile.supportsDualTargets()) {
    467                 r++;
    468                 c = 0;
    469                 rowIsDual = record.tile.supportsDualTargets();
    470             } else {
    471                 c++;
    472             }
    473             record.row = r;
    474             record.col = c;
    475             rows = r + 1;
    476         }
    477 
    478         View previousView = mBrightnessView;
    479         for (TileRecord record : mRecords) {
    480             if (record.tileView.setDual(record.tile.supportsDualTargets())) {
    481                 record.tileView.handleStateChanged(record.tile.getState());
    482             }
    483             if (record.tileView.getVisibility() == GONE) continue;
    484             final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
    485             final int ch = record.row == 0 ? mLargeCellHeight : mCellHeight;
    486             record.tileView.measure(exactly(cw), exactly(ch));
    487             previousView = record.tileView.updateAccessibilityOrder(previousView);
    488         }
    489         int h = rows == 0 ? brightnessHeight : (getRowTop(rows) + mPanelPaddingBottom);
    490         if (mFooter.hasFooter()) {
    491             h += mFooter.getView().getMeasuredHeight();
    492         }
    493         mDetail.measure(exactly(width), MeasureSpec.UNSPECIFIED);
    494         if (mDetail.getMeasuredHeight() < h) {
    495             mDetail.measure(exactly(width), exactly(h));
    496         }
    497         mGridHeight = h;
    498         setMeasuredDimension(width, Math.max(h, mDetail.getMeasuredHeight()));
    499     }
    500 
    501     private static int exactly(int size) {
    502         return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
    503     }
    504 
    505     @Override
    506     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    507         final int w = getWidth();
    508         mBrightnessView.layout(0, mBrightnessPaddingTop,
    509                 mBrightnessView.getMeasuredWidth(),
    510                 mBrightnessPaddingTop + mBrightnessView.getMeasuredHeight());
    511         boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
    512         for (TileRecord record : mRecords) {
    513             if (record.tileView.getVisibility() == GONE) continue;
    514             final int cols = getColumnCount(record.row);
    515             final int cw = record.row == 0 ? mLargeCellWidth : mCellWidth;
    516             final int extra = (w - cw * cols) / (cols + 1);
    517             int left = record.col * cw + (record.col + 1) * extra;
    518             final int top = getRowTop(record.row);
    519             int right;
    520             int tileWith = record.tileView.getMeasuredWidth();
    521             if (isRtl) {
    522                 right = w - left;
    523                 left = right - tileWith;
    524             } else {
    525                 right = left + tileWith;
    526             }
    527             record.tileView.layout(left, top, right, top + record.tileView.getMeasuredHeight());
    528         }
    529         final int dh = Math.max(mDetail.getMeasuredHeight(), getMeasuredHeight());
    530         mDetail.layout(0, 0, mDetail.getMeasuredWidth(), dh);
    531         if (mFooter.hasFooter()) {
    532             View footer = mFooter.getView();
    533             footer.layout(0, getMeasuredHeight() - footer.getMeasuredHeight(),
    534                     footer.getMeasuredWidth(), getMeasuredHeight());
    535         }
    536     }
    537 
    538     private int getRowTop(int row) {
    539         if (row <= 0) return mBrightnessView.getMeasuredHeight() + mBrightnessPaddingTop;
    540         return mBrightnessView.getMeasuredHeight() + mBrightnessPaddingTop
    541                 + mLargeCellHeight - mDualTileUnderlap + (row - 1) * mCellHeight;
    542     }
    543 
    544     private int getColumnCount(int row) {
    545         int cols = 0;
    546         for (TileRecord record : mRecords) {
    547             if (record.tileView.getVisibility() == GONE) continue;
    548             if (record.row == row) cols++;
    549         }
    550         return cols;
    551     }
    552 
    553     private void fireShowingDetail(QSTile.DetailAdapter detail) {
    554         if (mCallback != null) {
    555             mCallback.onShowingDetail(detail);
    556         }
    557     }
    558 
    559     private void fireToggleStateChanged(boolean state) {
    560         if (mCallback != null) {
    561             mCallback.onToggleStateChanged(state);
    562         }
    563     }
    564 
    565     private void fireScanStateChanged(boolean state) {
    566         if (mCallback != null) {
    567             mCallback.onScanStateChanged(state);
    568         }
    569     }
    570 
    571     private void setDetailRecord(Record r) {
    572         if (r == mDetailRecord) return;
    573         mDetailRecord = r;
    574         final boolean scanState = mDetailRecord instanceof TileRecord
    575                 && ((TileRecord) mDetailRecord).scanState;
    576         fireScanStateChanged(scanState);
    577     }
    578 
    579     private class H extends Handler {
    580         private static final int SHOW_DETAIL = 1;
    581         private static final int SET_TILE_VISIBILITY = 2;
    582         @Override
    583         public void handleMessage(Message msg) {
    584             if (msg.what == SHOW_DETAIL) {
    585                 handleShowDetail((Record)msg.obj, msg.arg1 != 0);
    586             } else if (msg.what == SET_TILE_VISIBILITY) {
    587                 handleSetTileVisibility((View)msg.obj, msg.arg1);
    588             }
    589         }
    590     }
    591 
    592     private static class Record {
    593         View detailView;
    594         DetailAdapter detailAdapter;
    595         int x;
    596         int y;
    597     }
    598 
    599     protected static final class TileRecord extends Record {
    600         public QSTile<?> tile;
    601         public QSTileView tileView;
    602         public int row;
    603         public int col;
    604         public boolean scanState;
    605         public boolean openingDetail;
    606     }
    607 
    608     private final AnimatorListenerAdapter mTeardownDetailWhenDone = new AnimatorListenerAdapter() {
    609         public void onAnimationEnd(Animator animation) {
    610             mDetailContent.removeAllViews();
    611             setDetailRecord(null);
    612             mClosingDetail = false;
    613         };
    614     };
    615 
    616     private final AnimatorListenerAdapter mHideGridContentWhenDone = new AnimatorListenerAdapter() {
    617         public void onAnimationCancel(Animator animation) {
    618             // If we have been cancelled, remove the listener so that onAnimationEnd doesn't get
    619             // called, this will avoid accidentally turning off the grid when we don't want to.
    620             animation.removeListener(this);
    621             redrawTile();
    622         };
    623 
    624         @Override
    625         public void onAnimationEnd(Animator animation) {
    626             // Only hide content if still in detail state.
    627             if (mDetailRecord != null) {
    628                 setGridContentVisibility(false);
    629                 redrawTile();
    630             }
    631         }
    632 
    633         private void redrawTile() {
    634             if (mDetailRecord instanceof TileRecord) {
    635                 final TileRecord tileRecord = (TileRecord) mDetailRecord;
    636                 tileRecord.openingDetail = false;
    637                 drawTile(tileRecord, tileRecord.tile.getState());
    638             }
    639         }
    640     };
    641 
    642     public interface Callback {
    643         void onShowingDetail(QSTile.DetailAdapter detail);
    644         void onToggleStateChanged(boolean state);
    645         void onScanStateChanged(boolean state);
    646     }
    647 }
    648