blob: 40c8aadfd5d4d23f92187f04272864c6a5005d8d [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
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
17package com.android.systemui.qs;
18
Jason Monke5b770e2017-03-03 21:49:29 -050019import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
Fabian Kozynski00d494d2019-04-04 09:53:50 -040020import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
Matt Pietal5a19cb62019-10-30 12:31:07 -040021import static com.android.systemui.util.Utils.useQsMediaPlayer;
Jason Monke5b770e2017-03-03 21:49:29 -050022
Rohan Shahdb2cfa32018-02-20 11:27:22 -080023import android.annotation.Nullable;
Jason Monk7e53f202016-01-28 10:40:20 -050024import android.content.ComponentName;
John Spurlockaf8d6c42014-05-07 17:49:08 -040025import android.content.Context;
Jorim Jaggie17c4b42014-08-26 17:27:31 +020026import android.content.res.Configuration;
John Spurlock4bf31982014-05-21 13:04:22 -040027import android.content.res.Resources;
Beth Thibodeau07d20c32019-10-16 13:45:56 -040028import android.graphics.drawable.Icon;
29import android.media.session.MediaSession;
Jason Monkcb4b31d2017-05-03 10:37:34 -040030import android.metrics.LogMaker;
Fabian Kozynski407ddb22018-10-03 15:04:56 -040031import android.os.Bundle;
John Spurlockaf8d6c42014-05-07 17:49:08 -040032import android.os.Handler;
33import android.os.Message;
Beth Thibodeau07d20c32019-10-16 13:45:56 -040034import android.service.notification.StatusBarNotification;
Jason Monke5b770e2017-03-03 21:49:29 -050035import android.service.quicksettings.Tile;
John Spurlockaf8d6c42014-05-07 17:49:08 -040036import android.util.AttributeSet;
Beth Thibodeau07d20c32019-10-16 13:45:56 -040037import android.util.Log;
John Spurlock7f8f22a2014-07-02 18:54:17 -040038import android.view.LayoutInflater;
John Spurlockaf8d6c42014-05-07 17:49:08 -040039import android.view.View;
Beth Thibodeau07d20c32019-10-16 13:45:56 -040040import android.view.ViewGroup;
Beth Thibodeau07d20c32019-10-16 13:45:56 -040041import android.widget.HorizontalScrollView;
Jason Monk520ea062015-08-18 14:53:06 -040042import android.widget.LinearLayout;
Jason Monkcb4b31d2017-05-03 10:37:34 -040043
Chris Wren457a21c2015-05-06 17:50:34 -040044import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010045import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jason Monk824ffff2017-04-11 15:49:06 -040046import com.android.settingslib.Utils;
Fabian Kozynskicc4c6c32020-03-09 15:11:33 -040047import com.android.settingslib.bluetooth.LocalBluetoothManager;
48import com.android.settingslib.media.InfoMediaManager;
Beth Thibodeau69b1dfd2019-11-08 15:39:25 -050049import com.android.settingslib.media.LocalMediaManager;
Jason Monkde850bb2017-02-01 19:26:30 -050050import com.android.systemui.Dependency;
Fabian Kozynski00d494d2019-04-04 09:53:50 -040051import com.android.systemui.Dumpable;
John Spurlockaf8d6c42014-05-07 17:49:08 -040052import com.android.systemui.R;
Fabian Kozynski5ca7a512019-10-16 19:56:11 +000053import com.android.systemui.broadcast.BroadcastDispatcher;
Beth Thibodeau7b6c1782020-03-05 11:43:51 -050054import com.android.systemui.dagger.qualifiers.Background;
Beth Thibodeaua51c3142020-03-17 17:27:04 -040055import com.android.systemui.dagger.qualifiers.Main;
Ned Burnsaaeb44b2020-02-12 23:48:26 -050056import com.android.systemui.dump.DumpManager;
Jason Monke5b770e2017-03-03 21:49:29 -050057import com.android.systemui.plugins.qs.DetailAdapter;
Jason Monk702e2eb2017-03-03 16:53:44 -050058import com.android.systemui.plugins.qs.QSTile;
59import com.android.systemui.plugins.qs.QSTileView;
60import com.android.systemui.qs.QSHost.Callback;
Jason Monkbd6dbb02015-09-03 15:46:25 -040061import com.android.systemui.qs.customize.QSCustomizer;
Jason Monk7e53f202016-01-28 10:40:20 -050062import com.android.systemui.qs.external.CustomTile;
Fabian Kozynskica30f572019-10-24 10:01:04 -040063import com.android.systemui.qs.logging.QSLogger;
Jorim Jaggi3f48f462014-07-08 16:53:29 +020064import com.android.systemui.settings.BrightnessController;
Muyuan Li3b0f8922016-04-18 19:27:29 -070065import com.android.systemui.settings.ToggleSliderView;
Beth Thibodeau7b6c1782020-03-05 11:43:51 -050066import com.android.systemui.statusbar.NotificationMediaManager;
Adrian Roos5fd872e2014-08-12 17:28:58 +020067import com.android.systemui.statusbar.policy.BrightnessMirrorController;
Adrian Roosb01793f2017-08-09 14:34:55 +020068import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
Jason Monkcaf37622015-08-18 12:33:50 -040069import com.android.systemui.tuner.TunerService;
70import com.android.systemui.tuner.TunerService.Tunable;
Robert Snoeberger7dffd372020-04-01 17:32:44 -040071import com.android.systemui.util.concurrency.DelayableExecutor;
John Spurlockaf8d6c42014-05-07 17:49:08 -040072
Fabian Kozynski00d494d2019-04-04 09:53:50 -040073import java.io.FileDescriptor;
74import java.io.PrintWriter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040075import java.util.ArrayList;
John Spurlockbceed062014-08-10 18:04:16 -040076import java.util.Collection;
Beth Thibodeau7b6c1782020-03-05 11:43:51 -050077import java.util.concurrent.Executor;
Fabian Kozynskica30f572019-10-24 10:01:04 -040078import java.util.stream.Collectors;
John Spurlockaf8d6c42014-05-07 17:49:08 -040079
Fabian Kozynski00d494d2019-04-04 09:53:50 -040080import javax.inject.Inject;
81import javax.inject.Named;
82
Rohan Shahd3cf7562018-02-23 11:12:28 -080083/** View that represents the quick settings tile panel (when expanded/pulled down). **/
Fabian Kozynski00d494d2019-04-04 09:53:50 -040084public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener,
85 Dumpable {
Jason Monkcaf37622015-08-18 12:33:50 -040086
87 public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
Rohan Shahd3cf7562018-02-23 11:12:28 -080088 public static final String QS_SHOW_HEADER = "qs_show_header";
John Spurlockaf8d6c42014-05-07 17:49:08 -040089
Fabian Kozynski0db39af2018-08-31 15:56:45 -040090 private static final String TAG = "QSPanel";
91
Jason Monkbd6dbb02015-09-03 15:46:25 -040092 protected final Context mContext;
Amin Shaikha07a17b2018-02-23 16:02:52 -050093 protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
Fabian Kozynskica30f572019-10-24 10:01:04 -040094 private String mCachedSpecs = "";
Jason Monk11a77442015-05-12 19:29:02 -040095 protected final View mBrightnessView;
John Spurlockaf8d6c42014-05-07 17:49:08 -040096 private final H mHandler = new H();
Jason Monk8c09ac72017-03-16 11:53:40 -040097 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
Amin Shaikha07a17b2018-02-23 16:02:52 -050098 private final QSTileRevealController mQsTileRevealController;
John Spurlockaf8d6c42014-05-07 17:49:08 -040099
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400100 private final LinearLayout mMediaCarousel;
101 private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>();
Beth Thibodeau7b6c1782020-03-05 11:43:51 -0500102 private final NotificationMediaManager mNotificationMediaManager;
Fabian Kozynskicc4c6c32020-03-09 15:11:33 -0400103 private final LocalBluetoothManager mLocalBluetoothManager;
Beth Thibodeaua51c3142020-03-17 17:27:04 -0400104 private final Executor mForegroundExecutor;
Robert Snoeberger7dffd372020-04-01 17:32:44 -0400105 private final DelayableExecutor mBackgroundExecutor;
Beth Thibodeau8cb6d422020-01-31 14:57:09 -0500106 private boolean mUpdateCarousel = false;
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400107
Muyuan Li0e9f5382016-04-27 15:51:15 -0700108 protected boolean mExpanded;
Jason Monk1bec6af2016-05-31 15:40:58 -0400109 protected boolean mListening;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400110
Jason Monke5b770e2017-03-03 21:49:29 -0500111 private QSDetail.Callback mCallback;
Jorim Jaggi3f48f462014-07-08 16:53:29 +0200112 private BrightnessController mBrightnessController;
Ned Burnsaaeb44b2020-02-12 23:48:26 -0500113 private final DumpManager mDumpManager;
Fabian Kozynskica30f572019-10-24 10:01:04 -0400114 private final QSLogger mQSLogger;
Jason Monkbbadff82015-11-06 15:47:26 -0500115 protected QSTileHost mHost;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400116
Jason Monke5b770e2017-03-03 21:49:29 -0500117 protected QSSecurityFooter mFooter;
Rohan Shah3090e792018-04-12 00:01:00 -0400118 private PageIndicator mFooterPageIndicator;
Selim Cineke32010a2014-08-20 23:50:41 +0200119 private boolean mGridContentVisible = true;
Jason Monk3d5f5512014-07-25 11:17:28 -0400120
Jason Monkbd6dbb02015-09-03 15:46:25 -0400121 protected QSTileLayout mTileLayout;
122
123 private QSCustomizer mCustomizePanel;
Jason Monk377e7ad2016-02-16 14:03:21 -0500124 private Record mDetailRecord;
Jason Monk520ea062015-08-18 14:53:06 -0400125
Adrian Roos429c9622016-06-15 13:02:17 -0700126 private BrightnessMirrorController mBrightnessMirrorController;
Jason Monke5b770e2017-03-03 21:49:29 -0500127 private View mDivider;
Adrian Roos429c9622016-06-15 13:02:17 -0700128
Fabian Kozynski00d494d2019-04-04 09:53:50 -0400129 @Inject
Fabian Kozynskica30f572019-10-24 10:01:04 -0400130 public QSPanel(
131 @Named(VIEW_CONTEXT) Context context,
132 AttributeSet attrs,
Ned Burnsaaeb44b2020-02-12 23:48:26 -0500133 DumpManager dumpManager,
Fabian Kozynskica30f572019-10-24 10:01:04 -0400134 BroadcastDispatcher broadcastDispatcher,
Beth Thibodeau7b6c1782020-03-05 11:43:51 -0500135 QSLogger qsLogger,
136 NotificationMediaManager notificationMediaManager,
Beth Thibodeaua51c3142020-03-17 17:27:04 -0400137 @Main Executor foregroundExecutor,
Robert Snoeberger7dffd372020-04-01 17:32:44 -0400138 @Background DelayableExecutor backgroundExecutor,
Fabian Kozynskicc4c6c32020-03-09 15:11:33 -0400139 @Nullable LocalBluetoothManager localBluetoothManager
Fabian Kozynskica30f572019-10-24 10:01:04 -0400140 ) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400141 super(context, attrs);
142 mContext = context;
Fabian Kozynskica30f572019-10-24 10:01:04 -0400143 mQSLogger = qsLogger;
Ned Burnsaaeb44b2020-02-12 23:48:26 -0500144 mDumpManager = dumpManager;
Beth Thibodeau7b6c1782020-03-05 11:43:51 -0500145 mNotificationMediaManager = notificationMediaManager;
Beth Thibodeaua51c3142020-03-17 17:27:04 -0400146 mForegroundExecutor = foregroundExecutor;
Beth Thibodeau7b6c1782020-03-05 11:43:51 -0500147 mBackgroundExecutor = backgroundExecutor;
Fabian Kozynskicc4c6c32020-03-09 15:11:33 -0400148 mLocalBluetoothManager = localBluetoothManager;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400149
Jason Monk162011e2016-02-19 08:11:55 -0500150 setOrientation(VERTICAL);
Jason Monk520ea062015-08-18 14:53:06 -0400151
Jason Monk231b0522018-01-04 10:49:55 -0500152 mBrightnessView = LayoutInflater.from(mContext).inflate(
153 R.layout.quick_settings_brightness_dialog, this, false);
Amin Shaikh15781d62018-02-16 16:00:13 -0500154 addView(mBrightnessView);
155
156 mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
157 R.layout.qs_paged_tile_layout, this, false);
Fabian Kozynskica30f572019-10-24 10:01:04 -0400158 mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), mCachedSpecs);
Jason Monk231b0522018-01-04 10:49:55 -0500159 mTileLayout.setListening(mListening);
Amin Shaikh15781d62018-02-16 16:00:13 -0500160 addView((View) mTileLayout);
161
Amin Shaikha07a17b2018-02-23 16:02:52 -0500162 mQsTileRevealController = new QSTileRevealController(mContext, this,
Rohan Shah3090e792018-04-12 00:01:00 -0400163 (PagedTileLayout) mTileLayout);
Jason Monk32508852017-01-18 09:17:13 -0500164
Jason Monke5b770e2017-03-03 21:49:29 -0500165 addDivider();
166
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400167 // Add media carousel
Matt Pietal5a19cb62019-10-30 12:31:07 -0400168 if (useQsMediaPlayer(context)) {
Beth Thibodeau899b5952020-01-07 17:50:07 -0500169 HorizontalScrollView mediaScrollView = (HorizontalScrollView) LayoutInflater.from(
170 mContext).inflate(R.layout.media_carousel, this, false);
171 mMediaCarousel = mediaScrollView.findViewById(R.id.media_carousel);
Beth Thibodeau8cb6d422020-01-31 14:57:09 -0500172 addView(mediaScrollView, 0);
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400173 } else {
174 mMediaCarousel = null;
175 }
176
phweiss4f70f102017-04-12 19:32:55 +0200177 mFooter = new QSSecurityFooter(this, context);
178 addView(mFooter.getView());
179
John Spurlockaf8d6c42014-05-07 17:49:08 -0400180 updateResources();
Jorim Jaggi3f48f462014-07-08 16:53:29 +0200181
182 mBrightnessController = new BrightnessController(getContext(),
Fabian Kozynski5ca7a512019-10-16 19:56:11 +0000183 findViewById(R.id.brightness_slider), broadcastDispatcher);
Jason Monke5b770e2017-03-03 21:49:29 -0500184 }
185
Beth Thibodeau8cb6d422020-01-31 14:57:09 -0500186 @Override
187 public void onVisibilityAggregated(boolean isVisible) {
188 super.onVisibilityAggregated(isVisible);
189 if (!isVisible && mUpdateCarousel) {
190 for (QSMediaPlayer player : mMediaPlayers) {
191 if (player.isPlaying()) {
192 LayoutParams lp = (LayoutParams) player.getView().getLayoutParams();
193 mMediaCarousel.removeView(player.getView());
194 mMediaCarousel.addView(player.getView(), 0, lp);
195 ((HorizontalScrollView) mMediaCarousel.getParent()).fullScroll(View.FOCUS_LEFT);
196 mUpdateCarousel = false;
197 break;
198 }
199 }
200 }
201 }
202
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400203 /**
204 * Add or update a player for the associated media session
205 * @param token
206 * @param icon
207 * @param iconColor
208 * @param bgColor
209 * @param actionsContainer
210 * @param notif
Beth Thibodeaua3d90982020-04-13 23:42:48 -0400211 * @param key
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400212 */
213 public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor,
Beth Thibodeaua3d90982020-04-13 23:42:48 -0400214 View actionsContainer, StatusBarNotification notif, String key) {
Matt Pietal5a19cb62019-10-30 12:31:07 -0400215 if (!useQsMediaPlayer(mContext)) {
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400216 // Shouldn't happen, but just in case
217 Log.e(TAG, "Tried to add media session without player!");
218 return;
219 }
Beth Thibodeau69b1dfd2019-11-08 15:39:25 -0500220 if (token == null) {
221 Log.e(TAG, "Media session token was null!");
222 return;
223 }
224
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400225 QSMediaPlayer player = null;
226 String packageName = notif.getPackageName();
227 for (QSMediaPlayer p : mMediaPlayers) {
228 if (p.getMediaSessionToken().equals(token)) {
Beth Thibodeaua3d90982020-04-13 23:42:48 -0400229 Log.d(TAG, "Found matching player by token " + packageName);
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400230 player = p;
231 break;
Beth Thibodeaua3d90982020-04-13 23:42:48 -0400232 } else if (packageName.equals(p.getMediaPlayerPackage()) && key.equals(p.getKey())) {
233 // Also match if it's the same package and notification key
234 Log.d(TAG, "Found matching player by package " + packageName + ", " + key);
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400235 player = p;
236 break;
237 }
238 }
239
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400240 int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width);
241 int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
242 LayoutParams lp = new LayoutParams(playerWidth, ViewGroup.LayoutParams.MATCH_PARENT);
243 lp.setMarginStart(padding);
244 lp.setMarginEnd(padding);
245
246 if (player == null) {
247 Log.d(TAG, "creating new player");
Robert Snoeberger9a7409b2020-04-09 18:12:27 -0400248 // Set up listener for device changes
249 // TODO: integrate with MediaTransferManager?
250 InfoMediaManager imm = new InfoMediaManager(mContext, notif.getPackageName(),
251 notif.getNotification(), mLocalBluetoothManager);
252 LocalMediaManager routeManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
253 imm, notif.getPackageName());
254
255 player = new QSMediaPlayer(mContext, this, mNotificationMediaManager, routeManager,
Beth Thibodeaua51c3142020-03-17 17:27:04 -0400256 mForegroundExecutor, mBackgroundExecutor);
Robert Snoeberger7dffd372020-04-01 17:32:44 -0400257 player.setListening(mListening);
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400258 if (player.isPlaying()) {
259 mMediaCarousel.addView(player.getView(), 0, lp); // add in front
260 } else {
261 mMediaCarousel.addView(player.getView(), lp); // add at end
262 }
Beth Thibodeau4e69f4c2019-10-24 14:13:49 -0400263 mMediaPlayers.add(player);
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400264 } else if (player.isPlaying()) {
Beth Thibodeau8cb6d422020-01-31 14:57:09 -0500265 mUpdateCarousel = true;
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400266 }
267
268 Log.d(TAG, "setting player session");
Beth Thibodeau7b6c1782020-03-05 11:43:51 -0500269 player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer,
Beth Thibodeaua3d90982020-04-13 23:42:48 -0400270 notif.getNotification(), key);
Beth Thibodeau4e69f4c2019-10-24 14:13:49 -0400271
272 if (mMediaPlayers.size() > 0) {
273 ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE);
274 }
Beth Thibodeau07d20c32019-10-16 13:45:56 -0400275 }
276
277 protected View getMediaPanel() {
278 return mMediaCarousel;
279 }
280
Beth Thibodeaufd51bb22019-11-25 13:57:06 -0500281 /**
282 * Remove the media player from the carousel
283 * @param player Player to remove
284 * @return true if removed, false if player was not found
285 */
286 protected boolean removeMediaPlayer(QSMediaPlayer player) {
287 // Remove from list
288 if (!mMediaPlayers.remove(player)) {
289 return false;
290 }
291
292 // Check if we need to collapse the carousel now
293 mMediaCarousel.removeView(player.getView());
294 if (mMediaPlayers.size() == 0) {
295 ((View) mMediaCarousel.getParent()).setVisibility(View.GONE);
Beth Thibodeaufd51bb22019-11-25 13:57:06 -0500296 }
297 return true;
298 }
299
Jason Monke5b770e2017-03-03 21:49:29 -0500300 protected void addDivider() {
301 mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false);
Jason Monk824ffff2017-04-11 15:49:06 -0400302 mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(),
303 getColorForState(mContext, Tile.STATE_ACTIVE)));
Jason Monke5b770e2017-03-03 21:49:29 -0500304 addView(mDivider);
305 }
306
Fabian Kozynski72cd84a92019-04-15 14:09:43 -0400307 @Override
308 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
309 // We want all the logic of LinearLayout#onMeasure, and for it to assign the excess space
310 // not used by the other children to PagedTileLayout. However, in this case, LinearLayout
311 // assumes that PagedTileLayout would use all the excess space. This is not the case as
312 // PagedTileLayout height is quantized (because it shows a certain number of rows).
313 // Therefore, after everything is measured, we need to make sure that we add up the correct
314 // total height
315 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
316 int height = getPaddingBottom() + getPaddingTop();
317 int numChildren = getChildCount();
318 for (int i = 0; i < numChildren; i++) {
319 View child = getChildAt(i);
320 if (child.getVisibility() != View.GONE) height += child.getMeasuredHeight();
321 }
322 setMeasuredDimension(getMeasuredWidth(), height);
323 }
324
Jason Monke5b770e2017-03-03 21:49:29 -0500325 public View getDivider() {
326 return mDivider;
Jason Monk32508852017-01-18 09:17:13 -0500327 }
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200328
Amin Shaikha07a17b2018-02-23 16:02:52 -0500329 public QSTileRevealController getQsTileRevealController() {
330 return mQsTileRevealController;
331 }
332
Jason Monk377e7ad2016-02-16 14:03:21 -0500333 public boolean isShowingCustomize() {
334 return mCustomizePanel != null && mCustomizePanel.isCustomizing();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400335 }
336
Jason Monkcaf37622015-08-18 12:33:50 -0400337 @Override
338 protected void onAttachedToWindow() {
339 super.onAttachedToWindow();
Rohan Shahdb2cfa32018-02-20 11:27:22 -0800340 final TunerService tunerService = Dependency.get(TunerService.class);
341 tunerService.addTunable(this, QS_SHOW_BRIGHTNESS);
Rohan Shahdb2cfa32018-02-20 11:27:22 -0800342
Jason Monkf160edca2016-03-30 13:48:56 -0400343 if (mHost != null) {
344 setTiles(mHost.getTiles());
345 }
Adrian Roosb01793f2017-08-09 14:34:55 +0200346 if (mBrightnessMirrorController != null) {
347 mBrightnessMirrorController.addCallback(this);
348 }
Ned Burnsaaeb44b2020-02-12 23:48:26 -0500349 mDumpManager.registerDumpable(getDumpableTag(), this);
Jason Monkcaf37622015-08-18 12:33:50 -0400350 }
351
352 @Override
353 protected void onDetachedFromWindow() {
Jason Monkde850bb2017-02-01 19:26:30 -0500354 Dependency.get(TunerService.class).removeTunable(this);
355 if (mHost != null) {
356 mHost.removeCallback(this);
357 }
Jason Monkf160edca2016-03-30 13:48:56 -0400358 for (TileRecord record : mRecords) {
359 record.tile.removeCallbacks();
360 }
Adrian Roosb01793f2017-08-09 14:34:55 +0200361 if (mBrightnessMirrorController != null) {
362 mBrightnessMirrorController.removeCallback(this);
363 }
Ned Burnsaaeb44b2020-02-12 23:48:26 -0500364 mDumpManager.unregisterDumpable(getDumpableTag());
Jason Monkcaf37622015-08-18 12:33:50 -0400365 super.onDetachedFromWindow();
366 }
367
Ned Burns66b6e372019-07-26 13:45:42 -0400368 protected String getDumpableTag() {
369 return TAG;
370 }
371
Jason Monkcaf37622015-08-18 12:33:50 -0400372 @Override
Jason Monk46dbfb42016-02-25 14:59:20 -0500373 public void onTilesChanged() {
374 setTiles(mHost.getTiles());
375 }
376
377 @Override
Jason Monkcaf37622015-08-18 12:33:50 -0400378 public void onTuningChanged(String key, String newValue) {
379 if (QS_SHOW_BRIGHTNESS.equals(key)) {
Rohan Shahdb2cfa32018-02-20 11:27:22 -0800380 updateViewVisibilityForTuningValue(mBrightnessView, newValue);
Jason Monkcaf37622015-08-18 12:33:50 -0400381 }
382 }
383
Rohan Shahdb2cfa32018-02-20 11:27:22 -0800384 private void updateViewVisibilityForTuningValue(View view, @Nullable String newValue) {
Henrik Baarda2c86e82019-03-14 12:23:04 +0100385 view.setVisibility(TunerService.parseIntegerSwitch(newValue, true) ? VISIBLE : GONE);
Rohan Shahdb2cfa32018-02-20 11:27:22 -0800386 }
387
Jason Monka9927322015-12-13 16:22:37 -0500388 public void openDetails(String subPanel) {
Jason Monk702e2eb2017-03-03 16:53:44 -0500389 QSTile tile = getTile(subPanel);
Fabian Kozynskie3137e32018-09-28 11:20:04 -0400390 // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory),
391 // QSFactory will not be able to create a tile and getTile will return null
392 if (tile != null) {
393 showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
394 }
Jason Monka9927322015-12-13 16:22:37 -0500395 }
396
Jason Monk702e2eb2017-03-03 16:53:44 -0500397 private QSTile getTile(String subPanel) {
Jason Monka9927322015-12-13 16:22:37 -0500398 for (int i = 0; i < mRecords.size(); i++) {
399 if (subPanel.equals(mRecords.get(i).tile.getTileSpec())) {
400 return mRecords.get(i).tile;
401 }
402 }
403 return mHost.createTile(subPanel);
404 }
405
Adrian Roos5fd872e2014-08-12 17:28:58 +0200406 public void setBrightnessMirror(BrightnessMirrorController c) {
Adrian Roosb01793f2017-08-09 14:34:55 +0200407 if (mBrightnessMirrorController != null) {
408 mBrightnessMirrorController.removeCallback(this);
409 }
Adrian Roos429c9622016-06-15 13:02:17 -0700410 mBrightnessMirrorController = c;
Adrian Roosb01793f2017-08-09 14:34:55 +0200411 if (mBrightnessMirrorController != null) {
412 mBrightnessMirrorController.addCallback(this);
413 }
414 updateBrightnessMirror();
415 }
416
417 @Override
418 public void onBrightnessMirrorReinflated(View brightnessMirror) {
419 updateBrightnessMirror();
Adrian Roos5fd872e2014-08-12 17:28:58 +0200420 }
421
Jason Monkb46059a2016-06-30 14:22:42 -0400422 View getBrightnessView() {
423 return mBrightnessView;
424 }
425
Jason Monke5b770e2017-03-03 21:49:29 -0500426 public void setCallback(QSDetail.Callback callback) {
John Spurlock5729d092014-05-29 17:42:51 -0400427 mCallback = callback;
428 }
429
Jason Monk8b9d67f2016-03-02 08:59:08 -0500430 public void setHost(QSTileHost host, QSCustomizer customizer) {
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200431 mHost = host;
Jason Monk46dbfb42016-02-25 14:59:20 -0500432 mHost.addCallback(this);
433 setTiles(mHost.getTiles());
Jason Monk9c7844c2017-01-18 15:21:53 -0500434 mFooter.setHostEnvironment(host);
Jason Monk8b9d67f2016-03-02 08:59:08 -0500435 mCustomizePanel = customizer;
436 if (mCustomizePanel != null) {
437 mCustomizePanel.setHost(mHost);
438 }
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200439 }
440
Rohan Shah3090e792018-04-12 00:01:00 -0400441 /**
442 * Links the footer's page indicator, which is used in landscape orientation to save space.
443 *
444 * @param pageIndicator indicator to use for page scrolling
445 */
446 public void setFooterPageIndicator(PageIndicator pageIndicator) {
447 if (mTileLayout instanceof PagedTileLayout) {
448 mFooterPageIndicator = pageIndicator;
449 updatePageIndicator();
450 }
451 }
452
453 private void updatePageIndicator() {
454 if (mTileLayout instanceof PagedTileLayout) {
Rohan Shah3090e792018-04-12 00:01:00 -0400455 if (mFooterPageIndicator != null) {
456 mFooterPageIndicator.setVisibility(View.GONE);
Rohan Shah3090e792018-04-12 00:01:00 -0400457
Fabian Kozynski4e76d1f2019-02-25 16:30:04 -0500458 ((PagedTileLayout) mTileLayout).setPageIndicator(mFooterPageIndicator);
459 }
Rohan Shah3090e792018-04-12 00:01:00 -0400460 }
461 }
462
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200463 public QSTileHost getHost() {
464 return mHost;
465 }
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200466
John Spurlockaf8d6c42014-05-07 17:49:08 -0400467 public void updateResources() {
John Spurlock4bf31982014-05-21 13:04:22 -0400468 final Resources res = mContext.getResources();
Rohan Shahd3cf7562018-02-23 11:12:28 -0800469 setPadding(0, res.getDimensionPixelSize(R.dimen.qs_panel_padding_top), 0, res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
Rohan Shah3090e792018-04-12 00:01:00 -0400470
471 updatePageIndicator();
472
John Spurlock1a462c12014-07-14 10:52:01 -0400473 if (mListening) {
474 refreshAllTiles();
475 }
Jason Monkcaf37622015-08-18 12:33:50 -0400476 if (mTileLayout != null) {
477 mTileLayout.updateResources();
478 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400479 }
480
Jorim Jaggie17c4b42014-08-26 17:27:31 +0200481 @Override
482 protected void onConfigurationChanged(Configuration newConfig) {
483 super.onConfigurationChanged(newConfig);
Jorim Jaggie17c4b42014-08-26 17:27:31 +0200484 mFooter.onConfigurationChanged();
Fabian Kozynskib1aee812019-04-04 14:17:48 -0400485 updateResources();
Adrian Roos429c9622016-06-15 13:02:17 -0700486
Adrian Roosb01793f2017-08-09 14:34:55 +0200487 updateBrightnessMirror();
488 }
489
490 public void updateBrightnessMirror() {
Adrian Roos429c9622016-06-15 13:02:17 -0700491 if (mBrightnessMirrorController != null) {
Adrian Roosb01793f2017-08-09 14:34:55 +0200492 ToggleSliderView brightnessSlider = findViewById(R.id.brightness_slider);
493 ToggleSliderView mirrorSlider = mBrightnessMirrorController.getMirror()
494 .findViewById(R.id.brightness_slider);
495 brightnessSlider.setMirror(mirrorSlider);
496 brightnessSlider.setMirrorController(mBrightnessMirrorController);
Adrian Roos429c9622016-06-15 13:02:17 -0700497 }
Jorim Jaggie17c4b42014-08-26 17:27:31 +0200498 }
499
Jason Monkbd6dbb02015-09-03 15:46:25 -0400500 public void onCollapse() {
Yoshinori Hirano1dd7d072016-09-05 16:11:59 +0900501 if (mCustomizePanel != null && mCustomizePanel.isShown()) {
Fabian Kozynski0db39af2018-08-31 15:56:45 -0400502 mCustomizePanel.hide();
Jason Monkbd6dbb02015-09-03 15:46:25 -0400503 }
504 }
505
John Spurlockaf8d6c42014-05-07 17:49:08 -0400506 public void setExpanded(boolean expanded) {
John Spurlock5729d092014-05-29 17:42:51 -0400507 if (mExpanded == expanded) return;
Fabian Kozynskica30f572019-10-24 10:01:04 -0400508 mQSLogger.logPanelExpanded(expanded, getDumpableTag());
John Spurlock5729d092014-05-29 17:42:51 -0400509 mExpanded = expanded;
Amin Shaikh15781d62018-02-16 16:00:13 -0500510 if (!mExpanded && mTileLayout instanceof PagedTileLayout) {
511 ((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
Jason Monk162011e2016-02-19 08:11:55 -0500512 }
Jason Monk8c09ac72017-03-16 11:53:40 -0400513 mMetricsLogger.visibility(MetricsEvent.QS_PANEL, mExpanded);
John Spurlock5729d092014-05-29 17:42:51 -0400514 if (!mExpanded) {
John Spurlockf7ae4422014-08-01 12:45:18 -0400515 closeDetail();
Chris Wren457a21c2015-05-06 17:50:34 -0400516 } else {
517 logTiles();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400518 }
Jorim Jaggie65e3102014-07-01 22:00:50 +0200519 }
520
Amin Shaikh5da602f2018-02-02 15:00:25 -0500521 public void setPageListener(final PagedTileLayout.PageListener pageListener) {
522 if (mTileLayout instanceof PagedTileLayout) {
523 ((PagedTileLayout) mTileLayout).setPageListener(pageListener);
Amin Shaikh5da602f2018-02-02 15:00:25 -0500524 }
525 }
526
Muyuan Li0e9f5382016-04-27 15:51:15 -0700527 public boolean isExpanded() {
528 return mExpanded;
529 }
530
Jorim Jaggie65e3102014-07-01 22:00:50 +0200531 public void setListening(boolean listening) {
532 if (mListening == listening) return;
533 mListening = listening;
Jason Monk1bec6af2016-05-31 15:40:58 -0400534 if (mTileLayout != null) {
Fabian Kozynskica30f572019-10-24 10:01:04 -0400535 mQSLogger.logAllTilesChangeListening(listening, getDumpableTag(), mCachedSpecs);
Jason Monk1bec6af2016-05-31 15:40:58 -0400536 mTileLayout.setListening(listening);
John Spurlock1a462c12014-07-14 10:52:01 -0400537 }
538 if (mListening) {
539 refreshAllTiles();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400540 }
Robert Snoeberger7dffd372020-04-01 17:32:44 -0400541 for (QSMediaPlayer player : mMediaPlayers) {
542 player.setListening(mListening);
543 }
Fabian Kozynskib9dc0c72018-09-20 13:00:17 -0400544 }
545
Fabian Kozynskica30f572019-10-24 10:01:04 -0400546 private String getTilesSpecs() {
547 return mRecords.stream()
548 .map(tileRecord -> tileRecord.tile.getTileSpec())
549 .collect(Collectors.joining(","));
550 }
551
Fabian Kozynskib9dc0c72018-09-20 13:00:17 -0400552 public void setListening(boolean listening, boolean expanded) {
553 setListening(listening && expanded);
554 getFooter().setListening(listening);
555 // Set the listening as soon as the QS fragment starts listening regardless of the expansion,
556 // so it will update the current brightness before the slider is visible.
557 setBrightnessListening(listening);
558 }
559
560 public void setBrightnessListening(boolean listening) {
561 if (listening) {
562 mBrightnessController.registerCallbacks();
563 } else {
564 mBrightnessController.unregisterCallbacks();
Jorim Jaggi3f48f462014-07-08 16:53:29 +0200565 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400566 }
567
Jorim Jaggi1ecd7cd2014-11-03 16:18:03 +0100568 public void refreshAllTiles() {
yuemingweb2bf7d182018-01-23 16:05:08 +0000569 mBrightnessController.checkRestrictionAndSetEnabled();
John Spurlock1a462c12014-07-14 10:52:01 -0400570 for (TileRecord r : mRecords) {
571 r.tile.refreshState();
572 }
Jason Monk3d5f5512014-07-25 11:17:28 -0400573 mFooter.refreshState();
John Spurlock1a462c12014-07-14 10:52:01 -0400574 }
575
Adrian Roos970be532014-11-21 15:50:16 +0100576 public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) {
577 int xInWindow = locationInWindow[0];
578 int yInWindow = locationInWindow[1];
Jason Monk377e7ad2016-02-16 14:03:21 -0500579 ((View) getParent()).getLocationInWindow(locationInWindow);
Adrian Roos970be532014-11-21 15:50:16 +0100580
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200581 Record r = new Record();
582 r.detailAdapter = adapter;
Adrian Roos970be532014-11-21 15:50:16 +0100583 r.x = xInWindow - locationInWindow[0];
584 r.y = yInWindow - locationInWindow[1];
585
586 locationInWindow[0] = xInWindow;
587 locationInWindow[1] = yInWindow;
588
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200589 showDetail(show, r);
590 }
591
Jason Monkdc35dcb2015-12-04 16:36:15 -0500592 protected void showDetail(boolean show, Record r) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400593 mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget();
594 }
595
Jason Monk702e2eb2017-03-03 16:53:44 -0500596 public void setTiles(Collection<QSTile> tiles) {
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400597 setTiles(tiles, false);
598 }
599
Jason Monk702e2eb2017-03-03 16:53:44 -0500600 public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
Amin Shaikha07a17b2018-02-23 16:02:52 -0500601 if (!collapsedView) {
602 mQsTileRevealController.updateRevealedTiles(tiles);
603 }
John Spurlockbceed062014-08-10 18:04:16 -0400604 for (TileRecord record : mRecords) {
Jason Monkcaf37622015-08-18 12:33:50 -0400605 mTileLayout.removeTile(record);
Xiaohui Chen08e266c2016-04-18 12:53:28 -0700606 record.tile.removeCallback(record.callback);
John Spurlockbceed062014-08-10 18:04:16 -0400607 }
608 mRecords.clear();
Fabian Kozynskica30f572019-10-24 10:01:04 -0400609 mCachedSpecs = "";
Jason Monk702e2eb2017-03-03 16:53:44 -0500610 for (QSTile tile : tiles) {
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400611 addTile(tile, collapsedView);
John Spurlockbceed062014-08-10 18:04:16 -0400612 }
John Spurlockbceed062014-08-10 18:04:16 -0400613 }
614
Jason Monkec87a872016-03-01 15:00:16 -0500615 protected void drawTile(TileRecord r, QSTile.State state) {
John Spurlock20c89052015-05-11 13:11:24 -0400616 r.tileView.onStateChanged(state);
617 }
618
Jason Monk702e2eb2017-03-03 16:53:44 -0500619 protected QSTileView createTileView(QSTile tile, boolean collapsedView) {
620 return mHost.createTileView(tile, collapsedView);
Jason Monkdc35dcb2015-12-04 16:36:15 -0500621 }
622
Xiaohui Chen66448932016-04-18 12:53:28 -0700623 protected boolean shouldShowDetail() {
624 return mExpanded;
625 }
626
Jason Monk702e2eb2017-03-03 16:53:44 -0500627 protected TileRecord addTile(final QSTile tile, boolean collapsedView) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400628 final TileRecord r = new TileRecord();
629 r.tile = tile;
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400630 r.tileView = createTileView(tile, collapsedView);
John Spurlockbceed062014-08-10 18:04:16 -0400631 final QSTile.Callback callback = new QSTile.Callback() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400632 @Override
633 public void onStateChanged(QSTile.State state) {
Jason Monk377e7ad2016-02-16 14:03:21 -0500634 drawTile(r, state);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400635 }
Jason Monkca894a02016-01-12 15:30:22 -0500636
John Spurlockaf8d6c42014-05-07 17:49:08 -0400637 @Override
638 public void onShowDetail(boolean show) {
Xiaohui Chen66448932016-04-18 12:53:28 -0700639 // Both the collapsed and full QS panels get this callback, this check determines
640 // which one should handle showing the detail.
641 if (shouldShowDetail()) {
642 QSPanel.this.showDetail(show, r);
643 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400644 }
Jason Monkca894a02016-01-12 15:30:22 -0500645
John Spurlock7f8f22a2014-07-02 18:54:17 -0400646 @Override
647 public void onToggleStateChanged(boolean state) {
648 if (mDetailRecord == r) {
649 fireToggleStateChanged(state);
650 }
651 }
Jason Monkca894a02016-01-12 15:30:22 -0500652
John Spurlock486b78e2014-07-07 08:37:56 -0400653 @Override
654 public void onScanStateChanged(boolean state) {
John Spurlock465cefa2014-08-16 10:39:33 -0400655 r.scanState = state;
John Spurlock486b78e2014-07-07 08:37:56 -0400656 if (mDetailRecord == r) {
John Spurlock465cefa2014-08-16 10:39:33 -0400657 fireScanStateChanged(r.scanState);
John Spurlock486b78e2014-07-07 08:37:56 -0400658 }
659 }
Selim Cinek4fda7b22014-08-18 22:07:25 +0200660
661 @Override
662 public void onAnnouncementRequested(CharSequence announcement) {
dooyoung.hwang1b006622016-12-22 20:04:20 +0900663 if (announcement != null) {
664 mHandler.obtainMessage(H.ANNOUNCE_FOR_ACCESSIBILITY, announcement)
665 .sendToTarget();
666 }
Selim Cinek4fda7b22014-08-18 22:07:25 +0200667 }
John Spurlockbceed062014-08-10 18:04:16 -0400668 };
Jason Monkca894a02016-01-12 15:30:22 -0500669 r.tile.addCallback(callback);
Xiaohui Chen08e266c2016-04-18 12:53:28 -0700670 r.callback = callback;
Jason Monk702e2eb2017-03-03 16:53:44 -0500671 r.tileView.init(r.tile);
John Spurlockccb6b9a2014-05-17 15:54:40 -0400672 r.tile.refreshState();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400673 mRecords.add(r);
Fabian Kozynskica30f572019-10-24 10:01:04 -0400674 mCachedSpecs = getTilesSpecs();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400675
Jason Monkcaf37622015-08-18 12:33:50 -0400676 if (mTileLayout != null) {
677 mTileLayout.addTile(r);
678 }
Muyuan Lib40004b2016-05-11 14:47:29 -0700679
680 return r;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400681 }
682
Jason Monk21428432016-02-10 16:42:38 -0500683
Jason Monk4bb7b1a2016-05-27 15:19:15 -0400684 public void showEdit(final View v) {
Jason Monk21428432016-02-10 16:42:38 -0500685 v.post(new Runnable() {
686 @Override
687 public void run() {
688 if (mCustomizePanel != null) {
689 if (!mCustomizePanel.isCustomizing()) {
Fabian Kozynski0db39af2018-08-31 15:56:45 -0400690 int[] loc = v.getLocationOnScreen();
Jason Monk4bb7b1a2016-05-27 15:19:15 -0400691 int x = loc[0] + v.getWidth() / 2;
692 int y = loc[1] + v.getHeight() / 2;
Jason Monk21428432016-02-10 16:42:38 -0500693 mCustomizePanel.show(x, y);
694 }
695 }
696
697 }
698 });
699 }
700
John Spurlockf7ae4422014-08-01 12:45:18 -0400701 public void closeDetail() {
Yoshinori Hirano1dd7d072016-09-05 16:11:59 +0900702 if (mCustomizePanel != null && mCustomizePanel.isShown()) {
Jason Monkbd6dbb02015-09-03 15:46:25 -0400703 // Treat this as a detail panel for now, to make things easy.
Fabian Kozynski0db39af2018-08-31 15:56:45 -0400704 mCustomizePanel.hide();
Jason Monkbd6dbb02015-09-03 15:46:25 -0400705 return;
706 }
John Spurlockf7ae4422014-08-01 12:45:18 -0400707 showDetail(false, mDetailRecord);
708 }
709
Jorim Jaggi8bc983e2014-12-10 17:45:50 +0100710 public int getGridHeight() {
Jason Monk162011e2016-02-19 08:11:55 -0500711 return getMeasuredHeight();
Jorim Jaggi8bc983e2014-12-10 17:45:50 +0100712 }
713
Jason Monkdc35dcb2015-12-04 16:36:15 -0500714 protected void handleShowDetail(Record r, boolean show) {
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200715 if (r instanceof TileRecord) {
716 handleShowDetailTile((TileRecord) r, show);
717 } else {
Adrian Roos970be532014-11-21 15:50:16 +0100718 int x = 0;
719 int y = 0;
720 if (r != null) {
721 x = r.x;
722 y = r.y;
723 }
724 handleShowDetailImpl(r, show, x, y);
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200725 }
726 }
727
728 private void handleShowDetailTile(TileRecord r, boolean show) {
Jason Monkefe3d3f2015-06-10 16:48:03 -0400729 if ((mDetailRecord != null) == show && mDetailRecord == r) return;
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200730
John Spurlockaf8d6c42014-05-07 17:49:08 -0400731 if (show) {
John Spurlock7f8f22a2014-07-02 18:54:17 -0400732 r.detailAdapter = r.tile.getDetailAdapter();
733 if (r.detailAdapter == null) return;
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200734 }
Jason Monk0d6a1c42015-04-20 16:38:51 -0400735 r.tile.setDetailListening(show);
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200736 int x = r.tileView.getLeft() + r.tileView.getWidth() / 2;
Jason Monk702e2eb2017-03-03 16:53:44 -0500737 int y = r.tileView.getDetailY() + mTileLayout.getOffsetTop(r) + getTop();
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200738 handleShowDetailImpl(r, show, x, y);
739 }
740
741 private void handleShowDetailImpl(Record r, boolean show, int x, int y) {
Jason Monk377e7ad2016-02-16 14:03:21 -0500742 setDetailRecord(show ? r : null);
743 fireShowingDetail(show ? r.detailAdapter : null, x, y);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400744 }
745
Muyuan Li4074b462016-05-06 13:40:42 -0700746 protected void setDetailRecord(Record r) {
Jason Monk377e7ad2016-02-16 14:03:21 -0500747 if (r == mDetailRecord) return;
748 mDetailRecord = r;
749 final boolean scanState = mDetailRecord instanceof TileRecord
750 && ((TileRecord) mDetailRecord).scanState;
751 fireScanStateChanged(scanState);
752 }
753
754 void setGridContentVisibility(boolean visible) {
Selim Cineke32010a2014-08-20 23:50:41 +0200755 int newVis = visible ? VISIBLE : INVISIBLE;
Jason Monk162011e2016-02-19 08:11:55 -0500756 setVisibility(newVis);
Chris Wren457a21c2015-05-06 17:50:34 -0400757 if (mGridContentVisible != visible) {
Jason Monk8c09ac72017-03-16 11:53:40 -0400758 mMetricsLogger.visibility(MetricsEvent.QS_PANEL, newVis);
Chris Wren457a21c2015-05-06 17:50:34 -0400759 }
Selim Cineke32010a2014-08-20 23:50:41 +0200760 mGridContentVisible = visible;
761 }
762
Chris Wren457a21c2015-05-06 17:50:34 -0400763 private void logTiles() {
764 for (int i = 0; i < mRecords.size(); i++) {
Jason Monkcb4b31d2017-05-03 10:37:34 -0400765 QSTile tile = mRecords.get(i).tile;
766 mMetricsLogger.write(tile.populate(new LogMaker(tile.getMetricsCategory())
767 .setType(MetricsEvent.TYPE_OPEN)));
Chris Wren457a21c2015-05-06 17:50:34 -0400768 }
769 }
770
Jason Monk377e7ad2016-02-16 14:03:21 -0500771 private void fireShowingDetail(DetailAdapter detail, int x, int y) {
John Spurlock5729d092014-05-29 17:42:51 -0400772 if (mCallback != null) {
Jason Monk377e7ad2016-02-16 14:03:21 -0500773 mCallback.onShowingDetail(detail, x, y);
John Spurlock7f8f22a2014-07-02 18:54:17 -0400774 }
775 }
776
777 private void fireToggleStateChanged(boolean state) {
778 if (mCallback != null) {
779 mCallback.onToggleStateChanged(state);
John Spurlock5729d092014-05-29 17:42:51 -0400780 }
781 }
782
John Spurlock486b78e2014-07-07 08:37:56 -0400783 private void fireScanStateChanged(boolean state) {
784 if (mCallback != null) {
785 mCallback.onScanStateChanged(state);
786 }
787 }
788
Jason Monk7e53f202016-01-28 10:40:20 -0500789 public void clickTile(ComponentName tile) {
790 final String spec = CustomTile.toSpec(tile);
791 final int N = mRecords.size();
792 for (int i = 0; i < N; i++) {
793 if (mRecords.get(i).tile.getTileSpec().equals(spec)) {
794 mRecords.get(i).tile.click();
795 break;
796 }
797 }
798 }
799
Jason Monk162011e2016-02-19 08:11:55 -0500800 QSTileLayout getTileLayout() {
801 return mTileLayout;
802 }
803
Jason Monk702e2eb2017-03-03 16:53:44 -0500804 QSTileView getTileView(QSTile tile) {
Jason Monk162011e2016-02-19 08:11:55 -0500805 for (TileRecord r : mRecords) {
806 if (r.tile == tile) {
807 return r.tileView;
808 }
809 }
810 return null;
811 }
812
Jason Monke5b770e2017-03-03 21:49:29 -0500813 public QSSecurityFooter getFooter() {
Jason Monkdf36aed2016-07-25 11:21:56 -0400814 return mFooter;
815 }
816
phweissa0cb2512016-12-14 21:37:48 +0100817 public void showDeviceMonitoringDialog() {
818 mFooter.showDeviceMonitoringDialog();
819 }
820
Amin Shaikhbc1a4e82018-04-16 12:14:35 -0400821 public void setMargins(int sideMargins) {
822 for (int i = 0; i < getChildCount(); i++) {
823 View view = getChildAt(i);
824 if (view != mTileLayout) {
825 LayoutParams lp = (LayoutParams) view.getLayoutParams();
826 lp.leftMargin = sideMargins;
827 lp.rightMargin = sideMargins;
828 }
829 }
830 }
831
John Spurlockaf8d6c42014-05-07 17:49:08 -0400832 private class H extends Handler {
833 private static final int SHOW_DETAIL = 1;
834 private static final int SET_TILE_VISIBILITY = 2;
dooyoung.hwang1b006622016-12-22 20:04:20 +0900835 private static final int ANNOUNCE_FOR_ACCESSIBILITY = 3;
Jason Monkcb4b31d2017-05-03 10:37:34 -0400836
John Spurlockaf8d6c42014-05-07 17:49:08 -0400837 @Override
838 public void handleMessage(Message msg) {
839 if (msg.what == SHOW_DETAIL) {
Jason Monkcb4b31d2017-05-03 10:37:34 -0400840 handleShowDetail((Record) msg.obj, msg.arg1 != 0);
dooyoung.hwang1b006622016-12-22 20:04:20 +0900841 } else if (msg.what == ANNOUNCE_FOR_ACCESSIBILITY) {
Jason Monkcb4b31d2017-05-03 10:37:34 -0400842 announceForAccessibility((CharSequence) msg.obj);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400843 }
844 }
845 }
846
Fabian Kozynski00d494d2019-04-04 09:53:50 -0400847 @Override
848 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
849 pw.println(getClass().getSimpleName() + ":");
850 pw.println(" Tile records:");
851 for (TileRecord record : mRecords) {
852 if (record.tile instanceof Dumpable) {
853 pw.print(" "); ((Dumpable) record.tile).dump(fd, pw, args);
854 pw.print(" "); pw.println(record.tileView.toString());
855 }
856 }
857 }
858
Jason Monkdc35dcb2015-12-04 16:36:15 -0500859 protected static class Record {
John Spurlock7f8f22a2014-07-02 18:54:17 -0400860 DetailAdapter detailAdapter;
Adrian Roos970be532014-11-21 15:50:16 +0100861 int x;
862 int y;
Adrian Roos1ef80fe2014-07-14 22:53:54 +0200863 }
864
Jason Monkbd6dbb02015-09-03 15:46:25 -0400865 public static final class TileRecord extends Record {
Jason Monk702e2eb2017-03-03 16:53:44 -0500866 public QSTile tile;
867 public com.android.systemui.plugins.qs.QSTileView tileView;
Jason Monk11a77442015-05-12 19:29:02 -0400868 public boolean scanState;
Xiaohui Chen08e266c2016-04-18 12:53:28 -0700869 public QSTile.Callback callback;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400870 }
871
Jason Monkcaf37622015-08-18 12:33:50 -0400872 public interface QSTileLayout {
Fabian Kozynski407ddb22018-10-03 15:04:56 -0400873
874 default void saveInstanceState(Bundle outState) {}
875
876 default void restoreInstanceState(Bundle savedInstanceState) {}
877
Jason Monkcaf37622015-08-18 12:33:50 -0400878 void addTile(TileRecord tile);
Jason Monkcb4b31d2017-05-03 10:37:34 -0400879
Jason Monkcaf37622015-08-18 12:33:50 -0400880 void removeTile(TileRecord tile);
Jason Monkcb4b31d2017-05-03 10:37:34 -0400881
Jason Monkcaf37622015-08-18 12:33:50 -0400882 int getOffsetTop(TileRecord tile);
Jason Monkcb4b31d2017-05-03 10:37:34 -0400883
Jason Monk9d02a432016-01-20 16:33:46 -0500884 boolean updateResources();
Jason Monk1bec6af2016-05-31 15:40:58 -0400885
886 void setListening(boolean listening);
Amin Shaikh0f8ea5432018-03-27 11:09:27 -0400887
888 default void setExpansion(float expansion) {}
Fabian Kozynski802279f2018-09-07 13:44:54 -0400889
890 int getNumVisibleTiles();
Jason Monkcaf37622015-08-18 12:33:50 -0400891 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400892}