• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.ComponentName;
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.content.res.Resources;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.util.AttributeSet;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.widget.ImageView;
29 import android.widget.LinearLayout;
30 import com.android.internal.logging.MetricsLogger;
31 import com.android.internal.logging.MetricsProto.MetricsEvent;
32 import com.android.systemui.R;
33 import com.android.systemui.qs.QSTile.DetailAdapter;
34 import com.android.systemui.qs.QSTile.Host.Callback;
35 import com.android.systemui.qs.customize.QSCustomizer;
36 import com.android.systemui.qs.external.CustomTile;
37 import com.android.systemui.settings.BrightnessController;
38 import com.android.systemui.settings.ToggleSlider;
39 import com.android.systemui.statusbar.phone.QSTileHost;
40 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
41 import com.android.systemui.tuner.TunerService;
42 import com.android.systemui.tuner.TunerService.Tunable;
43 
44 import java.util.ArrayList;
45 import java.util.Collection;
46 
47 /** View that represents the quick settings tile panel. **/
48 public class QSPanel extends LinearLayout implements Tunable, Callback {
49 
50     public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
51 
52     protected final Context mContext;
53     protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
54     protected final View mBrightnessView;
55     private final H mHandler = new H();
56 
57     private int mPanelPaddingBottom;
58     private int mBrightnessPaddingTop;
59     protected boolean mExpanded;
60     protected boolean mListening;
61 
62     private Callback mCallback;
63     private BrightnessController mBrightnessController;
64     protected QSTileHost mHost;
65 
66     protected QSFooter mFooter;
67     private boolean mGridContentVisible = true;
68 
69     protected QSTileLayout mTileLayout;
70 
71     private QSCustomizer mCustomizePanel;
72     private Record mDetailRecord;
73 
74     private BrightnessMirrorController mBrightnessMirrorController;
75 
QSPanel(Context context)76     public QSPanel(Context context) {
77         this(context, null);
78     }
79 
QSPanel(Context context, AttributeSet attrs)80     public QSPanel(Context context, AttributeSet attrs) {
81         super(context, attrs);
82         mContext = context;
83 
84         setOrientation(VERTICAL);
85 
86         mBrightnessView = LayoutInflater.from(context).inflate(
87                 R.layout.quick_settings_brightness_dialog, this, false);
88         addView(mBrightnessView);
89 
90         setupTileLayout();
91 
92         mFooter = new QSFooter(this, context);
93         addView(mFooter.getView());
94 
95         updateResources();
96 
97         mBrightnessController = new BrightnessController(getContext(),
98                 (ImageView) findViewById(R.id.brightness_icon),
99                 (ToggleSlider) findViewById(R.id.brightness_slider));
100 
101     }
102 
setupTileLayout()103     protected void setupTileLayout() {
104         mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
105                 R.layout.qs_paged_tile_layout, this, false);
106         mTileLayout.setListening(mListening);
107         addView((View) mTileLayout);
108     }
109 
isShowingCustomize()110     public boolean isShowingCustomize() {
111         return mCustomizePanel != null && mCustomizePanel.isCustomizing();
112     }
113 
114     @Override
onAttachedToWindow()115     protected void onAttachedToWindow() {
116         super.onAttachedToWindow();
117         TunerService.get(mContext).addTunable(this, QS_SHOW_BRIGHTNESS);
118         if (mHost != null) {
119             setTiles(mHost.getTiles());
120         }
121     }
122 
123     @Override
onDetachedFromWindow()124     protected void onDetachedFromWindow() {
125         TunerService.get(mContext).removeTunable(this);
126         mHost.removeCallback(this);
127         for (TileRecord record : mRecords) {
128             record.tile.removeCallbacks();
129         }
130         super.onDetachedFromWindow();
131     }
132 
133     @Override
onTilesChanged()134     public void onTilesChanged() {
135         setTiles(mHost.getTiles());
136     }
137 
138     @Override
onTuningChanged(String key, String newValue)139     public void onTuningChanged(String key, String newValue) {
140         if (QS_SHOW_BRIGHTNESS.equals(key)) {
141             mBrightnessView.setVisibility(newValue == null || Integer.parseInt(newValue) != 0
142                     ? VISIBLE : GONE);
143         }
144     }
145 
openDetails(String subPanel)146     public void openDetails(String subPanel) {
147         QSTile<?> tile = getTile(subPanel);
148         showDetailAdapter(true, tile.getDetailAdapter(), new int[] {getWidth() / 2, 0});
149     }
150 
getTile(String subPanel)151     private QSTile<?> getTile(String subPanel) {
152         for (int i = 0; i < mRecords.size(); i++) {
153             if (subPanel.equals(mRecords.get(i).tile.getTileSpec())) {
154                 return mRecords.get(i).tile;
155             }
156         }
157         return mHost.createTile(subPanel);
158     }
159 
setBrightnessMirror(BrightnessMirrorController c)160     public void setBrightnessMirror(BrightnessMirrorController c) {
161         mBrightnessMirrorController = c;
162         ToggleSlider brightnessSlider = (ToggleSlider) findViewById(R.id.brightness_slider);
163         ToggleSlider mirror = (ToggleSlider) c.getMirror().findViewById(R.id.brightness_slider);
164         brightnessSlider.setMirror(mirror);
165         brightnessSlider.setMirrorController(c);
166     }
167 
getBrightnessView()168     View getBrightnessView() {
169         return mBrightnessView;
170     }
171 
setCallback(Callback callback)172     public void setCallback(Callback callback) {
173         mCallback = callback;
174     }
175 
setHost(QSTileHost host, QSCustomizer customizer)176     public void setHost(QSTileHost host, QSCustomizer customizer) {
177         mHost = host;
178         mHost.addCallback(this);
179         setTiles(mHost.getTiles());
180         mFooter.setHost(host);
181         mCustomizePanel = customizer;
182         if (mCustomizePanel != null) {
183             mCustomizePanel.setHost(mHost);
184         }
185         mBrightnessController.setBackgroundLooper(host.getLooper());
186     }
187 
getHost()188     public QSTileHost getHost() {
189         return mHost;
190     }
191 
updateResources()192     public void updateResources() {
193         final Resources res = mContext.getResources();
194         mPanelPaddingBottom = res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom);
195         mBrightnessPaddingTop = res.getDimensionPixelSize(R.dimen.qs_brightness_padding_top);
196         setPadding(0, mBrightnessPaddingTop, 0, mPanelPaddingBottom);
197         for (TileRecord r : mRecords) {
198             r.tile.clearState();
199         }
200         if (mListening) {
201             refreshAllTiles();
202         }
203         if (mTileLayout != null) {
204             mTileLayout.updateResources();
205         }
206     }
207 
208     @Override
onConfigurationChanged(Configuration newConfig)209     protected void onConfigurationChanged(Configuration newConfig) {
210         super.onConfigurationChanged(newConfig);
211         mFooter.onConfigurationChanged();
212 
213         if (mBrightnessMirrorController != null) {
214             // Reload the mirror in case it got reinflated but we didn't.
215             setBrightnessMirror(mBrightnessMirrorController);
216         }
217     }
218 
onCollapse()219     public void onCollapse() {
220         if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
221             mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
222         }
223     }
224 
setExpanded(boolean expanded)225     public void setExpanded(boolean expanded) {
226         if (mExpanded == expanded) return;
227         mExpanded = expanded;
228         if (!mExpanded && mTileLayout instanceof PagedTileLayout) {
229             ((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
230         }
231         MetricsLogger.visibility(mContext, MetricsEvent.QS_PANEL, mExpanded);
232         if (!mExpanded) {
233             closeDetail();
234         } else {
235             logTiles();
236         }
237     }
238 
setListening(boolean listening)239     public void setListening(boolean listening) {
240         if (mListening == listening) return;
241         mListening = listening;
242         if (mTileLayout != null) {
243             mTileLayout.setListening(listening);
244         }
245         mFooter.setListening(mListening);
246         if (mListening) {
247             refreshAllTiles();
248         }
249         if (mBrightnessView.getVisibility() == View.VISIBLE) {
250             if (listening) {
251                 mBrightnessController.registerCallbacks();
252             } else {
253                 mBrightnessController.unregisterCallbacks();
254             }
255         }
256     }
257 
refreshAllTiles()258     public void refreshAllTiles() {
259         for (TileRecord r : mRecords) {
260             r.tile.refreshState();
261         }
262         mFooter.refreshState();
263     }
264 
showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow)265     public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) {
266         int xInWindow = locationInWindow[0];
267         int yInWindow = locationInWindow[1];
268         ((View) getParent()).getLocationInWindow(locationInWindow);
269 
270         Record r = new Record();
271         r.detailAdapter = adapter;
272         r.x = xInWindow - locationInWindow[0];
273         r.y = yInWindow - locationInWindow[1];
274 
275         locationInWindow[0] = xInWindow;
276         locationInWindow[1] = yInWindow;
277 
278         showDetail(show, r);
279     }
280 
showDetail(boolean show, Record r)281     protected void showDetail(boolean show, Record r) {
282         mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
283     }
284 
setTiles(Collection<QSTile<?>> tiles)285     public void setTiles(Collection<QSTile<?>> tiles) {
286         setTiles(tiles, false);
287     }
288 
setTiles(Collection<QSTile<?>> tiles, boolean collapsedView)289     public void setTiles(Collection<QSTile<?>> tiles, boolean collapsedView) {
290         for (TileRecord record : mRecords) {
291             mTileLayout.removeTile(record);
292             record.tile.removeCallback(record.callback);
293         }
294         mRecords.clear();
295         for (QSTile<?> tile : tiles) {
296             addTile(tile, collapsedView);
297         }
298     }
299 
drawTile(TileRecord r, QSTile.State state)300     protected void drawTile(TileRecord r, QSTile.State state) {
301         r.tileView.onStateChanged(state);
302     }
303 
createTileView(QSTile<?> tile, boolean collapsedView)304     protected QSTileBaseView createTileView(QSTile<?> tile, boolean collapsedView) {
305         return new QSTileView(mContext, tile.createTileView(mContext), collapsedView);
306     }
307 
shouldShowDetail()308     protected boolean shouldShowDetail() {
309         return mExpanded;
310     }
311 
addTile(final QSTile<?> tile, boolean collapsedView)312     protected void addTile(final QSTile<?> tile, boolean collapsedView) {
313         final TileRecord r = new TileRecord();
314         r.tile = tile;
315         r.tileView = createTileView(tile, collapsedView);
316         final QSTile.Callback callback = new QSTile.Callback() {
317             @Override
318             public void onStateChanged(QSTile.State state) {
319                 drawTile(r, state);
320             }
321 
322             @Override
323             public void onShowDetail(boolean show) {
324                 // Both the collapsed and full QS panels get this callback, this check determines
325                 // which one should handle showing the detail.
326                 if (shouldShowDetail()) {
327                     QSPanel.this.showDetail(show, r);
328                 }
329             }
330 
331             @Override
332             public void onToggleStateChanged(boolean state) {
333                 if (mDetailRecord == r) {
334                     fireToggleStateChanged(state);
335                 }
336             }
337 
338             @Override
339             public void onScanStateChanged(boolean state) {
340                 r.scanState = state;
341                 if (mDetailRecord == r) {
342                     fireScanStateChanged(r.scanState);
343                 }
344             }
345 
346             @Override
347             public void onAnnouncementRequested(CharSequence announcement) {
348                 announceForAccessibility(announcement);
349             }
350         };
351         r.tile.addCallback(callback);
352         r.callback = callback;
353         final View.OnClickListener click = new View.OnClickListener() {
354             @Override
355             public void onClick(View v) {
356                 onTileClick(r.tile);
357             }
358         };
359         final View.OnLongClickListener longClick = new View.OnLongClickListener() {
360             @Override
361             public boolean onLongClick(View v) {
362                 r.tile.longClick();
363                 return true;
364             }
365         };
366         r.tileView.init(click, longClick);
367         r.tile.refreshState();
368         mRecords.add(r);
369 
370         if (mTileLayout != null) {
371             mTileLayout.addTile(r);
372         }
373     }
374 
375 
showEdit(final View v)376     public void showEdit(final View v) {
377         v.post(new Runnable() {
378             @Override
379             public void run() {
380                 if (mCustomizePanel != null) {
381                     if (!mCustomizePanel.isCustomizing()) {
382                         int[] loc = new int[2];
383                         v.getLocationInWindow(loc);
384                         int x = loc[0] + v.getWidth() / 2;
385                         int y = loc[1] + v.getHeight() / 2;
386                         mCustomizePanel.show(x, y);
387                     }
388                 }
389 
390             }
391         });
392     }
393 
onTileClick(QSTile<?> tile)394     protected void onTileClick(QSTile<?> tile) {
395         tile.click();
396     }
397 
closeDetail()398     public void closeDetail() {
399         if (mCustomizePanel != null && mCustomizePanel.isCustomizing()) {
400             // Treat this as a detail panel for now, to make things easy.
401             mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
402             return;
403         }
404         showDetail(false, mDetailRecord);
405     }
406 
getGridHeight()407     public int getGridHeight() {
408         return getMeasuredHeight();
409     }
410 
handleShowDetail(Record r, boolean show)411     protected void handleShowDetail(Record r, boolean show) {
412         if (r instanceof TileRecord) {
413             handleShowDetailTile((TileRecord) r, show);
414         } else {
415             int x = 0;
416             int y = 0;
417             if (r != null) {
418                 x = r.x;
419                 y = r.y;
420             }
421             handleShowDetailImpl(r, show, x, y);
422         }
423     }
424 
handleShowDetailTile(TileRecord r, boolean show)425     private void handleShowDetailTile(TileRecord r, boolean show) {
426         if ((mDetailRecord != null) == show && mDetailRecord == r) return;
427 
428         if (show) {
429             r.detailAdapter = r.tile.getDetailAdapter();
430             if (r.detailAdapter == null) return;
431         }
432         r.tile.setDetailListening(show);
433         int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
434         int y = r.tileView.getTop() + mTileLayout.getOffsetTop(r) + r.tileView.getHeight() / 2
435                 + getTop();
436         handleShowDetailImpl(r, show, x, y);
437     }
438 
handleShowDetailImpl(Record r, boolean show, int x, int y)439     private void handleShowDetailImpl(Record r, boolean show, int x, int y) {
440         setDetailRecord(show ? r : null);
441         fireShowingDetail(show ? r.detailAdapter : null, x, y);
442     }
443 
setDetailRecord(Record r)444     private void setDetailRecord(Record r) {
445         if (r == mDetailRecord) return;
446         mDetailRecord = r;
447         final boolean scanState = mDetailRecord instanceof TileRecord
448                 && ((TileRecord) mDetailRecord).scanState;
449         fireScanStateChanged(scanState);
450     }
451 
setGridContentVisibility(boolean visible)452     void setGridContentVisibility(boolean visible) {
453         int newVis = visible ? VISIBLE : INVISIBLE;
454         setVisibility(newVis);
455         if (mGridContentVisible != visible) {
456             MetricsLogger.visibility(mContext, MetricsEvent.QS_PANEL, newVis);
457         }
458         mGridContentVisible = visible;
459     }
460 
logTiles()461     private void logTiles() {
462         for (int i = 0; i < mRecords.size(); i++) {
463             TileRecord tileRecord = mRecords.get(i);
464             MetricsLogger.visible(mContext, tileRecord.tile.getMetricsCategory());
465         }
466     }
467 
fireShowingDetail(DetailAdapter detail, int x, int y)468     private void fireShowingDetail(DetailAdapter detail, int x, int y) {
469         if (mCallback != null) {
470             mCallback.onShowingDetail(detail, x, y);
471         }
472     }
473 
fireToggleStateChanged(boolean state)474     private void fireToggleStateChanged(boolean state) {
475         if (mCallback != null) {
476             mCallback.onToggleStateChanged(state);
477         }
478     }
479 
fireScanStateChanged(boolean state)480     private void fireScanStateChanged(boolean state) {
481         if (mCallback != null) {
482             mCallback.onScanStateChanged(state);
483         }
484     }
485 
clickTile(ComponentName tile)486     public void clickTile(ComponentName tile) {
487         final String spec = CustomTile.toSpec(tile);
488         final int N = mRecords.size();
489         for (int i = 0; i < N; i++) {
490             if (mRecords.get(i).tile.getTileSpec().equals(spec)) {
491                 mRecords.get(i).tile.click();
492                 break;
493             }
494         }
495     }
496 
getTileLayout()497     QSTileLayout getTileLayout() {
498         return mTileLayout;
499     }
500 
getTileView(QSTile<?> tile)501     QSTileBaseView getTileView(QSTile<?> tile) {
502         for (TileRecord r : mRecords) {
503             if (r.tile == tile) {
504                 return r.tileView;
505             }
506         }
507         return null;
508     }
509 
getFooter()510     public QSFooter getFooter() {
511         return mFooter;
512     }
513 
514     private class H extends Handler {
515         private static final int SHOW_DETAIL = 1;
516         private static final int SET_TILE_VISIBILITY = 2;
517         @Override
handleMessage(Message msg)518         public void handleMessage(Message msg) {
519             if (msg.what == SHOW_DETAIL) {
520                 handleShowDetail((Record)msg.obj, msg.arg1 != 0);
521             }
522         }
523     }
524 
525     protected static class Record {
526         DetailAdapter detailAdapter;
527         int x;
528         int y;
529     }
530 
531     public static final class TileRecord extends Record {
532         public QSTile<?> tile;
533         public QSTileBaseView tileView;
534         public boolean scanState;
535         public QSTile.Callback callback;
536     }
537 
538     public interface Callback {
onShowingDetail(DetailAdapter detail, int x, int y)539         void onShowingDetail(DetailAdapter detail, int x, int y);
onToggleStateChanged(boolean state)540         void onToggleStateChanged(boolean state);
onScanStateChanged(boolean state)541         void onScanStateChanged(boolean state);
542     }
543 
544     public interface QSTileLayout {
addTile(TileRecord tile)545         void addTile(TileRecord tile);
removeTile(TileRecord tile)546         void removeTile(TileRecord tile);
getOffsetTop(TileRecord tile)547         int getOffsetTop(TileRecord tile);
updateResources()548         boolean updateResources();
549 
setListening(boolean listening)550         void setListening(boolean listening);
551     }
552 }
553