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 static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
     20 
     21 import android.app.ActionBar;
     22 import android.app.Activity;
     23 import android.bluetooth.BluetoothAdapter;
     24 import android.bluetooth.BluetoothDevice;
     25 import android.content.BroadcastReceiver;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.os.Bundle;
     30 import android.os.UserManager;
     31 import android.preference.Preference;
     32 import android.preference.PreferenceActivity;
     33 import android.preference.PreferenceCategory;
     34 import android.preference.PreferenceGroup;
     35 import android.preference.PreferenceScreen;
     36 import android.util.Log;
     37 import android.view.Gravity;
     38 import android.view.Menu;
     39 import android.view.MenuInflater;
     40 import android.view.MenuItem;
     41 import android.view.View;
     42 import android.widget.Switch;
     43 import android.widget.TextView;
     44 
     45 import com.android.settings.R;
     46 
     47 /**
     48  * BluetoothSettings is the Settings screen for Bluetooth configuration and
     49  * connection management.
     50  */
     51 public final class BluetoothSettings extends DeviceListPreferenceFragment {
     52     private static final String TAG = "BluetoothSettings";
     53 
     54     private static final int MENU_ID_SCAN = Menu.FIRST;
     55     private static final int MENU_ID_RENAME_DEVICE = Menu.FIRST + 1;
     56     private static final int MENU_ID_VISIBILITY_TIMEOUT = Menu.FIRST + 2;
     57     private static final int MENU_ID_SHOW_RECEIVED = Menu.FIRST + 3;
     58 
     59     /* Private intent to show the list of received files */
     60     private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES =
     61             "android.btopp.intent.action.OPEN_RECEIVED_FILES";
     62 
     63     private BluetoothEnabler mBluetoothEnabler;
     64 
     65     private BluetoothDiscoverableEnabler mDiscoverableEnabler;
     66 
     67     private PreferenceGroup mPairedDevicesCategory;
     68 
     69     private PreferenceGroup mAvailableDevicesCategory;
     70     private boolean mAvailableDevicesCategoryIsPresent;
     71     private boolean mActivityStarted;
     72 
     73     private TextView mEmptyView;
     74 
     75     private final IntentFilter mIntentFilter;
     76 
     77     private UserManager mUserManager;
     78 
     79     // accessed from inner class (not private to avoid thunks)
     80     Preference mMyDevicePreference;
     81 
     82     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
     83         @Override
     84         public void onReceive(Context context, Intent intent) {
     85             String action = intent.getAction();
     86             if (action.equals(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)) {
     87                 updateDeviceName();
     88             }
     89         }
     90 
     91         private void updateDeviceName() {
     92             if (mLocalAdapter.isEnabled() && mMyDevicePreference != null) {
     93                 mMyDevicePreference.setTitle(mLocalAdapter.getName());
     94             }
     95         }
     96     };
     97 
     98     public BluetoothSettings() {
     99         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
    100     }
    101 
    102     @Override
    103     public void onActivityCreated(Bundle savedInstanceState) {
    104         super.onActivityCreated(savedInstanceState);
    105         mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
    106         mActivityStarted = (savedInstanceState == null);    // don't auto start scan after rotation
    107 
    108         mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
    109         getListView().setEmptyView(mEmptyView);
    110     }
    111 
    112     @Override
    113     void addPreferencesForActivity() {
    114         addPreferencesFromResource(R.xml.bluetooth_settings);
    115 
    116         Activity activity = getActivity();
    117 
    118         Switch actionBarSwitch = new Switch(activity);
    119 
    120         if (activity instanceof PreferenceActivity) {
    121             PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
    122             if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) {
    123                 final int padding = activity.getResources().getDimensionPixelSize(
    124                         R.dimen.action_bar_switch_padding);
    125                 actionBarSwitch.setPaddingRelative(0, 0, padding, 0);
    126                 activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
    127                         ActionBar.DISPLAY_SHOW_CUSTOM);
    128                 activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams(
    129                         ActionBar.LayoutParams.WRAP_CONTENT,
    130                         ActionBar.LayoutParams.WRAP_CONTENT,
    131                         Gravity.CENTER_VERTICAL | Gravity.END));
    132             }
    133         }
    134 
    135         mBluetoothEnabler = new BluetoothEnabler(activity, actionBarSwitch);
    136 
    137         setHasOptionsMenu(true);
    138     }
    139 
    140     @Override
    141     public void onResume() {
    142         // resume BluetoothEnabler before calling super.onResume() so we don't get
    143         // any onDeviceAdded() callbacks before setting up view in updateContent()
    144         if (mBluetoothEnabler != null) {
    145             mBluetoothEnabler.resume();
    146         }
    147         super.onResume();
    148 
    149         if (mDiscoverableEnabler != null) {
    150             mDiscoverableEnabler.resume();
    151         }
    152         getActivity().registerReceiver(mReceiver, mIntentFilter);
    153         if (mLocalAdapter != null) {
    154             updateContent(mLocalAdapter.getBluetoothState(), mActivityStarted);
    155         }
    156     }
    157 
    158     @Override
    159     public void onPause() {
    160         super.onPause();
    161         if (mBluetoothEnabler != null) {
    162             mBluetoothEnabler.pause();
    163         }
    164         getActivity().unregisterReceiver(mReceiver);
    165         if (mDiscoverableEnabler != null) {
    166             mDiscoverableEnabler.pause();
    167         }
    168     }
    169 
    170     @Override
    171     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    172         if (mLocalAdapter == null) return;
    173         // If the user is not allowed to configure bluetooth, do not show the menu.
    174         if (mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH)) return;
    175 
    176         boolean bluetoothIsEnabled = mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON;
    177         boolean isDiscovering = mLocalAdapter.isDiscovering();
    178         int textId = isDiscovering ? R.string.bluetooth_searching_for_devices :
    179             R.string.bluetooth_search_for_devices;
    180         menu.add(Menu.NONE, MENU_ID_SCAN, 0, textId)
    181                 .setEnabled(bluetoothIsEnabled && !isDiscovering)
    182                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    183         menu.add(Menu.NONE, MENU_ID_RENAME_DEVICE, 0, R.string.bluetooth_rename_device)
    184                 .setEnabled(bluetoothIsEnabled)
    185                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    186         menu.add(Menu.NONE, MENU_ID_VISIBILITY_TIMEOUT, 0, R.string.bluetooth_visibility_timeout)
    187                 .setEnabled(bluetoothIsEnabled)
    188                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    189         menu.add(Menu.NONE, MENU_ID_SHOW_RECEIVED, 0, R.string.bluetooth_show_received_files)
    190                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    191         super.onCreateOptionsMenu(menu, inflater);
    192     }
    193 
    194     @Override
    195     public boolean onOptionsItemSelected(MenuItem item) {
    196         switch (item.getItemId()) {
    197             case MENU_ID_SCAN:
    198                 if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
    199                     startScanning();
    200                 }
    201                 return true;
    202 
    203             case MENU_ID_RENAME_DEVICE:
    204                 new BluetoothNameDialogFragment().show(
    205                         getFragmentManager(), "rename device");
    206                 return true;
    207 
    208             case MENU_ID_VISIBILITY_TIMEOUT:
    209                 new BluetoothVisibilityTimeoutFragment().show(
    210                         getFragmentManager(), "visibility timeout");
    211                 return true;
    212 
    213             case MENU_ID_SHOW_RECEIVED:
    214                 Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);
    215                 getActivity().sendBroadcast(intent);
    216                 return true;
    217         }
    218         return super.onOptionsItemSelected(item);
    219     }
    220 
    221     private void startScanning() {
    222         if (mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH)) return;
    223         if (!mAvailableDevicesCategoryIsPresent) {
    224             getPreferenceScreen().addPreference(mAvailableDevicesCategory);
    225         }
    226         mLocalAdapter.startScanning(true);
    227     }
    228 
    229     @Override
    230     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
    231         mLocalAdapter.stopScanning();
    232         super.onDevicePreferenceClick(btPreference);
    233     }
    234 
    235     private void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId,
    236             BluetoothDeviceFilter.Filter filter) {
    237         preferenceGroup.setTitle(titleId);
    238         getPreferenceScreen().addPreference(preferenceGroup);
    239         setFilter(filter);
    240         setDeviceListGroup(preferenceGroup);
    241         addCachedDevices();
    242         preferenceGroup.setEnabled(true);
    243     }
    244 
    245     private void updateContent(int bluetoothState, boolean scanState) {
    246         final PreferenceScreen preferenceScreen = getPreferenceScreen();
    247         int messageId = 0;
    248 
    249         switch (bluetoothState) {
    250             case BluetoothAdapter.STATE_ON:
    251                 preferenceScreen.removeAll();
    252                 preferenceScreen.setOrderingAsAdded(true);
    253                 mDevicePreferenceMap.clear();
    254 
    255                 // This device
    256                 if (mMyDevicePreference == null) {
    257                     mMyDevicePreference = new Preference(getActivity());
    258                 }
    259                 mMyDevicePreference.setTitle(mLocalAdapter.getName());
    260                 if (getResources().getBoolean(com.android.internal.R.bool.config_voice_capable)) {
    261                     mMyDevicePreference.setIcon(R.drawable.ic_bt_cellphone);    // for phones
    262                 } else {
    263                     mMyDevicePreference.setIcon(R.drawable.ic_bt_laptop);   // for tablets, etc.
    264                 }
    265                 mMyDevicePreference.setPersistent(false);
    266                 mMyDevicePreference.setEnabled(true);
    267                 preferenceScreen.addPreference(mMyDevicePreference);
    268 
    269                 if (! mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH)) {
    270                     if (mDiscoverableEnabler == null) {
    271                         mDiscoverableEnabler = new BluetoothDiscoverableEnabler(getActivity(),
    272                                 mLocalAdapter, mMyDevicePreference);
    273                         mDiscoverableEnabler.resume();
    274                         LocalBluetoothManager.getInstance(getActivity()).setDiscoverableEnabler(
    275                                 mDiscoverableEnabler);
    276                     }
    277                 }
    278 
    279                 // Paired devices category
    280                 if (mPairedDevicesCategory == null) {
    281                     mPairedDevicesCategory = new PreferenceCategory(getActivity());
    282                 } else {
    283                     mPairedDevicesCategory.removeAll();
    284                 }
    285                 addDeviceCategory(mPairedDevicesCategory,
    286                         R.string.bluetooth_preference_paired_devices,
    287                         BluetoothDeviceFilter.BONDED_DEVICE_FILTER);
    288                 int numberOfPairedDevices = mPairedDevicesCategory.getPreferenceCount();
    289 
    290                 if (mDiscoverableEnabler != null) {
    291                     mDiscoverableEnabler.setNumberOfPairedDevices(numberOfPairedDevices);
    292                 }
    293 
    294                 // Available devices category
    295                 if (mAvailableDevicesCategory == null) {
    296                     mAvailableDevicesCategory = new BluetoothProgressCategory(getActivity(), null);
    297                 } else {
    298                     mAvailableDevicesCategory.removeAll();
    299                 }
    300                 if (! mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH)) {
    301                     addDeviceCategory(mAvailableDevicesCategory,
    302                             R.string.bluetooth_preference_found_devices,
    303                             BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER);
    304                 }
    305                 int numberOfAvailableDevices = mAvailableDevicesCategory.getPreferenceCount();
    306                 mAvailableDevicesCategoryIsPresent = true;
    307 
    308                 if (numberOfAvailableDevices == 0) {
    309                     preferenceScreen.removePreference(mAvailableDevicesCategory);
    310                     mAvailableDevicesCategoryIsPresent = false;
    311                 }
    312 
    313                 if (numberOfPairedDevices == 0) {
    314                     preferenceScreen.removePreference(mPairedDevicesCategory);
    315                     if (scanState == true) {
    316                         mActivityStarted = false;
    317                         startScanning();
    318                     } else {
    319                         if (!mAvailableDevicesCategoryIsPresent) {
    320                             getPreferenceScreen().addPreference(mAvailableDevicesCategory);
    321                         }
    322                     }
    323                 }
    324                 getActivity().invalidateOptionsMenu();
    325                 return; // not break
    326 
    327             case BluetoothAdapter.STATE_TURNING_OFF:
    328                 messageId = R.string.bluetooth_turning_off;
    329                 break;
    330 
    331             case BluetoothAdapter.STATE_OFF:
    332                 messageId = R.string.bluetooth_empty_list_bluetooth_off;
    333                 break;
    334 
    335             case BluetoothAdapter.STATE_TURNING_ON:
    336                 messageId = R.string.bluetooth_turning_on;
    337                 break;
    338         }
    339 
    340         setDeviceListGroup(preferenceScreen);
    341         removeAllDevices();
    342         mEmptyView.setText(messageId);
    343         getActivity().invalidateOptionsMenu();
    344     }
    345 
    346     @Override
    347     public void onBluetoothStateChanged(int bluetoothState) {
    348         super.onBluetoothStateChanged(bluetoothState);
    349         updateContent(bluetoothState, true);
    350     }
    351 
    352     @Override
    353     public void onScanningStateChanged(boolean started) {
    354         super.onScanningStateChanged(started);
    355         // Update options' enabled state
    356         getActivity().invalidateOptionsMenu();
    357     }
    358 
    359     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
    360         setDeviceListGroup(getPreferenceScreen());
    361         removeAllDevices();
    362         updateContent(mLocalAdapter.getBluetoothState(), false);
    363     }
    364 
    365     private final View.OnClickListener mDeviceProfilesListener = new View.OnClickListener() {
    366         public void onClick(View v) {
    367             // User clicked on advanced options icon for a device in the list
    368             if (v.getTag() instanceof CachedBluetoothDevice) {
    369                 if (mUserManager.hasUserRestriction(DISALLOW_CONFIG_BLUETOOTH)) return;
    370 
    371                 CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag();
    372 
    373                 Bundle args = new Bundle(1);
    374                 args.putParcelable(DeviceProfilesSettings.EXTRA_DEVICE, device.getDevice());
    375 
    376                 ((PreferenceActivity) getActivity()).startPreferencePanel(
    377                         DeviceProfilesSettings.class.getName(), args,
    378                         R.string.bluetooth_device_advanced_title, null, null, 0);
    379             } else {
    380                 Log.w(TAG, "onClick() called for other View: " + v); // TODO remove
    381             }
    382         }
    383     };
    384 
    385     /**
    386      * Add a listener, which enables the advanced settings icon.
    387      * @param preference the newly added preference
    388      */
    389     @Override
    390     void initDevicePreference(BluetoothDevicePreference preference) {
    391         CachedBluetoothDevice cachedDevice = preference.getCachedDevice();
    392         if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
    393             // Only paired device have an associated advanced settings screen
    394             preference.setOnSettingsClickListener(mDeviceProfilesListener);
    395         }
    396     }
    397 
    398     @Override
    399     protected int getHelpResource() {
    400         return R.string.help_url_bluetooth;
    401     }
    402 }
    403