blob: 22c42d4a6a572e37f84ccc39f1d7f14cdd795558 [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.tiles;
18
Jason Monk3fd0b142017-08-30 18:11:21 -040019import static com.android.settingslib.graph.BluetoothDeviceLayerDrawable.createLayerDrawable;
20
Rohan Shahd5df1752018-01-19 10:45:09 -080021import android.annotation.Nullable;
Jason Monkfac25382016-07-19 14:13:37 -040022import android.bluetooth.BluetoothAdapter;
Rohan Shahd5df1752018-01-19 10:45:09 -080023import android.bluetooth.BluetoothClass;
Jason Monkbe3c5db2015-02-04 13:00:55 -050024import android.bluetooth.BluetoothDevice;
25import android.bluetooth.BluetoothProfile;
John Spurlock486b78e2014-07-07 08:37:56 -040026import android.content.Context;
John Spurlockaf8d6c42014-05-07 17:49:08 -040027import android.content.Intent;
Evan Laird4673c382017-09-27 13:00:50 -040028import android.graphics.drawable.Drawable;
John Spurlockaf8d6c42014-05-07 17:49:08 -040029import android.provider.Settings;
Jason Monk32508852017-01-18 09:17:13 -050030import android.service.quicksettings.Tile;
Amin Shaikh3468e5a2018-04-18 11:39:07 -040031import android.text.TextUtils;
John Spurlock486b78e2014-07-07 08:37:56 -040032import android.view.View;
33import android.view.ViewGroup;
Julia Reynolds20aef8a2016-05-04 16:44:08 -040034import android.widget.Switch;
Chris Wrenf6e9228b2016-01-26 18:04:35 -050035
Chris Wren457a21c2015-05-06 17:50:34 -040036import com.android.internal.logging.MetricsLogger;
Tamas Berghammer383db5eb2016-06-22 15:21:38 +010037import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Jack He6258aae2017-06-29 17:01:23 -070038import com.android.settingslib.Utils;
Jason Monkbe3c5db2015-02-04 13:00:55 -050039import com.android.settingslib.bluetooth.CachedBluetoothDevice;
Rohan Shahd5df1752018-01-19 10:45:09 -080040import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
Jason Monk9c7844c2017-01-18 15:21:53 -050041import com.android.systemui.Dependency;
John Spurlockaf8d6c42014-05-07 17:49:08 -040042import com.android.systemui.R;
Jason Monkec34da82017-02-24 15:57:05 -050043import com.android.systemui.plugins.ActivityStarter;
Jason Monke5b770e2017-03-03 21:49:29 -050044import com.android.systemui.plugins.qs.DetailAdapter;
Jason Monkbe3235a2017-04-05 09:29:53 -040045import com.android.systemui.plugins.qs.QSTile.BooleanState;
John Spurlock486b78e2014-07-07 08:37:56 -040046import com.android.systemui.qs.QSDetailItems;
47import com.android.systemui.qs.QSDetailItems.Item;
Jason Monk702e2eb2017-03-03 16:53:44 -050048import com.android.systemui.qs.QSHost;
Jason Monk702e2eb2017-03-03 16:53:44 -050049import com.android.systemui.qs.tileimpl.QSTileImpl;
John Spurlockaf8d6c42014-05-07 17:49:08 -040050import com.android.systemui.statusbar.policy.BluetoothController;
John Spurlock486b78e2014-07-07 08:37:56 -040051
Jason Monkc17d3292016-01-30 12:49:41 -050052import java.util.ArrayList;
Jason Monkbe3c5db2015-02-04 13:00:55 -050053import java.util.Collection;
Amin Shaikh45fb7a72018-04-18 14:14:38 -040054import java.util.List;
John Spurlockaf8d6c42014-05-07 17:49:08 -040055
56/** Quick settings tile: Bluetooth **/
Jason Monk702e2eb2017-03-03 16:53:44 -050057public class BluetoothTile extends QSTileImpl<BooleanState> {
John Spurlockaf8d6c42014-05-07 17:49:08 -040058 private static final Intent BLUETOOTH_SETTINGS = new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
59
60 private final BluetoothController mController;
John Spurlock486b78e2014-07-07 08:37:56 -040061 private final BluetoothDetailAdapter mDetailAdapter;
Jason Monk9c7844c2017-01-18 15:21:53 -050062 private final ActivityStarter mActivityStarter;
John Spurlockaf8d6c42014-05-07 17:49:08 -040063
Jason Monk702e2eb2017-03-03 16:53:44 -050064 public BluetoothTile(QSHost host) {
John Spurlockaf8d6c42014-05-07 17:49:08 -040065 super(host);
Jason Monk9c7844c2017-01-18 15:21:53 -050066 mController = Dependency.get(BluetoothController.class);
67 mActivityStarter = Dependency.get(ActivityStarter.class);
Muyuan Li0e9f5382016-04-27 15:51:15 -070068 mDetailAdapter = (BluetoothDetailAdapter) createDetailAdapter();
John Spurlockccb6b9a2014-05-17 15:54:40 -040069 }
70
71 @Override
John Spurlock486b78e2014-07-07 08:37:56 -040072 public DetailAdapter getDetailAdapter() {
73 return mDetailAdapter;
74 }
75
76 @Override
Jason Monk62b63a02016-02-02 15:15:31 -050077 public BooleanState newTileState() {
John Spurlockaf8d6c42014-05-07 17:49:08 -040078 return new BooleanState();
79 }
80
81 @Override
Jason Monk1c6116c2017-09-06 17:33:01 -040082 public void handleSetListening(boolean listening) {
John Spurlockccb6b9a2014-05-17 15:54:40 -040083 if (listening) {
Jason Monk88529052016-11-04 13:29:58 -040084 mController.addCallback(mCallback);
John Spurlockccb6b9a2014-05-17 15:54:40 -040085 } else {
Jason Monk88529052016-11-04 13:29:58 -040086 mController.removeCallback(mCallback);
John Spurlockccb6b9a2014-05-17 15:54:40 -040087 }
John Spurlockaf8d6c42014-05-07 17:49:08 -040088 }
89
90 @Override
Jason Monk32508852017-01-18 09:17:13 -050091 protected void handleClick() {
Jason Monkdc35dcb2015-12-04 16:36:15 -050092 // Secondary clicks are header clicks, just toggle.
Jason Monk3c3b6502017-07-27 15:41:26 -040093 final boolean isEnabled = mState.value;
Amin Shaikhd03a7432018-03-01 15:46:55 -050094 // Immediately enter transient enabling state when turning bluetooth on.
95 refreshState(isEnabled ? null : ARG_SHOW_TRANSIENT_ENABLING);
John Spurlockaf8d6c42014-05-07 17:49:08 -040096 mController.setBluetoothEnabled(!isEnabled);
97 }
98
99 @Override
Jason Monk76c67aa2016-02-19 14:49:42 -0500100 public Intent getLongClickIntent() {
101 return new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
102 }
103
104 @Override
Jason Monk32508852017-01-18 09:17:13 -0500105 protected void handleSecondaryClick() {
Sudheer Shankab6fc9312016-01-27 19:59:03 +0000106 if (!mController.canConfigBluetooth()) {
Jason Monk9c7844c2017-01-18 15:21:53 -0500107 mActivityStarter.postStartActivityDismissingKeyguard(
108 new Intent(Settings.ACTION_BLUETOOTH_SETTINGS), 0);
Sudheer Shankab6fc9312016-01-27 19:59:03 +0000109 return;
110 }
Jason Monk48e093e2016-07-15 10:37:07 -0400111 showDetail(true);
Jason Monk3c3b6502017-07-27 15:41:26 -0400112 if (!mState.value) {
113 mController.setBluetoothEnabled(true);
114 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400115 }
116
117 @Override
Jason Monk39c98e62016-03-16 09:18:35 -0400118 public CharSequence getTileLabel() {
119 return mContext.getString(R.string.quick_settings_bluetooth_label);
120 }
121
122 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400123 protected void handleUpdateState(BooleanState state, Object arg) {
Amin Shaikhd03a7432018-03-01 15:46:55 -0500124 final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
125 final boolean enabled = transientEnabling || mController.isBluetoothEnabled();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400126 final boolean connected = mController.isBluetoothConnected();
Amin Shaikhc5483402018-04-26 13:51:54 -0400127 final boolean connecting = mController.isBluetoothConnecting();
128 state.isTransient = transientEnabling || connecting ||
129 mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
Jason Monk2db46192017-02-01 17:18:22 -0500130 state.dualTarget = true;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400131 state.value = enabled;
Jason Monk395617f2017-05-05 14:07:58 -0400132 if (state.slash == null) {
133 state.slash = new SlashState();
134 }
135 state.slash.isSlashed = !enabled;
Jason Monkc17b7d92018-01-15 15:59:41 -0500136 state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
Amin Shaikh3468e5a2018-04-18 11:39:07 -0400137 state.secondaryLabel = TextUtils.emptyIfNull(
Amin Shaikhc5483402018-04-26 13:51:54 -0400138 getSecondaryLabel(enabled, connecting, connected, state.isTransient));
John Spurlockaf8d6c42014-05-07 17:49:08 -0400139 if (enabled) {
140 if (connected) {
Rohan Shah43216482018-02-09 11:07:29 -0800141 state.icon = new BluetoothConnectedTileIcon();
Amin Shaikh45fb7a72018-04-18 14:14:38 -0400142 if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) {
143 state.label = mController.getConnectedDeviceName();
144 }
Amin Shaikh3468e5a2018-04-18 11:39:07 -0400145 state.contentDescription =
146 mContext.getString(R.string.accessibility_bluetooth_name, state.label)
147 + ", " + state.secondaryLabel;
Jason Monk110e5f62017-03-31 13:45:12 -0400148 } else if (state.isTransient) {
149 state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation);
Amin Shaikh3468e5a2018-04-18 11:39:07 -0400150 state.contentDescription = state.secondaryLabel;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400151 } else {
John Spurlock2d695812014-10-30 13:25:21 -0400152 state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
Selim Cinek4fda7b22014-08-18 22:07:25 +0200153 state.contentDescription = mContext.getString(
Amin Shaikh3468e5a2018-04-18 11:39:07 -0400154 R.string.accessibility_quick_settings_bluetooth) + ","
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400155 + mContext.getString(R.string.accessibility_not_connected);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400156 }
Jason Monk32508852017-01-18 09:17:13 -0500157 state.state = Tile.STATE_ACTIVE;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400158 } else {
Jason Monk395617f2017-05-05 14:07:58 -0400159 state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on);
Selim Cinek4fda7b22014-08-18 22:07:25 +0200160 state.contentDescription = mContext.getString(
Amin Shaikh3468e5a2018-04-18 11:39:07 -0400161 R.string.accessibility_quick_settings_bluetooth);
Jason Monk32508852017-01-18 09:17:13 -0500162 state.state = Tile.STATE_INACTIVE;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400163 }
Selim Cinek4fda7b22014-08-18 22:07:25 +0200164
Jason Monk28df4ed2017-03-23 10:25:37 -0400165 state.dualLabelContentDescription = mContext.getResources().getString(
Julia Reynolds20aef8a2016-05-04 16:44:08 -0400166 R.string.accessibility_quick_settings_open_settings, getTileLabel());
Jason Monk702e2eb2017-03-03 16:53:44 -0500167 state.expandedAccessibilityClassName = Switch.class.getName();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400168 }
169
Rohan Shahd5df1752018-01-19 10:45:09 -0800170 /**
171 * Returns the secondary label to use for the given bluetooth connection in the form of the
172 * battery level or bluetooth profile name. If the bluetooth is disabled, there's no connected
173 * devices, or we can't map the bluetooth class to a profile, this instead returns {@code null}.
Rohan Shahd5df1752018-01-19 10:45:09 -0800174 * @param enabled whether bluetooth is enabled
Amin Shaikhc5483402018-04-26 13:51:54 -0400175 * @param connecting whether bluetooth is connecting to a device
Rohan Shahd5df1752018-01-19 10:45:09 -0800176 * @param connected whether there's a device connected via bluetooth
Amin Shaikhd03a7432018-03-01 15:46:55 -0500177 * @param isTransient whether bluetooth is currently in a transient state turning on
Rohan Shahd5df1752018-01-19 10:45:09 -0800178 */
179 @Nullable
Amin Shaikhc5483402018-04-26 13:51:54 -0400180 private String getSecondaryLabel(boolean enabled, boolean connecting, boolean connected,
181 boolean isTransient) {
182 if (connecting) {
183 return mContext.getString(R.string.quick_settings_connecting);
184 }
Amin Shaikhd03a7432018-03-01 15:46:55 -0500185 if (isTransient) {
186 return mContext.getString(R.string.quick_settings_bluetooth_secondary_label_transient);
187 }
Rohan Shahd5df1752018-01-19 10:45:09 -0800188
Amin Shaikh45fb7a72018-04-18 14:14:38 -0400189 List<CachedBluetoothDevice> connectedDevices = mController.getConnectedDevices();
190 if (enabled && connected && !connectedDevices.isEmpty()) {
191 if (connectedDevices.size() > 1) {
192 // TODO(b/76102598): add a new string for "X connected devices" after P
193 return mContext.getResources().getQuantityString(
194 R.plurals.quick_settings_hotspot_secondary_label_num_devices,
195 connectedDevices.size(),
196 connectedDevices.size());
197 }
198
199 CachedBluetoothDevice lastDevice = connectedDevices.get(0);
Rohan Shahd5df1752018-01-19 10:45:09 -0800200 final int batteryLevel = lastDevice.getBatteryLevel();
201
202 if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
203 return mContext.getString(
204 R.string.quick_settings_bluetooth_secondary_label_battery_level,
205 Utils.formatPercentage(batteryLevel));
206
207 } else {
208 final BluetoothClass bluetoothClass = lastDevice.getBtClass();
209 if (bluetoothClass != null) {
Stanley Tng90855412018-11-29 13:47:45 -0800210 if (lastDevice.isHearingAidDevice()) {
211 return mContext.getString(
212 R.string.quick_settings_bluetooth_secondary_label_hearing_aids);
213 } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
Rohan Shahd5df1752018-01-19 10:45:09 -0800214 return mContext.getString(
215 R.string.quick_settings_bluetooth_secondary_label_audio);
216 } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
217 return mContext.getString(
218 R.string.quick_settings_bluetooth_secondary_label_headset);
219 } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HID)) {
220 return mContext.getString(
221 R.string.quick_settings_bluetooth_secondary_label_input);
222 }
223 }
224 }
225 }
226
227 return null;
228 }
229
Selim Cinek4fda7b22014-08-18 22:07:25 +0200230 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400231 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500232 return MetricsEvent.QS_BLUETOOTH;
Chris Wren457a21c2015-05-06 17:50:34 -0400233 }
234
235 @Override
Selim Cinek4fda7b22014-08-18 22:07:25 +0200236 protected String composeChangeAnnouncement() {
237 if (mState.value) {
238 return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_on);
239 } else {
240 return mContext.getString(R.string.accessibility_quick_settings_bluetooth_changed_off);
241 }
242 }
243
Jason Monkc3f42c12016-02-05 12:33:13 -0500244 @Override
245 public boolean isAvailable() {
246 return mController.isBluetoothSupported();
Jason Monkba2318e2015-12-08 09:04:23 -0500247 }
248
John Spurlockd1c86e22014-06-01 00:04:53 -0400249 private final BluetoothController.Callback mCallback = new BluetoothController.Callback() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400250 @Override
Jason Monka7d92b62015-05-27 10:20:37 -0400251 public void onBluetoothStateChange(boolean enabled) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400252 refreshState();
Jason Monkbe3235a2017-04-05 09:29:53 -0400253 if (isShowingDetail()) {
254 mDetailAdapter.updateItems();
Jason Monk3c3b6502017-07-27 15:41:26 -0400255 fireToggleStateChanged(mDetailAdapter.getToggleState());
Jason Monkbe3235a2017-04-05 09:29:53 -0400256 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400257 }
Jason Monka7d92b62015-05-27 10:20:37 -0400258
John Spurlock486b78e2014-07-07 08:37:56 -0400259 @Override
Jason Monkbe3c5db2015-02-04 13:00:55 -0500260 public void onBluetoothDevicesChanged() {
PauloftheWest6cdeaa02014-10-06 09:27:08 -0700261 refreshState();
Jason Monkbe3235a2017-04-05 09:29:53 -0400262 if (isShowingDetail()) {
263 mDetailAdapter.updateItems();
264 }
John Spurlock486b78e2014-07-07 08:37:56 -0400265 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400266 };
John Spurlock486b78e2014-07-07 08:37:56 -0400267
Muyuan Li0e9f5382016-04-27 15:51:15 -0700268 @Override
269 protected DetailAdapter createDetailAdapter() {
270 return new BluetoothDetailAdapter();
271 }
272
Rohan Shahd5df1752018-01-19 10:45:09 -0800273 /**
274 * Bluetooth icon wrapper for Quick Settings with a battery indicator that reflects the
275 * connected device's battery level. This is used instead of
276 * {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to use a context
277 * that reflects dark/light theme attributes.
278 */
jackqdyulei786a4902017-10-24 13:27:53 -0700279 private class BluetoothBatteryTileIcon extends Icon {
Rohan Shahd5df1752018-01-19 10:45:09 -0800280 private int mBatteryLevel;
jackqdyulei4b7f43b2017-10-19 11:05:09 -0700281 private float mIconScale;
Evan Laird4673c382017-09-27 13:00:50 -0400282
Rohan Shahd5df1752018-01-19 10:45:09 -0800283 BluetoothBatteryTileIcon(int batteryLevel, float iconScale) {
284 mBatteryLevel = batteryLevel;
jackqdyulei4b7f43b2017-10-19 11:05:09 -0700285 mIconScale = iconScale;
Evan Laird4673c382017-09-27 13:00:50 -0400286 }
287
288 @Override
289 public Drawable getDrawable(Context context) {
jackqdyuleic9d6a172017-09-18 18:55:41 -0700290 // This method returns Pair<Drawable, String> while first value is the drawable
Rohan Shahd5df1752018-01-19 10:45:09 -0800291 return BluetoothDeviceLayerDrawable.createLayerDrawable(
292 context,
293 R.drawable.ic_qs_bluetooth_connected,
294 mBatteryLevel,
295 mIconScale);
Evan Laird4673c382017-09-27 13:00:50 -0400296 }
297 }
298
Rohan Shah43216482018-02-09 11:07:29 -0800299
300 /**
301 * Bluetooth icon wrapper (when connected with no battery indicator) for Quick Settings. This is
302 * used instead of {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to
303 * use a context that reflects dark/light theme attributes.
304 */
305 private class BluetoothConnectedTileIcon extends Icon {
306
307 BluetoothConnectedTileIcon() {
308 // Do nothing. Default constructor to limit visibility.
309 }
310
311 @Override
312 public Drawable getDrawable(Context context) {
313 // This method returns Pair<Drawable, String> - the first value is the drawable.
314 return context.getDrawable(R.drawable.ic_qs_bluetooth_connected);
315 }
316 }
317
Muyuan Li0e9f5382016-04-27 15:51:15 -0700318 protected class BluetoothDetailAdapter implements DetailAdapter, QSDetailItems.Callback {
Jason Monk9ef03b42017-06-13 12:49:55 -0400319 // We probably won't ever have space in the UI for more than 20 devices, so don't
320 // get info for them.
321 private static final int MAX_DEVICES = 20;
John Spurlock486b78e2014-07-07 08:37:56 -0400322 private QSDetailItems mItems;
323
324 @Override
Jason Monkc06fbb12016-01-08 14:12:18 -0500325 public CharSequence getTitle() {
326 return mContext.getString(R.string.quick_settings_bluetooth_label);
John Spurlock486b78e2014-07-07 08:37:56 -0400327 }
328
329 @Override
330 public Boolean getToggleState() {
331 return mState.value;
332 }
333
334 @Override
Jason Monkfac25382016-07-19 14:13:37 -0400335 public boolean getToggleEnabled() {
336 return mController.getBluetoothState() == BluetoothAdapter.STATE_OFF
337 || mController.getBluetoothState() == BluetoothAdapter.STATE_ON;
338 }
339
340 @Override
John Spurlock486b78e2014-07-07 08:37:56 -0400341 public Intent getSettingsIntent() {
342 return BLUETOOTH_SETTINGS;
343 }
344
345 @Override
346 public void setToggleState(boolean state) {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500347 MetricsLogger.action(mContext, MetricsEvent.QS_BLUETOOTH_TOGGLE, state);
John Spurlock486b78e2014-07-07 08:37:56 -0400348 mController.setBluetoothEnabled(state);
John Spurlock486b78e2014-07-07 08:37:56 -0400349 }
350
351 @Override
Chris Wren457a21c2015-05-06 17:50:34 -0400352 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500353 return MetricsEvent.QS_BLUETOOTH_DETAILS;
Chris Wren457a21c2015-05-06 17:50:34 -0400354 }
355
356 @Override
John Spurlock486b78e2014-07-07 08:37:56 -0400357 public View createDetailView(Context context, View convertView, ViewGroup parent) {
358 mItems = QSDetailItems.convertOrInflate(context, convertView, parent);
359 mItems.setTagSuffix("Bluetooth");
John Spurlock486b78e2014-07-07 08:37:56 -0400360 mItems.setCallback(this);
361 updateItems();
362 setItemsVisible(mState.value);
363 return mItems;
364 }
365
366 public void setItemsVisible(boolean visible) {
367 if (mItems == null) return;
368 mItems.setItemsVisible(visible);
369 }
370
371 private void updateItems() {
372 if (mItems == null) return;
Jason Monkbe3235a2017-04-05 09:29:53 -0400373 if (mController.isBluetoothEnabled()) {
374 mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
375 R.string.quick_settings_bluetooth_detail_empty_text);
376 } else {
377 mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
378 R.string.bt_is_off);
379 }
Jason Monkc17d3292016-01-30 12:49:41 -0500380 ArrayList<Item> items = new ArrayList<Item>();
Jason Monkbe3c5db2015-02-04 13:00:55 -0500381 final Collection<CachedBluetoothDevice> devices = mController.getDevices();
John Spurlock486b78e2014-07-07 08:37:56 -0400382 if (devices != null) {
tturney6f991c22017-04-18 13:59:40 -0700383 int connectedDevices = 0;
Jason Monk9ef03b42017-06-13 12:49:55 -0400384 int count = 0;
Jason Monkbe3c5db2015-02-04 13:00:55 -0500385 for (CachedBluetoothDevice device : devices) {
Jason Monk9ef03b42017-06-13 12:49:55 -0400386 if (mController.getBondState(device) == BluetoothDevice.BOND_NONE) continue;
John Spurlock486b78e2014-07-07 08:37:56 -0400387 final Item item = new Item();
jackqdyulei786a4902017-10-24 13:27:53 -0700388 item.iconResId = R.drawable.ic_qs_bluetooth_on;
Jason Monkbe3c5db2015-02-04 13:00:55 -0500389 item.line1 = device.getName();
tturney6f991c22017-04-18 13:59:40 -0700390 item.tag = device;
Jack Hea46af4e2017-07-26 23:51:23 -0700391 int state = device.getMaxConnectionState();
Jason Monkbe3c5db2015-02-04 13:00:55 -0500392 if (state == BluetoothProfile.STATE_CONNECTED) {
jackqdyulei786a4902017-10-24 13:27:53 -0700393 item.iconResId = R.drawable.ic_qs_bluetooth_connected;
Jack He6258aae2017-06-29 17:01:23 -0700394 int batteryLevel = device.getBatteryLevel();
395 if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
Rohan Shahd5df1752018-01-19 10:45:09 -0800396 item.icon = new BluetoothBatteryTileIcon(batteryLevel,1 /* iconScale */);
Jack He6258aae2017-06-29 17:01:23 -0700397 item.line2 = mContext.getString(
398 R.string.quick_settings_connected_battery_level,
399 Utils.formatPercentage(batteryLevel));
400 } else {
401 item.line2 = mContext.getString(R.string.quick_settings_connected);
402 }
John Spurlock486b78e2014-07-07 08:37:56 -0400403 item.canDisconnect = true;
tturney6f991c22017-04-18 13:59:40 -0700404 items.add(connectedDevices, item);
405 connectedDevices++;
Jason Monkbe3c5db2015-02-04 13:00:55 -0500406 } else if (state == BluetoothProfile.STATE_CONNECTING) {
jackqdyulei786a4902017-10-24 13:27:53 -0700407 item.iconResId = R.drawable.ic_qs_bluetooth_connecting;
John Spurlock486b78e2014-07-07 08:37:56 -0400408 item.line2 = mContext.getString(R.string.quick_settings_connecting);
tturney6f991c22017-04-18 13:59:40 -0700409 items.add(connectedDevices, item);
410 } else {
411 items.add(item);
John Spurlock486b78e2014-07-07 08:37:56 -0400412 }
Jason Monk9ef03b42017-06-13 12:49:55 -0400413 if (++count == MAX_DEVICES) {
414 break;
415 }
John Spurlock486b78e2014-07-07 08:37:56 -0400416 }
417 }
Jason Monkc17d3292016-01-30 12:49:41 -0500418 mItems.setItems(items.toArray(new Item[items.size()]));
Jason Monkbe3c5db2015-02-04 13:00:55 -0500419 }
420
John Spurlock486b78e2014-07-07 08:37:56 -0400421 @Override
422 public void onDetailItemClick(Item item) {
423 if (item == null || item.tag == null) return;
Jason Monkbe3c5db2015-02-04 13:00:55 -0500424 final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
425 if (device != null && device.getMaxConnectionState()
426 == BluetoothProfile.STATE_DISCONNECTED) {
John Spurlock486b78e2014-07-07 08:37:56 -0400427 mController.connect(device);
428 }
429 }
430
431 @Override
432 public void onDetailItemDisconnect(Item item) {
433 if (item == null || item.tag == null) return;
Jason Monkbe3c5db2015-02-04 13:00:55 -0500434 final CachedBluetoothDevice device = (CachedBluetoothDevice) item.tag;
John Spurlock486b78e2014-07-07 08:37:56 -0400435 if (device != null) {
436 mController.disconnect(device);
437 }
438 }
439 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400440}