• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BluetoothSettings()98     public BluetoothSettings() {
99         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
100     }
101 
102     @Override
onActivityCreated(Bundle savedInstanceState)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
addPreferencesForActivity()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
onResume()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
onPause()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
onCreateOptionsMenu(Menu menu, MenuInflater inflater)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
onOptionsItemSelected(MenuItem item)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 
startScanning()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
onDevicePreferenceClick(BluetoothDevicePreference btPreference)230     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
231         mLocalAdapter.stopScanning();
232         super.onDevicePreferenceClick(btPreference);
233     }
234 
addDeviceCategory(PreferenceGroup preferenceGroup, int titleId, BluetoothDeviceFilter.Filter filter)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 
updateContent(int bluetoothState, boolean scanState)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
onBluetoothStateChanged(int bluetoothState)347     public void onBluetoothStateChanged(int bluetoothState) {
348         super.onBluetoothStateChanged(bluetoothState);
349         updateContent(bluetoothState, true);
350     }
351 
352     @Override
onScanningStateChanged(boolean started)353     public void onScanningStateChanged(boolean started) {
354         super.onScanningStateChanged(started);
355         // Update options' enabled state
356         getActivity().invalidateOptionsMenu();
357     }
358 
onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)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
initDevicePreference(BluetoothDevicePreference preference)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
getHelpResource()399     protected int getHelpResource() {
400         return R.string.help_url_bluetooth;
401     }
402 }
403