• 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 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 
BluetoothSettings()96     public BluetoothSettings() {
97         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
98     }
99 
100     @Override
onActivityCreated(Bundle savedInstanceState)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
addPreferencesForActivity()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
onResume()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
onPause()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
onCreateOptionsMenu(Menu menu, MenuInflater inflater)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
onOptionsItemSelected(MenuItem item)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 
startScanning()215     private void startScanning() {
216         if (!mAvailableDevicesCategoryIsPresent) {
217             getPreferenceScreen().addPreference(mAvailableDevicesCategory);
218         }
219         mLocalAdapter.startScanning(true);
220     }
221 
222     @Override
onDevicePreferenceClick(BluetoothDevicePreference btPreference)223     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
224         mLocalAdapter.stopScanning();
225         super.onDevicePreferenceClick(btPreference);
226     }
227 
addDeviceCategory(PreferenceGroup preferenceGroup, int titleId, BluetoothDeviceFilter.Filter filter)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 
updateContent(int bluetoothState, boolean scanState)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
onBluetoothStateChanged(int bluetoothState)334     public void onBluetoothStateChanged(int bluetoothState) {
335         super.onBluetoothStateChanged(bluetoothState);
336         updateContent(bluetoothState, true);
337     }
338 
339     @Override
onScanningStateChanged(boolean started)340     public void onScanningStateChanged(boolean started) {
341         super.onScanningStateChanged(started);
342         // Update options' enabled state
343         getActivity().invalidateOptionsMenu();
344     }
345 
onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)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
initDevicePreference(BluetoothDevicePreference preference)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
getHelpResource()384     protected int getHelpResource() {
385         return R.string.help_url_bluetooth;
386     }
387 }
388