Home | History | Annotate | Download | only in bluetooth
      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 
    130     public BluetoothSettings() {
    131         super(DISALLOW_CONFIG_BLUETOOTH);
    132         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
    133     }
    134 
    135     @Override
    136     protected int getMetricsCategory() {
    137         return MetricsEvent.BLUETOOTH;
    138     }
    139 
    140     @Override
    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
    154     public void onDestroyView() {
    155         super.onDestroyView();
    156 
    157         mBluetoothEnabler.teardownSwitchBar();
    158     }
    159 
    160     @Override
    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
    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
    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
    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
    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 
    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
    293     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
    294         mLocalAdapter.stopScanning();
    295         super.onDevicePreferenceClick(btPreference);
    296     }
    297 
    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 
    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 
    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
    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
    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
    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
    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
    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 
    509         public SummaryProvider(Context context, SummaryLoader summaryLoader) {
    510             mBluetoothManager = Utils.getLocalBtManager(context);
    511             mContext = context;
    512             mSummaryLoader = summaryLoader;
    513         }
    514 
    515         @Override
    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 
    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
    537         public void onBluetoothStateChanged(int bluetoothState) {
    538             mEnabled = bluetoothState == BluetoothAdapter.STATE_ON;
    539             mSummaryLoader.setSummary(this, getSummary());
    540         }
    541 
    542         @Override
    543         public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
    544             mConnected = state == BluetoothAdapter.STATE_CONNECTED;
    545             mSummaryLoader.setSummary(this, getSummary());
    546         }
    547 
    548         @Override
    549         public void onScanningStateChanged(boolean started) {
    550 
    551         }
    552 
    553         @Override
    554         public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
    555 
    556         }
    557 
    558         @Override
    559         public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
    560 
    561         }
    562 
    563         @Override
    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