blob: 2e4654c3994507383d62dd7c5c4fefd53b3af482 [file] [log] [blame]
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -08001/*
2 * Copyright (C) 2008 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.settings.bluetooth;
18
Fan Zhangc7162cd2018-06-18 15:21:41 -070019import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
20
Fan Zhang31b21002019-01-16 13:49:47 -080021import android.app.settings.SettingsEnums;
Jake Hamby436b29e2011-02-07 18:21:25 -080022import android.bluetooth.BluetoothDevice;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080023import android.content.Context;
Jake Hamby436b29e2011-02-07 18:21:25 -080024import android.content.DialogInterface;
pkanwar8fa3d442016-04-29 17:43:24 -070025import android.content.res.Resources;
jackqdyulei76611372017-08-09 09:57:27 -070026import android.graphics.drawable.Drawable;
Maggie Benthall11b69bf2013-04-08 16:05:38 -040027import android.os.UserManager;
Jake Hambyca9812a2011-07-12 11:05:46 -070028import android.text.Html;
Jake Hamby436b29e2011-02-07 18:21:25 -080029import android.text.TextUtils;
pkanwar8fa3d442016-04-29 17:43:24 -070030import android.util.Pair;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080031import android.util.TypedValue;
timpeng0dde3a32019-04-12 16:53:56 +080032import android.view.View;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080033import android.widget.ImageView;
Jake Hambyc090feb2010-12-07 16:27:21 -080034
hughchen14e0fe22019-07-23 16:18:15 +080035import androidx.annotation.IntDef;
jackqdyulei36948ce2019-03-29 15:00:07 -070036import androidx.annotation.VisibleForTesting;
Fan Zhang23f8d592018-08-28 15:11:40 -070037import androidx.appcompat.app.AlertDialog;
38import androidx.preference.Preference;
39import androidx.preference.PreferenceViewHolder;
40
Jake Hamby436b29e2011-02-07 18:21:25 -080041import com.android.settings.R;
Doris Ling133b0962017-03-09 17:35:22 -080042import com.android.settings.overlay.FeatureFactory;
Fan Zhang00d8ff52017-04-10 16:26:42 -070043import com.android.settings.widget.GearPreference;
hughchen9d4b6342019-03-25 18:06:21 +080044import com.android.settingslib.bluetooth.BluetoothUtils;
Fan Zhangc7162cd2018-06-18 15:21:41 -070045import com.android.settingslib.bluetooth.CachedBluetoothDevice;
Leif Hendrik Wilden28dee1f2018-01-11 10:15:36 -080046import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080047
hughchen14e0fe22019-07-23 16:18:15 +080048import java.lang.annotation.Retention;
49import java.lang.annotation.RetentionPolicy;
50
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080051/**
52 * BluetoothDevicePreference is the preference type used to display each remote
53 * Bluetooth device in the Bluetooth Settings screen.
54 */
Fan Zhang00d8ff52017-04-10 16:26:42 -070055public final class BluetoothDevicePreference extends GearPreference implements
56 CachedBluetoothDevice.Callback {
57 private static final String TAG = "BluetoothDevicePref";
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080058
59 private static int sDimAlpha = Integer.MIN_VALUE;
Michael Chane6531e22009-09-30 14:26:10 -070060
hughchen14e0fe22019-07-23 16:18:15 +080061 @Retention(RetentionPolicy.SOURCE)
62 @IntDef({SortType.TYPE_DEFAULT,
hughchen043e43f2020-02-03 13:59:06 +080063 SortType.TYPE_FIFO,
64 SortType.TYPE_NO_SORT})
hughchen14e0fe22019-07-23 16:18:15 +080065 public @interface SortType {
66 int TYPE_DEFAULT = 1;
67 int TYPE_FIFO = 2;
hughchen043e43f2020-02-03 13:59:06 +080068 int TYPE_NO_SORT = 3;
hughchen14e0fe22019-07-23 16:18:15 +080069 }
70
Jake Hamby39ef2252010-12-16 18:33:28 -080071 private final CachedBluetoothDevice mCachedDevice;
Fan Zhang00d8ff52017-04-10 16:26:42 -070072 private final UserManager mUserManager;
jackqdyuleicf1ce052017-11-16 16:06:14 -080073 private final boolean mShowDevicesWithoutNames;
hughchen14e0fe22019-07-23 16:18:15 +080074 private final long mCurrentTime;
75 private final int mType;
Amith Yamasani48e90002010-10-26 13:44:33 -070076
Jake Hamby436b29e2011-02-07 18:21:25 -080077 private AlertDialog mDisconnectDialog;
pkanwar8fa3d442016-04-29 17:43:24 -070078 private String contentDescription = null;
hughchen7ef4fd82018-04-24 15:46:30 +080079 private boolean mHideSecondTarget = false;
jackqdyulei36948ce2019-03-29 15:00:07 -070080 @VisibleForTesting
81 boolean mNeedNotifyHierarchyChanged = false;
pkanwar8fa3d442016-04-29 17:43:24 -070082 /* Talk-back descriptions for various BT icons */
Antony Sargent04a3b212017-05-04 15:06:32 -070083 Resources mResources;
pkanwar8fa3d442016-04-29 17:43:24 -070084
Jack He19ba3202017-05-31 18:37:28 -070085 public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice,
hughchen14e0fe22019-07-23 16:18:15 +080086 boolean showDeviceWithoutNames, @SortType int type) {
Fan Zhang00d8ff52017-04-10 16:26:42 -070087 super(context, null);
Antony Sargent04a3b212017-05-04 15:06:32 -070088 mResources = getContext().getResources();
Fan Zhang00d8ff52017-04-10 16:26:42 -070089 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
jackqdyuleicf1ce052017-11-16 16:06:14 -080090 mShowDevicesWithoutNames = showDeviceWithoutNames;
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080091
92 if (sDimAlpha == Integer.MIN_VALUE) {
93 TypedValue outValue = new TypedValue();
94 context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
Jake Hamby436b29e2011-02-07 18:21:25 -080095 sDimAlpha = (int) (outValue.getFloat() * 255);
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -080096 }
Michael Chane6531e22009-09-30 14:26:10 -070097
Nick Pellyd63c0112009-08-14 18:43:18 -070098 mCachedDevice = cachedDevice;
hughchen14e0fe22019-07-23 16:18:15 +080099 mCurrentTime = System.currentTimeMillis();
100 mType = type;
Michael Chane6531e22009-09-30 14:26:10 -0700101
Jake Hambyc090feb2010-12-07 16:27:21 -0800102 onDeviceAttributesChanged();
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800103 }
Michael Chane6531e22009-09-30 14:26:10 -0700104
jackqdyulei36948ce2019-03-29 15:00:07 -0700105 public void setNeedNotifyHierarchyChanged(boolean needNotifyHierarchyChanged) {
106 mNeedNotifyHierarchyChanged = needNotifyHierarchyChanged;
Jason Monk2071eda2016-02-25 13:55:48 -0500107 }
108
Fan Zhang00d8ff52017-04-10 16:26:42 -0700109 @Override
110 protected boolean shouldHideSecondTarget() {
111 return mCachedDevice == null
112 || mCachedDevice.getBondState() != BluetoothDevice.BOND_BONDED
hughchen7ef4fd82018-04-24 15:46:30 +0800113 || mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH)
114 || mHideSecondTarget;
Amith Yamasani48e90002010-10-26 13:44:33 -0700115 }
116
Fan Zhang00d8ff52017-04-10 16:26:42 -0700117 @Override
118 protected int getSecondTargetResId() {
119 return R.layout.preference_widget_gear;
120 }
121
122 CachedBluetoothDevice getCachedDevice() {
123 return mCachedDevice;
Doris Ling457c3cb2017-03-28 19:53:31 +0000124 }
125
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800126 @Override
127 protected void onPrepareForRemoval() {
128 super.onPrepareForRemoval();
Jake Hamby436b29e2011-02-07 18:21:25 -0800129 if (mDisconnectDialog != null) {
130 mDisconnectDialog.dismiss();
131 mDisconnectDialog = null;
132 }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800133 }
134
Hugh Chen194dd882020-09-02 17:00:13 +0800135 @Override
136 public void onAttached() {
137 super.onAttached();
138 mCachedDevice.registerCallback(this);
139 }
140
141 @Override
142 public void onDetached() {
143 super.onDetached();
144 mCachedDevice.unregisterCallback(this);
145 }
146
Fan Zhang00d8ff52017-04-10 16:26:42 -0700147 public CachedBluetoothDevice getBluetoothDevice() {
148 return mCachedDevice;
149 }
150
hughchen7ef4fd82018-04-24 15:46:30 +0800151 public void hideSecondTarget(boolean hideSecondTarget) {
152 mHideSecondTarget = hideSecondTarget;
153 }
154
Jake Hambyc090feb2010-12-07 16:27:21 -0800155 public void onDeviceAttributesChanged() {
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800156 /*
157 * The preference framework takes care of making sure the value has
Jake Hamby436b29e2011-02-07 18:21:25 -0800158 * changed before proceeding. It will also call notifyChanged() if
159 * any preference info has changed from the previous value.
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800160 */
Nick Pellyd63c0112009-08-14 18:43:18 -0700161 setTitle(mCachedDevice.getName());
Jack He04f59672017-06-28 12:08:55 -0700162 // Null check is done at the framework
163 setSummary(mCachedDevice.getConnectionSummary());
pkanwar8fa3d442016-04-29 17:43:24 -0700164
hughchen9d4b6342019-03-25 18:06:21 +0800165 final Pair<Drawable, String> pair =
166 BluetoothUtils.getBtRainbowDrawableWithDescription(getContext(), mCachedDevice);
jackqdyulei76611372017-08-09 09:57:27 -0700167 if (pair.first != null) {
pkanwar8fa3d442016-04-29 17:43:24 -0700168 setIcon(pair.first);
169 contentDescription = pair.second;
Jake Hamby79be0b32011-08-01 16:36:35 -0700170 }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800171
172 // Used to gray out the item
Jake Hamby436b29e2011-02-07 18:21:25 -0800173 setEnabled(!mCachedDevice.isBusy());
Michael Chane6531e22009-09-30 14:26:10 -0700174
Jack He19ba3202017-05-31 18:37:28 -0700175 // Device is only visible in the UI if it has a valid name besides MAC address or when user
176 // allows showing devices without user-friendly name in developer settings
jackqdyuleicf1ce052017-11-16 16:06:14 -0800177 setVisible(mShowDevicesWithoutNames || mCachedDevice.hasHumanReadableName());
Jack He19ba3202017-05-31 18:37:28 -0700178
Jake Hamby436b29e2011-02-07 18:21:25 -0800179 // This could affect ordering, so notify that
jackqdyulei36948ce2019-03-29 15:00:07 -0700180 if (mNeedNotifyHierarchyChanged) {
181 notifyHierarchyChanged();
182 }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800183 }
184
185 @Override
Jason Monk39b46742015-09-10 15:52:51 -0400186 public void onBindViewHolder(PreferenceViewHolder view) {
Michael Chan132b2142009-07-01 14:06:43 -0700187 // Disable this view if the bluetooth enable/disable preference view is off
Jake Hambyc090feb2010-12-07 16:27:21 -0800188 if (null != findPreferenceInHierarchy("bt_checkbox")) {
Yue Lixina41e2f92009-07-09 16:48:52 +0800189 setDependency("bt_checkbox");
190 }
Michael Chan132b2142009-07-01 14:06:43 -0700191
Jake Hamby79be0b32011-08-01 16:36:35 -0700192 if (mCachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
Fan Zhang00d8ff52017-04-10 16:26:42 -0700193 ImageView deviceDetails = (ImageView) view.findViewById(R.id.settings_button);
PauloftheWestda431352014-06-18 12:00:31 -0700194
Jake Hamby79be0b32011-08-01 16:36:35 -0700195 if (deviceDetails != null) {
196 deviceDetails.setOnClickListener(this);
Jake Hamby79be0b32011-08-01 16:36:35 -0700197 }
198 }
pkanwar8fa3d442016-04-29 17:43:24 -0700199 final ImageView imageView = (ImageView) view.findViewById(android.R.id.icon);
200 if (imageView != null) {
201 imageView.setContentDescription(contentDescription);
timpeng0dde3a32019-04-12 16:53:56 +0800202 // Set property to prevent Talkback from reading out.
203 imageView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
jackqdyulei36ce63d2019-03-19 10:18:19 -0700204 imageView.setElevation(
205 getContext().getResources().getDimension(R.dimen.bt_icon_elevation));
pkanwar8fa3d442016-04-29 17:43:24 -0700206 }
Jason Monk39b46742015-09-10 15:52:51 -0400207 super.onBindViewHolder(view);
Amith Yamasani48e90002010-10-26 13:44:33 -0700208 }
209
Gilles Debunne4346cda2011-06-28 14:01:50 -0700210 @Override
Jake Hamby436b29e2011-02-07 18:21:25 -0800211 public boolean equals(Object o) {
212 if ((o == null) || !(o instanceof BluetoothDevicePreference)) {
213 return false;
214 }
215 return mCachedDevice.equals(
216 ((BluetoothDevicePreference) o).mCachedDevice);
217 }
218
Gilles Debunne4346cda2011-06-28 14:01:50 -0700219 @Override
Jake Hamby436b29e2011-02-07 18:21:25 -0800220 public int hashCode() {
221 return mCachedDevice.hashCode();
222 }
223
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800224 @Override
225 public int compareTo(Preference another) {
226 if (!(another instanceof BluetoothDevicePreference)) {
Gilles Debunne4346cda2011-06-28 14:01:50 -0700227 // Rely on default sort
228 return super.compareTo(another);
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800229 }
Michael Chane6531e22009-09-30 14:26:10 -0700230
hughchen14e0fe22019-07-23 16:18:15 +0800231 switch (mType) {
232 case SortType.TYPE_DEFAULT:
233 return mCachedDevice
234 .compareTo(((BluetoothDevicePreference) another).mCachedDevice);
235 case SortType.TYPE_FIFO:
hughchen41eab2c2019-08-14 16:27:12 +0800236 return mCurrentTime > ((BluetoothDevicePreference) another).mCurrentTime ? 1 : -1;
hughchen14e0fe22019-07-23 16:18:15 +0800237 default:
238 return super.compareTo(another);
239 }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800240 }
Michael Chane6531e22009-09-30 14:26:10 -0700241
Jake Hamby436b29e2011-02-07 18:21:25 -0800242 void onClicked() {
Jack He5b017f72017-08-30 19:18:40 -0700243 Context context = getContext();
Jake Hamby436b29e2011-02-07 18:21:25 -0800244 int bondState = mCachedDevice.getBondState();
245
Doris Ling133b0962017-03-09 17:35:22 -0800246 final MetricsFeatureProvider metricsFeatureProvider =
Jack He5b017f72017-08-30 19:18:40 -0700247 FeatureFactory.getFactory(context).getMetricsFeatureProvider();
Doris Ling133b0962017-03-09 17:35:22 -0800248
Jake Hamby436b29e2011-02-07 18:21:25 -0800249 if (mCachedDevice.isConnected()) {
Jack He5b017f72017-08-30 19:18:40 -0700250 metricsFeatureProvider.action(context,
Fan Zhang31b21002019-01-16 13:49:47 -0800251 SettingsEnums.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
Paul Westc0963dd2014-08-27 20:31:38 +0000252 askDisconnect();
Jake Hamby436b29e2011-02-07 18:21:25 -0800253 } else if (bondState == BluetoothDevice.BOND_BONDED) {
Jack He5b017f72017-08-30 19:18:40 -0700254 metricsFeatureProvider.action(context,
Fan Zhang31b21002019-01-16 13:49:47 -0800255 SettingsEnums.ACTION_SETTINGS_BLUETOOTH_CONNECT);
hughchen0ab32832020-01-14 16:07:59 +0800256 mCachedDevice.connect();
Jake Hamby436b29e2011-02-07 18:21:25 -0800257 } else if (bondState == BluetoothDevice.BOND_NONE) {
Jack He5b017f72017-08-30 19:18:40 -0700258 metricsFeatureProvider.action(context,
Fan Zhang31b21002019-01-16 13:49:47 -0800259 SettingsEnums.ACTION_SETTINGS_BLUETOOTH_PAIR);
Jack He5b017f72017-08-30 19:18:40 -0700260 if (!mCachedDevice.hasHumanReadableName()) {
261 metricsFeatureProvider.action(context,
Fan Zhang31b21002019-01-16 13:49:47 -0800262 SettingsEnums.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
Jack He5b017f72017-08-30 19:18:40 -0700263 }
Jake Hamby436b29e2011-02-07 18:21:25 -0800264 pair();
265 }
266 }
267
268 // Show disconnect confirmation dialog for a device.
269 private void askDisconnect() {
270 Context context = getContext();
271 String name = mCachedDevice.getName();
272 if (TextUtils.isEmpty(name)) {
273 name = context.getString(R.string.bluetooth_device);
274 }
Jake Hambyca9812a2011-07-12 11:05:46 -0700275 String message = context.getString(R.string.bluetooth_disconnect_all_profiles, name);
276 String title = context.getString(R.string.bluetooth_disconnect_title);
Jake Hamby436b29e2011-02-07 18:21:25 -0800277
278 DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() {
279 public void onClick(DialogInterface dialog, int which) {
280 mCachedDevice.disconnect();
281 }
282 };
283
284 mDisconnectDialog = Utils.showDisconnectDialog(context,
Jake Hambyca9812a2011-07-12 11:05:46 -0700285 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message));
Jake Hamby436b29e2011-02-07 18:21:25 -0800286 }
287
288 private void pair() {
289 if (!mCachedDevice.startPairing()) {
290 Utils.showError(getContext(), mCachedDevice.getName(),
291 R.string.bluetooth_pairing_error_message);
292 }
293 }
The Android Open Source Projectafc4ab22009-03-03 19:32:34 -0800294}