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.Activity;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.content.BroadcastReceiver;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.res.Resources;
28 import android.os.Bundle;
29 import android.provider.Settings;
30 import android.support.v7.preference.Preference;
31 import android.support.v7.preference.PreferenceCategory;
32 import android.support.v7.preference.PreferenceGroup;
33 import android.support.v7.preference.PreferenceScreen;
34 import android.text.BidiFormatter;
35 import android.text.Spannable;
36 import android.text.style.TextAppearanceSpan;
37 import android.util.Log;
38 import android.view.Menu;
39 import android.view.MenuInflater;
40 import android.view.MenuItem;
41 import android.view.View;
42 import android.widget.TextView;
43 import com.android.internal.logging.MetricsLogger;
44 import com.android.internal.logging.MetricsProto.MetricsEvent;
45 import com.android.settings.LinkifyUtils;
46 import com.android.settings.R;
47 import com.android.settings.SettingsActivity;
48 import com.android.settings.dashboard.SummaryLoader;
49 import com.android.settings.location.ScanningSettings;
50 import com.android.settings.search.BaseSearchIndexProvider;
51 import com.android.settings.search.Indexable;
52 import com.android.settings.search.SearchIndexableRaw;
53 import com.android.settings.widget.SwitchBar;
54 import com.android.settingslib.bluetooth.BluetoothCallback;
55 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
56 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
57 import com.android.settingslib.bluetooth.LocalBluetoothManager;
58 
59 import java.util.ArrayList;
60 import java.util.List;
61 import java.util.Locale;
62 import java.util.Set;
63 
64 import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
65 
66 /**
67  * BluetoothSettings is the Settings screen for Bluetooth configuration and
68  * connection management.
69  */
70 public final class BluetoothSettings extends DeviceListPreferenceFragment implements Indexable {
71     private static final String TAG = "BluetoothSettings";
72 
73     private static final int MENU_ID_SCAN = Menu.FIRST;
74     private static final int MENU_ID_RENAME_DEVICE = Menu.FIRST + 1;
75     private static final int MENU_ID_SHOW_RECEIVED = Menu.FIRST + 2;
76 
77     /* Private intent to show the list of received files */
78     private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES =
79             "android.btopp.intent.action.OPEN_RECEIVED_FILES";
80 
81     private static final String KEY_PAIRED_DEVICES = "paired_devices";
82 
83     private static View mSettingsDialogView = null;
84 
85     private BluetoothEnabler mBluetoothEnabler;
86 
87     private PreferenceGroup mPairedDevicesCategory;
88     private PreferenceGroup mAvailableDevicesCategory;
89     private boolean mAvailableDevicesCategoryIsPresent;
90 
91     private boolean mInitialScanStarted;
92     private boolean mInitiateDiscoverable;
93 
94     private SwitchBar mSwitchBar;
95 
96     private final IntentFilter mIntentFilter;
97 
98 
99     // accessed from inner class (not private to avoid thunks)
100     Preference mMyDevicePreference;
101 
102     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
103         @Override
104         public void onReceive(Context context, Intent intent) {
105             final String action = intent.getAction();
106             final int state =
107                 intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
108 
109             if (action.equals(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)) {
110                 updateDeviceName(context);
111             }
112 
113             if (state == BluetoothAdapter.STATE_ON) {
114                 mInitiateDiscoverable = true;
115             }
116         }
117 
118         private void updateDeviceName(Context context) {
119             if (mLocalAdapter.isEnabled() && mMyDevicePreference != null) {
120                 final Resources res = context.getResources();
121                 final Locale locale = res.getConfiguration().getLocales().get(0);
122                 final BidiFormatter bidiFormatter = BidiFormatter.getInstance(locale);
123                 mMyDevicePreference.setSummary(res.getString(
124                             R.string.bluetooth_is_visible_message,
125                             bidiFormatter.unicodeWrap(mLocalAdapter.getName())));
126             }
127         }
128     };
129 
BluetoothSettings()130     public BluetoothSettings() {
131         super(DISALLOW_CONFIG_BLUETOOTH);
132         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
133     }
134 
135     @Override
getMetricsCategory()136     protected int getMetricsCategory() {
137         return MetricsEvent.BLUETOOTH;
138     }
139 
140     @Override
onActivityCreated(Bundle savedInstanceState)141     public void onActivityCreated(Bundle savedInstanceState) {
142         super.onActivityCreated(savedInstanceState);
143         mInitialScanStarted = false;
144         mInitiateDiscoverable = true;
145 
146         final SettingsActivity activity = (SettingsActivity) getActivity();
147         mSwitchBar = activity.getSwitchBar();
148 
149         mBluetoothEnabler = new BluetoothEnabler(activity, mSwitchBar);
150         mBluetoothEnabler.setupSwitchBar();
151     }
152 
153     @Override
onDestroyView()154     public void onDestroyView() {
155         super.onDestroyView();
156 
157         mBluetoothEnabler.teardownSwitchBar();
158     }
159 
160     @Override
addPreferencesForActivity()161     void addPreferencesForActivity() {
162         addPreferencesFromResource(R.xml.bluetooth_settings);
163 
164         mPairedDevicesCategory = new PreferenceCategory(getPrefContext());
165         mPairedDevicesCategory.setKey(KEY_PAIRED_DEVICES);
166         mPairedDevicesCategory.setOrder(1);
167         getPreferenceScreen().addPreference(mPairedDevicesCategory);
168 
169         mAvailableDevicesCategory = new BluetoothProgressCategory(getActivity());
170         mAvailableDevicesCategory.setSelectable(false);
171         mAvailableDevicesCategory.setOrder(2);
172         getPreferenceScreen().addPreference(mAvailableDevicesCategory);
173 
174         mMyDevicePreference = new Preference(getPrefContext());
175         mMyDevicePreference.setSelectable(false);
176         mMyDevicePreference.setOrder(3);
177         getPreferenceScreen().addPreference(mMyDevicePreference);
178 
179         setHasOptionsMenu(true);
180     }
181 
182     @Override
onResume()183     public void onResume() {
184         // resume BluetoothEnabler before calling super.onResume() so we don't get
185         // any onDeviceAdded() callbacks before setting up view in updateContent()
186         if (mBluetoothEnabler != null) {
187             mBluetoothEnabler.resume(getActivity());
188         }
189         super.onResume();
190 
191         mInitiateDiscoverable = true;
192 
193         if (isUiRestricted()) {
194             setDeviceListGroup(getPreferenceScreen());
195             if (!isUiRestrictedByOnlyAdmin()) {
196                 getEmptyTextView().setText(R.string.bluetooth_empty_list_user_restricted);
197             }
198             removeAllDevices();
199             return;
200         }
201 
202         getActivity().registerReceiver(mReceiver, mIntentFilter);
203         if (mLocalAdapter != null) {
204             updateContent(mLocalAdapter.getBluetoothState());
205         }
206     }
207 
208     @Override
onPause()209     public void onPause() {
210         super.onPause();
211         if (mBluetoothEnabler != null) {
212             mBluetoothEnabler.pause();
213         }
214 
215         // Make the device only visible to connected devices.
216         mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
217 
218         if (isUiRestricted()) {
219             return;
220         }
221 
222         getActivity().unregisterReceiver(mReceiver);
223     }
224 
225     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)226     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
227         if (mLocalAdapter == null) return;
228         // If the user is not allowed to configure bluetooth, do not show the menu.
229         if (isUiRestricted()) return;
230 
231         boolean bluetoothIsEnabled = mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON;
232         boolean isDiscovering = mLocalAdapter.isDiscovering();
233         int textId = isDiscovering ? R.string.bluetooth_searching_for_devices :
234             R.string.bluetooth_search_for_devices;
235         menu.add(Menu.NONE, MENU_ID_SCAN, 0, textId)
236                 .setEnabled(bluetoothIsEnabled && !isDiscovering)
237                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
238         menu.add(Menu.NONE, MENU_ID_RENAME_DEVICE, 0, R.string.bluetooth_rename_device)
239                 .setEnabled(bluetoothIsEnabled)
240                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
241         menu.add(Menu.NONE, MENU_ID_SHOW_RECEIVED, 0, R.string.bluetooth_show_received_files)
242                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
243         super.onCreateOptionsMenu(menu, inflater);
244     }
245 
246     @Override
onOptionsItemSelected(MenuItem item)247     public boolean onOptionsItemSelected(MenuItem item) {
248         switch (item.getItemId()) {
249             case MENU_ID_SCAN:
250                 if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
251                     MetricsLogger.action(getActivity(), MetricsEvent.ACTION_BLUETOOTH_SCAN);
252                     startScanning();
253                 }
254                 return true;
255 
256             case MENU_ID_RENAME_DEVICE:
257                 MetricsLogger.action(getActivity(), MetricsEvent.ACTION_BLUETOOTH_RENAME);
258                 new BluetoothNameDialogFragment().show(
259                         getFragmentManager(), "rename device");
260                 return true;
261 
262             case MENU_ID_SHOW_RECEIVED:
263                 MetricsLogger.action(getActivity(), MetricsEvent.ACTION_BLUETOOTH_FILES);
264                 Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);
265                 getActivity().sendBroadcast(intent);
266                 return true;
267         }
268         return super.onOptionsItemSelected(item);
269     }
270 
startScanning()271     private void startScanning() {
272         if (isUiRestricted()) {
273             return;
274         }
275 
276         if (!mAvailableDevicesCategoryIsPresent) {
277             getPreferenceScreen().addPreference(mAvailableDevicesCategory);
278             mAvailableDevicesCategoryIsPresent = true;
279         }
280 
281         if (mAvailableDevicesCategory != null) {
282             setDeviceListGroup(mAvailableDevicesCategory);
283             removeAllDevices();
284         }
285 
286         mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
287         mAvailableDevicesCategory.removeAll();
288         mInitialScanStarted = true;
289         mLocalAdapter.startScanning(true);
290     }
291 
292     @Override
onDevicePreferenceClick(BluetoothDevicePreference btPreference)293     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
294         mLocalAdapter.stopScanning();
295         super.onDevicePreferenceClick(btPreference);
296     }
297 
addDeviceCategory(PreferenceGroup preferenceGroup, int titleId, BluetoothDeviceFilter.Filter filter, boolean addCachedDevices)298     private void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId,
299             BluetoothDeviceFilter.Filter filter, boolean addCachedDevices) {
300         cacheRemoveAllPrefs(preferenceGroup);
301         preferenceGroup.setTitle(titleId);
302         setFilter(filter);
303         setDeviceListGroup(preferenceGroup);
304         if (addCachedDevices) {
305             addCachedDevices();
306         }
307         preferenceGroup.setEnabled(true);
308         removeCachedPrefs(preferenceGroup);
309     }
310 
updateContent(int bluetoothState)311     private void updateContent(int bluetoothState) {
312         final PreferenceScreen preferenceScreen = getPreferenceScreen();
313         int messageId = 0;
314 
315         switch (bluetoothState) {
316             case BluetoothAdapter.STATE_ON:
317                 mDevicePreferenceMap.clear();
318 
319                 if (isUiRestricted()) {
320                     messageId = R.string.bluetooth_empty_list_user_restricted;
321                     break;
322                 }
323                 if (getPreferenceScreen().getPreferenceCount() == 0) {
324                     getPreferenceScreen().addPreference(mPairedDevicesCategory);
325                     getPreferenceScreen().addPreference(mAvailableDevicesCategory);
326                     getPreferenceScreen().addPreference(mMyDevicePreference);
327                 }
328 
329                 // Paired devices category
330                 addDeviceCategory(mPairedDevicesCategory,
331                         R.string.bluetooth_preference_paired_devices,
332                         BluetoothDeviceFilter.BONDED_DEVICE_FILTER, true);
333                 int numberOfPairedDevices = mPairedDevicesCategory.getPreferenceCount();
334 
335                 if (isUiRestricted() || numberOfPairedDevices <= 0) {
336                     if (preferenceScreen.findPreference(KEY_PAIRED_DEVICES) != null) {
337                         preferenceScreen.removePreference(mPairedDevicesCategory);
338                     }
339                 } else {
340                     if (preferenceScreen.findPreference(KEY_PAIRED_DEVICES) == null) {
341                         preferenceScreen.addPreference(mPairedDevicesCategory);
342                     }
343                 }
344 
345                 // Available devices category
346                 addDeviceCategory(mAvailableDevicesCategory,
347                         R.string.bluetooth_preference_found_devices,
348                         BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER, mInitialScanStarted);
349 
350                 if (!mInitialScanStarted) {
351                     startScanning();
352                 }
353 
354                 final Resources res = getResources();
355                 final Locale locale = res.getConfiguration().getLocales().get(0);
356                 final BidiFormatter bidiFormatter = BidiFormatter.getInstance(locale);
357                 mMyDevicePreference.setSummary(res.getString(
358                             R.string.bluetooth_is_visible_message,
359                             bidiFormatter.unicodeWrap(mLocalAdapter.getName())));
360 
361                 getActivity().invalidateOptionsMenu();
362 
363                 // mLocalAdapter.setScanMode is internally synchronized so it is okay for multiple
364                 // threads to execute.
365                 if (mInitiateDiscoverable) {
366                     // Make the device visible to other devices.
367                     mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
368                     mInitiateDiscoverable = false;
369                 }
370                 return; // not break
371 
372             case BluetoothAdapter.STATE_TURNING_OFF:
373                 messageId = R.string.bluetooth_turning_off;
374                 break;
375 
376             case BluetoothAdapter.STATE_OFF:
377                 setOffMessage();
378                 if (isUiRestricted()) {
379                     messageId = R.string.bluetooth_empty_list_user_restricted;
380                 }
381                 break;
382 
383             case BluetoothAdapter.STATE_TURNING_ON:
384                 messageId = R.string.bluetooth_turning_on;
385                 mInitialScanStarted = false;
386                 break;
387         }
388 
389         setDeviceListGroup(preferenceScreen);
390         removeAllDevices();
391         if (messageId != 0) {
392             getEmptyTextView().setText(messageId);
393         }
394         if (!isUiRestricted()) {
395             getActivity().invalidateOptionsMenu();
396         }
397     }
398 
setOffMessage()399     private void setOffMessage() {
400         final TextView emptyView = getEmptyTextView();
401         if (emptyView == null) {
402             return;
403         }
404         final CharSequence briefText = getText(R.string.bluetooth_empty_list_bluetooth_off);
405 
406         final ContentResolver resolver = getActivity().getContentResolver();
407         final boolean bleScanningMode = Settings.Global.getInt(
408                 resolver, Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0) == 1;
409 
410         if (!bleScanningMode) {
411             // Show only the brief text if the scanning mode has been turned off.
412             emptyView.setText(briefText, TextView.BufferType.SPANNABLE);
413         } else {
414             final StringBuilder contentBuilder = new StringBuilder();
415             contentBuilder.append(briefText);
416             contentBuilder.append("\n\n");
417             contentBuilder.append(getText(R.string.ble_scan_notify_text));
418             LinkifyUtils.linkify(emptyView, contentBuilder, new LinkifyUtils.OnClickListener() {
419                 @Override
420                 public void onClick() {
421                     final SettingsActivity activity =
422                             (SettingsActivity) BluetoothSettings.this.getActivity();
423                     activity.startPreferencePanel(ScanningSettings.class.getName(), null,
424                             R.string.location_scanning_screen_title, null, null, 0);
425                 }
426             });
427         }
428         getPreferenceScreen().removeAll();
429         Spannable boldSpan = (Spannable) emptyView.getText();
430         boldSpan.setSpan(
431                 new TextAppearanceSpan(getActivity(), android.R.style.TextAppearance_Medium), 0,
432                 briefText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
433     }
434 
435     @Override
onBluetoothStateChanged(int bluetoothState)436     public void onBluetoothStateChanged(int bluetoothState) {
437         super.onBluetoothStateChanged(bluetoothState);
438         // If BT is turned off/on staying in the same BT Settings screen
439         // discoverability to be set again
440         if (BluetoothAdapter.STATE_ON == bluetoothState)
441             mInitiateDiscoverable = true;
442         updateContent(bluetoothState);
443     }
444 
445     @Override
onScanningStateChanged(boolean started)446     public void onScanningStateChanged(boolean started) {
447         super.onScanningStateChanged(started);
448         // Update options' enabled state
449         if (getActivity() != null) {
450             getActivity().invalidateOptionsMenu();
451         }
452     }
453 
454     @Override
onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)455     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
456         setDeviceListGroup(getPreferenceScreen());
457         removeAllDevices();
458         updateContent(mLocalAdapter.getBluetoothState());
459     }
460 
461     private final View.OnClickListener mDeviceProfilesListener = new View.OnClickListener() {
462         @Override
463         public void onClick(View v) {
464             // User clicked on advanced options icon for a device in the list
465             if (!(v.getTag() instanceof CachedBluetoothDevice)) {
466                 Log.w(TAG, "onClick() called for other View: " + v);
467                 return;
468             }
469 
470             final CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag();
471             Bundle args = new Bundle();
472             args.putString(DeviceProfilesSettings.ARG_DEVICE_ADDRESS,
473                     device.getDevice().getAddress());
474             DeviceProfilesSettings profileSettings = new DeviceProfilesSettings();
475             profileSettings.setArguments(args);
476             profileSettings.show(getFragmentManager(),
477                     DeviceProfilesSettings.class.getSimpleName());
478         }
479     };
480 
481     /**
482      * Add a listener, which enables the advanced settings icon.
483      * @param preference the newly added preference
484      */
485     @Override
initDevicePreference(BluetoothDevicePreference preference)486     void initDevicePreference(BluetoothDevicePreference preference) {
487         CachedBluetoothDevice cachedDevice = preference.getCachedDevice();
488         if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
489             // Only paired device have an associated advanced settings screen
490             preference.setOnSettingsClickListener(mDeviceProfilesListener);
491         }
492     }
493 
494     @Override
getHelpResource()495     protected int getHelpResource() {
496         return R.string.help_url_bluetooth;
497     }
498 
499     private static class SummaryProvider
500             implements SummaryLoader.SummaryProvider, BluetoothCallback {
501 
502         private final LocalBluetoothManager mBluetoothManager;
503         private final Context mContext;
504         private final SummaryLoader mSummaryLoader;
505 
506         private boolean mEnabled;
507         private boolean mConnected;
508 
SummaryProvider(Context context, SummaryLoader summaryLoader)509         public SummaryProvider(Context context, SummaryLoader summaryLoader) {
510             mBluetoothManager = Utils.getLocalBtManager(context);
511             mContext = context;
512             mSummaryLoader = summaryLoader;
513         }
514 
515         @Override
setListening(boolean listening)516         public void setListening(boolean listening) {
517             BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter();
518             if (defaultAdapter == null) return;
519             if (listening) {
520                 mEnabled = defaultAdapter.isEnabled();
521                 mConnected =
522                         defaultAdapter.getConnectionState() == BluetoothAdapter.STATE_CONNECTED;
523                 mSummaryLoader.setSummary(this, getSummary());
524                 mBluetoothManager.getEventManager().registerCallback(this);
525             } else {
526                 mBluetoothManager.getEventManager().unregisterCallback(this);
527             }
528         }
529 
getSummary()530         private CharSequence getSummary() {
531             return mContext.getString(!mEnabled ? R.string.bluetooth_disabled
532                     : mConnected ? R.string.bluetooth_connected
533                     : R.string.bluetooth_disconnected);
534         }
535 
536         @Override
onBluetoothStateChanged(int bluetoothState)537         public void onBluetoothStateChanged(int bluetoothState) {
538             mEnabled = bluetoothState == BluetoothAdapter.STATE_ON;
539             mSummaryLoader.setSummary(this, getSummary());
540         }
541 
542         @Override
onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state)543         public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
544             mConnected = state == BluetoothAdapter.STATE_CONNECTED;
545             mSummaryLoader.setSummary(this, getSummary());
546         }
547 
548         @Override
onScanningStateChanged(boolean started)549         public void onScanningStateChanged(boolean started) {
550 
551         }
552 
553         @Override
onDeviceAdded(CachedBluetoothDevice cachedDevice)554         public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
555 
556         }
557 
558         @Override
onDeviceDeleted(CachedBluetoothDevice cachedDevice)559         public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
560 
561         }
562 
563         @Override
onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)564         public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
565 
566         }
567     }
568 
569     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
570             = new SummaryLoader.SummaryProviderFactory() {
571         @Override
572         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
573                                                                    SummaryLoader summaryLoader) {
574             return new SummaryProvider(activity, summaryLoader);
575         }
576     };
577 
578     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
579         new BaseSearchIndexProvider() {
580             @Override
581             public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
582 
583                 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
584 
585                 final Resources res = context.getResources();
586 
587                 // Add fragment title
588                 SearchIndexableRaw data = new SearchIndexableRaw(context);
589                 data.title = res.getString(R.string.bluetooth_settings);
590                 data.screenTitle = res.getString(R.string.bluetooth_settings);
591                 result.add(data);
592 
593                 // Add cached paired BT devices
594                 LocalBluetoothManager lbtm = Utils.getLocalBtManager(context);
595                 // LocalBluetoothManager.getInstance can return null if the device does not
596                 // support bluetooth (e.g. the emulator).
597                 if (lbtm != null) {
598                     Set<BluetoothDevice> bondedDevices =
599                             lbtm.getBluetoothAdapter().getBondedDevices();
600 
601                     for (BluetoothDevice device : bondedDevices) {
602                         data = new SearchIndexableRaw(context);
603                         data.title = device.getName();
604                         data.screenTitle = res.getString(R.string.bluetooth_settings);
605                         data.enabled = enabled;
606                         result.add(data);
607                     }
608                 }
609                 return result;
610             }
611         };
612 }
613