OSDN Git Service

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