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