BluetoothDevicePreference.java revision c0963dd7c5a35fdb007a2954644c1352060350d1
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 17package com.android.settings.bluetooth; 18 19import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH; 20 21import android.app.AlertDialog; 22import android.bluetooth.BluetoothClass; 23import android.bluetooth.BluetoothDevice; 24import android.bluetooth.BluetoothProfile; 25import android.content.Context; 26import android.content.DialogInterface; 27import android.os.UserManager; 28import android.preference.Preference; 29import android.text.Html; 30import android.text.TextUtils; 31import android.util.Log; 32import android.util.TypedValue; 33import android.view.View; 34import android.view.View.OnClickListener; 35import android.widget.ImageView; 36 37import com.android.settings.R; 38import com.android.settings.search.Index; 39import com.android.settings.search.SearchIndexableRaw; 40 41import java.util.List; 42 43/** 44 * BluetoothDevicePreference is the preference type used to display each remote 45 * Bluetooth device in the Bluetooth Settings screen. 46 */ 47public final class BluetoothDevicePreference extends Preference implements 48 CachedBluetoothDevice.Callback, OnClickListener { 49 private static final String TAG = "BluetoothDevicePreference"; 50 51 private static int sDimAlpha = Integer.MIN_VALUE; 52 53 private final CachedBluetoothDevice mCachedDevice; 54 55 private OnClickListener mOnSettingsClickListener; 56 57 private AlertDialog mDisconnectDialog; 58 59 public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) { 60 super(context); 61 62 if (sDimAlpha == Integer.MIN_VALUE) { 63 TypedValue outValue = new TypedValue(); 64 context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true); 65 sDimAlpha = (int) (outValue.getFloat() * 255); 66 } 67 68 mCachedDevice = cachedDevice; 69 70 if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) { 71 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 72 if (! um.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH)) { 73 setWidgetLayoutResource(R.layout.preference_bluetooth); 74 } 75 } 76 77 mCachedDevice.registerCallback(this); 78 79 onDeviceAttributesChanged(); 80 } 81 82 CachedBluetoothDevice getCachedDevice() { 83 return mCachedDevice; 84 } 85 86 public void setOnSettingsClickListener(OnClickListener listener) { 87 mOnSettingsClickListener = listener; 88 } 89 90 @Override 91 protected void onPrepareForRemoval() { 92 super.onPrepareForRemoval(); 93 mCachedDevice.unregisterCallback(this); 94 if (mDisconnectDialog != null) { 95 mDisconnectDialog.dismiss(); 96 mDisconnectDialog = null; 97 } 98 } 99 100 public void onDeviceAttributesChanged() { 101 /* 102 * The preference framework takes care of making sure the value has 103 * changed before proceeding. It will also call notifyChanged() if 104 * any preference info has changed from the previous value. 105 */ 106 setTitle(mCachedDevice.getName()); 107 108 int summaryResId = getConnectionSummary(); 109 if (summaryResId != 0) { 110 setSummary(summaryResId); 111 } else { 112 setSummary(null); // empty summary for unpaired devices 113 } 114 115 int iconResId = getBtClassDrawable(); 116 if (iconResId != 0) { 117 setIcon(iconResId); 118 } 119 120 // Used to gray out the item 121 setEnabled(!mCachedDevice.isBusy()); 122 123 // This could affect ordering, so notify that 124 notifyHierarchyChanged(); 125 } 126 127 @Override 128 protected void onBindView(View view) { 129 // Disable this view if the bluetooth enable/disable preference view is off 130 if (null != findPreferenceInHierarchy("bt_checkbox")) { 131 setDependency("bt_checkbox"); 132 } 133 134 if (mCachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) { 135 ImageView deviceDetails = (ImageView) view.findViewById(R.id.deviceDetails); 136 137 if (deviceDetails != null) { 138 deviceDetails.setOnClickListener(this); 139 deviceDetails.setTag(mCachedDevice); 140 } 141 } 142 143 super.onBindView(view); 144 } 145 146 public void onClick(View v) { 147 // Should never be null by construction 148 if (mOnSettingsClickListener != null) { 149 mOnSettingsClickListener.onClick(v); 150 } 151 } 152 153 @Override 154 public boolean equals(Object o) { 155 if ((o == null) || !(o instanceof BluetoothDevicePreference)) { 156 return false; 157 } 158 return mCachedDevice.equals( 159 ((BluetoothDevicePreference) o).mCachedDevice); 160 } 161 162 @Override 163 public int hashCode() { 164 return mCachedDevice.hashCode(); 165 } 166 167 @Override 168 public int compareTo(Preference another) { 169 if (!(another instanceof BluetoothDevicePreference)) { 170 // Rely on default sort 171 return super.compareTo(another); 172 } 173 174 return mCachedDevice 175 .compareTo(((BluetoothDevicePreference) another).mCachedDevice); 176 } 177 178 void onClicked() { 179 int bondState = mCachedDevice.getBondState(); 180 181 if (mCachedDevice.isConnected()) { 182 askDisconnect(); 183 } else if (bondState == BluetoothDevice.BOND_BONDED) { 184 mCachedDevice.connect(true); 185 } else if (bondState == BluetoothDevice.BOND_NONE) { 186 pair(); 187 } 188 } 189 190 // Show disconnect confirmation dialog for a device. 191 private void askDisconnect() { 192 Context context = getContext(); 193 String name = mCachedDevice.getName(); 194 if (TextUtils.isEmpty(name)) { 195 name = context.getString(R.string.bluetooth_device); 196 } 197 String message = context.getString(R.string.bluetooth_disconnect_all_profiles, name); 198 String title = context.getString(R.string.bluetooth_disconnect_title); 199 200 DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() { 201 public void onClick(DialogInterface dialog, int which) { 202 mCachedDevice.disconnect(); 203 } 204 }; 205 206 mDisconnectDialog = Utils.showDisconnectDialog(context, 207 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message)); 208 } 209 210 private void pair() { 211 if (!mCachedDevice.startPairing()) { 212 Utils.showError(getContext(), mCachedDevice.getName(), 213 R.string.bluetooth_pairing_error_message); 214 } else { 215 final Context context = getContext(); 216 217 SearchIndexableRaw data = new SearchIndexableRaw(context); 218 data.className = BluetoothSettings.class.getName(); 219 data.title = mCachedDevice.getName(); 220 data.screenTitle = context.getResources().getString(R.string.bluetooth_settings); 221 data.iconResId = R.drawable.ic_settings_bluetooth2; 222 data.enabled = true; 223 224 Index.getInstance(context).updateFromSearchIndexableData(data); 225 } 226 } 227 228 private int getConnectionSummary() { 229 final CachedBluetoothDevice cachedDevice = mCachedDevice; 230 231 boolean profileConnected = false; // at least one profile is connected 232 boolean a2dpNotConnected = false; // A2DP is preferred but not connected 233 boolean headsetNotConnected = false; // Headset is preferred but not connected 234 235 for (LocalBluetoothProfile profile : cachedDevice.getProfiles()) { 236 int connectionStatus = cachedDevice.getProfileConnectionState(profile); 237 238 switch (connectionStatus) { 239 case BluetoothProfile.STATE_CONNECTING: 240 case BluetoothProfile.STATE_DISCONNECTING: 241 return Utils.getConnectionStateSummary(connectionStatus); 242 243 case BluetoothProfile.STATE_CONNECTED: 244 profileConnected = true; 245 break; 246 247 case BluetoothProfile.STATE_DISCONNECTED: 248 if (profile.isProfileReady()) { 249 if (profile instanceof A2dpProfile) { 250 a2dpNotConnected = true; 251 } else if (profile instanceof HeadsetProfile) { 252 headsetNotConnected = true; 253 } 254 } 255 break; 256 } 257 } 258 259 if (profileConnected) { 260 if (a2dpNotConnected && headsetNotConnected) { 261 return R.string.bluetooth_connected_no_headset_no_a2dp; 262 } else if (a2dpNotConnected) { 263 return R.string.bluetooth_connected_no_a2dp; 264 } else if (headsetNotConnected) { 265 return R.string.bluetooth_connected_no_headset; 266 } else { 267 return R.string.bluetooth_connected; 268 } 269 } 270 271 switch (cachedDevice.getBondState()) { 272 case BluetoothDevice.BOND_BONDING: 273 return R.string.bluetooth_pairing; 274 275 case BluetoothDevice.BOND_BONDED: 276 case BluetoothDevice.BOND_NONE: 277 default: 278 return 0; 279 } 280 } 281 282 private int getBtClassDrawable() { 283 BluetoothClass btClass = mCachedDevice.getBtClass(); 284 if (btClass != null) { 285 switch (btClass.getMajorDeviceClass()) { 286 case BluetoothClass.Device.Major.COMPUTER: 287 return R.drawable.ic_bt_laptop; 288 289 case BluetoothClass.Device.Major.PHONE: 290 return R.drawable.ic_bt_cellphone; 291 292 case BluetoothClass.Device.Major.PERIPHERAL: 293 return HidProfile.getHidClassDrawable(btClass); 294 295 case BluetoothClass.Device.Major.IMAGING: 296 return R.drawable.ic_bt_imaging; 297 298 default: 299 // unrecognized device class; continue 300 } 301 } else { 302 Log.w(TAG, "mBtClass is null"); 303 } 304 305 List<LocalBluetoothProfile> profiles = mCachedDevice.getProfiles(); 306 for (LocalBluetoothProfile profile : profiles) { 307 int resId = profile.getDrawableResource(btClass); 308 if (resId != 0) { 309 return resId; 310 } 311 } 312 if (btClass != null) { 313 if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) { 314 return R.drawable.ic_bt_headphones_a2dp; 315 316 } 317 if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) { 318 return R.drawable.ic_bt_headset_hfp; 319 } 320 } 321 return R.drawable.ic_settings_bluetooth2; 322 } 323} 324