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         mBluetoothEnabler.resume();
    142         super.onResume();
    143 
    144         if (mDiscoverableEnabler != null) {
    145             mDiscoverableEnabler.resume();
    146         }
    147         getActivity().registerReceiver(mReceiver, mIntentFilter);
    148 
    149         updateContent(mLocalAdapter.getBluetoothState(), mActivityStarted);
    150     }
    151 
    152     @Override
    153     public void onPause() {
    154         super.onPause();
    155         mBluetoothEnabler.pause();
    156         getActivity().unregisterReceiver(mReceiver);
    157         if (mDiscoverableEnabler != null) {
    158             mDiscoverableEnabler.pause();
    159         }
    160     }
    161 
    162     @Override
    163     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    164         boolean bluetoothIsEnabled = mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON;
    165         boolean isDiscovering = mLocalAdapter.isDiscovering();
    166         int textId = isDiscovering ? R.string.bluetooth_searching_for_devices :
    167             R.string.bluetooth_search_for_devices;
    168         menu.add(Menu.NONE, MENU_ID_SCAN, 0, textId)
    169                 .setEnabled(bluetoothIsEnabled && !isDiscovering)
    170                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    171         menu.add(Menu.NONE, MENU_ID_RENAME_DEVICE, 0, R.string.bluetooth_rename_device)
    172                 .setEnabled(bluetoothIsEnabled)
    173                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    174         menu.add(Menu.NONE, MENU_ID_VISIBILITY_TIMEOUT, 0, R.string.bluetooth_visibility_timeout)
    175                 .setEnabled(bluetoothIsEnabled)
    176                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    177         menu.add(Menu.NONE, MENU_ID_SHOW_RECEIVED, 0, R.string.bluetooth_show_received_files)
    178                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    179     }
    180 
    181     @Override
    182     public boolean onOptionsItemSelected(MenuItem item) {
    183         switch (item.getItemId()) {
    184             case MENU_ID_SCAN:
    185                 if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
    186                     startScanning();
    187                 }
    188                 return true;
    189 
    190             case MENU_ID_RENAME_DEVICE:
    191                 new BluetoothNameDialogFragment().show(
    192                         getFragmentManager(), "rename device");
    193                 return true;
    194 
    195             case MENU_ID_VISIBILITY_TIMEOUT:
    196                 new BluetoothVisibilityTimeoutFragment().show(
    197                         getFragmentManager(), "visibility timeout");
    198                 return true;
    199 
    200             case MENU_ID_SHOW_RECEIVED:
    201                 Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);
    202                 getActivity().sendBroadcast(intent);
    203                 return true;
    204         }
    205         return super.onOptionsItemSelected(item);
    206     }
    207 
    208     private void startScanning() {
    209         if (!mAvailableDevicesCategoryIsPresent) {
    210             getPreferenceScreen().addPreference(mAvailableDevicesCategory);
    211         }
    212         mLocalAdapter.startScanning(true);
    213     }
    214 
    215     @Override
    216     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
    217         mLocalAdapter.stopScanning();
    218         super.onDevicePreferenceClick(btPreference);
    219     }
    220 
    221     private void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId,
    222             BluetoothDeviceFilter.Filter filter) {
    223         preferenceGroup.setTitle(titleId);
    224         getPreferenceScreen().addPreference(preferenceGroup);
    225         setFilter(filter);
    226         setDeviceListGroup(preferenceGroup);
    227         addCachedDevices();
    228         preferenceGroup.setEnabled(true);
    229     }
    230 
    231     private void updateContent(int bluetoothState, boolean scanState) {
    232         final PreferenceScreen preferenceScreen = getPreferenceScreen();
    233         int messageId = 0;
    234 
    235         switch (bluetoothState) {
    236             case BluetoothAdapter.STATE_ON:
    237                 preferenceScreen.removeAll();
    238                 preferenceScreen.setOrderingAsAdded(true);
    239                 mDevicePreferenceMap.clear();
    240 
    241                 // This device
    242                 if (mMyDevicePreference == null) {
    243                     mMyDevicePreference = new Preference(getActivity());
    244                 }
    245                 mMyDevicePreference.setTitle(mLocalAdapter.getName());
    246                 if (getResources().getBoolean(com.android.internal.R.bool.config_voice_capable)) {
    247                     mMyDevicePreference.setIcon(R.drawable.ic_bt_cellphone);    // for phones
    248                 } else {
    249                     mMyDevicePreference.setIcon(R.drawable.ic_bt_laptop);   // for tablets, etc.
    250                 }
    251                 mMyDevicePreference.setPersistent(false);
    252                 mMyDevicePreference.setEnabled(true);
    253                 preferenceScreen.addPreference(mMyDevicePreference);
    254 
    255                 if (mDiscoverableEnabler == null) {
    256                     mDiscoverableEnabler = new BluetoothDiscoverableEnabler(getActivity(),
    257                             mLocalAdapter, mMyDevicePreference);
    258                     mDiscoverableEnabler.resume();
    259                     LocalBluetoothManager.getInstance(getActivity()).setDiscoverableEnabler(
    260                             mDiscoverableEnabler);
    261                 }
    262 
    263                 // Paired devices category
    264                 if (mPairedDevicesCategory == null) {
    265                     mPairedDevicesCategory = new PreferenceCategory(getActivity());
    266                 } else {
    267                     mPairedDevicesCategory.removeAll();
    268                 }
    269                 addDeviceCategory(mPairedDevicesCategory,
    270                         R.string.bluetooth_preference_paired_devices,
    271                         BluetoothDeviceFilter.BONDED_DEVICE_FILTER);
    272                 int numberOfPairedDevices = mPairedDevicesCategory.getPreferenceCount();
    273 
    274                 mDiscoverableEnabler.setNumberOfPairedDevices(numberOfPairedDevices);
    275 
    276                 // Available devices category
    277                 if (mAvailableDevicesCategory == null) {
    278                     mAvailableDevicesCategory = new ProgressCategory(getActivity(), null);
    279                 } else {
    280                     mAvailableDevicesCategory.removeAll();
    281                 }
    282                 addDeviceCategory(mAvailableDevicesCategory,
    283                         R.string.bluetooth_preference_found_devices,
    284                         BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER);
    285                 int numberOfAvailableDevices = mAvailableDevicesCategory.getPreferenceCount();
    286                 mAvailableDevicesCategoryIsPresent = true;
    287 
    288                 if (numberOfAvailableDevices == 0) {
    289                     preferenceScreen.removePreference(mAvailableDevicesCategory);
    290                     mAvailableDevicesCategoryIsPresent = false;
    291                 }
    292 
    293                 if (numberOfPairedDevices == 0) {
    294                     preferenceScreen.removePreference(mPairedDevicesCategory);
    295                     if (scanState == true) {
    296                         mActivityStarted = false;
    297                         startScanning();
    298                     } else {
    299                         if (!mAvailableDevicesCategoryIsPresent) {
    300                             getPreferenceScreen().addPreference(mAvailableDevicesCategory);
    301                         }
    302                     }
    303                 }
    304                 getActivity().invalidateOptionsMenu();
    305                 return; // not break
    306 
    307             case BluetoothAdapter.STATE_TURNING_OFF:
    308                 messageId = R.string.bluetooth_turning_off;
    309                 break;
    310 
    311             case BluetoothAdapter.STATE_OFF:
    312                 messageId = R.string.bluetooth_empty_list_bluetooth_off;
    313                 break;
    314 
    315             case BluetoothAdapter.STATE_TURNING_ON:
    316                 messageId = R.string.bluetooth_turning_on;
    317                 break;
    318         }
    319 
    320         setDeviceListGroup(preferenceScreen);
    321         removeAllDevices();
    322         mEmptyView.setText(messageId);
    323         getActivity().invalidateOptionsMenu();
    324     }
    325 
    326     @Override
    327     public void onBluetoothStateChanged(int bluetoothState) {
    328         super.onBluetoothStateChanged(bluetoothState);
    329         updateContent(bluetoothState, true);
    330     }
    331 
    332     @Override
    333     public void onScanningStateChanged(boolean started) {
    334         super.onScanningStateChanged(started);
    335         // Update options' enabled state
    336         getActivity().invalidateOptionsMenu();
    337     }
    338 
    339     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
    340         setDeviceListGroup(getPreferenceScreen());
    341         removeAllDevices();
    342         updateContent(mLocalAdapter.getBluetoothState(), false);
    343     }
    344 
    345     private final View.OnClickListener mDeviceProfilesListener = new View.OnClickListener() {
    346         public void onClick(View v) {
    347             // User clicked on advanced options icon for a device in the list
    348             if (v.getTag() instanceof CachedBluetoothDevice) {
    349                 CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag();
    350 
    351                 Bundle args = new Bundle(1);
    352                 args.putParcelable(DeviceProfilesSettings.EXTRA_DEVICE, device.getDevice());
    353 
    354                 ((PreferenceActivity) getActivity()).startPreferencePanel(
    355                         DeviceProfilesSettings.class.getName(), args,
    356                         R.string.bluetooth_device_advanced_title, null, null, 0);
    357             } else {
    358                 Log.w(TAG, "onClick() called for other View: " + v); // TODO remove
    359             }
    360         }
    361     };
    362 
    363     /**
    364      * Add a listener, which enables the advanced settings icon.
    365      * @param preference the newly added preference
    366      */
    367     @Override
    368     void initDevicePreference(BluetoothDevicePreference preference) {
    369         CachedBluetoothDevice cachedDevice = preference.getCachedDevice();
    370         if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
    371             // Only paired device have an associated advanced settings screen
    372             preference.setOnSettingsClickListener(mDeviceProfilesListener);
    373         }
    374     }
    375 }
    376