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.Dialog;
21 import android.app.DialogFragment;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothProfile;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.os.Bundle;
27 import android.preference.CheckBoxPreference;
28 import android.preference.EditTextPreference;
29 import android.text.Html;
30 import android.text.TextUtils;
31 import android.util.Log;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.View.OnClickListener;
35 import android.view.ViewGroup;
36 import android.widget.CheckBox;
37 import android.widget.EditText;
38 import android.widget.TextView;
40 import com.android.settings.R;
41 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
42 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
43 import com.android.settingslib.bluetooth.LocalBluetoothManager;
44 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
45 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
46 import com.android.settingslib.bluetooth.MapProfile;
47 import com.android.settingslib.bluetooth.PanProfile;
48 import com.android.settingslib.bluetooth.PbapServerProfile;
50 import java.util.HashMap;
52 public final class DeviceProfilesSettings extends DialogFragment implements
53 CachedBluetoothDevice.Callback, DialogInterface.OnClickListener, OnClickListener {
54 private static final String TAG = "DeviceProfilesSettings";
56 public static final String ARG_DEVICE_ADDRESS = "device_address";
58 private static final String KEY_PROFILE_CONTAINER = "profile_container";
59 private static final String KEY_UNPAIR = "unpair";
60 private static final String KEY_PBAP_SERVER = "PBAP Server";
62 private CachedBluetoothDevice mCachedDevice;
63 private LocalBluetoothManager mManager;
64 private LocalBluetoothProfileManager mProfileManager;
66 private ViewGroup mProfileContainer;
67 private TextView mProfileLabel;
68 private EditTextPreference mDeviceNamePref;
70 private final HashMap<LocalBluetoothProfile, CheckBoxPreference> mAutoConnectPrefs
71 = new HashMap<LocalBluetoothProfile, CheckBoxPreference>();
73 private AlertDialog mDisconnectDialog;
74 private boolean mProfileGroupIsRemoved;
76 private View mRootView;
79 public void onCreate(Bundle savedInstanceState) {
80 super.onCreate(savedInstanceState);
82 mManager = Utils.getLocalBtManager(getActivity());
83 CachedBluetoothDeviceManager deviceManager = mManager.getCachedDeviceManager();
85 String address = getArguments().getString(ARG_DEVICE_ADDRESS);
86 BluetoothDevice remoteDevice = mManager.getBluetoothAdapter().getRemoteDevice(address);
88 mCachedDevice = deviceManager.findDevice(remoteDevice);
89 if (mCachedDevice == null) {
90 mCachedDevice = deviceManager.addDevice(mManager.getBluetoothAdapter(),
91 mManager.getProfileManager(), remoteDevice);
93 mProfileManager = mManager.getProfileManager();
97 public Dialog onCreateDialog(Bundle savedInstanceState) {
98 mRootView = LayoutInflater.from(getContext()).inflate(R.layout.device_profiles_settings,
100 mProfileContainer = (ViewGroup) mRootView.findViewById(R.id.profiles_section);
101 mProfileLabel = (TextView) mRootView.findViewById(R.id.profiles_label);
102 final EditText deviceName = (EditText) mRootView.findViewById(R.id.name);
103 deviceName.setText(mCachedDevice.getName(), TextView.BufferType.EDITABLE);
104 return new AlertDialog.Builder(getContext())
106 .setNegativeButton(R.string.forget, this)
107 .setPositiveButton(R.string.okay, this)
108 .setTitle(R.string.bluetooth_preference_paired_devices)
113 public void onClick(DialogInterface dialog, int which) {
115 case DialogInterface.BUTTON_POSITIVE:
116 EditText deviceName = (EditText) mRootView.findViewById(R.id.name);
117 mCachedDevice.setName(deviceName.getText().toString());
119 case DialogInterface.BUTTON_NEGATIVE:
120 mCachedDevice.unpair();
121 com.android.settings.bluetooth.Utils.updateSearchIndex(getContext(),
122 BluetoothSettings.class.getName(), mCachedDevice.getName(),
123 getString(R.string.bluetooth_settings),
124 R.drawable.ic_settings_bluetooth, false);
130 public void onDestroy() {
132 if (mDisconnectDialog != null) {
133 mDisconnectDialog.dismiss();
134 mDisconnectDialog = null;
136 if (mCachedDevice != null) {
137 mCachedDevice.unregisterCallback(this);
142 public void onSaveInstanceState(Bundle outState) {
143 super.onSaveInstanceState(outState);
147 public void onResume() {
150 mManager.setForegroundActivity(getActivity());
151 if (mCachedDevice != null) {
152 mCachedDevice.registerCallback(this);
153 if (mCachedDevice.getBondState() == BluetoothDevice.BOND_NONE) {
157 addPreferencesForProfiles();
163 public void onPause() {
166 if (mCachedDevice != null) {
167 mCachedDevice.unregisterCallback(this);
170 mManager.setForegroundActivity(null);
173 private void addPreferencesForProfiles() {
174 mProfileContainer.removeAllViews();
175 for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
176 CheckBox pref = createProfilePreference(profile);
177 mProfileContainer.addView(pref);
180 final int pbapPermission = mCachedDevice.getPhonebookPermissionChoice();
181 // Only provide PBAP cabability if the client device has requested PBAP.
182 if (pbapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
183 final PbapServerProfile psp = mManager.getProfileManager().getPbapProfile();
184 CheckBox pbapPref = createProfilePreference(psp);
185 mProfileContainer.addView(pbapPref);
188 final MapProfile mapProfile = mManager.getProfileManager().getMapProfile();
189 final int mapPermission = mCachedDevice.getMessagePermissionChoice();
190 if (mapPermission != CachedBluetoothDevice.ACCESS_UNKNOWN) {
191 CheckBox mapPreference = createProfilePreference(mapProfile);
192 mProfileContainer.addView(mapPreference);
195 showOrHideProfileGroup();
198 private void showOrHideProfileGroup() {
199 int numProfiles = mProfileContainer.getChildCount();
200 if (!mProfileGroupIsRemoved && numProfiles == 0) {
201 mProfileContainer.setVisibility(View.GONE);
202 mProfileLabel.setVisibility(View.GONE);
203 mProfileGroupIsRemoved = true;
204 } else if (mProfileGroupIsRemoved && numProfiles != 0) {
205 mProfileContainer.setVisibility(View.VISIBLE);
206 mProfileLabel.setVisibility(View.VISIBLE);
207 mProfileGroupIsRemoved = false;
212 * Creates a checkbox preference for the particular profile. The key will be
213 * the profile's name.
215 * @param profile The profile for which the preference controls.
216 * @return A preference that allows the user to choose whether this profile
217 * will be connected to.
219 private CheckBox createProfilePreference(LocalBluetoothProfile profile) {
220 CheckBox pref = new CheckBox(getActivity());
221 pref.setTag(profile.toString());
222 pref.setText(profile.getNameResource(mCachedDevice.getDevice()));
223 pref.setOnClickListener(this);
225 refreshProfilePreference(pref, profile);
231 public void onClick(View v) {
232 if (v instanceof CheckBox) {
233 LocalBluetoothProfile prof = getProfileOf(v);
234 onProfileClicked(prof, (CheckBox) v);
238 private void onProfileClicked(LocalBluetoothProfile profile, CheckBox profilePref) {
239 BluetoothDevice device = mCachedDevice.getDevice();
241 if (KEY_PBAP_SERVER.equals(profilePref.getTag())) {
242 final int newPermission = mCachedDevice.getPhonebookPermissionChoice()
243 == CachedBluetoothDevice.ACCESS_ALLOWED ? CachedBluetoothDevice.ACCESS_REJECTED
244 : CachedBluetoothDevice.ACCESS_ALLOWED;
245 mCachedDevice.setPhonebookPermissionChoice(newPermission);
246 profilePref.setChecked(newPermission == CachedBluetoothDevice.ACCESS_ALLOWED);
250 if (!profilePref.isChecked()) {
251 // Recheck it, until the dialog is done.
252 profilePref.setChecked(true);
253 askDisconnect(mManager.getForegroundActivity(), profile);
255 if (profile instanceof MapProfile) {
256 mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
258 if (profile.isPreferred(device)) {
259 // profile is preferred but not connected: disable auto-connect
260 if (profile instanceof PanProfile) {
261 mCachedDevice.connectProfile(profile);
263 profile.setPreferred(device, false);
266 profile.setPreferred(device, true);
267 mCachedDevice.connectProfile(profile);
269 refreshProfilePreference(profilePref, profile);
273 private void askDisconnect(Context context,
274 final LocalBluetoothProfile profile) {
275 // local reference for callback
276 final CachedBluetoothDevice device = mCachedDevice;
277 String name = device.getName();
278 if (TextUtils.isEmpty(name)) {
279 name = context.getString(R.string.bluetooth_device);
282 String profileName = context.getString(profile.getNameResource(device.getDevice()));
284 String title = context.getString(R.string.bluetooth_disable_profile_title);
285 String message = context.getString(R.string.bluetooth_disable_profile_message,
288 DialogInterface.OnClickListener disconnectListener =
289 new DialogInterface.OnClickListener() {
290 public void onClick(DialogInterface dialog, int which) {
291 device.disconnect(profile);
292 profile.setPreferred(device.getDevice(), false);
293 if (profile instanceof MapProfile) {
294 device.setMessagePermissionChoice(BluetoothDevice.ACCESS_REJECTED);
296 refreshProfilePreference(findProfile(profile.toString()), profile);
300 mDisconnectDialog = Utils.showDisconnectDialog(context,
301 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message));
305 public void onDeviceAttributesChanged() {
309 private void refresh() {
310 final EditText deviceNameField = (EditText) mRootView.findViewById(R.id.name);
311 if (deviceNameField != null) {
312 deviceNameField.setText(mCachedDevice.getName());
318 private void refreshProfiles() {
319 for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
320 CheckBox profilePref = findProfile(profile.toString());
321 if (profilePref == null) {
322 profilePref = createProfilePreference(profile);
323 mProfileContainer.addView(profilePref);
325 refreshProfilePreference(profilePref, profile);
328 for (LocalBluetoothProfile profile : mCachedDevice.getRemovedProfiles()) {
329 CheckBox profilePref = findProfile(profile.toString());
330 if (profilePref != null) {
331 Log.d(TAG, "Removing " + profile.toString() + " from profile list");
332 mProfileContainer.removeView(profilePref);
336 showOrHideProfileGroup();
339 private CheckBox findProfile(String profile) {
340 return (CheckBox) mProfileContainer.findViewWithTag(profile);
343 private void refreshProfilePreference(CheckBox profilePref,
344 LocalBluetoothProfile profile) {
345 BluetoothDevice device = mCachedDevice.getDevice();
347 // Gray out checkbox while connecting and disconnecting.
348 profilePref.setEnabled(!mCachedDevice.isBusy());
350 if (profile instanceof MapProfile) {
351 profilePref.setChecked(mCachedDevice.getMessagePermissionChoice()
352 == CachedBluetoothDevice.ACCESS_ALLOWED);
354 } else if (profile instanceof PbapServerProfile) {
355 profilePref.setChecked(mCachedDevice.getPhonebookPermissionChoice()
356 == CachedBluetoothDevice.ACCESS_ALLOWED);
358 } else if (profile instanceof PanProfile) {
359 profilePref.setChecked(profile.getConnectionStatus(device) ==
360 BluetoothProfile.STATE_CONNECTED);
363 profilePref.setChecked(profile.isPreferred(device));
367 private LocalBluetoothProfile getProfileOf(View v) {
368 if (!(v instanceof CheckBox)) {
371 String key = (String) v.getTag();
372 if (TextUtils.isEmpty(key)) return null;
375 return mProfileManager.getProfileByName(key);
376 } catch (IllegalArgumentException ignored) {