1 /*
2  * Copyright (C) 2015 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.Context;
20 import android.util.AttributeSet;
21 import android.view.Gravity;
22 import android.view.View;
23 import android.widget.LinearLayout;
24 import android.widget.Space;
25 
26 import com.android.systemui.Dependency;
27 import com.android.systemui.R;
28 import com.android.systemui.plugins.qs.*;
29 import com.android.systemui.plugins.qs.QSTile.SignalState;
30 import com.android.systemui.plugins.qs.QSTile.State;
31 import com.android.systemui.plugins.qs.QSTileView;
32 import com.android.systemui.qs.customize.QSCustomizer;
33 import com.android.systemui.tuner.TunerService;
34 import com.android.systemui.tuner.TunerService.Tunable;
35 
36 import java.util.ArrayList;
37 import java.util.Collection;
38 
39 /**
40  * Version of QSPanel that only shows N Quick Tiles in the QS Header.
41  */
42 public class QuickQSPanel extends QSPanel {
43 
44     public static final String NUM_QUICK_TILES = "sysui_qqs_count";
45 
46     private int mMaxTiles;
47     protected QSPanel mFullPanel;
48 
QuickQSPanel(Context context, AttributeSet attrs)49     public QuickQSPanel(Context context, AttributeSet attrs) {
50         super(context, attrs);
51         if (mFooter != null) {
52             removeView((View) mFooter.getView());
53         }
54         if (mTileLayout != null) {
55             for (int i = 0; i < mRecords.size(); i++) {
56                 mTileLayout.removeTile(mRecords.get(i));
57             }
58             removeView((View) mTileLayout);
59         }
60         mTileLayout = new HeaderTileLayout(context);
61         mTileLayout.setListening(mListening);
62         addView((View) mTileLayout, 0 /* Between brightness and footer */);
63         super.setPadding(0, 0, 0, 0);
64     }
65 
66     @Override
setPadding(int left, int top, int right, int bottom)67     public void setPadding(int left, int top, int right, int bottom) {
68         // Always have no padding.
69     }
70 
71     @Override
addDivider()72     protected void addDivider() {
73     }
74 
75     @Override
onAttachedToWindow()76     protected void onAttachedToWindow() {
77         super.onAttachedToWindow();
78         Dependency.get(TunerService.class).addTunable(mNumTiles, NUM_QUICK_TILES);
79     }
80 
81     @Override
onDetachedFromWindow()82     protected void onDetachedFromWindow() {
83         super.onDetachedFromWindow();
84         Dependency.get(TunerService.class).removeTunable(mNumTiles);
85     }
86 
setQSPanelAndHeader(QSPanel fullPanel, View header)87     public void setQSPanelAndHeader(QSPanel fullPanel, View header) {
88         mFullPanel = fullPanel;
89     }
90 
91     @Override
shouldShowDetail()92     protected boolean shouldShowDetail() {
93         return !mExpanded;
94     }
95 
96     @Override
drawTile(TileRecord r, State state)97     protected void drawTile(TileRecord r, State state) {
98         if (state instanceof SignalState) {
99             SignalState copy = new SignalState();
100             state.copyTo(copy);
101             // No activity shown in the quick panel.
102             copy.activityIn = false;
103             copy.activityOut = false;
104             state = copy;
105         }
106         super.drawTile(r, state);
107     }
108 
109     @Override
setHost(QSTileHost host, QSCustomizer customizer)110     public void setHost(QSTileHost host, QSCustomizer customizer) {
111         super.setHost(host, customizer);
112         setTiles(mHost.getTiles());
113     }
114 
setMaxTiles(int maxTiles)115     public void setMaxTiles(int maxTiles) {
116         mMaxTiles = maxTiles;
117         if (mHost != null) {
118             setTiles(mHost.getTiles());
119         }
120     }
121 
122     @Override
onTuningChanged(String key, String newValue)123     public void onTuningChanged(String key, String newValue) {
124         // No tunings for you.
125         if (key.equals(QS_SHOW_BRIGHTNESS)) {
126             // No Brightness for you.
127             super.onTuningChanged(key, "0");
128         }
129     }
130 
131     @Override
setTiles(Collection<QSTile> tiles)132     public void setTiles(Collection<QSTile> tiles) {
133         ArrayList<QSTile> quickTiles = new ArrayList<>();
134         for (QSTile tile : tiles) {
135             quickTiles.add(tile);
136             if (quickTiles.size() == mMaxTiles) {
137                 break;
138             }
139         }
140         super.setTiles(quickTiles, true);
141     }
142 
143     private final Tunable mNumTiles = new Tunable() {
144         @Override
145         public void onTuningChanged(String key, String newValue) {
146             setMaxTiles(getNumQuickTiles(mContext));
147         }
148     };
149 
getNumQuickTiles(Context context)150     public static int getNumQuickTiles(Context context) {
151         return Dependency.get(TunerService.class).getValue(NUM_QUICK_TILES, 6);
152     }
153 
154     private static class HeaderTileLayout extends LinearLayout implements QSTileLayout {
155 
156         protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
157         private boolean mListening;
158 
HeaderTileLayout(Context context)159         public HeaderTileLayout(Context context) {
160             super(context);
161             setClipChildren(false);
162             setClipToPadding(false);
163             setGravity(Gravity.CENTER_VERTICAL);
164             setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
165         }
166 
167         @Override
setListening(boolean listening)168         public void setListening(boolean listening) {
169             if (mListening == listening) return;
170             mListening = listening;
171             for (TileRecord record : mRecords) {
172                 record.tile.setListening(this, mListening);
173             }
174         }
175 
176         @Override
addTile(TileRecord tile)177         public void addTile(TileRecord tile) {
178             if (getChildCount() != 0) {
179                 // Add a spacer.
180                 addView(new Space(mContext), getChildCount(), generateSpaceParams());
181             }
182             addView(tile.tileView, getChildCount(), generateLayoutParams());
183             mRecords.add(tile);
184             tile.tile.setListening(this, mListening);
185         }
186 
generateSpaceParams()187         private LayoutParams generateSpaceParams() {
188             int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
189             LayoutParams lp = new LayoutParams(0, size);
190             lp.weight = 1;
191             lp.gravity = Gravity.CENTER;
192             return lp;
193         }
194 
generateLayoutParams()195         private LayoutParams generateLayoutParams() {
196             int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
197             LayoutParams lp = new LayoutParams(size, size);
198             lp.gravity = Gravity.CENTER;
199             return lp;
200         }
201 
202         @Override
removeTile(TileRecord tile)203         public void removeTile(TileRecord tile) {
204             int childIndex = getChildIndex(tile.tileView);
205             // Remove the tile.
206             removeViewAt(childIndex);
207             if (getChildCount() != 0) {
208                 // Remove its spacer as well.
209                 removeViewAt(childIndex);
210             }
211             mRecords.remove(tile);
212             tile.tile.setListening(this, false);
213         }
214 
getChildIndex(QSTileView tileView)215         private int getChildIndex(QSTileView tileView) {
216             final int N = getChildCount();
217             for (int i = 0; i < N; i++) {
218                 if (getChildAt(i) == tileView) {
219                     return i;
220                 }
221             }
222             return -1;
223         }
224 
225         @Override
getOffsetTop(TileRecord tile)226         public int getOffsetTop(TileRecord tile) {
227             return 0;
228         }
229 
230         @Override
updateResources()231         public boolean updateResources() {
232             // No resources here.
233             return false;
234         }
235 
236         @Override
hasOverlappingRendering()237         public boolean hasOverlappingRendering() {
238             return false;
239         }
240 
241         @Override
onMeasure(int widthMeasureSpec, int heightMeasureSpec)242         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
243             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
244             if (mRecords != null && mRecords.size() > 0) {
245                 View previousView = this;
246                 for (TileRecord record : mRecords) {
247                     if (record.tileView.getVisibility() == GONE) continue;
248                     previousView = record.tileView.updateAccessibilityOrder(previousView);
249                 }
250                 mRecords.get(0).tileView.setAccessibilityTraversalAfter(
251                         R.id.alarm_status_collapsed);
252                 mRecords.get(mRecords.size() - 1).tileView.setAccessibilityTraversalBefore(
253                         R.id.expand_indicator);
254             }
255         }
256     }
257 }
258