• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.settings.bluetooth;
18 
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;
40 
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;
45 
46 import java.util.HashMap;
47 
48 /**
49  * This preference fragment presents the user with all of the profiles
50  * for a particular device, and allows them to be individually connected
51  * (or disconnected).
52  */
53 public final class DeviceProfilesSettings extends SettingsPreferenceFragment
54         implements CachedBluetoothDevice.Callback, Preference.OnPreferenceChangeListener {
55     private static final String TAG = "DeviceProfilesSettings";
56 
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";
60 
61     private CachedBluetoothDevice mCachedDevice;
62     private LocalBluetoothManager mManager;
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     @Override
onCreate(Bundle savedInstanceState)75     public void onCreate(Bundle savedInstanceState) {
76         super.onCreate(savedInstanceState);
77 
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);
82 
83         mManager = LocalBluetoothManager.getInstance(getActivity());
84         CachedBluetoothDeviceManager deviceManager =
85                 mManager.getCachedDeviceManager();
86         mProfileManager = mManager.getProfileManager();
87     }
88 
89     @Override
onDestroy()90     public void onDestroy() {
91         super.onDestroy();
92         if (mDisconnectDialog != null) {
93             mDisconnectDialog.dismiss();
94             mDisconnectDialog = null;
95         }
96         if (mCachedDevice != null) {
97             mCachedDevice.unregisterCallback(this);
98         }
99     }
100 
101     @Override
onSaveInstanceState(Bundle outState)102     public void onSaveInstanceState(Bundle outState) {
103         super.onSaveInstanceState(outState);
104     }
105 
106     @Override
onResume()107     public void onResume() {
108         super.onResume();
109 
110         mManager.setForegroundActivity(getActivity());
111         if (mCachedDevice != null) {
112             mCachedDevice.registerCallback(this);
113             if (mCachedDevice.getBondState() == BluetoothDevice.BOND_NONE) {
114                 finish();
115                 return;
116             }
117             refresh();
118         }
119     }
120 
121     @Override
onPause()122     public void onPause() {
123         super.onPause();
124 
125         if (mCachedDevice != null) {
126             mCachedDevice.unregisterCallback(this);
127         }
128 
129         mManager.setForegroundActivity(null);
130     }
131 
setDevice(CachedBluetoothDevice cachedDevice)132     public void setDevice(CachedBluetoothDevice cachedDevice) {
133         mCachedDevice = cachedDevice;
134 
135         if (isResumed()) {
136             mCachedDevice.registerCallback(this);
137             addPreferencesForProfiles();
138             refresh();
139         }
140     }
141 
addPreferencesForProfiles()142     private void addPreferencesForProfiles() {
143         mProfileContainer.removeAll();
144         for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
145             Preference pref = createProfilePreference(profile);
146             mProfileContainer.addPreference(pref);
147         }
148 
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);
155         }
156 
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);
162         }
163 
164         showOrHideProfileGroup();
165     }
166 
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;
175         }
176     }
177 
178     /**
179      * Creates a checkbox preference for the particular profile. The key will be
180      * the profile's name.
181      *
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.
185      */
createProfilePreference(LocalBluetoothProfile profile)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);
194 
195         int iconResource = profile.getDrawableResource(mCachedDevice.getBtClass());
196         if (iconResource != 0) {
197             pref.setIcon(getResources().getDrawable(iconResource));
198         }
199 
200         refreshProfilePreference(pref, profile);
201 
202         return pref;
203     }
204 
onPreferenceChange(Preference preference, Object newValue)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
212         } else {
213             return false;
214         }
215 
216         return true;
217     }
218 
onProfileClicked(LocalBluetoothProfile profile, CheckBoxPreference profilePref)219     private void onProfileClicked(LocalBluetoothProfile profile, CheckBoxPreference profilePref) {
220         BluetoothDevice device = mCachedDevice.getDevice();
221 
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);
228             return;
229         }
230 
231         int status = profile.getConnectionStatus(device);
232         boolean isConnected =
233                 status == BluetoothProfile.STATE_CONNECTED;
234 
235         if (profilePref.isChecked()) {
236             askDisconnect(mManager.getForegroundActivity(), profile);
237         } else {
238             if (profile instanceof MapProfile) {
239                 mCachedDevice.setMessagePermissionChoice(BluetoothDevice.ACCESS_ALLOWED);
240                 refreshProfilePreference(profilePref, profile);
241             }
242             if (profile.isPreferred(device)) {
243                 // profile is preferred but not connected: disable auto-connect
244                 profile.setPreferred(device, false);
245                 refreshProfilePreference(profilePref, profile);
246             } else {
247                 profile.setPreferred(device, true);
248                 mCachedDevice.connectProfile(profile);
249             }
250         }
251     }
252 
askDisconnect(Context context, final LocalBluetoothProfile 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);
260         }
261 
262         String profileName = context.getString(profile.getNameResource(device.getDevice()));
263 
264         String title = context.getString(R.string.bluetooth_disable_profile_title);
265         String message = context.getString(R.string.bluetooth_disable_profile_message,
266                 profileName, name);
267 
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);
277                 }
278             }
279         };
280 
281         mDisconnectDialog = Utils.showDisconnectDialog(context,
282                 mDisconnectDialog, disconnectListener, title, Html.fromHtml(message));
283     }
284 
285     @Override
onDeviceAttributesChanged()286     public void onDeviceAttributesChanged() {
287         refresh();
288     }
289 
refresh()290     private void refresh() {
291         final EditText deviceNameField = (EditText) getView().findViewById(R.id.name);
292         if (deviceNameField != null) {
293             deviceNameField.setText(mCachedDevice.getName());
294         }
295 
296         refreshProfiles();
297     }
298 
refreshProfiles()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);
305             } else {
306                 refreshProfilePreference(profilePref, profile);
307             }
308         }
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);
314             }
315         }
316 
317         showOrHideProfileGroup();
318     }
319 
refreshProfilePreference(CheckBoxPreference profilePref, LocalBluetoothProfile profile)320     private void refreshProfilePreference(CheckBoxPreference profilePref,
321             LocalBluetoothProfile profile) {
322         BluetoothDevice device = mCachedDevice.getDevice();
323 
324         // Gray out checkbox while connecting and disconnecting.
325         profilePref.setEnabled(!mCachedDevice.isBusy());
326 
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);
334         } else {
335             profilePref.setChecked(profile.isPreferred(device));
336         }
337     }
338 
getProfileOf(Preference pref)339     private LocalBluetoothProfile getProfileOf(Preference pref) {
340         if (!(pref instanceof CheckBoxPreference)) {
341             return null;
342         }
343         String key = pref.getKey();
344         if (TextUtils.isEmpty(key)) return null;
345 
346         try {
347             return mProfileManager.getProfileByName(pref.getKey());
348         } catch (IllegalArgumentException ignored) {
349             return null;
350         }
351     }
352 
getProfilePreferenceIndex(int profIndex)353     private int getProfilePreferenceIndex(int profIndex) {
354         return mProfileContainer.getOrder() + profIndex * 10;
355     }
356 }
357