blob: d377f1c793a9b10ba331f2fb84e4cfe4f6fbc55c [file] [log] [blame]
Jason Monkc34befb2015-10-07 16:40:02 -04001/*
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
17package com.android.systemui.qs;
18
Fabian Kozynski00d494d2019-04-04 09:53:50 -040019import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
20
Jason Monkc34befb2015-10-07 16:40:02 -040021import android.content.Context;
Rohan Shah14394642018-03-13 20:10:12 -070022import android.content.res.Configuration;
Fabian Kozynski802279f2018-09-07 13:44:54 -040023import android.graphics.Rect;
Jason Monkc34befb2015-10-07 16:40:02 -040024import android.util.AttributeSet;
Jason Monkc133d262015-10-27 12:32:45 -040025import android.view.Gravity;
Jason Monkc34befb2015-10-07 16:40:02 -040026import android.view.View;
Jason Monkc34befb2015-10-07 16:40:02 -040027import android.widget.LinearLayout;
Julia Reynolds20aef8a2016-05-04 16:44:08 -040028
Jason Monkde850bb2017-02-01 19:26:30 -050029import com.android.systemui.Dependency;
Fabian Kozynski00d494d2019-04-04 09:53:50 -040030import com.android.systemui.DumpController;
Jason Monkc34befb2015-10-07 16:40:02 -040031import com.android.systemui.R;
Charles Hece2a7c02017-10-11 20:25:20 +010032import com.android.systemui.plugins.qs.QSTile;
Jason Monk702e2eb2017-03-03 16:53:44 -050033import com.android.systemui.plugins.qs.QSTile.SignalState;
34import com.android.systemui.plugins.qs.QSTile.State;
Jason Monka9df9992016-03-15 12:41:13 -040035import com.android.systemui.qs.customize.QSCustomizer;
Jason Monk8fb77872016-03-03 16:39:42 -050036import com.android.systemui.tuner.TunerService;
37import com.android.systemui.tuner.TunerService.Tunable;
Matt Pietal5a19cb62019-10-30 12:31:07 -040038import com.android.systemui.util.Utils;
Jason Monkc34befb2015-10-07 16:40:02 -040039
40import java.util.ArrayList;
41import java.util.Collection;
42
Fabian Kozynski00d494d2019-04-04 09:53:50 -040043import javax.inject.Inject;
44import javax.inject.Named;
45
Jason Monkc34befb2015-10-07 16:40:02 -040046/**
Jason Monkdc35dcb2015-12-04 16:36:15 -050047 * Version of QSPanel that only shows N Quick Tiles in the QS Header.
Jason Monkc34befb2015-10-07 16:40:02 -040048 */
49public class QuickQSPanel extends QSPanel {
50
Jason Monk8fb77872016-03-03 16:39:42 -050051 public static final String NUM_QUICK_TILES = "sysui_qqs_count";
Fabian Kozynski802279f2018-09-07 13:44:54 -040052 private static final String TAG = "QuickQSPanel";
Fabian Kozynskic8d8f052019-09-09 09:15:04 -040053 // Start it at 6 so a non-zero value can be obtained statically.
54 private static int sDefaultMaxTiles = 6;
Jason Monk8fb77872016-03-03 16:39:42 -050055
Charles Hece2a7c02017-10-11 20:25:20 +010056 private boolean mDisabledByPolicy;
Jason Monkdc35dcb2015-12-04 16:36:15 -050057 private int mMaxTiles;
Muyuan Li0e9f5382016-04-27 15:51:15 -070058 protected QSPanel mFullPanel;
Beth Thibodeau07d20c32019-10-16 13:45:56 -040059 private QuickQSMediaPlayer mMediaPlayer;
Jason Monkdc35dcb2015-12-04 16:36:15 -050060
Fabian Kozynski00d494d2019-04-04 09:53:50 -040061 @Inject
62 public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
63 DumpController dumpController) {
64 super(context, attrs, dumpController);
phweisse3983312017-02-17 15:14:19 +010065 if (mFooter != null) {
Jason Monk231b0522018-01-04 10:49:55 -050066 removeView(mFooter.getView());
phweisse3983312017-02-17 15:14:19 +010067 }
Jason Monkc34befb2015-10-07 16:40:02 -040068 if (mTileLayout != null) {
69 for (int i = 0; i < mRecords.size(); i++) {
70 mTileLayout.removeTile(mRecords.get(i));
71 }
Amin Shaikh15781d62018-02-16 16:00:13 -050072 removeView((View) mTileLayout);
Jason Monkc34befb2015-10-07 16:40:02 -040073 }
Beth Thibodeau07d20c32019-10-16 13:45:56 -040074
Matt Pietal5a19cb62019-10-30 12:31:07 -040075 if (Utils.useQsMediaPlayer(context)) {
Beth Thibodeau07d20c32019-10-16 13:45:56 -040076 LinearLayout mHorizontalLinearLayout = new LinearLayout(mContext);
77 mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
78 mHorizontalLinearLayout.setClipChildren(false);
79 mHorizontalLinearLayout.setClipToPadding(false);
80
81 LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1);
82
83 mTileLayout = new DoubleLineTileLayout(context);
84 lp.setMarginEnd(10);
85 lp.setMarginStart(0);
86 mHorizontalLinearLayout.addView((View) mTileLayout, lp);
87
88 mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout);
89
90 lp.setMarginEnd(0);
91 lp.setMarginStart(10);
92 mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp);
93
94 sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
95
96 mTileLayout.setListening(mListening);
97 addView(mHorizontalLinearLayout, 0 /* Between brightness and footer */);
98 super.setPadding(0, 0, 0, 0);
99 } else {
100 sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns);
101 mTileLayout = new HeaderTileLayout(context);
102 mTileLayout.setListening(mListening);
103 addView((View) mTileLayout, 0 /* Between brightness and footer */);
104 super.setPadding(0, 0, 0, 0);
105 }
106 }
107
108 public QuickQSMediaPlayer getMediaPlayer() {
109 return mMediaPlayer;
Jason Monk7b3d4e42017-05-30 12:47:10 -0400110 }
111
112 @Override
113 public void setPadding(int left, int top, int right, int bottom) {
114 // Always have no padding.
Jason Monkc34befb2015-10-07 16:40:02 -0400115 }
116
Jason Monk51c444b2016-01-06 16:32:29 -0500117 @Override
Jason Monke5b770e2017-03-03 21:49:29 -0500118 protected void addDivider() {
119 }
120
121 @Override
Jason Monk8fb77872016-03-03 16:39:42 -0500122 protected void onAttachedToWindow() {
123 super.onAttachedToWindow();
Jason Monkde850bb2017-02-01 19:26:30 -0500124 Dependency.get(TunerService.class).addTunable(mNumTiles, NUM_QUICK_TILES);
Jason Monk8fb77872016-03-03 16:39:42 -0500125 }
126
127 @Override
128 protected void onDetachedFromWindow() {
129 super.onDetachedFromWindow();
Jason Monkde850bb2017-02-01 19:26:30 -0500130 Dependency.get(TunerService.class).removeTunable(mNumTiles);
Jason Monk8fb77872016-03-03 16:39:42 -0500131 }
132
Ned Burns66b6e372019-07-26 13:45:42 -0400133 @Override
134 protected String getDumpableTag() {
135 return TAG;
136 }
137
Jason Monkdc35dcb2015-12-04 16:36:15 -0500138 public void setQSPanelAndHeader(QSPanel fullPanel, View header) {
139 mFullPanel = fullPanel;
Jason Monkdc35dcb2015-12-04 16:36:15 -0500140 }
141
142 @Override
Jason Monkec87a872016-03-01 15:00:16 -0500143 protected void drawTile(TileRecord r, State state) {
144 if (state instanceof SignalState) {
Jason Monk702e2eb2017-03-03 16:53:44 -0500145 SignalState copy = new SignalState();
Jason Monkec87a872016-03-01 15:00:16 -0500146 state.copyTo(copy);
147 // No activity shown in the quick panel.
Jason Monk702e2eb2017-03-03 16:53:44 -0500148 copy.activityIn = false;
149 copy.activityOut = false;
Jason Monkec87a872016-03-01 15:00:16 -0500150 state = copy;
151 }
152 super.drawTile(r, state);
153 }
154
155 @Override
Jason Monka9df9992016-03-15 12:41:13 -0400156 public void setHost(QSTileHost host, QSCustomizer customizer) {
157 super.setHost(host, customizer);
158 setTiles(mHost.getTiles());
159 }
160
Jason Monkdc35dcb2015-12-04 16:36:15 -0500161 public void setMaxTiles(int maxTiles) {
162 mMaxTiles = maxTiles;
Jason Monka9df9992016-03-15 12:41:13 -0400163 if (mHost != null) {
164 setTiles(mHost.getTiles());
165 }
Jason Monkdc35dcb2015-12-04 16:36:15 -0500166 }
167
168 @Override
Jason Monkc34befb2015-10-07 16:40:02 -0400169 public void onTuningChanged(String key, String newValue) {
Rohan Shahd3cf7562018-02-23 11:12:28 -0800170 if (QS_SHOW_BRIGHTNESS.equals(key)) {
Rohan Shahdb2cfa32018-02-20 11:27:22 -0800171 // No Brightness or Tooltip for you!
Jason Monkc34befb2015-10-07 16:40:02 -0400172 super.onTuningChanged(key, "0");
173 }
174 }
175
176 @Override
Jason Monk702e2eb2017-03-03 16:53:44 -0500177 public void setTiles(Collection<QSTile> tiles) {
178 ArrayList<QSTile> quickTiles = new ArrayList<>();
179 for (QSTile tile : tiles) {
Jason Monkdc35dcb2015-12-04 16:36:15 -0500180 quickTiles.add(tile);
181 if (quickTiles.size() == mMaxTiles) {
Jason Monkc34befb2015-10-07 16:40:02 -0400182 break;
183 }
184 }
Jason Monkc59249f2016-06-17 10:19:43 -0400185 super.setTiles(quickTiles, true);
Jason Monkc34befb2015-10-07 16:40:02 -0400186 }
187
Jason Monk8fb77872016-03-03 16:39:42 -0500188 private final Tunable mNumTiles = new Tunable() {
189 @Override
190 public void onTuningChanged(String key, String newValue) {
Fabian Kozynskic8d8f052019-09-09 09:15:04 -0400191 setMaxTiles(parseNumTiles(newValue));
Jason Monk8fb77872016-03-03 16:39:42 -0500192 }
193 };
194
Fabian Kozynskic8d8f052019-09-09 09:15:04 -0400195 public int getNumQuickTiles() {
196 return mMaxTiles;
197 }
198
199 /**
200 * Parses the String setting into the number of tiles. Defaults to {@code mDefaultMaxTiles}
201 *
202 * @param numTilesValue value of the setting to parse
203 * @return parsed value of numTilesValue OR {@code mDefaultMaxTiles} on error
204 */
205 public static int parseNumTiles(String numTilesValue) {
206 try {
207 return Integer.parseInt(numTilesValue);
208 } catch (NumberFormatException e) {
209 // Couldn't read an int from the new setting value. Use default.
210 return sDefaultMaxTiles;
211 }
212 }
213
214 public static int getDefaultMaxTiles() {
215 return sDefaultMaxTiles;
Jason Monk8fb77872016-03-03 16:39:42 -0500216 }
217
Charles Hece2a7c02017-10-11 20:25:20 +0100218 void setDisabledByPolicy(boolean disabled) {
219 if (disabled != mDisabledByPolicy) {
220 mDisabledByPolicy = disabled;
221 setVisibility(disabled ? View.GONE : View.VISIBLE);
222 }
223 }
224
225 /**
226 * Sets the visibility of this {@link QuickQSPanel}. This method has no effect when this panel
227 * is disabled by policy through {@link #setDisabledByPolicy(boolean)}, and in this case the
228 * visibility will always be {@link View#GONE}. This method is called externally by
229 * {@link QSAnimator} only.
230 */
231 @Override
232 public void setVisibility(int visibility) {
233 if (mDisabledByPolicy) {
234 if (getVisibility() == View.GONE) {
235 return;
236 }
237 visibility = View.GONE;
238 }
239 super.setVisibility(visibility);
240 }
241
Fabian Kozynski802279f2018-09-07 13:44:54 -0400242 private static class HeaderTileLayout extends TileLayout {
Jason Monkc34befb2015-10-07 16:40:02 -0400243
Jason Monk1bec6af2016-05-31 15:40:58 -0400244 private boolean mListening;
Fabian Kozynskidc23ef62019-04-22 14:30:59 -0400245 private Rect mClippingBounds = new Rect();
Jason Monkfece2ab2016-02-04 11:41:18 -0500246
Jason Monkc34befb2015-10-07 16:40:02 -0400247 public HeaderTileLayout(Context context) {
248 super(context);
Jason Monkdc35dcb2015-12-04 16:36:15 -0500249 setClipChildren(false);
250 setClipToPadding(false);
Fabian Kozynski7a4e3312019-04-24 11:10:21 -0400251 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
252 LayoutParams.MATCH_PARENT);
253 lp.gravity = Gravity.CENTER_HORIZONTAL;
254 setLayoutParams(lp);
Jason Monkfece2ab2016-02-04 11:41:18 -0500255 }
256
257 @Override
Rohan Shah14394642018-03-13 20:10:12 -0700258 protected void onConfigurationChanged(Configuration newConfig) {
259 super.onConfigurationChanged(newConfig);
Fabian Kozynski802279f2018-09-07 13:44:54 -0400260 updateResources();
261 }
262
263 @Override
264 public void onFinishInflate(){
265 updateResources();
Amin Shaikh6a298d62018-06-18 16:44:14 -0400266 }
Rohan Shah14394642018-03-13 20:10:12 -0700267
Fabian Kozynski52728302018-09-07 12:45:59 +0000268 private LayoutParams generateTileLayoutParams() {
Fabian Kozynski802279f2018-09-07 13:44:54 -0400269 LayoutParams lp = new LayoutParams(mCellWidth, mCellHeight);
Fabian Kozynski52728302018-09-07 12:45:59 +0000270 return lp;
271 }
272
273 @Override
Fabian Kozynski802279f2018-09-07 13:44:54 -0400274 protected void addTileView(TileRecord tile) {
275 addView(tile.tileView, getChildCount(), generateTileLayoutParams());
Fabian Kozynski52728302018-09-07 12:45:59 +0000276 }
277
278 @Override
Fabian Kozynski802279f2018-09-07 13:44:54 -0400279 protected void onLayout(boolean changed, int l, int t, int r, int b) {
280 // We only care about clipping on the right side
Fabian Kozynskidc23ef62019-04-22 14:30:59 -0400281 mClippingBounds.set(0, 0, r - l, 10000);
282 setClipBounds(mClippingBounds);
Fabian Kozynski802279f2018-09-07 13:44:54 -0400283
284 calculateColumns();
285
286 for (int i = 0; i < mRecords.size(); i++) {
287 mRecords.get(i).tileView.setVisibility( i < mColumns ? View.VISIBLE : View.GONE);
288 }
289
290 setAccessibilityOrder();
291 layoutTileRecords(mColumns);
Jason Monkc34befb2015-10-07 16:40:02 -0400292 }
293
294 @Override
Jason Monk9d02a432016-01-20 16:33:46 -0500295 public boolean updateResources() {
Fabian Kozynski802279f2018-09-07 13:44:54 -0400296 mCellWidth = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
297 mCellHeight = mCellWidth;
298
Jason Monk9d02a432016-01-20 16:33:46 -0500299 return false;
Jason Monkc34befb2015-10-07 16:40:02 -0400300 }
Jason Monkc5bdafb2016-02-25 16:24:21 -0500301
Fabian Kozynski802279f2018-09-07 13:44:54 -0400302 private boolean calculateColumns() {
303 int prevNumColumns = mColumns;
304 int maxTiles = mRecords.size();
Fabian Kozynski40c9f0d2018-08-29 16:21:42 -0400305
Fabian Kozynski802279f2018-09-07 13:44:54 -0400306 if (maxTiles == 0){ // Early return during setup
307 mColumns = 0;
308 return true;
Fabian Kozynski52728302018-09-07 12:45:59 +0000309 }
310
Fabian Kozynski802279f2018-09-07 13:44:54 -0400311 final int availableWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
Fabian Kozynskidc23ef62019-04-22 14:30:59 -0400312 final int leftoverWhitespace = availableWidth - maxTiles * mCellWidth;
Fabian Kozynski1967f7d2018-10-02 09:18:52 -0400313 final int smallestHorizontalMarginNeeded;
Fabian Kozynskidc23ef62019-04-22 14:30:59 -0400314 smallestHorizontalMarginNeeded = leftoverWhitespace / Math.max(1, maxTiles - 1);
Fabian Kozynski802279f2018-09-07 13:44:54 -0400315
316 if (smallestHorizontalMarginNeeded > 0){
317 mCellMarginHorizontal = smallestHorizontalMarginNeeded;
318 mColumns = maxTiles;
319 } else{
320 mColumns = mCellWidth == 0 ? 1 :
321 Math.min(maxTiles, availableWidth / mCellWidth );
Fabian Kozynski4372f7c2019-11-11 12:16:27 -0500322 // If we can only fit one column, use mCellMarginHorizontal to center it.
323 if (mColumns == 1) {
324 mCellMarginHorizontal = (availableWidth - mCellWidth) / 2;
325 } else {
326 mCellMarginHorizontal =
327 (availableWidth - mColumns * mCellWidth) / (mColumns - 1);
328 }
329
Fabian Kozynski802279f2018-09-07 13:44:54 -0400330 }
331 return mColumns != prevNumColumns;
332 }
333
334 private void setAccessibilityOrder() {
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400335 if (mRecords != null && mRecords.size() > 0) {
336 View previousView = this;
337 for (TileRecord record : mRecords) {
338 if (record.tileView.getVisibility() == GONE) continue;
339 previousView = record.tileView.updateAccessibilityOrder(previousView);
340 }
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400341 mRecords.get(mRecords.size() - 1).tileView.setAccessibilityTraversalBefore(
342 R.id.expand_indicator);
343 }
344 }
Amin Shaikh6a298d62018-06-18 16:44:14 -0400345
Fabian Kozynski802279f2018-09-07 13:44:54 -0400346 @Override
347 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
348 // Measure each QS tile.
349 for (TileRecord record : mRecords) {
350 if (record.tileView.getVisibility() == GONE) continue;
351 record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
Amin Shaikh6a298d62018-06-18 16:44:14 -0400352 }
Fabian Kozynski802279f2018-09-07 13:44:54 -0400353
354 int height = mCellHeight;
355 if (height < 0) height = 0;
356
357 setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), height);
358 }
359
360 @Override
361 public int getNumVisibleTiles() {
362 return mColumns;
363 }
364
365 @Override
366 protected int getColumnStart(int column) {
Fabian Kozynski4372f7c2019-11-11 12:16:27 -0500367 if (mColumns == 1) {
368 // Only one column/tile. Use the margin to center the tile.
369 return getPaddingStart() + mCellMarginHorizontal;
370 }
Fabian Kozynski802279f2018-09-07 13:44:54 -0400371 return getPaddingStart() + column * (mCellWidth + mCellMarginHorizontal);
Amin Shaikh6a298d62018-06-18 16:44:14 -0400372 }
Jason Monkc34befb2015-10-07 16:40:02 -0400373 }
374}