1 /* 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 17 package com.android.settings.bluetooth; 18 19 import android.app.AlertDialog; 20 import android.bluetooth.BluetoothDevice; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.res.Resources; 24 import android.graphics.drawable.Drawable; 25 import android.os.UserManager; 26 import android.support.v7.preference.Preference; 27 import android.support.v7.preference.PreferenceViewHolder; 28 import android.text.Html; 29 import android.text.TextUtils; 30 import android.util.Pair; 31 import android.util.TypedValue; 32 import android.widget.ImageView; 33 34 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 35 import com.android.settings.R; 36 import com.android.settings.core.instrumentation.MetricsFeatureProvider; 37 import com.android.settings.overlay.FeatureFactory; 38 import com.android.settings.widget.GearPreference; 39 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 40 41 import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; 42 43 /** 44 * BluetoothDevicePreference is the preference type used to display each remote 45 * Bluetooth device in the Bluetooth Settings screen. 46 */ 47 public final class BluetoothDevicePreference extends GearPreference implements 48 CachedBluetoothDevice.Callback { 49 private static final String TAG = "BluetoothDevicePref"; 50 51 private static int sDimAlpha = Integer.MIN_VALUE; 52 53 private final CachedBluetoothDevice mCachedDevice; 54 private final UserManager mUserManager; 55 56 private AlertDialog mDisconnectDialog; 57 private String contentDescription = null; 58 private DeviceListPreferenceFragment mDeviceListPreferenceFragment; 59 /* Talk-back descriptions for various BT icons */ 60 Resources mResources; 61 62 public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice, 63 DeviceListPreferenceFragment deviceListPreferenceFragment) { 64 super(context, null); 65 mResources = getContext().getResources(); 66 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 67 mDeviceListPreferenceFragment = deviceListPreferenceFragment; 68 69 if (sDimAlpha == Integer.MIN_VALUE) { 70 TypedValue outValue = new TypedValue(); 71 context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true); 72 sDimAlpha = (int) (outValue.getFloat() * 255); 73 } 74 75 mCachedDevice = cachedDevice; 76 mCachedDevice.registerCallback(this); 77 78 onDeviceAttributesChanged(); 79 } 80 81 void rebind() { 82 notifyChanged(); 83 } 84 85 @Override 86 protected boolean shouldHideSecondTarget() { 87 return mCachedDevice == null 88 || mCachedDevice.getBondState() != BluetoothDevice.BOND_BONDED 89 || mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH); 90 } 91 92 @Override 93 protected int getSecondTargetResId() { 94 return R.layout.preference_widget_gear; 95 } 96 97 CachedBluetoothDevice getCachedDevice() { 98 return mCachedDevice; 99 } 100 101 @Override 102 protected void onPrepareForRemoval() { 103 super.onPrepareForRemoval(); 104 mCachedDevice.unregisterCallback(this); 105 if (mDisconnectDialog != null) { 106 mDisconnectDialog.dismiss(); 107 mDisconnectDialog = null; 108 } 109 } 110 111 public CachedBluetoothDevice getBluetoothDevice() { 112 return mCachedDevice; 113 } 114 115 public void onDeviceAttributesChanged() { 116 /* 117 * The preference framework takes care of making sure the value has 118 * changed before proceeding. It will also call notifyChanged() if 119 * any preference info has changed from the previous value. 120 */ 121 setTitle(mCachedDevice.getName()); 122 // Null check is done at the framework 123 setSummary(mCachedDevice.getConnectionSummary()); 124 125 final Pair<Drawable, String> pair = Utils.getBtClassDrawableWithDescription(getContext(), 126 mCachedDevice); 127 if (pair.first != null) { 128 setIcon(pair.first); 129 contentDescription = pair.second; 130 } 131 132 // Used to gray out the item 133 setEnabled(!mCachedDevice.isBusy()); 134 135 // Device is only visible in the UI if it has a valid name besides MAC address or when user 136 // allows showing devices without user-friendly name in developer settings 137 setVisible(mDeviceListPreferenceFragment.shouldShowDevicesWithoutNames() 138 || mCachedDevice.hasHumanReadableName()); 139 140 // This could affect ordering, so notify that 141 notifyHierarchyChanged(); 142 } 143 144 @Override 145 public void onBindViewHolder(PreferenceViewHolder view) { 146 // Disable this view if the bluetooth enable/disable preference view is off 147 if (null != findPreferenceInHierarchy("bt_checkbox")) { 148 setDependency("bt_checkbox"); 149 } 150 151 if (mCachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) { 152 ImageView deviceDetails = (ImageView) view.findViewById(R.id.settings_button); 153 154 if (deviceDetails != null) { 155 deviceDetails.setOnClickListener(this); 156 } 157 } 158 final ImageView imageView = (ImageView) view.findViewById(android.R.id.icon); 159 if (imageView != null) { 160 imageView.setContentDescription(contentDescription); 161 } 162 super.onBindViewHolder(view); 163 } 164 165 @Override 166 public boolean equals(Object o) { 167 if ((o == null) || !(o instanceof BluetoothDevicePreference)) { 168 return false; 169 } 170 return mCachedDevice.equals( 171 ((BluetoothDevicePreference) o).mCachedDevice); 172 } 173 174 @Override 175 public int hashCode() { 176 return mCachedDevice.hashCode(); 177 } 178 179 @Override 180 public int compareTo(Preference another) { 181 if (!(another instanceof BluetoothDevicePreference)) { 182 // Rely on default sort 183 return super.compareTo(another); 184 } 185 186 return mCachedDevice 187 .compareTo(((BluetoothDevicePreference) another).mCachedDevice); 188 } 189 190 void onClicked() { 191 Context context = getContext(); 192 int bondState = mCachedDevice.getBondState(); 193 194 final MetricsFeatureProvider metricsFeatureProvider = 195 FeatureFactory.getFactory(context).getMetricsFeatureProvider(); 196 197 if (mCachedDevice.isConnected()) { 198 metricsFeatureProvider.action(context, 199 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT); 200 askDisconnect(); 201 } else if (bondState == BluetoothDevice.BOND_BONDED) { 202 metricsFeatureProvider.action(context, 203 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT); 204 mCachedDevice.connect(true); 205 } else if (bondState == BluetoothDevice.BOND_NONE) { 206 metricsFeatureProvider.action(context, 207 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR); 208 if (!mCachedDevice.hasHumanReadableName()) { 209 metricsFeatureProvider.action(context, 210 MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES); 211 } 212 pair(); 213 } 214 } 215 216 // Show disconnect confirmation dialog for a device. 217 private void askDisconnect() { 218 Context context = getContext(); 219 String name = mCachedDevice.getName(); 220 if (TextUtils.isEmpty(name)) { 221 name = context.getString(R.string.bluetooth_device); 222 } 223 String message = context.getString(R.string.bluetooth_disconnect_all_profiles, name); 224 String title = context.getString(R.string.bluetooth_disconnect_title); 225 226 DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() { 227 public void onClick(DialogInterface dialog, int which) { 228 mCachedDevice.disconnect(); 229 } 230 }; 231 232 mDisconnectDialog = Utils.showDisconnectDialog(context, 233 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message)); 234 } 235 236 private void pair() { 237 if (!mCachedDevice.startPairing()) { 238 Utils.showError(getContext(), mCachedDevice.getName(), 239 R.string.bluetooth_pairing_error_message); 240 } 241 } 242 243 } 244