2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.bluetooth;
19 import android.app.AlertDialog;
20 import android.app.Fragment;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothProfile;
23 import android.content.Context;
24 import android.content.DialogInterface;
25 import android.os.Bundle;
26 import android.preference.CheckBoxPreference;
27 import android.preference.EditTextPreference;
28 import android.preference.Preference;
29 import android.preference.PreferenceGroup;
30 import android.preference.PreferenceScreen;
31 import android.text.Html;
32 import android.text.TextUtils;
33 import android.util.Log;
34 import android.view.View;
35 import android.widget.EditText;
36 import android.text.TextWatcher;
37 import android.app.Dialog;
38 import android.widget.Button;
39 import android.text.Editable;
41 import com.android.settings.R;
42 import com.android.settings.SettingsPreferenceFragment;
43 import com.android.settings.search.Index;
44 import com.android.settings.search.SearchIndexableRaw;
46 import java.util.HashMap;
49 * This preference fragment presents the user with all of the profiles
50 * for a particular device, and allows them to be individually connected
53 public final class DeviceProfilesSettings extends SettingsPreferenceFragment
54 implements CachedBluetoothDevice.Callback, Preference.OnPreferenceChangeListener {
55 private static final String TAG = "DeviceProfilesSettings";
57 private static final String KEY_PROFILE_CONTAINER = "profile_container";
58 private static final String KEY_UNPAIR = "unpair";
59 private static final String KEY_PBAP_SERVER = "PBAP Server";
61 private CachedBluetoothDevice mCachedDevice;
62 private LocalBluetoothManager mManager;
63 private LocalBluetoothProfileManager mProfileManager;
65 private PreferenceGroup mProfileContainer;
66 private EditTextPreference mDeviceNamePref;
68 private final HashMap<LocalBluetoothProfile, CheckBoxPreference> mAutoConnectPrefs
69 = new HashMap<LocalBluetoothProfile, CheckBoxPreference>();
71 private AlertDialog mDisconnectDialog;
72 private boolean mProfileGroupIsRemoved;
75 public void onCreate(Bundle savedInstanceState) {
76 super.onCreate(savedInstanceState);
78 addPreferencesFromResource(R.xml.bluetooth_device_advanced);
79 getPreferenceScreen().setOrderingAsAdded(false);
80 mProfileContainer = (PreferenceGroup) findPreference(KEY_PROFILE_CONTAINER);
81 mProfileContainer.setLayoutResource(R.layout.bluetooth_preference_category);
83 mManager = LocalBluetoothManager.getInstance(getActivity());
84 CachedBluetoothDeviceManager deviceManager =
85 mManager.getCachedDeviceManager();
86 mProfileManager = mManager.getProfileManager();
90 public void onDestroy() {
92 if (mDisconnectDialog != null) {
93 mDisconnectDialog.dismiss();
94 mDisconnectDialog = null;
96 if (mCachedDevice != null) {
97 mCachedDevice.unregisterCallback(this);
102 public void onSaveInstanceState(Bundle outState) {
103 super.onSaveInstanceState(outState);
107 public void onResume() {
110 mManager.setForegroundActivity(getActivity());
111 if (mCachedDevice != null) {
112 mCachedDevice.registerCallback(this);
113 if (mCachedDevice.getBondState() == BluetoothDevice.BOND_NONE) {
122 public void onPause() {
125 if (mCachedDevice != null) {
126 mCachedDevice.unregisterCallback(this);
129 mManager.setForegroundActivity(null);
132 public void setDevice(CachedBluetoothDevice cachedDevice) {
133 mCachedDevice = cachedDevice;
136 mCachedDevice.registerCallback(this);
137 addPreferencesForProfiles();
142 private void addPreferencesForProfiles() {
143 mProfileContainer.removeAll();
144 for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
145 Preference pref = createProfilePreference(profile);
146 mProfileContainer.addPreference(pref);
149 final int pbapPermission = mCachedDevice.getPhonebookPermissionChoice();
150 // Only provide PBAP cabability if the client device has requested PBAP.
151 if (pbapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
152 final PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
153 CheckBoxPreference pbapPref = createProfilePreference(psp);
154 mProfileContainer.addPreference(pbapPref);
157 final MapProfile mapProfile = mManager.getProfileManager().getMapProfile();
158 final int mapPermission = mCachedDevice.getMessagePermissionChoice();
159 if (mapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
160 CheckBoxPreference mapPreference = createProfilePreference(mapProfile);
161 mProfileContainer.addPreference(mapPreference);
164 showOrHideProfileGroup();
167 private void showOrHideProfileGroup() {
168 int numProfiles = mProfileContainer.getPreferenceCount();
169 if (!mProfileGroupIsRemoved && numProfiles == 0) {
170 getPreferenceScreen().removePreference(mProfileContainer);
171 mProfileGroupIsRemoved = true;
172 } else if (mProfileGroupIsRemoved && numProfiles != 0) {
173 getPreferenceScreen().addPreference(mProfileContainer);
174 mProfileGroupIsRemoved = false;
179 * Creates a checkbox preference for the particular profile. The key will be
180 * the profile's name.
182 * @param profile The profile for which the preference controls.
183 * @return A preference that allows the user to choose whether this profile
184 * will be connected to.
186 private CheckBoxPreference createProfilePreference(LocalBluetoothProfile profile) {
187 CheckBoxPreference pref = new CheckBoxPreference(getActivity());
188 pref.setLayoutResource(R.layout.preference_start_widget);
189 pref.setKey(profile.toString());
190 pref.setTitle(profile.getNameResource(mCachedDevice.getDevice()));
191 pref.setPersistent(false);
192 pref.setOrder(getProfilePreferenceIndex(profile.getOrdinal()));
193 pref.setOnPreferenceChangeListener(this);
195 int iconResource = profile.getDrawableResource(mCachedDevice.getBtClass());
196 if (iconResource != 0) {
197 pref.setIcon(getResources().getDrawable(iconResource));
200 refreshProfilePreference(pref, profile);
205 public boolean onPreferenceChange(Preference preference, Object newValue) {
206 if (preference == mDeviceNamePref) {
207 mCachedDevice.setName((String) newValue);
208 } else if (preference instanceof CheckBoxPreference) {
209 LocalBluetoothProfile prof = getProfileOf(preference);
210 onProfileClicked(prof, (CheckBoxPreference) preference);
211 return false; // checkbox will update from onDeviceAttributesChanged() callback
219 private void onProfileClicked(LocalBluetoothProfile profile, CheckBoxPreference profilePref) {
220 BluetoothDevice device = mCachedDevice.getDevice();
222 if (profilePref.getKey().equals(KEY_PBAP_SERVER)) {
223 final int newPermission = mCachedDevice.getPhonebookPermissionChoice()
224 == CachedBluetoothDevice.ACCESS_ALLOWED ? CachedBluetoothDevice.ACCESS_REJECTED
225 : CachedBluetoothDevice.ACCESS_ALLOWED;
226 mCachedDevice.setPhonebookPermissionChoice(newPermission);
227 profilePref.setChecked(newPermission == CachedBluetoothDevice.ACCESS_ALLOWED);
231 int status = profile.getConnectionStatus(device);
232 boolean isConnected =
233 status == BluetoothProfile.STATE_CONNECTED;
235 if (profilePref.isChecked()) {
236 askDisconnect(mManager.getForegroundActivity(), profile);
238 if (profile instanceof MapProfile) {
239 mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
240 refreshProfilePreference(profilePref, profile);
242 if (profile.isPreferred(device)) {
243 // profile is preferred but not connected: disable auto-connect
244 profile.setPreferred(device, false);
245 refreshProfilePreference(profilePref, profile);
247 profile.setPreferred(device, true);
248 mCachedDevice.connectProfile(profile);
253 private void askDisconnect(Context context,
254 final LocalBluetoothProfile profile) {
255 // local reference for callback
256 final CachedBluetoothDevice device = mCachedDevice;
257 String name = device.getName();
258 if (TextUtils.isEmpty(name)) {
259 name = context.getString(R.string.bluetooth_device);
262 String profileName = context.getString(profile.getNameResource(device.getDevice()));
264 String title = context.getString(R.string.bluetooth_disable_profile_title);
265 String message = context.getString(R.string.bluetooth_disable_profile_message,
268 DialogInterface.OnClickListener disconnectListener =
269 new DialogInterface.OnClickListener() {
270 public void onClick(DialogInterface dialog, int which) {
271 device.disconnect(profile);
272 profile.setPreferred(device.getDevice(), false);
273 if (profile instanceof MapProfile) {
274 device.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED);
275 refreshProfilePreference(
276 (CheckBoxPreference)findPreference(profile.toString()), profile);
281 mDisconnectDialog = Utils.showDisconnectDialog(context,
282 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message));
286 public void onDeviceAttributesChanged() {
290 private void refresh() {
291 final EditText deviceNameField = (EditText) getView().findViewById(R.id.name);
292 if (deviceNameField != null) {
293 deviceNameField.setText(mCachedDevice.getName());
299 private void refreshProfiles() {
300 for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
301 CheckBoxPreference profilePref = (CheckBoxPreference)findPreference(profile.toString());
302 if (profilePref == null) {
303 profilePref = createProfilePreference(profile);
304 mProfileContainer.addPreference(profilePref);
306 refreshProfilePreference(profilePref, profile);
309 for (LocalBluetoothProfile profile : mCachedDevice.getRemovedProfiles()) {
310 Preference profilePref = findPreference(profile.toString());
311 if (profilePref != null) {
312 Log.d(TAG, "Removing " + profile.toString() + " from profile list");
313 mProfileContainer.removePreference(profilePref);
317 showOrHideProfileGroup();
320 private void refreshProfilePreference(CheckBoxPreference profilePref,
321 LocalBluetoothProfile profile) {
322 BluetoothDevice device = mCachedDevice.getDevice();
324 // Gray out checkbox while connecting and disconnecting.
325 profilePref.setEnabled(!mCachedDevice.isBusy());
327 if (profile instanceof MapProfile) {
328 profilePref.setChecked(mCachedDevice.getMessagePermissionChoice()
329 == CachedBluetoothDevice.ACCESS_ALLOWED);
330 } else if (profile instanceof PbapServerProfile) {
331 // Handle PBAP specially.
332 profilePref.setChecked(mCachedDevice.getPhonebookPermissionChoice()
333 == CachedBluetoothDevice.ACCESS_ALLOWED);
335 profilePref.setChecked(profile.isPreferred(device));
339 private LocalBluetoothProfile getProfileOf(Preference pref) {
340 if (!(pref instanceof CheckBoxPreference)) {
343 String key = pref.getKey();
344 if (TextUtils.isEmpty(key)) return null;
347 return mProfileManager.getProfileByName(pref.getKey());
348 } catch (IllegalArgumentException ignored) {
353 private int getProfilePreferenceIndex(int profIndex) {
354 return mProfileContainer.getOrder() + profIndex * 10;