DeviceProfilesSettings.java revision fa46de5c68f078a595aa61d61ceae6b7951c3aa7
1/* 2 * Copyright (C) 2011 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 android.app.AlertDialog; 20import android.bluetooth.BluetoothDevice; 21import android.bluetooth.BluetoothProfile; 22import android.content.Context; 23import android.content.DialogInterface; 24import android.os.Bundle; 25import android.preference.CheckBoxPreference; 26import android.preference.EditTextPreference; 27import android.preference.Preference; 28import android.preference.PreferenceGroup; 29import android.preference.PreferenceScreen; 30import android.text.Html; 31import android.text.TextUtils; 32import android.util.Log; 33import android.view.View; 34import android.widget.EditText; 35import android.text.TextWatcher; 36import android.app.Dialog; 37import android.widget.Button; 38import android.text.Editable; 39import com.android.settings.R; 40import com.android.settings.SettingsPreferenceFragment; 41import com.android.settings.search.Index; 42import com.android.settings.search.SearchIndexableRaw; 43 44import java.util.HashMap; 45 46/** 47 * This preference fragment presents the user with all of the profiles 48 * for a particular device, and allows them to be individually connected 49 * (or disconnected). 50 */ 51public final class DeviceProfilesSettings extends SettingsPreferenceFragment 52 implements CachedBluetoothDevice.Callback, Preference.OnPreferenceChangeListener { 53 private static final String TAG = "DeviceProfilesSettings"; 54 55 private static final String KEY_RENAME_DEVICE = "rename_device"; 56 private static final String KEY_PROFILE_CONTAINER = "profile_container"; 57 private static final String KEY_UNPAIR = "unpair"; 58 59 public static final String EXTRA_DEVICE = "device"; 60 private RenameEditTextPreference mRenameDeviceNamePref; 61 private LocalBluetoothManager mManager; 62 private CachedBluetoothDevice mCachedDevice; 63 private LocalBluetoothProfileManager mProfileManager; 64 65 private PreferenceGroup mProfileContainer; 66 private EditTextPreference mDeviceNamePref; 67 68 private final HashMap<LocalBluetoothProfile, CheckBoxPreference> mAutoConnectPrefs 69 = new HashMap<LocalBluetoothProfile, CheckBoxPreference>(); 70 71 private AlertDialog mDisconnectDialog; 72 private boolean mProfileGroupIsRemoved; 73 74 private class RenameEditTextPreference implements TextWatcher{ 75 public void afterTextChanged(Editable s) { 76 Dialog d = mDeviceNamePref.getDialog(); 77 if (d instanceof AlertDialog) { 78 ((AlertDialog) d).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(s.length() > 0); 79 } 80 } 81 82 // TextWatcher interface 83 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 84 // not used 85 } 86 87 // TextWatcher interface 88 public void onTextChanged(CharSequence s, int start, int before, int count) { 89 // not used 90 } 91 } 92 93 @Override 94 public void onCreate(Bundle savedInstanceState) { 95 super.onCreate(savedInstanceState); 96 97 BluetoothDevice device; 98 if (savedInstanceState != null) { 99 device = savedInstanceState.getParcelable(EXTRA_DEVICE); 100 } else { 101 Bundle args = getArguments(); 102 device = args.getParcelable(EXTRA_DEVICE); 103 } 104 105 addPreferencesFromResource(R.xml.bluetooth_device_advanced); 106 getPreferenceScreen().setOrderingAsAdded(false); 107 mProfileContainer = (PreferenceGroup) findPreference(KEY_PROFILE_CONTAINER); 108 mDeviceNamePref = (EditTextPreference) findPreference(KEY_RENAME_DEVICE); 109 110 if (device == null) { 111 Log.w(TAG, "Activity started without a remote Bluetooth device"); 112 finish(); 113 return; // TODO: test this failure path 114 } 115 mRenameDeviceNamePref = new RenameEditTextPreference(); 116 mManager = LocalBluetoothManager.getInstance(getActivity()); 117 CachedBluetoothDeviceManager deviceManager = 118 mManager.getCachedDeviceManager(); 119 mProfileManager = mManager.getProfileManager(); 120 mCachedDevice = deviceManager.findDevice(device); 121 if (mCachedDevice == null) { 122 Log.w(TAG, "Device not found, cannot connect to it"); 123 finish(); 124 return; // TODO: test this failure path 125 } 126 127 String deviceName = mCachedDevice.getName(); 128 mDeviceNamePref.setSummary(deviceName); 129 mDeviceNamePref.setText(deviceName); 130 mDeviceNamePref.setOnPreferenceChangeListener(this); 131 132 // Add a preference for each profile 133 addPreferencesForProfiles(); 134 } 135 136 @Override 137 public void onDestroy() { 138 super.onDestroy(); 139 if (mDisconnectDialog != null) { 140 mDisconnectDialog.dismiss(); 141 mDisconnectDialog = null; 142 } 143 } 144 145 @Override 146 public void onSaveInstanceState(Bundle outState) { 147 super.onSaveInstanceState(outState); 148 outState.putParcelable(EXTRA_DEVICE, mCachedDevice.getDevice()); 149 } 150 151 @Override 152 public void onResume() { 153 super.onResume(); 154 155 mManager.setForegroundActivity(getActivity()); 156 mCachedDevice.registerCallback(this); 157 if(mCachedDevice.getBondState() == BluetoothDevice.BOND_NONE) 158 finish(); 159 refresh(); 160 EditText et = mDeviceNamePref.getEditText(); 161 if (et != null) { 162 et.addTextChangedListener(mRenameDeviceNamePref); 163 Dialog d = mDeviceNamePref.getDialog(); 164 if (d instanceof AlertDialog) { 165 Button b = ((AlertDialog) d).getButton(AlertDialog.BUTTON_POSITIVE); 166 b.setEnabled(et.getText().length() > 0); 167 } 168 } 169 } 170 171 @Override 172 public void onPause() { 173 super.onPause(); 174 175 mCachedDevice.unregisterCallback(this); 176 mManager.setForegroundActivity(null); 177 } 178 179 private void addPreferencesForProfiles() { 180 for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) { 181 Preference pref = createProfilePreference(profile); 182 mProfileContainer.addPreference(pref); 183 } 184 showOrHideProfileGroup(); 185 } 186 187 private void showOrHideProfileGroup() { 188 int numProfiles = mProfileContainer.getPreferenceCount(); 189 if (!mProfileGroupIsRemoved && numProfiles == 0) { 190 getPreferenceScreen().removePreference(mProfileContainer); 191 mProfileGroupIsRemoved = true; 192 } else if (mProfileGroupIsRemoved && numProfiles != 0) { 193 getPreferenceScreen().addPreference(mProfileContainer); 194 mProfileGroupIsRemoved = false; 195 } 196 } 197 198 /** 199 * Creates a checkbox preference for the particular profile. The key will be 200 * the profile's name. 201 * 202 * @param profile The profile for which the preference controls. 203 * @return A preference that allows the user to choose whether this profile 204 * will be connected to. 205 */ 206 private CheckBoxPreference createProfilePreference(LocalBluetoothProfile profile) { 207 CheckBoxPreference pref = new CheckBoxPreference(getActivity()); 208 pref.setKey(profile.toString()); 209 pref.setTitle(profile.getNameResource(mCachedDevice.getDevice())); 210 pref.setPersistent(false); 211 pref.setOrder(getProfilePreferenceIndex(profile.getOrdinal())); 212 pref.setOnPreferenceChangeListener(this); 213 214 int iconResource = profile.getDrawableResource(mCachedDevice.getBtClass()); 215 if (iconResource != 0) { 216 pref.setIcon(getResources().getDrawable(iconResource)); 217 } 218 219 /** 220 * Gray out profile while connecting and disconnecting 221 */ 222 pref.setEnabled(!mCachedDevice.isBusy()); 223 224 refreshProfilePreference(pref, profile); 225 226 return pref; 227 } 228 229 @Override 230 public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) { 231 String key = preference.getKey(); 232 if (key.equals(KEY_UNPAIR)) { 233 unpairDevice(); 234 finish(); 235 final Context context = preference.getContext(); 236 237 SearchIndexableRaw data = new SearchIndexableRaw(context); 238 data.className = BluetoothSettings.class.getName(); 239 data.title = mCachedDevice.getName(); 240 data.screenTitle = context.getResources().getString(R.string.bluetooth_settings); 241 data.iconResId = R.drawable.ic_settings_bluetooth2; 242 data.enabled = false; 243 244 Index.getInstance(context).updateFromSearchIndexableData(data); 245 return true; 246 } 247 248 return super.onPreferenceTreeClick(screen, preference); 249 } 250 251 public boolean onPreferenceChange(Preference preference, Object newValue) { 252 if (preference == mDeviceNamePref) { 253 mCachedDevice.setName((String) newValue); 254 } else if (preference instanceof CheckBoxPreference) { 255 LocalBluetoothProfile prof = getProfileOf(preference); 256 onProfileClicked(prof, (CheckBoxPreference) preference); 257 return false; // checkbox will update from onDeviceAttributesChanged() callback 258 } else { 259 return false; 260 } 261 262 return true; 263 } 264 265 private void onProfileClicked(LocalBluetoothProfile profile, CheckBoxPreference profilePref) { 266 BluetoothDevice device = mCachedDevice.getDevice(); 267 268 int status = profile.getConnectionStatus(device); 269 boolean isConnected = 270 status == BluetoothProfile.STATE_CONNECTED; 271 272 if (isConnected) { 273 askDisconnect(getActivity(), profile); 274 } else { 275 if (profile.isPreferred(device)) { 276 // profile is preferred but not connected: disable auto-connect 277 profile.setPreferred(device, false); 278 refreshProfilePreference(profilePref, profile); 279 } else { 280 profile.setPreferred(device, true); 281 mCachedDevice.connectProfile(profile); 282 } 283 } 284 } 285 286 private void askDisconnect(Context context, 287 final LocalBluetoothProfile profile) { 288 // local reference for callback 289 final CachedBluetoothDevice device = mCachedDevice; 290 String name = device.getName(); 291 if (TextUtils.isEmpty(name)) { 292 name = context.getString(R.string.bluetooth_device); 293 } 294 295 String profileName = context.getString(profile.getNameResource(device.getDevice())); 296 297 String title = context.getString(R.string.bluetooth_disable_profile_title); 298 String message = context.getString(R.string.bluetooth_disable_profile_message, 299 profileName, name); 300 301 DialogInterface.OnClickListener disconnectListener = 302 new DialogInterface.OnClickListener() { 303 public void onClick(DialogInterface dialog, int which) { 304 device.disconnect(profile); 305 profile.setPreferred(device.getDevice(), false); 306 } 307 }; 308 309 mDisconnectDialog = Utils.showDisconnectDialog(context, 310 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message)); 311 } 312 313 public void onDeviceAttributesChanged() { 314 refresh(); 315 } 316 317 private void refresh() { 318 String deviceName = mCachedDevice.getName(); 319 mDeviceNamePref.setSummary(deviceName); 320 mDeviceNamePref.setText(deviceName); 321 322 refreshProfiles(); 323 } 324 325 private void refreshProfiles() { 326 for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) { 327 CheckBoxPreference profilePref = (CheckBoxPreference)findPreference(profile.toString()); 328 if (profilePref == null) { 329 profilePref = createProfilePreference(profile); 330 mProfileContainer.addPreference(profilePref); 331 } else { 332 refreshProfilePreference(profilePref, profile); 333 } 334 } 335 for (LocalBluetoothProfile profile : mCachedDevice.getRemovedProfiles()) { 336 Preference profilePref = findPreference(profile.toString()); 337 if (profilePref != null) { 338 Log.d(TAG, "Removing " + profile.toString() + " from profile list"); 339 mProfileContainer.removePreference(profilePref); 340 } 341 } 342 showOrHideProfileGroup(); 343 } 344 345 private void refreshProfilePreference(CheckBoxPreference profilePref, 346 LocalBluetoothProfile profile) { 347 BluetoothDevice device = mCachedDevice.getDevice(); 348 349 /* 350 * Gray out checkbox while connecting and disconnecting 351 */ 352 profilePref.setEnabled(!mCachedDevice.isBusy()); 353 profilePref.setChecked(profile.isPreferred(device)); 354 profilePref.setSummary(profile.getSummaryResourceForDevice(device)); 355 } 356 357 private LocalBluetoothProfile getProfileOf(Preference pref) { 358 if (!(pref instanceof CheckBoxPreference)) { 359 return null; 360 } 361 String key = pref.getKey(); 362 if (TextUtils.isEmpty(key)) return null; 363 364 try { 365 return mProfileManager.getProfileByName(pref.getKey()); 366 } catch (IllegalArgumentException ignored) { 367 return null; 368 } 369 } 370 371 private int getProfilePreferenceIndex(int profIndex) { 372 return mProfileContainer.getOrder() + profIndex * 10; 373 } 374 375 private void unpairDevice() { 376 mCachedDevice.unpair(); 377 } 378} 379