Refactor Bluetooth settings for readability and performance.
Major refactoring of Bluetooth settings classes. - Moved all functionality from LocalBluetoothManager into new LocalBluetoothAdapter and LocalBluetoothPreferences, and into existing classes. - Refactored functionality from BluetoothEventRedirector into new BluetoothEventManager class, deleting the original version. New version uses a HashMap from action Strings to implementers of the BluetoothEventManager.Handler interface. - Created new BluetoothDiscoveryReceiver to update shared preferences timestamp for Bluetooth discovery start/finish. This is the only event handling we need to do when the settings app is not visible, so it has its own receiver entry in AndroidManifest.xml. Edits are written using QueuedWork.singleThreadExecutor(), which BroadcastReceiver knows about and will wait for completion, eliminating the need for PendingResult. - Miscellaneous cleanups to code style and logic for readability. - Pulled some large switch statement code blocks into new methods. - Changed all Bluetooth state references to the new BluetoothProfile constants. - Changed use of deprecated Notification constructor in BluetoothPairingRequest to use Notification.Builder. - Moved Utf8ByteLengthFilter helper function from BluetoothNamePreference into its own class, and moved test cases into the same package. - Moved all LocalBluetoothProfileManager functionality related to specific profiles into new top-level classes (A2dpProfile, etc.), all implementing the LocalBluetoothProfile interface. - Moved all UI-related methods from CachedBluetoothDevice into the class that uses the method, or into the static Utils class for shared methods. Change-Id: I6d49b7f4ae0c7d7dcf62551ee40b51ecb5fe4f47
This commit is contained in:
@@ -863,6 +863,15 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".bluetooth.BluetoothDiscoveryReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
|
||||||
|
<action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".bluetooth.DockEventReceiver">
|
android:name=".bluetooth.DockEventReceiver">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@@ -60,7 +60,6 @@
|
|||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="bt_device_list"
|
android:key="bt_device_list"
|
||||||
android:title="@string/bluetooth_preference_paired_devices"
|
android:title="@string/bluetooth_preference_paired_devices"
|
||||||
android:dependency="bt_checkbox"
|
|
||||||
android:orderingFromXml="false" />
|
android:orderingFromXml="false" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
|
@@ -35,8 +35,6 @@ import android.os.Environment;
|
|||||||
import android.preference.CheckBoxPreference;
|
import android.preference.CheckBoxPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewParent;
|
import android.view.ViewParent;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
@@ -49,7 +47,6 @@ import java.util.Locale;
|
|||||||
* Displays preferences for Tethering.
|
* Displays preferences for Tethering.
|
||||||
*/
|
*/
|
||||||
public class TetherSettings extends SettingsPreferenceFragment {
|
public class TetherSettings extends SettingsPreferenceFragment {
|
||||||
private static final String TAG = "TetheringSettings";
|
|
||||||
|
|
||||||
private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
|
private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
|
||||||
private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
|
private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
|
||||||
@@ -66,8 +63,6 @@ public class TetherSettings extends SettingsPreferenceFragment {
|
|||||||
private WebView mView;
|
private WebView mView;
|
||||||
private CheckBoxPreference mUsbTether;
|
private CheckBoxPreference mUsbTether;
|
||||||
|
|
||||||
private CheckBoxPreference mEnableWifiAp;
|
|
||||||
private PreferenceScreen mWifiApSettings;
|
|
||||||
private WifiApEnabler mWifiApEnabler;
|
private WifiApEnabler mWifiApEnabler;
|
||||||
|
|
||||||
private CheckBoxPreference mBluetoothTether;
|
private CheckBoxPreference mBluetoothTether;
|
||||||
@@ -95,9 +90,9 @@ public class TetherSettings extends SettingsPreferenceFragment {
|
|||||||
BluetoothProfile.PAN);
|
BluetoothProfile.PAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckBoxPreference enableWifiAp =
|
||||||
mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
|
(CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
|
||||||
mWifiApSettings = (PreferenceScreen) findPreference(WIFI_AP_SETTINGS);
|
Preference wifiApSettings = findPreference(WIFI_AP_SETTINGS);
|
||||||
mUsbTether = (CheckBoxPreference) findPreference(USB_TETHER_SETTINGS);
|
mUsbTether = (CheckBoxPreference) findPreference(USB_TETHER_SETTINGS);
|
||||||
mBluetoothTether = (CheckBoxPreference) findPreference(ENABLE_BLUETOOTH_TETHERING);
|
mBluetoothTether = (CheckBoxPreference) findPreference(ENABLE_BLUETOOTH_TETHERING);
|
||||||
mTetherHelp = (PreferenceScreen) findPreference(TETHERING_HELP);
|
mTetherHelp = (PreferenceScreen) findPreference(TETHERING_HELP);
|
||||||
@@ -118,8 +113,8 @@ public class TetherSettings extends SettingsPreferenceFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!wifiAvailable) {
|
if (!wifiAvailable) {
|
||||||
getPreferenceScreen().removePreference(mEnableWifiAp);
|
getPreferenceScreen().removePreference(enableWifiAp);
|
||||||
getPreferenceScreen().removePreference(mWifiApSettings);
|
getPreferenceScreen().removePreference(wifiApSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bluetoothAvailable) {
|
if (!bluetoothAvailable) {
|
||||||
@@ -132,7 +127,7 @@ public class TetherSettings extends SettingsPreferenceFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mWifiApEnabler = new WifiApEnabler(activity, mEnableWifiAp);
|
mWifiApEnabler = new WifiApEnabler(activity, enableWifiAp);
|
||||||
mView = new WebView(activity);
|
mView = new WebView(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,22 +149,22 @@ public class TetherSettings extends SettingsPreferenceFragment {
|
|||||||
// check for the full language + country resource, if not there, try just language
|
// check for the full language + country resource, if not there, try just language
|
||||||
final AssetManager am = getActivity().getAssets();
|
final AssetManager am = getActivity().getAssets();
|
||||||
String path = HELP_PATH.replace("%y", locale.getLanguage().toLowerCase());
|
String path = HELP_PATH.replace("%y", locale.getLanguage().toLowerCase());
|
||||||
path = path.replace("%z", "_"+locale.getCountry().toLowerCase());
|
path = path.replace("%z", '_'+locale.getCountry().toLowerCase());
|
||||||
boolean useCountry = true;
|
boolean useCountry = true;
|
||||||
InputStream is = null;
|
InputStream is = null;
|
||||||
try {
|
try {
|
||||||
is = am.open(path);
|
is = am.open(path);
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) {
|
||||||
useCountry = false;
|
useCountry = false;
|
||||||
} finally {
|
} finally {
|
||||||
if (is != null) {
|
if (is != null) {
|
||||||
try {
|
try {
|
||||||
is.close();
|
is.close();
|
||||||
} catch (Exception e) {}
|
} catch (Exception ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String url = HELP_URL.replace("%y", locale.getLanguage().toLowerCase());
|
String url = HELP_URL.replace("%y", locale.getLanguage().toLowerCase());
|
||||||
url = url.replace("%z", (useCountry ? "_"+locale.getCountry().toLowerCase() : ""));
|
url = url.replace("%z", useCountry ? '_'+locale.getCountry().toLowerCase() : "");
|
||||||
if ((mUsbRegexs.length != 0) && (mWifiRegexs.length == 0)) {
|
if ((mUsbRegexs.length != 0) && (mWifiRegexs.length == 0)) {
|
||||||
url = url.replace("%x", USB_HELP_MODIFIER);
|
url = url.replace("%x", USB_HELP_MODIFIER);
|
||||||
} else if ((mWifiRegexs.length != 0) && (mUsbRegexs.length == 0)) {
|
} else if ((mWifiRegexs.length != 0) && (mUsbRegexs.length == 0)) {
|
||||||
@@ -271,10 +266,8 @@ public class TetherSettings extends SettingsPreferenceFragment {
|
|||||||
String[] errored) {
|
String[] errored) {
|
||||||
ConnectivityManager cm =
|
ConnectivityManager cm =
|
||||||
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
boolean usbTethered = false;
|
|
||||||
boolean usbAvailable = false;
|
boolean usbAvailable = false;
|
||||||
int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
boolean usbErrored = false;
|
|
||||||
boolean massStorageActive =
|
boolean massStorageActive =
|
||||||
Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
|
Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
|
||||||
for (String s : available) {
|
for (String s : available) {
|
||||||
@@ -287,11 +280,13 @@ public class TetherSettings extends SettingsPreferenceFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
boolean usbTethered = false;
|
||||||
for (String s : tethered) {
|
for (String s : tethered) {
|
||||||
for (String regex : mUsbRegexs) {
|
for (String regex : mUsbRegexs) {
|
||||||
if (s.matches(regex)) usbTethered = true;
|
if (s.matches(regex)) usbTethered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
boolean usbErrored = false;
|
||||||
for (String s: errored) {
|
for (String s: errored) {
|
||||||
for (String regex : mUsbRegexs) {
|
for (String regex : mUsbRegexs) {
|
||||||
if (s.matches(regex)) usbErrored = true;
|
if (s.matches(regex)) usbErrored = true;
|
||||||
@@ -329,25 +324,23 @@ public class TetherSettings extends SettingsPreferenceFragment {
|
|||||||
String[] errored) {
|
String[] errored) {
|
||||||
ConnectivityManager cm =
|
ConnectivityManager cm =
|
||||||
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
boolean bluetoothTethered = false;
|
|
||||||
boolean bluetoothAvailable = false;
|
|
||||||
int bluetoothError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
int bluetoothError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
boolean bluetoothErrored = false;
|
|
||||||
for (String s : available) {
|
for (String s : available) {
|
||||||
for (String regex : mBluetoothRegexs) {
|
for (String regex : mBluetoothRegexs) {
|
||||||
if (s.matches(regex)) {
|
if (s.matches(regex)) {
|
||||||
bluetoothAvailable = true;
|
|
||||||
if (bluetoothError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
|
if (bluetoothError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
|
||||||
bluetoothError = cm.getLastTetherError(s);
|
bluetoothError = cm.getLastTetherError(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
boolean bluetoothTethered = false;
|
||||||
for (String s : tethered) {
|
for (String s : tethered) {
|
||||||
for (String regex : mBluetoothRegexs) {
|
for (String regex : mBluetoothRegexs) {
|
||||||
if (s.matches(regex)) bluetoothTethered = true;
|
if (s.matches(regex)) bluetoothTethered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
boolean bluetoothErrored = false;
|
||||||
for (String s: errored) {
|
for (String s: errored) {
|
||||||
for (String regex : mBluetoothRegexs) {
|
for (String regex : mBluetoothRegexs) {
|
||||||
if (s.matches(regex)) bluetoothErrored = true;
|
if (s.matches(regex)) bluetoothErrored = true;
|
||||||
@@ -458,7 +451,7 @@ public class TetherSettings extends SettingsPreferenceFragment {
|
|||||||
return super.onPreferenceTreeClick(screen, preference);
|
return super.onPreferenceTreeClick(screen, preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String findIface(String[] ifaces, String[] regexes) {
|
private static String findIface(String[] ifaces, String[] regexes) {
|
||||||
for (String iface : ifaces) {
|
for (String iface : ifaces) {
|
||||||
for (String regex : regexes) {
|
for (String regex : regexes) {
|
||||||
if (iface.matches(regex)) {
|
if (iface.matches(regex)) {
|
||||||
|
@@ -19,6 +19,8 @@ package com.android.settings;
|
|||||||
import com.android.internal.telephony.TelephonyIntents;
|
import com.android.internal.telephony.TelephonyIntents;
|
||||||
import com.android.internal.telephony.TelephonyProperties;
|
import com.android.internal.telephony.TelephonyProperties;
|
||||||
import com.android.settings.bluetooth.BluetoothEnabler;
|
import com.android.settings.bluetooth.BluetoothEnabler;
|
||||||
|
import com.android.settings.bluetooth.LocalBluetoothAdapter;
|
||||||
|
import com.android.settings.bluetooth.LocalBluetoothManager;
|
||||||
import com.android.settings.wifi.WifiEnabler;
|
import com.android.settings.wifi.WifiEnabler;
|
||||||
import com.android.settings.nfc.NfcEnabler;
|
import com.android.settings.nfc.NfcEnabler;
|
||||||
|
|
||||||
@@ -103,7 +105,8 @@ public class WirelessSettings extends SettingsPreferenceFragment {
|
|||||||
mAirplaneModeEnabler = new AirplaneModeEnabler(activity, airplane);
|
mAirplaneModeEnabler = new AirplaneModeEnabler(activity, airplane);
|
||||||
mAirplaneModePreference = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE);
|
mAirplaneModePreference = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE);
|
||||||
mWifiEnabler = new WifiEnabler(activity, wifi);
|
mWifiEnabler = new WifiEnabler(activity, wifi);
|
||||||
mBtEnabler = new BluetoothEnabler(activity, bt);
|
mBtEnabler = new BluetoothEnabler(activity,
|
||||||
|
LocalBluetoothManager.getInstance(activity).getBluetoothAdapter(), bt);
|
||||||
mNfcEnabler = new NfcEnabler(activity, nfc);
|
mNfcEnabler = new NfcEnabler(activity, nfc);
|
||||||
|
|
||||||
String toggleable = Settings.System.getString(activity.getContentResolver(),
|
String toggleable = Settings.System.getString(activity.getContentResolver(),
|
||||||
|
166
src/com/android/settings/bluetooth/A2dpProfile.java
Normal file
166
src/com/android/settings/bluetooth/A2dpProfile.java
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothA2dp;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.bluetooth.BluetoothUuid;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A2dpProfile handles Bluetooth A2DP.
|
||||||
|
* TODO: add null checks around calls to mService object.
|
||||||
|
*/
|
||||||
|
final class A2dpProfile implements LocalBluetoothProfile {
|
||||||
|
private BluetoothA2dp mService;
|
||||||
|
|
||||||
|
static final ParcelUuid[] SINK_UUIDS = {
|
||||||
|
BluetoothUuid.AudioSink,
|
||||||
|
BluetoothUuid.AdvAudioDist,
|
||||||
|
};
|
||||||
|
|
||||||
|
static final String NAME = "A2DP";
|
||||||
|
|
||||||
|
// Order of this profile in device profiles list
|
||||||
|
private static final int ORDINAL = 1;
|
||||||
|
|
||||||
|
// These callbacks run on the main thread.
|
||||||
|
private final class A2dpServiceListener
|
||||||
|
implements BluetoothProfile.ServiceListener {
|
||||||
|
|
||||||
|
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||||
|
mService = (BluetoothA2dp) proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(int profile) {
|
||||||
|
mService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
A2dpProfile(Context context) {
|
||||||
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
adapter.getProfileProxy(context, new A2dpServiceListener(),
|
||||||
|
BluetoothProfile.A2DP);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAutoConnectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<BluetoothDevice> getConnectedDevices() {
|
||||||
|
return mService.getDevicesMatchingConnectionStates(
|
||||||
|
new int[] {BluetoothProfile.STATE_CONNECTED,
|
||||||
|
BluetoothProfile.STATE_CONNECTING,
|
||||||
|
BluetoothProfile.STATE_DISCONNECTING});
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connect(BluetoothDevice device) {
|
||||||
|
List<BluetoothDevice> sinks = getConnectedDevices();
|
||||||
|
if (sinks != null) {
|
||||||
|
for (BluetoothDevice sink : sinks) {
|
||||||
|
mService.disconnect(sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mService.connect(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean disconnect(BluetoothDevice device) {
|
||||||
|
return mService.disconnect(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectionStatus(BluetoothDevice device) {
|
||||||
|
return mService.getConnectionState(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPreferred(BluetoothDevice device) {
|
||||||
|
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPreferred(BluetoothDevice device) {
|
||||||
|
return mService.getPriority(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreferred(BluetoothDevice device, boolean preferred) {
|
||||||
|
if (preferred) {
|
||||||
|
if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
|
||||||
|
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isA2dpPlaying() {
|
||||||
|
List<BluetoothDevice> sinks = mService.getConnectedDevices();
|
||||||
|
if (!sinks.isEmpty()) {
|
||||||
|
if (mService.isA2dpPlaying(sinks.get(0))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isProfileReady() {
|
||||||
|
return mService != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrdinal() {
|
||||||
|
return ORDINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameResource() {
|
||||||
|
return R.string.bluetooth_profile_a2dp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisconnectResource() {
|
||||||
|
return R.string.bluetooth_disconnect_a2dp_profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSummaryResourceForDevice(BluetoothDevice device) {
|
||||||
|
int state = mService.getConnectionState(device);
|
||||||
|
switch (state) {
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
return R.string.bluetooth_a2dp_profile_summary_use_for;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
return R.string.bluetooth_a2dp_profile_summary_connected;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Utils.getConnectionStateSummary(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDrawableResource(BluetoothClass btClass) {
|
||||||
|
return R.drawable.ic_bt_headphones_a2dp;
|
||||||
|
}
|
||||||
|
}
|
29
src/com/android/settings/bluetooth/BluetoothCallback.java
Normal file
29
src/com/android/settings/bluetooth/BluetoothCallback.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BluetoothCallback provides a callback interface for the settings
|
||||||
|
* UI to receive events from {@link BluetoothEventManager}.
|
||||||
|
*/
|
||||||
|
interface BluetoothCallback {
|
||||||
|
void onBluetoothStateChanged(int bluetoothState);
|
||||||
|
void onScanningStateChanged(boolean started);
|
||||||
|
void onDeviceAdded(CachedBluetoothDevice cachedDevice);
|
||||||
|
void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
|
||||||
|
void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
|
||||||
|
}
|
159
src/com/android/settings/bluetooth/BluetoothDeviceFilter.java
Normal file
159
src/com/android/settings/bluetooth/BluetoothDeviceFilter.java
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothUuid;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BluetoothDeviceFilter contains a static method that returns a
|
||||||
|
* Filter object that returns whether or not the BluetoothDevice
|
||||||
|
* passed to it matches the specified filter type constant from
|
||||||
|
* {@link android.bluetooth.BluetoothDevicePicker}.
|
||||||
|
*/
|
||||||
|
final class BluetoothDeviceFilter {
|
||||||
|
private static final String TAG = "BluetoothDeviceFilter";
|
||||||
|
|
||||||
|
/** The filter interface to external classes. */
|
||||||
|
interface Filter {
|
||||||
|
boolean matches(BluetoothDevice device);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** All filter singleton (referenced directly). */
|
||||||
|
static final Filter ALL_FILTER = new AllFilter();
|
||||||
|
|
||||||
|
/** Bonded devices only filter (referenced directly). */
|
||||||
|
static final Filter BONDED_DEVICE_FILTER = new BondedDeviceFilter();
|
||||||
|
|
||||||
|
/** Table of singleton filter objects. */
|
||||||
|
private static final Filter[] FILTERS = {
|
||||||
|
ALL_FILTER, // FILTER_TYPE_ALL
|
||||||
|
new AudioFilter(), // FILTER_TYPE_AUDIO
|
||||||
|
new TransferFilter(), // FILTER_TYPE_TRANSFER
|
||||||
|
new PanuFilter(), // FILTER_TYPE_PANU
|
||||||
|
new NapFilter() // FILTER_TYPE_NAP
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Private constructor. */
|
||||||
|
private BluetoothDeviceFilter() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the singleton {@link Filter} object for the specified type,
|
||||||
|
* or {@link #ALL_FILTER} if the type value is out of range.
|
||||||
|
*
|
||||||
|
* @param filterType a constant from BluetoothDevicePicker
|
||||||
|
* @return a singleton object implementing the {@link Filter} interface.
|
||||||
|
*/
|
||||||
|
static Filter getFilter(int filterType) {
|
||||||
|
if (filterType >= 0 && filterType < FILTERS.length) {
|
||||||
|
return FILTERS[filterType];
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Invalid filter type " + filterType + " for device picker");
|
||||||
|
return ALL_FILTER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Filter that matches all devices. */
|
||||||
|
private static final class AllFilter implements Filter {
|
||||||
|
public boolean matches(BluetoothDevice device) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Filter that matches only bonded devices. */
|
||||||
|
private static final class BondedDeviceFilter implements Filter {
|
||||||
|
public boolean matches(BluetoothDevice device) {
|
||||||
|
return device.getBondState() == BluetoothDevice.BOND_BONDED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parent class of filters based on UUID and/or Bluetooth class. */
|
||||||
|
private abstract static class ClassUuidFilter implements Filter {
|
||||||
|
abstract boolean matches(ParcelUuid[] uuids, BluetoothClass btClass);
|
||||||
|
|
||||||
|
public boolean matches(BluetoothDevice device) {
|
||||||
|
return matches(device.getUuids(), device.getBluetoothClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Filter that matches devices that support AUDIO profiles. */
|
||||||
|
private static final class AudioFilter extends ClassUuidFilter {
|
||||||
|
@Override
|
||||||
|
boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
|
||||||
|
if (uuids != null) {
|
||||||
|
if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (BluetoothUuid.containsAnyUuid(uuids, HeadsetProfile.UUIDS)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (btClass != null) {
|
||||||
|
if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP) ||
|
||||||
|
btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Filter that matches devices that support Object Transfer. */
|
||||||
|
private static final class TransferFilter extends ClassUuidFilter {
|
||||||
|
@Override
|
||||||
|
boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
|
||||||
|
if (uuids != null) {
|
||||||
|
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return btClass != null
|
||||||
|
&& btClass.doesClassMatch(BluetoothClass.PROFILE_OPP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Filter that matches devices that support PAN User (PANU) profile. */
|
||||||
|
private static final class PanuFilter extends ClassUuidFilter {
|
||||||
|
@Override
|
||||||
|
boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
|
||||||
|
if (uuids != null) {
|
||||||
|
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.PANU)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return btClass != null
|
||||||
|
&& btClass.doesClassMatch(BluetoothClass.PROFILE_PANU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Filter that matches devices that support NAP profile. */
|
||||||
|
private static final class NapFilter extends ClassUuidFilter {
|
||||||
|
@Override
|
||||||
|
boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
|
||||||
|
if (uuids != null) {
|
||||||
|
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return btClass != null
|
||||||
|
&& btClass.doesClassMatch(BluetoothClass.PROFILE_NAP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -16,27 +16,32 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import android.app.AlertDialog;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
|
import com.android.settings.R;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BluetoothDevicePreference is the preference type used to display each remote
|
* BluetoothDevicePreference is the preference type used to display each remote
|
||||||
* Bluetooth device in the Bluetooth Settings screen.
|
* Bluetooth device in the Bluetooth Settings screen.
|
||||||
*/
|
*/
|
||||||
public class BluetoothDevicePreference extends Preference implements
|
public final class BluetoothDevicePreference extends Preference implements
|
||||||
CachedBluetoothDevice.Callback, OnClickListener {
|
CachedBluetoothDevice.Callback, OnClickListener {
|
||||||
private static final String TAG = "BluetoothDevicePreference";
|
private static final String TAG = "BluetoothDevicePreference";
|
||||||
|
|
||||||
@@ -48,11 +53,7 @@ public class BluetoothDevicePreference extends Preference implements
|
|||||||
|
|
||||||
private OnClickListener mOnSettingsClickListener;
|
private OnClickListener mOnSettingsClickListener;
|
||||||
|
|
||||||
/**
|
private AlertDialog mDisconnectDialog;
|
||||||
* Cached local copy of whether the device is busy. This is only updated
|
|
||||||
* from {@link #onDeviceAttributesChanged()}.
|
|
||||||
*/
|
|
||||||
private boolean mIsBusy;
|
|
||||||
|
|
||||||
public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) {
|
public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -60,19 +61,19 @@ public class BluetoothDevicePreference extends Preference implements
|
|||||||
if (sDimAlpha == Integer.MIN_VALUE) {
|
if (sDimAlpha == Integer.MIN_VALUE) {
|
||||||
TypedValue outValue = new TypedValue();
|
TypedValue outValue = new TypedValue();
|
||||||
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
|
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
|
||||||
sDimAlpha = (int) ((outValue.getFloat() * 255) * 0.5);
|
sDimAlpha = (int) (outValue.getFloat() * 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
mCachedDevice = cachedDevice;
|
mCachedDevice = cachedDevice;
|
||||||
|
|
||||||
setWidgetLayoutResource(R.layout.preference_bluetooth);
|
setWidgetLayoutResource(R.layout.preference_bluetooth);
|
||||||
|
|
||||||
cachedDevice.registerCallback(this);
|
mCachedDevice.registerCallback(this);
|
||||||
|
|
||||||
onDeviceAttributesChanged();
|
onDeviceAttributesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachedBluetoothDevice getCachedDevice() {
|
CachedBluetoothDevice getCachedDevice() {
|
||||||
return mCachedDevice;
|
return mCachedDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,41 +85,29 @@ public class BluetoothDevicePreference extends Preference implements
|
|||||||
protected void onPrepareForRemoval() {
|
protected void onPrepareForRemoval() {
|
||||||
super.onPrepareForRemoval();
|
super.onPrepareForRemoval();
|
||||||
mCachedDevice.unregisterCallback(this);
|
mCachedDevice.unregisterCallback(this);
|
||||||
|
if (mDisconnectDialog != null) {
|
||||||
|
mDisconnectDialog.dismiss();
|
||||||
|
mDisconnectDialog = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDeviceAttributesChanged() {
|
public void onDeviceAttributesChanged() {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The preference framework takes care of making sure the value has
|
* The preference framework takes care of making sure the value has
|
||||||
* changed before proceeding.
|
* changed before proceeding. It will also call notifyChanged() if
|
||||||
|
* any preference info has changed from the previous value.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
setTitle(mCachedDevice.getName());
|
setTitle(mCachedDevice.getName());
|
||||||
|
|
||||||
/*
|
setSummary(getConnectionSummary());
|
||||||
* TODO: Showed "Paired" even though it was "Connected". This may be
|
|
||||||
* related to BluetoothHeadset not bound to the actual
|
|
||||||
* BluetoothHeadsetService when we got here.
|
|
||||||
*/
|
|
||||||
setSummary(mCachedDevice.getSummary());
|
|
||||||
|
|
||||||
// Used to gray out the item
|
// Used to gray out the item
|
||||||
mIsBusy = mCachedDevice.isBusy();
|
setEnabled(!mCachedDevice.isBusy());
|
||||||
|
|
||||||
// Data has changed
|
// This could affect ordering, so notify that
|
||||||
notifyChanged();
|
|
||||||
|
|
||||||
// This could affect ordering, so notify that also
|
|
||||||
notifyHierarchyChanged();
|
notifyHierarchyChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
// Temp fix until we have 2053751 fixed in the framework
|
|
||||||
setEnabled(true);
|
|
||||||
return super.isEnabled() && !mIsBusy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onBindView(View view) {
|
protected void onBindView(View view) {
|
||||||
// Disable this view if the bluetooth enable/disable preference view is off
|
// Disable this view if the bluetooth enable/disable preference view is off
|
||||||
@@ -129,17 +118,17 @@ public class BluetoothDevicePreference extends Preference implements
|
|||||||
super.onBindView(view);
|
super.onBindView(view);
|
||||||
|
|
||||||
ImageView btClass = (ImageView) view.findViewById(android.R.id.icon);
|
ImageView btClass = (ImageView) view.findViewById(android.R.id.icon);
|
||||||
btClass.setImageResource(mCachedDevice.getBtClassDrawable());
|
btClass.setImageResource(getBtClassDrawable());
|
||||||
btClass.setAlpha(!mIsBusy ? 255 : sDimAlpha);
|
btClass.setAlpha(isEnabled() ? 255 : sDimAlpha);
|
||||||
|
|
||||||
mDeviceSettings = (ImageView) view.findViewById(R.id.deviceDetails);
|
mDeviceSettings = (ImageView) view.findViewById(R.id.deviceDetails);
|
||||||
if (mOnSettingsClickListener != null) {
|
if (mOnSettingsClickListener != null) {
|
||||||
mDeviceSettings.setOnClickListener(this);
|
mDeviceSettings.setOnClickListener(this);
|
||||||
mDeviceSettings.setTag(mCachedDevice);
|
mDeviceSettings.setTag(mCachedDevice);
|
||||||
mDeviceSettings.setAlpha(!mIsBusy ? 255 : sDimAlpha);
|
mDeviceSettings.setAlpha(isEnabled() ? 255 : sDimAlpha);
|
||||||
} else { // Hide the settings icon and divider
|
} else { // Hide the settings icon and divider
|
||||||
mDeviceSettings.setVisibility(View.GONE);
|
mDeviceSettings.setVisibility(View.GONE);
|
||||||
ImageView divider = (ImageView) view.findViewById(R.id.divider);
|
View divider = view.findViewById(R.id.divider);
|
||||||
if (divider != null) {
|
if (divider != null) {
|
||||||
divider.setVisibility(View.GONE);
|
divider.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
@@ -148,9 +137,10 @@ public class BluetoothDevicePreference extends Preference implements
|
|||||||
LayoutInflater inflater = (LayoutInflater)
|
LayoutInflater inflater = (LayoutInflater)
|
||||||
getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
ViewGroup profilesGroup = (ViewGroup) view.findViewById(R.id.profileIcons);
|
ViewGroup profilesGroup = (ViewGroup) view.findViewById(R.id.profileIcons);
|
||||||
Map<Profile, Drawable> profileIcons = mCachedDevice.getProfileIcons();
|
for (LocalBluetoothProfile profile : mCachedDevice.getProfiles()) {
|
||||||
for (Profile profile : profileIcons.keySet()) {
|
int iconResource = profile.getDrawableResource(mCachedDevice.getBtClass());
|
||||||
Drawable icon = profileIcons.get(profile);
|
if (iconResource != 0) {
|
||||||
|
Drawable icon = getContext().getResources().getDrawable(iconResource);
|
||||||
inflater.inflate(R.layout.profile_icon_small, profilesGroup, true);
|
inflater.inflate(R.layout.profile_icon_small, profilesGroup, true);
|
||||||
ImageView imageView =
|
ImageView imageView =
|
||||||
(ImageView) profilesGroup.getChildAt(profilesGroup.getChildCount() - 1);
|
(ImageView) profilesGroup.getChildAt(profilesGroup.getChildCount() - 1);
|
||||||
@@ -159,12 +149,27 @@ public class BluetoothDevicePreference extends Preference implements
|
|||||||
imageView.setAlpha(profileEnabled ? 255 : sDimAlpha);
|
imageView.setAlpha(profileEnabled ? 255 : sDimAlpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (v == mDeviceSettings) {
|
if (v == mDeviceSettings) {
|
||||||
if (mOnSettingsClickListener != null) mOnSettingsClickListener.onClick(v);
|
if (mOnSettingsClickListener != null) {
|
||||||
|
mOnSettingsClickListener.onClick(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if ((o == null) || !(o instanceof BluetoothDevicePreference)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return mCachedDevice.equals(
|
||||||
|
((BluetoothDevicePreference) o).mCachedDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return mCachedDevice.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(Preference another) {
|
public int compareTo(Preference another) {
|
||||||
@@ -173,7 +178,112 @@ public class BluetoothDevicePreference extends Preference implements
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mCachedDevice.compareTo(((BluetoothDevicePreference) another).mCachedDevice);
|
return mCachedDevice
|
||||||
|
.compareTo(((BluetoothDevicePreference) another).mCachedDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onClicked() {
|
||||||
|
int bondState = mCachedDevice.getBondState();
|
||||||
|
|
||||||
|
if (mCachedDevice.isConnected()) {
|
||||||
|
askDisconnect();
|
||||||
|
} else if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||||
|
mCachedDevice.connect(true);
|
||||||
|
} else if (bondState == BluetoothDevice.BOND_NONE) {
|
||||||
|
pair();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show disconnect confirmation dialog for a device.
|
||||||
|
private void askDisconnect() {
|
||||||
|
Context context = getContext();
|
||||||
|
String name = mCachedDevice.getName();
|
||||||
|
if (TextUtils.isEmpty(name)) {
|
||||||
|
name = context.getString(R.string.bluetooth_device);
|
||||||
|
}
|
||||||
|
String message = context.getString(R.string.bluetooth_disconnect_blank, name);
|
||||||
|
|
||||||
|
DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
mCachedDevice.disconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mDisconnectDialog = Utils.showDisconnectDialog(context,
|
||||||
|
mDisconnectDialog, disconnectListener, name, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void pair() {
|
||||||
|
if (!mCachedDevice.startPairing()) {
|
||||||
|
Utils.showError(getContext(), mCachedDevice.getName(),
|
||||||
|
R.string.bluetooth_pairing_error_message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getConnectionSummary() {
|
||||||
|
final CachedBluetoothDevice cachedDevice = mCachedDevice;
|
||||||
|
final BluetoothDevice device = cachedDevice.getDevice();
|
||||||
|
|
||||||
|
// if any profiles are connected or busy, return that status
|
||||||
|
for (LocalBluetoothProfile profile : cachedDevice.getProfiles()) {
|
||||||
|
int connectionStatus = profile.getConnectionStatus(device);
|
||||||
|
|
||||||
|
if (connectionStatus != BluetoothProfile.STATE_DISCONNECTED) {
|
||||||
|
return Utils.getConnectionStateSummary(connectionStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cachedDevice.getBondState()) {
|
||||||
|
case BluetoothDevice.BOND_BONDED:
|
||||||
|
return R.string.bluetooth_paired;
|
||||||
|
case BluetoothDevice.BOND_BONDING:
|
||||||
|
return R.string.bluetooth_pairing;
|
||||||
|
case BluetoothDevice.BOND_NONE:
|
||||||
|
return R.string.bluetooth_not_connected;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getBtClassDrawable() {
|
||||||
|
BluetoothClass btClass = mCachedDevice.getBtClass();
|
||||||
|
if (btClass != null) {
|
||||||
|
switch (btClass.getMajorDeviceClass()) {
|
||||||
|
case BluetoothClass.Device.Major.COMPUTER:
|
||||||
|
return R.drawable.ic_bt_laptop;
|
||||||
|
|
||||||
|
case BluetoothClass.Device.Major.PHONE:
|
||||||
|
return R.drawable.ic_bt_cellphone;
|
||||||
|
|
||||||
|
case BluetoothClass.Device.Major.PERIPHERAL:
|
||||||
|
return HidProfile.getHidClassDrawable(btClass);
|
||||||
|
|
||||||
|
case BluetoothClass.Device.Major.IMAGING:
|
||||||
|
return R.drawable.ic_bt_imaging;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// unrecognized device class; continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "mBtClass is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LocalBluetoothProfile> profiles = mCachedDevice.getProfiles();
|
||||||
|
for (LocalBluetoothProfile profile : profiles) {
|
||||||
|
int resId = profile.getDrawableResource(btClass);
|
||||||
|
if (resId != 0) {
|
||||||
|
return resId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (btClass != null) {
|
||||||
|
if (btClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
|
||||||
|
return R.drawable.ic_bt_headphones_a2dp;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (btClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
|
||||||
|
return R.drawable.ic_bt_headset_hfp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,41 +16,34 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
|
import android.preference.CheckBoxPreference;
|
||||||
import android.preference.ListPreference;
|
import android.preference.ListPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.CheckBoxPreference;
|
|
||||||
import android.provider.Settings;
|
import com.android.settings.R;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BluetoothDiscoverableEnabler is a helper to manage the "Discoverable"
|
* BluetoothDiscoverableEnabler is a helper to manage the "Discoverable"
|
||||||
* checkbox. It sets/unsets discoverability and keeps track of how much time
|
* checkbox. It sets/unsets discoverability and keeps track of how much time
|
||||||
* until the the discoverability is automatically turned off.
|
* until the the discoverability is automatically turned off.
|
||||||
*/
|
*/
|
||||||
public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChangeListener {
|
final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChangeListener {
|
||||||
private static final String TAG = "BluetoothDiscoverableEnabler";
|
|
||||||
|
|
||||||
private static final String SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT =
|
private static final String SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT =
|
||||||
"debug.bt.discoverable_time";
|
"debug.bt.discoverable_time";
|
||||||
|
|
||||||
static final int DISCOVERABLE_TIMEOUT_TWO_MINUTES = 120;
|
private static final int DISCOVERABLE_TIMEOUT_TWO_MINUTES = 120;
|
||||||
static final int DISCOVERABLE_TIMEOUT_FIVE_MINUTES = 300;
|
private static final int DISCOVERABLE_TIMEOUT_FIVE_MINUTES = 300;
|
||||||
static final int DISCOVERABLE_TIMEOUT_ONE_HOUR = 3600;
|
private static final int DISCOVERABLE_TIMEOUT_ONE_HOUR = 3600;
|
||||||
static final int DISCOVERABLE_TIMEOUT_NEVER = 0;
|
static final int DISCOVERABLE_TIMEOUT_NEVER = 0;
|
||||||
|
|
||||||
static final String SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP =
|
|
||||||
"discoverable_end_timestamp";
|
|
||||||
|
|
||||||
private static final String VALUE_DISCOVERABLE_TIMEOUT_TWO_MINUTES = "twomin";
|
private static final String VALUE_DISCOVERABLE_TIMEOUT_TWO_MINUTES = "twomin";
|
||||||
private static final String VALUE_DISCOVERABLE_TIMEOUT_FIVE_MINUTES = "fivemin";
|
private static final String VALUE_DISCOVERABLE_TIMEOUT_FIVE_MINUTES = "fivemin";
|
||||||
private static final String VALUE_DISCOVERABLE_TIMEOUT_ONE_HOUR = "onehour";
|
private static final String VALUE_DISCOVERABLE_TIMEOUT_ONE_HOUR = "onehour";
|
||||||
@@ -63,7 +56,7 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
|
|||||||
private final CheckBoxPreference mCheckBoxPreference;
|
private final CheckBoxPreference mCheckBoxPreference;
|
||||||
private final ListPreference mTimeoutListPreference;
|
private final ListPreference mTimeoutListPreference;
|
||||||
|
|
||||||
private final LocalBluetoothManager mLocalManager;
|
private final LocalBluetoothAdapter mLocalAdapter;
|
||||||
|
|
||||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
@@ -84,7 +77,7 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public BluetoothDiscoverableEnabler(Context context,
|
BluetoothDiscoverableEnabler(Context context, LocalBluetoothAdapter adapter,
|
||||||
CheckBoxPreference checkBoxPreference, ListPreference timeoutListPreference) {
|
CheckBoxPreference checkBoxPreference, ListPreference timeoutListPreference) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mUiHandler = new Handler();
|
mUiHandler = new Handler();
|
||||||
@@ -95,15 +88,15 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
|
|||||||
// we actually want to persist this since can't infer from BT device state
|
// we actually want to persist this since can't infer from BT device state
|
||||||
mTimeoutListPreference.setPersistent(true);
|
mTimeoutListPreference.setPersistent(true);
|
||||||
|
|
||||||
mLocalManager = LocalBluetoothManager.getInstance(context);
|
mLocalAdapter = adapter;
|
||||||
if (mLocalManager == null) {
|
if (adapter == null) {
|
||||||
// Bluetooth not supported
|
// Bluetooth not supported
|
||||||
checkBoxPreference.setEnabled(false);
|
checkBoxPreference.setEnabled(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resume() {
|
public void resume() {
|
||||||
if (mLocalManager == null) {
|
if (mLocalAdapter == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,11 +104,11 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
|
|||||||
mContext.registerReceiver(mReceiver, filter);
|
mContext.registerReceiver(mReceiver, filter);
|
||||||
mCheckBoxPreference.setOnPreferenceChangeListener(this);
|
mCheckBoxPreference.setOnPreferenceChangeListener(this);
|
||||||
mTimeoutListPreference.setOnPreferenceChangeListener(this);
|
mTimeoutListPreference.setOnPreferenceChangeListener(this);
|
||||||
handleModeChanged(mLocalManager.getBluetoothAdapter().getScanMode());
|
handleModeChanged(mLocalAdapter.getScanMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pause() {
|
public void pause() {
|
||||||
if (mLocalManager == null) {
|
if (mLocalAdapter == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,43 +130,37 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setEnabled(final boolean enable) {
|
private void setEnabled(boolean enable) {
|
||||||
BluetoothAdapter manager = mLocalManager.getBluetoothAdapter();
|
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
int timeout = getDiscoverableTimeout();
|
int timeout = getDiscoverableTimeout();
|
||||||
manager.setDiscoverableTimeout(timeout);
|
mLocalAdapter.setDiscoverableTimeout(timeout);
|
||||||
|
|
||||||
long endTimestamp = System.currentTimeMillis() + timeout * 1000L;
|
long endTimestamp = System.currentTimeMillis() + timeout * 1000L;
|
||||||
persistDiscoverableEndTimestamp(endTimestamp);
|
LocalBluetoothPreferences.persistDiscoverableEndTimestamp(mContext, endTimestamp);
|
||||||
|
|
||||||
updateCountdownSummary();
|
updateCountdownSummary();
|
||||||
|
|
||||||
manager.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, timeout);
|
mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, timeout);
|
||||||
} else {
|
} else {
|
||||||
manager.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
|
mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateTimerDisplay(int timeout) {
|
private void updateTimerDisplay(int timeout) {
|
||||||
if (getDiscoverableTimeout() == DISCOVERABLE_TIMEOUT_NEVER) {
|
if (getDiscoverableTimeout() == DISCOVERABLE_TIMEOUT_NEVER) {
|
||||||
mCheckBoxPreference.setSummaryOn(
|
mCheckBoxPreference.setSummaryOn(
|
||||||
mContext.getResources()
|
mContext.getString(R.string.bluetooth_is_discoverable_always));
|
||||||
.getString(R.string.bluetooth_is_discoverable_always));
|
|
||||||
} else {
|
} else {
|
||||||
mCheckBoxPreference.setSummaryOn(
|
mCheckBoxPreference.setSummaryOn(
|
||||||
mContext.getResources()
|
mContext.getString(R.string.bluetooth_is_discoverable, timeout));
|
||||||
.getString(R.string.bluetooth_is_discoverable, timeout));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getDiscoverableTimeout() {
|
private int getDiscoverableTimeout() {
|
||||||
int timeout = SystemProperties.getInt(SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT, -1);
|
int timeout = SystemProperties.getInt(SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT, -1);
|
||||||
if (timeout < 0) {
|
if (timeout < 0) {
|
||||||
String timeoutValue = null;
|
String timeoutValue = mTimeoutListPreference.getValue();
|
||||||
if (mTimeoutListPreference != null && mTimeoutListPreference.getValue() != null) {
|
if (timeoutValue == null) {
|
||||||
timeoutValue = mTimeoutListPreference.getValue().toString();
|
|
||||||
} else {
|
|
||||||
mTimeoutListPreference.setValue(VALUE_DISCOVERABLE_TIMEOUT_TWO_MINUTES);
|
mTimeoutListPreference.setValue(VALUE_DISCOVERABLE_TIMEOUT_TWO_MINUTES);
|
||||||
return DISCOVERABLE_TIMEOUT_TWO_MINUTES;
|
return DISCOVERABLE_TIMEOUT_TWO_MINUTES;
|
||||||
}
|
}
|
||||||
@@ -192,12 +179,6 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
|
|||||||
return timeout;
|
return timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void persistDiscoverableEndTimestamp(long endTimestamp) {
|
|
||||||
SharedPreferences.Editor editor = mLocalManager.getSharedPreferences().edit();
|
|
||||||
editor.putLong(SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP, endTimestamp);
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleModeChanged(int mode) {
|
private void handleModeChanged(int mode) {
|
||||||
if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
|
if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
|
||||||
mCheckBoxPreference.setChecked(true);
|
mCheckBoxPreference.setChecked(true);
|
||||||
@@ -208,14 +189,13 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateCountdownSummary() {
|
private void updateCountdownSummary() {
|
||||||
int mode = mLocalManager.getBluetoothAdapter().getScanMode();
|
int mode = mLocalAdapter.getScanMode();
|
||||||
if (mode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
|
if (mode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long currentTimestamp = System.currentTimeMillis();
|
long currentTimestamp = System.currentTimeMillis();
|
||||||
long endTimestamp = mLocalManager.getSharedPreferences().getLong(
|
long endTimestamp = LocalBluetoothPreferences.getDiscoverableEndTimestamp(mContext);
|
||||||
SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP, 0);
|
|
||||||
|
|
||||||
if (currentTimestamp > endTimestamp) {
|
if (currentTimestamp > endTimestamp) {
|
||||||
// We're still in discoverable mode, but maybe there isn't a timeout.
|
// We're still in discoverable mode, but maybe there isn't a timeout.
|
||||||
@@ -231,6 +211,4 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
|
|||||||
mUiHandler.postDelayed(mUpdateCountdownSummaryRunnable, 1000);
|
mUiHandler.postDelayed(mUpdateCountdownSummaryRunnable, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BluetoothDiscoveryReceiver updates a timestamp when the
|
||||||
|
* Bluetooth adapter starts or finishes discovery mode. This
|
||||||
|
* is used to decide whether to open an alert dialog or
|
||||||
|
* create a notification when we receive a pairing request.
|
||||||
|
*
|
||||||
|
* <p>Note that the discovery start/finish intents are also handled
|
||||||
|
* by {@link BluetoothEventManager} to update the UI, if visible.
|
||||||
|
*/
|
||||||
|
public final class BluetoothDiscoveryReceiver extends BroadcastReceiver {
|
||||||
|
private static final String TAG = "BluetoothDiscoveryReceiver";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
String action = intent.getAction();
|
||||||
|
Log.v(TAG, "Received: " + action);
|
||||||
|
|
||||||
|
if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED) ||
|
||||||
|
action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
|
||||||
|
LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -34,13 +34,14 @@ import android.widget.Toast;
|
|||||||
* preference. It turns on/off Bluetooth and ensures the summary of the
|
* preference. It turns on/off Bluetooth and ensures the summary of the
|
||||||
* preference reflects the current state.
|
* preference reflects the current state.
|
||||||
*/
|
*/
|
||||||
public class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
|
public final class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final CheckBoxPreference mCheckBox;
|
private final CheckBoxPreference mCheckBox;
|
||||||
private final CharSequence mOriginalSummary;
|
private final CharSequence mOriginalSummary;
|
||||||
|
|
||||||
private final LocalBluetoothManager mLocalManager;
|
private final LocalBluetoothAdapter mLocalAdapter;
|
||||||
private final IntentFilter mIntentFilter;
|
private final IntentFilter mIntentFilter;
|
||||||
|
|
||||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
@@ -49,14 +50,15 @@ public class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public BluetoothEnabler(Context context, CheckBoxPreference checkBox) {
|
public BluetoothEnabler(Context context, LocalBluetoothAdapter adapter,
|
||||||
|
CheckBoxPreference checkBox) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mCheckBox = checkBox;
|
mCheckBox = checkBox;
|
||||||
mOriginalSummary = checkBox.getSummary();
|
mOriginalSummary = checkBox.getSummary();
|
||||||
checkBox.setPersistent(false);
|
checkBox.setPersistent(false);
|
||||||
|
|
||||||
mLocalManager = LocalBluetoothManager.getInstance(context);
|
mLocalAdapter = adapter;
|
||||||
if (mLocalManager == null) {
|
if (adapter == null) {
|
||||||
// Bluetooth is not supported
|
// Bluetooth is not supported
|
||||||
checkBox.setEnabled(false);
|
checkBox.setEnabled(false);
|
||||||
}
|
}
|
||||||
@@ -64,19 +66,19 @@ public class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void resume() {
|
public void resume() {
|
||||||
if (mLocalManager == null) {
|
if (mLocalAdapter == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bluetooth state is not sticky, so set it manually
|
// Bluetooth state is not sticky, so set it manually
|
||||||
handleStateChanged(mLocalManager.getBluetoothState());
|
handleStateChanged(mLocalAdapter.getBluetoothState());
|
||||||
|
|
||||||
mContext.registerReceiver(mReceiver, mIntentFilter);
|
mContext.registerReceiver(mReceiver, mIntentFilter);
|
||||||
mCheckBox.setOnPreferenceChangeListener(this);
|
mCheckBox.setOnPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pause() {
|
public void pause() {
|
||||||
if (mLocalManager == null) {
|
if (mLocalAdapter == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,14 +97,14 @@ public class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLocalManager.setBluetoothEnabled(enable);
|
mLocalAdapter.setBluetoothEnabled(enable);
|
||||||
mCheckBox.setEnabled(false);
|
mCheckBox.setEnabled(false);
|
||||||
|
|
||||||
// Don't update UI to opposite state until we're sure
|
// Don't update UI to opposite state until we're sure
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ void handleStateChanged(int state) {
|
void handleStateChanged(int state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case BluetoothAdapter.STATE_TURNING_ON:
|
case BluetoothAdapter.STATE_TURNING_ON:
|
||||||
mCheckBox.setSummary(R.string.wifi_starting);
|
mCheckBox.setSummary(R.string.wifi_starting);
|
||||||
|
398
src/com/android/settings/bluetooth/BluetoothEventManager.java
Normal file
398
src/com/android/settings/bluetooth/BluetoothEventManager.java
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BluetoothEventManager receives broadcasts and callbacks from the Bluetooth
|
||||||
|
* API and dispatches the event on the UI thread to the right class in the
|
||||||
|
* Settings.
|
||||||
|
*/
|
||||||
|
final class BluetoothEventManager {
|
||||||
|
private static final String TAG = "BluetoothEventManager";
|
||||||
|
|
||||||
|
private final LocalBluetoothAdapter mLocalAdapter;
|
||||||
|
private final CachedBluetoothDeviceManager mDeviceManager;
|
||||||
|
private LocalBluetoothProfileManager mProfileManager;
|
||||||
|
private final IntentFilter mIntentFilter;
|
||||||
|
private final Map<String, Handler> mHandlerMap;
|
||||||
|
|
||||||
|
private final Collection<BluetoothCallback> mCallbacks =
|
||||||
|
new ArrayList<BluetoothCallback>();
|
||||||
|
|
||||||
|
interface Handler {
|
||||||
|
void onReceive(Context context, Intent intent, BluetoothDevice device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addHandler(String action, Handler handler) {
|
||||||
|
mHandlerMap.put(action, handler);
|
||||||
|
mIntentFilter.addAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set profile manager after construction due to circular dependency
|
||||||
|
void setProfileManager(LocalBluetoothProfileManager manager) {
|
||||||
|
mProfileManager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
BluetoothEventManager(LocalBluetoothAdapter adapter,
|
||||||
|
CachedBluetoothDeviceManager deviceManager) {
|
||||||
|
mLocalAdapter = adapter;
|
||||||
|
mDeviceManager = deviceManager;
|
||||||
|
mIntentFilter = new IntentFilter();
|
||||||
|
mHandlerMap = new HashMap<String, Handler>();
|
||||||
|
|
||||||
|
// Bluetooth on/off broadcasts
|
||||||
|
addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler());
|
||||||
|
|
||||||
|
// Discovery broadcasts
|
||||||
|
addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, new ScanningStateChangedHandler(true));
|
||||||
|
addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, new ScanningStateChangedHandler(false));
|
||||||
|
addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler());
|
||||||
|
addHandler(BluetoothDevice.ACTION_DISAPPEARED, new DeviceDisappearedHandler());
|
||||||
|
addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler());
|
||||||
|
|
||||||
|
// Pairing broadcasts
|
||||||
|
addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());
|
||||||
|
addHandler(BluetoothDevice.ACTION_PAIRING_CANCEL, new PairingCancelHandler());
|
||||||
|
|
||||||
|
// Fine-grained state broadcasts
|
||||||
|
addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
|
||||||
|
addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
|
||||||
|
|
||||||
|
// Dock event broadcasts
|
||||||
|
addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Bluetooth-related activity is now in the foreground. Register to
|
||||||
|
* start receiving Bluetooth events.
|
||||||
|
* @param context a Context object for the current Activity
|
||||||
|
*/
|
||||||
|
void resume(Context context) {
|
||||||
|
if (mLocalAdapter.syncBluetoothState()) {
|
||||||
|
// adapter state changed while we were paused: send callbacks
|
||||||
|
int newState = mLocalAdapter.getState();
|
||||||
|
synchronized (mCallbacks) {
|
||||||
|
for (BluetoothCallback callback : mCallbacks) {
|
||||||
|
callback.onBluetoothStateChanged(newState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.registerReceiver(mBroadcastReceiver, mIntentFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pause(Context context) {
|
||||||
|
context.unregisterReceiver(mBroadcastReceiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Register to start receiving callbacks for Bluetooth events. */
|
||||||
|
void registerCallback(BluetoothCallback callback) {
|
||||||
|
synchronized (mCallbacks) {
|
||||||
|
mCallbacks.add(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Unregister to stop receiving callbacks for Bluetooth events. */
|
||||||
|
void unregisterCallback(BluetoothCallback callback) {
|
||||||
|
synchronized (mCallbacks) {
|
||||||
|
mCallbacks.remove(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can't be called from a broadcast receiver where the filter is set in the Manifest.
|
||||||
|
private static String getDockedDeviceAddress(Context context) {
|
||||||
|
// This works only because these broadcast intents are "sticky"
|
||||||
|
Intent i = context.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
|
||||||
|
if (i != null) {
|
||||||
|
int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
|
||||||
|
if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
|
||||||
|
BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
if (device != null) {
|
||||||
|
return device.getAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
Log.v(TAG, "Received " + intent.getAction());
|
||||||
|
|
||||||
|
String action = intent.getAction();
|
||||||
|
BluetoothDevice device = intent
|
||||||
|
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
|
||||||
|
Handler handler = mHandlerMap.get(action);
|
||||||
|
if (handler != null) {
|
||||||
|
handler.onReceive(context, intent, device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private class AdapterStateChangedHandler implements Handler {
|
||||||
|
public void onReceive(Context context, Intent intent,
|
||||||
|
BluetoothDevice device) {
|
||||||
|
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
|
||||||
|
BluetoothAdapter.ERROR);
|
||||||
|
// update local profiles and get paired devices
|
||||||
|
mLocalAdapter.setBluetoothStateInt(state);
|
||||||
|
// send callback to update UI and possibly start scanning
|
||||||
|
synchronized (mCallbacks) {
|
||||||
|
for (BluetoothCallback callback : mCallbacks) {
|
||||||
|
callback.onBluetoothStateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ScanningStateChangedHandler implements Handler {
|
||||||
|
private final boolean mStarted;
|
||||||
|
|
||||||
|
ScanningStateChangedHandler(boolean started) {
|
||||||
|
mStarted = started;
|
||||||
|
}
|
||||||
|
public void onReceive(Context context, Intent intent,
|
||||||
|
BluetoothDevice device) {
|
||||||
|
synchronized (mCallbacks) {
|
||||||
|
for (BluetoothCallback callback : mCallbacks) {
|
||||||
|
callback.onScanningStateChanged(mStarted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mDeviceManager.onScanningStateChanged(mStarted);
|
||||||
|
LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DeviceFoundHandler implements Handler {
|
||||||
|
public void onReceive(Context context, Intent intent,
|
||||||
|
BluetoothDevice device) {
|
||||||
|
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
|
||||||
|
BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
|
||||||
|
String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
|
||||||
|
// TODO Pick up UUID. They should be available for 2.1 devices.
|
||||||
|
// Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
|
||||||
|
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
|
||||||
|
if (cachedDevice == null) {
|
||||||
|
cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
|
||||||
|
Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice: "
|
||||||
|
+ cachedDevice);
|
||||||
|
// callback to UI to create Preference for new device
|
||||||
|
dispatchDeviceAdded(cachedDevice);
|
||||||
|
}
|
||||||
|
cachedDevice.setRssi(rssi);
|
||||||
|
cachedDevice.setBtClass(btClass);
|
||||||
|
cachedDevice.setName(name);
|
||||||
|
cachedDevice.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
|
||||||
|
synchronized (mCallbacks) {
|
||||||
|
for (BluetoothCallback callback : mCallbacks) {
|
||||||
|
callback.onDeviceAdded(cachedDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DeviceDisappearedHandler implements Handler {
|
||||||
|
public void onReceive(Context context, Intent intent,
|
||||||
|
BluetoothDevice device) {
|
||||||
|
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
|
||||||
|
if (cachedDevice == null) {
|
||||||
|
Log.w(TAG, "received ACTION_DISAPPEARED for an unknown device: " + device);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mDeviceManager.onDeviceDisappeared(cachedDevice)) {
|
||||||
|
synchronized (mCallbacks) {
|
||||||
|
for (BluetoothCallback callback : mCallbacks) {
|
||||||
|
callback.onDeviceDeleted(cachedDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NameChangedHandler implements Handler {
|
||||||
|
public void onReceive(Context context, Intent intent,
|
||||||
|
BluetoothDevice device) {
|
||||||
|
mDeviceManager.onDeviceNameUpdated(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BondStateChangedHandler implements Handler {
|
||||||
|
public void onReceive(Context context, Intent intent,
|
||||||
|
BluetoothDevice device) {
|
||||||
|
if (device == null) {
|
||||||
|
Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
|
||||||
|
BluetoothDevice.ERROR);
|
||||||
|
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
|
||||||
|
if (cachedDevice == null) {
|
||||||
|
Log.w(TAG, "CachedBluetoothDevice for device " + device +
|
||||||
|
" not found, calling readPairedDevices().");
|
||||||
|
if (!readPairedDevices()) {
|
||||||
|
Log.e(TAG, "Got bonding state changed for " + device +
|
||||||
|
", but we have no record of that device.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cachedDevice = mDeviceManager.findDevice(device);
|
||||||
|
if (cachedDevice == null) {
|
||||||
|
Log.e(TAG, "Got bonding state changed for " + device +
|
||||||
|
", but device not added in cache.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (mCallbacks) {
|
||||||
|
for (BluetoothCallback callback : mCallbacks) {
|
||||||
|
callback.onDeviceBondStateChanged(cachedDevice, bondState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cachedDevice.onBondingStateChanged(bondState);
|
||||||
|
|
||||||
|
if (bondState == BluetoothDevice.BOND_NONE) {
|
||||||
|
if (device.isBluetoothDock()) {
|
||||||
|
// After a dock is unpaired, we will forget the settings
|
||||||
|
LocalBluetoothPreferences
|
||||||
|
.removeDockAutoConnectSetting(context, device.getAddress());
|
||||||
|
|
||||||
|
// if the device is undocked, remove it from the list as well
|
||||||
|
if (!device.getAddress().equals(getDockedDeviceAddress(context))) {
|
||||||
|
mDeviceManager.onDeviceDisappeared(cachedDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
|
||||||
|
BluetoothDevice.ERROR);
|
||||||
|
|
||||||
|
showUnbondMessage(context, cachedDevice.getName(), reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when we have reached the unbonded state.
|
||||||
|
*
|
||||||
|
* @param reason one of the error reasons from
|
||||||
|
* BluetoothDevice.UNBOND_REASON_*
|
||||||
|
*/
|
||||||
|
private void showUnbondMessage(Context context, String name, int reason) {
|
||||||
|
int errorMsg;
|
||||||
|
|
||||||
|
switch(reason) {
|
||||||
|
case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
|
||||||
|
errorMsg = R.string.bluetooth_pairing_pin_error_message;
|
||||||
|
break;
|
||||||
|
case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
|
||||||
|
errorMsg = R.string.bluetooth_pairing_rejected_error_message;
|
||||||
|
break;
|
||||||
|
case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
|
||||||
|
errorMsg = R.string.bluetooth_pairing_device_down_error_message;
|
||||||
|
break;
|
||||||
|
case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
|
||||||
|
case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
|
||||||
|
case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
|
||||||
|
case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
|
||||||
|
errorMsg = R.string.bluetooth_pairing_error_message;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Utils.showError(context, name, errorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ClassChangedHandler implements Handler {
|
||||||
|
public void onReceive(Context context, Intent intent,
|
||||||
|
BluetoothDevice device) {
|
||||||
|
mDeviceManager.onBtClassChanged(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UuidChangedHandler implements Handler {
|
||||||
|
public void onReceive(Context context, Intent intent,
|
||||||
|
BluetoothDevice device) {
|
||||||
|
mDeviceManager.onUuidChanged(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PairingCancelHandler implements Handler {
|
||||||
|
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
|
||||||
|
if (device == null) {
|
||||||
|
Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int errorMsg = R.string.bluetooth_pairing_error_message;
|
||||||
|
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
|
||||||
|
Utils.showError(context, cachedDevice.getName(), errorMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DockEventHandler implements Handler {
|
||||||
|
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
|
||||||
|
// Remove if unpair device upon undocking
|
||||||
|
int anythingButUnDocked = Intent.EXTRA_DOCK_STATE_UNDOCKED + 1;
|
||||||
|
int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, anythingButUnDocked);
|
||||||
|
if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
|
||||||
|
if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
|
||||||
|
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
|
||||||
|
if (cachedDevice != null) {
|
||||||
|
mDeviceManager.onDeviceDisappeared(cachedDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean readPairedDevices() {
|
||||||
|
Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
|
||||||
|
if (bondedDevices == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean deviceAdded = false;
|
||||||
|
for (BluetoothDevice device : bondedDevices) {
|
||||||
|
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
|
||||||
|
if (cachedDevice == null) {
|
||||||
|
cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
|
||||||
|
dispatchDeviceAdded(cachedDevice);
|
||||||
|
deviceAdded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceAdded;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,257 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.bluetooth;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
|
|
||||||
|
|
||||||
import android.bluetooth.BluetoothA2dp;
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
|
||||||
import android.bluetooth.BluetoothClass;
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
|
||||||
import android.bluetooth.BluetoothHeadset;
|
|
||||||
import android.bluetooth.BluetoothInputDevice;
|
|
||||||
import android.bluetooth.BluetoothPan;
|
|
||||||
import android.bluetooth.BluetoothProfile;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BluetoothEventRedirector receives broadcasts and callbacks from the Bluetooth
|
|
||||||
* API and dispatches the event on the UI thread to the right class in the
|
|
||||||
* Settings.
|
|
||||||
*/
|
|
||||||
class BluetoothEventRedirector {
|
|
||||||
private static final String TAG = "BluetoothEventRedirector";
|
|
||||||
|
|
||||||
/* package */ final LocalBluetoothManager mManager;
|
|
||||||
|
|
||||||
private final ThreadPoolExecutor mSerialExecutor = new ThreadPoolExecutor(
|
|
||||||
0, 1, 1000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
|
|
||||||
|
|
||||||
private final Handler mHandler = new Handler();
|
|
||||||
|
|
||||||
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
Log.v(TAG, "Received " + intent.getAction());
|
|
||||||
|
|
||||||
String action = intent.getAction();
|
|
||||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
||||||
|
|
||||||
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
|
|
||||||
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
|
|
||||||
BluetoothAdapter.ERROR);
|
|
||||||
mManager.setBluetoothStateInt(state);
|
|
||||||
} else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
|
|
||||||
PendingResult pr = goAsync(); // so loading shared prefs doesn't kill animation
|
|
||||||
persistDiscoveringTimestamp(pr, true);
|
|
||||||
} else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
|
|
||||||
PendingResult pr = goAsync(); // so loading shared prefs doesn't kill animation
|
|
||||||
persistDiscoveringTimestamp(pr, false);
|
|
||||||
} else if (action.equals(BluetoothDevice.ACTION_FOUND)) {
|
|
||||||
short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE);
|
|
||||||
BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
|
|
||||||
String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
|
|
||||||
// TODO Pick up UUID. They should be available for 2.1 devices.
|
|
||||||
// Skip for now, there's a bluez problem and we are not getting uuids even for 2.1.
|
|
||||||
mManager.getCachedDeviceManager().onDeviceAppeared(device, rssi, btClass, name);
|
|
||||||
|
|
||||||
} else if (action.equals(BluetoothDevice.ACTION_DISAPPEARED)) {
|
|
||||||
mManager.getCachedDeviceManager().onDeviceDisappeared(device);
|
|
||||||
|
|
||||||
} else if (action.equals(BluetoothDevice.ACTION_NAME_CHANGED)) {
|
|
||||||
mManager.getCachedDeviceManager().onDeviceNameUpdated(device);
|
|
||||||
|
|
||||||
} else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
|
|
||||||
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
|
|
||||||
BluetoothDevice.ERROR);
|
|
||||||
CachedBluetoothDeviceManager cachedDeviceMgr = mManager.getCachedDeviceManager();
|
|
||||||
cachedDeviceMgr.onBondingStateChanged(device, bondState);
|
|
||||||
if (bondState == BluetoothDevice.BOND_NONE) {
|
|
||||||
if (device.isBluetoothDock()) {
|
|
||||||
// After a dock is unpaired, we will forget the
|
|
||||||
// settings
|
|
||||||
mManager.removeDockAutoConnectSetting(device.getAddress());
|
|
||||||
|
|
||||||
// if the device is undocked, remove it from the list as
|
|
||||||
// well
|
|
||||||
if (!device.getAddress().equals(getDockedDeviceAddress(context))) {
|
|
||||||
cachedDeviceMgr.onDeviceDisappeared(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
|
|
||||||
BluetoothDevice.ERROR);
|
|
||||||
cachedDeviceMgr.showUnbondMessage(device, reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
|
|
||||||
int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
|
|
||||||
int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
|
|
||||||
if (newState == BluetoothProfile.STATE_DISCONNECTED &&
|
|
||||||
oldState == BluetoothProfile.STATE_CONNECTING) {
|
|
||||||
Log.i(TAG, "Failed to connect BT headset");
|
|
||||||
}
|
|
||||||
|
|
||||||
mManager.getCachedDeviceManager().onProfileStateChanged(device,
|
|
||||||
Profile.HEADSET, newState);
|
|
||||||
} else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
|
|
||||||
int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
|
|
||||||
int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
|
|
||||||
if (newState == BluetoothProfile.STATE_DISCONNECTED &&
|
|
||||||
oldState == BluetoothProfile.STATE_CONNECTING) {
|
|
||||||
Log.i(TAG, "Failed to connect BT A2DP");
|
|
||||||
}
|
|
||||||
|
|
||||||
mManager.getCachedDeviceManager().onProfileStateChanged(device,
|
|
||||||
Profile.A2DP, newState);
|
|
||||||
} else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) {
|
|
||||||
final int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
|
|
||||||
final int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
|
|
||||||
if (newState == BluetoothProfile.STATE_DISCONNECTED &&
|
|
||||||
oldState == BluetoothProfile.STATE_CONNECTING) {
|
|
||||||
Log.i(TAG, "Failed to connect BT HID");
|
|
||||||
}
|
|
||||||
|
|
||||||
mManager.getCachedDeviceManager().onProfileStateChanged(device,
|
|
||||||
Profile.HID, newState);
|
|
||||||
|
|
||||||
} else if (action.equals(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED)) {
|
|
||||||
final int role = intent.getIntExtra(
|
|
||||||
BluetoothPan.EXTRA_LOCAL_ROLE, 0);
|
|
||||||
if (role == BluetoothPan.LOCAL_PANU_ROLE) {
|
|
||||||
final int newState = intent.getIntExtra(
|
|
||||||
BluetoothPan.EXTRA_STATE, 0);
|
|
||||||
final int oldState = intent.getIntExtra(
|
|
||||||
BluetoothPan.EXTRA_PREVIOUS_STATE, 0);
|
|
||||||
if (newState == BluetoothPan.STATE_DISCONNECTED &&
|
|
||||||
oldState == BluetoothPan.STATE_CONNECTING) {
|
|
||||||
Log.i(TAG, "Failed to connect BT PAN");
|
|
||||||
}
|
|
||||||
mManager.getCachedDeviceManager().onProfileStateChanged(device,
|
|
||||||
Profile.PAN, newState);
|
|
||||||
}
|
|
||||||
} else if (action.equals(BluetoothDevice.ACTION_CLASS_CHANGED)) {
|
|
||||||
mManager.getCachedDeviceManager().onBtClassChanged(device);
|
|
||||||
|
|
||||||
} else if (action.equals(BluetoothDevice.ACTION_UUID)) {
|
|
||||||
mManager.getCachedDeviceManager().onUuidChanged(device);
|
|
||||||
|
|
||||||
} else if (action.equals(BluetoothDevice.ACTION_PAIRING_CANCEL)) {
|
|
||||||
int errorMsg = R.string.bluetooth_pairing_error_message;
|
|
||||||
mManager.showError(device, errorMsg);
|
|
||||||
|
|
||||||
} else if (action.equals(Intent.ACTION_DOCK_EVENT)) {
|
|
||||||
// Remove if unpair device upon undocking
|
|
||||||
int anythingButUnDocked = Intent.EXTRA_DOCK_STATE_UNDOCKED + 1;
|
|
||||||
int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, anythingButUnDocked);
|
|
||||||
if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
|
|
||||||
if (device != null && device.getBondState() == BluetoothDevice.BOND_NONE) {
|
|
||||||
mManager.getCachedDeviceManager().onDeviceDisappeared(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public BluetoothEventRedirector(LocalBluetoothManager localBluetoothManager) {
|
|
||||||
mManager = localBluetoothManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerReceiver() {
|
|
||||||
IntentFilter filter = new IntentFilter();
|
|
||||||
|
|
||||||
// Bluetooth on/off broadcasts
|
|
||||||
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
|
|
||||||
|
|
||||||
// Discovery broadcasts
|
|
||||||
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
|
|
||||||
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
|
|
||||||
filter.addAction(BluetoothDevice.ACTION_DISAPPEARED);
|
|
||||||
filter.addAction(BluetoothDevice.ACTION_FOUND);
|
|
||||||
filter.addAction(BluetoothDevice.ACTION_NAME_CHANGED);
|
|
||||||
|
|
||||||
// Pairing broadcasts
|
|
||||||
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
|
|
||||||
filter.addAction(BluetoothDevice.ACTION_PAIRING_CANCEL);
|
|
||||||
|
|
||||||
// Fine-grained state broadcasts
|
|
||||||
filter.addAction(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
|
|
||||||
filter.addAction(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
|
|
||||||
filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
|
|
||||||
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
|
|
||||||
filter.addAction(BluetoothDevice.ACTION_CLASS_CHANGED);
|
|
||||||
filter.addAction(BluetoothDevice.ACTION_UUID);
|
|
||||||
|
|
||||||
// Dock event broadcasts
|
|
||||||
filter.addAction(Intent.ACTION_DOCK_EVENT);
|
|
||||||
|
|
||||||
mManager.getContext().registerReceiver(mBroadcastReceiver, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
|
||||||
mManager.getContext().unregisterReceiver(mBroadcastReceiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This can't be called from a broadcast receiver where the filter is set in the Manifest.
|
|
||||||
/* package */ String getDockedDeviceAddress(Context context) {
|
|
||||||
// This works only because these broadcast intents are "sticky"
|
|
||||||
Intent i = context.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
|
|
||||||
if (i != null) {
|
|
||||||
int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
|
|
||||||
if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
|
|
||||||
BluetoothDevice device = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
||||||
if (device != null) {
|
|
||||||
return device.getAddress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* package */ void persistDiscoveringTimestamp(
|
|
||||||
final BroadcastReceiver.PendingResult pr, final boolean newState) {
|
|
||||||
// Load the shared preferences and edit it on a background
|
|
||||||
// thread (but serialized!), but then post back to the main
|
|
||||||
// thread to run the onScanningStateChanged callbacks which
|
|
||||||
// update the UI...
|
|
||||||
mSerialExecutor.submit(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
SharedPreferences.Editor editor = mManager.getSharedPreferences().edit();
|
|
||||||
editor.putLong(
|
|
||||||
LocalBluetoothManager.SHARED_PREFERENCES_KEY_DISCOVERING_TIMESTAMP,
|
|
||||||
System.currentTimeMillis());
|
|
||||||
editor.apply();
|
|
||||||
mHandler.post(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
mManager.onScanningStateChanged(newState);
|
|
||||||
pr.finish();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -16,21 +16,18 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment to scan and show the discoverable devices.
|
* Fragment to scan and show the discoverable devices.
|
||||||
*/
|
*/
|
||||||
public class BluetoothFindNearby extends DeviceListPreferenceFragment {
|
public final class BluetoothFindNearby extends DeviceListPreferenceFragment {
|
||||||
|
|
||||||
private static final String TAG = "BluetoothFindNearby";
|
@Override
|
||||||
|
void addPreferencesForActivity() {
|
||||||
void addPreferencesForActivity(Activity activity) {
|
|
||||||
addPreferencesFromResource(R.xml.device_picker);
|
addPreferencesFromResource(R.xml.device_picker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,35 +35,37 @@ public class BluetoothFindNearby extends DeviceListPreferenceFragment {
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (mSelectedDevice != null) {
|
if (mSelectedDevice != null) {
|
||||||
CachedBluetoothDevice device =
|
CachedBluetoothDeviceManager manager = mLocalManager.getCachedDeviceManager();
|
||||||
mLocalManager.getCachedDeviceManager().findDevice(mSelectedDevice);
|
CachedBluetoothDevice device = manager.findDevice(mSelectedDevice);
|
||||||
if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
|
if (device != null && device.getBondState() == BluetoothDevice.BOND_BONDED) {
|
||||||
// selected device was paired, so return from this screen
|
// selected device was paired, so return from this screen
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mLocalManager.startScanning(true);
|
mLocalAdapter.startScanning(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
|
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
|
||||||
mLocalManager.stopScanning();
|
mLocalAdapter.stopScanning();
|
||||||
super.onDevicePreferenceClick(btPreference);
|
super.onDevicePreferenceClick(btPreference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice,
|
public void onDeviceBondStateChanged(CachedBluetoothDevice
|
||||||
int bondState) {
|
cachedDevice, int bondState) {
|
||||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||||
// return from scan screen after successful auto-pairing
|
// return from scan screen after successful auto-pairing
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBluetoothStateChanged(int bluetoothState) {
|
@Override
|
||||||
|
public void onBluetoothStateChanged(int bluetoothState) {
|
||||||
super.onBluetoothStateChanged(bluetoothState);
|
super.onBluetoothStateChanged(bluetoothState);
|
||||||
|
|
||||||
if (bluetoothState == BluetoothAdapter.STATE_ON) {
|
if (bluetoothState == BluetoothAdapter.STATE_ON) {
|
||||||
mLocalManager.startScanning(false);
|
mLocalAdapter.startScanning(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,12 +22,12 @@ import android.app.Dialog;
|
|||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.preference.EditTextPreference;
|
import android.preference.EditTextPreference;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputFilter;
|
import android.text.InputFilter;
|
||||||
import android.text.Spanned;
|
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -38,13 +38,13 @@ import android.widget.EditText;
|
|||||||
* Bluetooth name. It asks the user for a name, and persists it via the
|
* Bluetooth name. It asks the user for a name, and persists it via the
|
||||||
* Bluetooth API.
|
* Bluetooth API.
|
||||||
*/
|
*/
|
||||||
public class BluetoothNamePreference extends EditTextPreference implements TextWatcher {
|
public final class BluetoothNamePreference extends EditTextPreference implements TextWatcher {
|
||||||
private static final String TAG = "BluetoothNamePreference";
|
// private static final String TAG = "BluetoothNamePreference";
|
||||||
private static final int BLUETOOTH_NAME_MAX_LENGTH_BYTES = 248;
|
private static final int BLUETOOTH_NAME_MAX_LENGTH_BYTES = 248;
|
||||||
|
|
||||||
private LocalBluetoothManager mLocalManager;
|
private final LocalBluetoothAdapter mLocalAdapter;
|
||||||
|
|
||||||
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
@@ -61,7 +61,7 @@ public class BluetoothNamePreference extends EditTextPreference implements TextW
|
|||||||
public BluetoothNamePreference(Context context, AttributeSet attrs) {
|
public BluetoothNamePreference(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
|
|
||||||
mLocalManager = LocalBluetoothManager.getInstance(context);
|
mLocalAdapter = LocalBluetoothManager.getInstance(context).getBluetoothAdapter();
|
||||||
|
|
||||||
setSummaryToName();
|
setSummaryToName();
|
||||||
}
|
}
|
||||||
@@ -97,16 +97,17 @@ public class BluetoothNamePreference extends EditTextPreference implements TextW
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setSummaryToName() {
|
private void setSummaryToName() {
|
||||||
BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
|
if (mLocalAdapter != null && mLocalAdapter.isEnabled()) {
|
||||||
if (adapter.isEnabled()) {
|
setSummary(mLocalAdapter.getName());
|
||||||
setSummary(adapter.getName());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean persistString(String value) {
|
protected boolean persistString(String value) {
|
||||||
BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
|
// Persist with Bluez instead of shared preferences
|
||||||
adapter.setName(value);
|
if (mLocalAdapter != null) {
|
||||||
|
mLocalAdapter.setName(value);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,8 +117,8 @@ public class BluetoothNamePreference extends EditTextPreference implements TextW
|
|||||||
|
|
||||||
// The dialog should be created by now
|
// The dialog should be created by now
|
||||||
EditText et = getEditText();
|
EditText et = getEditText();
|
||||||
if (et != null) {
|
if (et != null && mLocalAdapter != null) {
|
||||||
et.setText(mLocalManager.getBluetoothAdapter().getName());
|
et.setText(mLocalAdapter.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ public class BluetoothNamePreference extends EditTextPreference implements TextW
|
|||||||
public void afterTextChanged(Editable s) {
|
public void afterTextChanged(Editable s) {
|
||||||
Dialog d = getDialog();
|
Dialog d = getDialog();
|
||||||
if (d instanceof AlertDialog) {
|
if (d instanceof AlertDialog) {
|
||||||
((AlertDialog) d).getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(s.length() > 0);
|
((AlertDialog) d).getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(s.length() > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,67 +140,4 @@ public class BluetoothNamePreference extends EditTextPreference implements TextW
|
|||||||
// not used
|
// not used
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This filter will constrain edits so that the text length is not
|
|
||||||
* greater than the specified number of bytes using UTF-8 encoding.
|
|
||||||
* <p>The JNI method used by {@link android.server.BluetoothService}
|
|
||||||
* to convert UTF-16 to UTF-8 doesn't support surrogate pairs,
|
|
||||||
* therefore code points outside of the basic multilingual plane
|
|
||||||
* (0000-FFFF) will be encoded as a pair of 3-byte UTF-8 characters,
|
|
||||||
* rather than a single 4-byte UTF-8 encoding. Dalvik implements this
|
|
||||||
* conversion in {@code convertUtf16ToUtf8()} in
|
|
||||||
* {@code dalvik/vm/UtfString.c}.
|
|
||||||
* <p>This JNI method is unlikely to change in the future due to
|
|
||||||
* backwards compatibility requirements. It's also unclear whether
|
|
||||||
* the installed base of Bluetooth devices would correctly handle the
|
|
||||||
* encoding of surrogate pairs in UTF-8 as 4 bytes rather than 6.
|
|
||||||
* However, this filter will still work in scenarios where surrogate
|
|
||||||
* pairs are encoded as 4 bytes, with the caveat that the maximum
|
|
||||||
* length will be constrained more conservatively than necessary.
|
|
||||||
*/
|
|
||||||
public static class Utf8ByteLengthFilter implements InputFilter {
|
|
||||||
private int mMaxBytes;
|
|
||||||
|
|
||||||
public Utf8ByteLengthFilter(int maxBytes) {
|
|
||||||
mMaxBytes = maxBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CharSequence filter(CharSequence source, int start, int end,
|
|
||||||
Spanned dest, int dstart, int dend) {
|
|
||||||
int srcByteCount = 0;
|
|
||||||
// count UTF-8 bytes in source substring
|
|
||||||
for (int i = start; i < end; i++) {
|
|
||||||
char c = source.charAt(i);
|
|
||||||
srcByteCount += (c < 0x0080) ? 1 : (c < 0x0800 ? 2 : 3);
|
|
||||||
}
|
|
||||||
int destLen = dest.length();
|
|
||||||
int destByteCount = 0;
|
|
||||||
// count UTF-8 bytes in destination excluding replaced section
|
|
||||||
for (int i = 0; i < destLen; i++) {
|
|
||||||
if (i < dstart || i >= dend) {
|
|
||||||
char c = dest.charAt(i);
|
|
||||||
destByteCount += (c < 0x0080) ? 1 : (c < 0x0800 ? 2 : 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int keepBytes = mMaxBytes - destByteCount;
|
|
||||||
if (keepBytes <= 0) {
|
|
||||||
return "";
|
|
||||||
} else if (keepBytes >= srcByteCount) {
|
|
||||||
return null; // use original dest string
|
|
||||||
} else {
|
|
||||||
// find end position of largest sequence that fits in keepBytes
|
|
||||||
for (int i = start; i < end; i++) {
|
|
||||||
char c = source.charAt(i);
|
|
||||||
keepBytes -= (c < 0x0080) ? 1 : (c < 0x0800 ? 2 : 3);
|
|
||||||
if (keepBytes < 0) {
|
|
||||||
return source.subSequence(start, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the entire substring fits, we should have returned null
|
|
||||||
// above, so this line should not be reached. If for some
|
|
||||||
// reason it is, return null to use the original dest string.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -42,33 +42,37 @@ import com.android.settings.R;
|
|||||||
* BluetoothPairingDialog asks the user to enter a PIN / Passkey / simple confirmation
|
* BluetoothPairingDialog asks the user to enter a PIN / Passkey / simple confirmation
|
||||||
* for pairing with a remote Bluetooth device. It is an activity that appears as a dialog.
|
* for pairing with a remote Bluetooth device. It is an activity that appears as a dialog.
|
||||||
*/
|
*/
|
||||||
public class BluetoothPairingDialog extends AlertActivity implements DialogInterface.OnClickListener,
|
public final class BluetoothPairingDialog extends AlertActivity implements DialogInterface.OnClickListener,
|
||||||
TextWatcher {
|
TextWatcher {
|
||||||
private static final String TAG = "BluetoothPairingDialog";
|
private static final String TAG = "BluetoothPairingDialog";
|
||||||
|
|
||||||
private static final int BLUETOOTH_PIN_MAX_LENGTH = 16;
|
private static final int BLUETOOTH_PIN_MAX_LENGTH = 16;
|
||||||
private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
|
private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
|
||||||
private LocalBluetoothManager mLocalManager;
|
|
||||||
private BluetoothDevice mDevice;
|
private BluetoothDevice mDevice;
|
||||||
private int mType;
|
private int mType;
|
||||||
private String mPairingKey;
|
private String mPairingKey;
|
||||||
private EditText mPairingView;
|
private EditText mPairingView;
|
||||||
private Button mOkButton;
|
private Button mOkButton;
|
||||||
|
|
||||||
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
/**
|
||||||
|
* Dismiss the dialog if the bond state changes to bonded or none,
|
||||||
|
* or if pairing was canceled for {@link #mDevice}.
|
||||||
|
*/
|
||||||
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
|
String action = intent.getAction();
|
||||||
|
if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
|
||||||
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
|
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
|
||||||
BluetoothDevice.ERROR);
|
BluetoothDevice.ERROR);
|
||||||
if (bondState == BluetoothDevice.BOND_BONDED ||
|
if (bondState == BluetoothDevice.BOND_BONDED ||
|
||||||
bondState == BluetoothDevice.BOND_NONE) {
|
bondState == BluetoothDevice.BOND_NONE) {
|
||||||
dismissDialog();
|
dismiss();
|
||||||
}
|
}
|
||||||
} else if(BluetoothDevice.ACTION_PAIRING_CANCEL.equals(intent.getAction())) {
|
} else if (BluetoothDevice.ACTION_PAIRING_CANCEL.equals(action)) {
|
||||||
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
if (device == null || device.equals(mDevice)) {
|
if (device == null || device.equals(mDevice)) {
|
||||||
dismissDialog();
|
dismiss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,32 +85,47 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
|
|||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST))
|
if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST))
|
||||||
{
|
{
|
||||||
Log.e(TAG,
|
Log.e(TAG, "Error: this activity may be started only with intent " +
|
||||||
"Error: this activity may be started only with intent " +
|
|
||||||
BluetoothDevice.ACTION_PAIRING_REQUEST);
|
BluetoothDevice.ACTION_PAIRING_REQUEST);
|
||||||
finish();
|
finish();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLocalManager = LocalBluetoothManager.getInstance(this);
|
LocalBluetoothManager manager = LocalBluetoothManager.getInstance(this);
|
||||||
|
if (manager == null) {
|
||||||
|
Log.e(TAG, "Error: BluetoothAdapter not supported by system");
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CachedBluetoothDeviceManager deviceManager = manager.getCachedDeviceManager();
|
||||||
|
|
||||||
mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
mDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
|
mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
|
||||||
if (mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
|
|
||||||
createUserEntryDialog();
|
switch (mType) {
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY) {
|
case BluetoothDevice.PAIRING_VARIANT_PIN:
|
||||||
createUserEntryDialog();
|
case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION){
|
createUserEntryDialog(deviceManager);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
|
||||||
int passkey =
|
int passkey =
|
||||||
intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
|
intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
|
||||||
if (passkey == BluetoothDevice.ERROR) {
|
if (passkey == BluetoothDevice.ERROR) {
|
||||||
Log.e(TAG, "Invalid ConfirmationPasskey received, not showing any dialog");
|
Log.e(TAG, "Invalid Confirmation Passkey received, not showing any dialog");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mPairingKey = String.format("%06d", passkey);
|
mPairingKey = String.format("%06d", passkey);
|
||||||
createConfirmationDialog();
|
createConfirmationDialog(deviceManager);
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
|
break;
|
||||||
createConsentDialog();
|
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY ||
|
case BluetoothDevice.PAIRING_VARIANT_CONSENT:
|
||||||
mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
|
case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
|
||||||
|
createConsentDialog(deviceManager);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
|
||||||
int pairingKey =
|
int pairingKey =
|
||||||
intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
|
intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
|
||||||
if (pairingKey == BluetoothDevice.ERROR) {
|
if (pairingKey == BluetoothDevice.ERROR) {
|
||||||
@@ -118,10 +137,10 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
|
|||||||
} else {
|
} else {
|
||||||
mPairingKey = String.format("%04d", pairingKey);
|
mPairingKey = String.format("%04d", pairingKey);
|
||||||
}
|
}
|
||||||
createDisplayPasskeyOrPinDialog();
|
createDisplayPasskeyOrPinDialog(deviceManager);
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT) {
|
break;
|
||||||
createConsentDialog();
|
|
||||||
} else {
|
default:
|
||||||
Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
|
Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,67 +152,79 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
|
|||||||
registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
|
registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createUserEntryDialog() {
|
private void createUserEntryDialog(CachedBluetoothDeviceManager deviceManager) {
|
||||||
final AlertController.AlertParams p = mAlertParams;
|
final AlertController.AlertParams p = mAlertParams;
|
||||||
p.mIconId = android.R.drawable.ic_dialog_info;
|
p.mIconId = android.R.drawable.ic_dialog_info;
|
||||||
p.mTitle = getString(R.string.bluetooth_pairing_request);
|
p.mTitle = getString(R.string.bluetooth_pairing_request);
|
||||||
p.mView = createView();
|
p.mView = createView(deviceManager);
|
||||||
p.mPositiveButtonText = getString(android.R.string.ok);
|
p.mPositiveButtonText = getString(android.R.string.ok);
|
||||||
p.mPositiveButtonListener = this;
|
p.mPositiveButtonListener = this;
|
||||||
p.mNegativeButtonText = getString(android.R.string.cancel);
|
p.mNegativeButtonText = getString(android.R.string.cancel);
|
||||||
p.mNegativeButtonListener = this;
|
p.mNegativeButtonListener = this;
|
||||||
setupAlert();
|
setupAlert();
|
||||||
|
|
||||||
mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
|
mOkButton = mAlert.getButton(BUTTON_POSITIVE);
|
||||||
mOkButton.setEnabled(false);
|
mOkButton.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private View createView() {
|
private View createView(CachedBluetoothDeviceManager deviceManager) {
|
||||||
View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
|
View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
|
||||||
|
String name = deviceManager.getName(mDevice);
|
||||||
String name = mLocalManager.getCachedDeviceManager().getName(mDevice);
|
|
||||||
TextView messageView = (TextView) view.findViewById(R.id.message);
|
TextView messageView = (TextView) view.findViewById(R.id.message);
|
||||||
mPairingView = (EditText) view.findViewById(R.id.text);
|
mPairingView = (EditText) view.findViewById(R.id.text);
|
||||||
mPairingView.addTextChangedListener(this);
|
mPairingView.addTextChangedListener(this);
|
||||||
|
|
||||||
if (mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
|
switch (mType) {
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_PIN:
|
||||||
messageView.setText(getString(R.string.bluetooth_enter_pin_msg, name));
|
messageView.setText(getString(R.string.bluetooth_enter_pin_msg, name));
|
||||||
// Maximum of 16 characters in a PIN adb sync
|
// Maximum of 16 characters in a PIN adb sync
|
||||||
mPairingView.setFilters(new InputFilter[] {
|
mPairingView.setFilters(new InputFilter[] {
|
||||||
new LengthFilter(BLUETOOTH_PIN_MAX_LENGTH) });
|
new LengthFilter(BLUETOOTH_PIN_MAX_LENGTH) });
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY){
|
break;
|
||||||
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
|
||||||
messageView.setText(getString(R.string.bluetooth_enter_passkey_msg, name));
|
messageView.setText(getString(R.string.bluetooth_enter_passkey_msg, name));
|
||||||
// Maximum of 6 digits for passkey
|
// Maximum of 6 digits for passkey
|
||||||
mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER |
|
mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER |
|
||||||
InputType.TYPE_NUMBER_FLAG_SIGNED);
|
InputType.TYPE_NUMBER_FLAG_SIGNED);
|
||||||
mPairingView.setFilters(new InputFilter[] {
|
mPairingView.setFilters(new InputFilter[] {
|
||||||
new LengthFilter(BLUETOOTH_PASSKEY_MAX_LENGTH)});
|
new LengthFilter(BLUETOOTH_PASSKEY_MAX_LENGTH)});
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
|
break;
|
||||||
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
|
||||||
mPairingView.setVisibility(View.GONE);
|
mPairingView.setVisibility(View.GONE);
|
||||||
messageView.setText(getString(R.string.bluetooth_confirm_passkey_msg, name,
|
messageView.setText(getString(R.string.bluetooth_confirm_passkey_msg, name,
|
||||||
mPairingKey));
|
mPairingKey));
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
|
break;
|
||||||
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_CONSENT:
|
||||||
mPairingView.setVisibility(View.GONE);
|
mPairingView.setVisibility(View.GONE);
|
||||||
messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
|
messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY ||
|
break;
|
||||||
mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
|
||||||
mPairingView.setVisibility(View.GONE);
|
mPairingView.setVisibility(View.GONE);
|
||||||
messageView.setText(getString(R.string.bluetooth_display_passkey_pin_msg, name,
|
messageView.setText(getString(R.string.bluetooth_display_passkey_pin_msg, name,
|
||||||
mPairingKey));
|
mPairingKey));
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT) {
|
break;
|
||||||
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
|
||||||
mPairingView.setVisibility(View.GONE);
|
mPairingView.setVisibility(View.GONE);
|
||||||
messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
|
messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
|
||||||
} else {
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
Log.e(TAG, "Incorrect pairing type received, not creating view");
|
Log.e(TAG, "Incorrect pairing type received, not creating view");
|
||||||
}
|
}
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createConfirmationDialog() {
|
private void createConfirmationDialog(CachedBluetoothDeviceManager deviceManager) {
|
||||||
final AlertController.AlertParams p = mAlertParams;
|
final AlertController.AlertParams p = mAlertParams;
|
||||||
p.mIconId = android.R.drawable.ic_dialog_info;
|
p.mIconId = android.R.drawable.ic_dialog_info;
|
||||||
p.mTitle = getString(R.string.bluetooth_pairing_request);
|
p.mTitle = getString(R.string.bluetooth_pairing_request);
|
||||||
p.mView = createView();
|
p.mView = createView(deviceManager);
|
||||||
p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
|
p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
|
||||||
p.mPositiveButtonListener = this;
|
p.mPositiveButtonListener = this;
|
||||||
p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
|
p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
|
||||||
@@ -201,11 +232,11 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
|
|||||||
setupAlert();
|
setupAlert();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createConsentDialog() {
|
private void createConsentDialog(CachedBluetoothDeviceManager deviceManager) {
|
||||||
final AlertController.AlertParams p = mAlertParams;
|
final AlertController.AlertParams p = mAlertParams;
|
||||||
p.mIconId = android.R.drawable.ic_dialog_info;
|
p.mIconId = android.R.drawable.ic_dialog_info;
|
||||||
p.mTitle = getString(R.string.bluetooth_pairing_request);
|
p.mTitle = getString(R.string.bluetooth_pairing_request);
|
||||||
p.mView = createView();
|
p.mView = createView(deviceManager);
|
||||||
p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
|
p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
|
||||||
p.mPositiveButtonListener = this;
|
p.mPositiveButtonListener = this;
|
||||||
p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
|
p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
|
||||||
@@ -213,11 +244,12 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
|
|||||||
setupAlert();
|
setupAlert();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDisplayPasskeyOrPinDialog() {
|
private void createDisplayPasskeyOrPinDialog(
|
||||||
|
CachedBluetoothDeviceManager deviceManager) {
|
||||||
final AlertController.AlertParams p = mAlertParams;
|
final AlertController.AlertParams p = mAlertParams;
|
||||||
p.mIconId = android.R.drawable.ic_dialog_info;
|
p.mIconId = android.R.drawable.ic_dialog_info;
|
||||||
p.mTitle = getString(R.string.bluetooth_pairing_request);
|
p.mTitle = getString(R.string.bluetooth_pairing_request);
|
||||||
p.mView = createView();
|
p.mView = createView(deviceManager);
|
||||||
p.mNegativeButtonText = getString(android.R.string.cancel);
|
p.mNegativeButtonText = getString(android.R.string.cancel);
|
||||||
p.mNegativeButtonListener = this;
|
p.mNegativeButtonListener = this;
|
||||||
setupAlert();
|
setupAlert();
|
||||||
@@ -244,31 +276,36 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismissDialog() {
|
|
||||||
this.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onPair(String value) {
|
private void onPair(String value) {
|
||||||
if (mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
|
switch (mType) {
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_PIN:
|
||||||
byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
|
byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
|
||||||
if (pinBytes == null) {
|
if (pinBytes == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mDevice.setPin(pinBytes);
|
mDevice.setPin(pinBytes);
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY) {
|
break;
|
||||||
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
|
||||||
int passkey = Integer.parseInt(value);
|
int passkey = Integer.parseInt(value);
|
||||||
mDevice.setPasskey(passkey);
|
mDevice.setPasskey(passkey);
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
|
break;
|
||||||
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_CONSENT:
|
||||||
mDevice.setPairingConfirmation(true);
|
mDevice.setPairingConfirmation(true);
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
|
break;
|
||||||
mDevice.setPairingConfirmation(true);
|
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
|
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
|
||||||
// Do Nothing.
|
case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
|
// Do nothing.
|
||||||
// Do Nothing
|
break;
|
||||||
} else if (mType == BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT) {
|
|
||||||
|
case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
|
||||||
mDevice.setRemoteOutOfBandData();
|
mDevice.setRemoteOutOfBandData();
|
||||||
} else {
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
Log.e(TAG, "Incorrect pairing type received");
|
Log.e(TAG, "Incorrect pairing type received");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,11 +316,12 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
|
|||||||
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case DialogInterface.BUTTON_POSITIVE:
|
case BUTTON_POSITIVE:
|
||||||
onPair(mPairingView.getText().toString());
|
onPair(mPairingView.getText().toString());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DialogInterface.BUTTON_NEGATIVE:
|
case BUTTON_NEGATIVE:
|
||||||
|
default:
|
||||||
onCancel();
|
onCancel();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -35,17 +35,15 @@ import android.os.PowerManager;
|
|||||||
* confirmation entry dialog. Otherwise it puts a Notification in the status bar, which can
|
* confirmation entry dialog. Otherwise it puts a Notification in the status bar, which can
|
||||||
* be clicked to bring up the Pairing entry dialog.
|
* be clicked to bring up the Pairing entry dialog.
|
||||||
*/
|
*/
|
||||||
public class BluetoothPairingRequest extends BroadcastReceiver {
|
public final class BluetoothPairingRequest extends BroadcastReceiver {
|
||||||
|
|
||||||
public static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth;
|
private static final int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
|
if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
|
||||||
|
// convert broadcast intent into activity intent (same action string)
|
||||||
LocalBluetoothManager localManager = LocalBluetoothManager.getInstance(context);
|
|
||||||
|
|
||||||
BluetoothDevice device =
|
BluetoothDevice device =
|
||||||
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
|
int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
|
||||||
@@ -68,39 +66,35 @@ public class BluetoothPairingRequest extends BroadcastReceiver {
|
|||||||
(PowerManager)context.getSystemService(Context.POWER_SERVICE);
|
(PowerManager)context.getSystemService(Context.POWER_SERVICE);
|
||||||
String deviceAddress = device != null ? device.getAddress() : null;
|
String deviceAddress = device != null ? device.getAddress() : null;
|
||||||
if (powerManager.isScreenOn() &&
|
if (powerManager.isScreenOn() &&
|
||||||
localManager.shouldShowDialogInForeground(deviceAddress)) {
|
LocalBluetoothPreferences.shouldShowDialogInForeground(context, deviceAddress)) {
|
||||||
// Since the screen is on and the BT-related activity is in the foreground,
|
// Since the screen is on and the BT-related activity is in the foreground,
|
||||||
// just open the dialog
|
// just open the dialog
|
||||||
context.startActivity(pairingIntent);
|
context.startActivity(pairingIntent);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Put up a notification that leads to the dialog
|
// Put up a notification that leads to the dialog
|
||||||
Resources res = context.getResources();
|
Resources res = context.getResources();
|
||||||
Notification notification = new Notification(
|
Notification.Builder builder = new Notification.Builder(context)
|
||||||
android.R.drawable.stat_sys_data_bluetooth,
|
.setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
|
||||||
res.getString(R.string.bluetooth_notif_ticker),
|
.setTicker(res.getString(R.string.bluetooth_notif_ticker));
|
||||||
System.currentTimeMillis());
|
|
||||||
|
|
||||||
PendingIntent pending = PendingIntent.getActivity(context, 0,
|
PendingIntent pending = PendingIntent.getActivity(context, 0,
|
||||||
pairingIntent, PendingIntent.FLAG_ONE_SHOT);
|
pairingIntent, PendingIntent.FLAG_ONE_SHOT);
|
||||||
|
|
||||||
String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
|
String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
|
||||||
if (TextUtils.isEmpty(name)) {
|
if (TextUtils.isEmpty(name)) {
|
||||||
name = (device != null ? device.getName() :
|
name = device != null ? device.getName() :
|
||||||
context.getString(android.R.string.unknownName));
|
context.getString(android.R.string.unknownName);
|
||||||
}
|
}
|
||||||
|
|
||||||
notification.setLatestEventInfo(context,
|
builder.setContentTitle(res.getString(R.string.bluetooth_notif_title))
|
||||||
res.getString(R.string.bluetooth_notif_title),
|
.setContentText(res.getString(R.string.bluetooth_notif_message, name))
|
||||||
res.getString(R.string.bluetooth_notif_message, name),
|
.setContentIntent(pending)
|
||||||
pending);
|
.setAutoCancel(true)
|
||||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
.setDefaults(Notification.DEFAULT_SOUND);
|
||||||
notification.defaults |= Notification.DEFAULT_SOUND;
|
|
||||||
|
|
||||||
NotificationManager manager = (NotificationManager)
|
NotificationManager manager = (NotificationManager)
|
||||||
context.getSystemService(Context.NOTIFICATION_SERVICE);
|
context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
manager.notify(NOTIFICATION_ID, notification);
|
manager.notify(NOTIFICATION_ID, builder.getNotification());
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (action.equals(BluetoothDevice.ACTION_PAIRING_CANCEL)) {
|
} else if (action.equals(BluetoothDevice.ACTION_PAIRING_CANCEL)) {
|
||||||
|
@@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
@@ -26,22 +23,24 @@ import android.view.View;
|
|||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BluetoothProfilePreference is the preference type used to display each profile for a
|
* BluetoothProfilePreference is the preference type used to display each profile for a
|
||||||
* particular bluetooth device.
|
* particular bluetooth device.
|
||||||
*/
|
*/
|
||||||
public class BluetoothProfilePreference extends Preference implements OnClickListener {
|
final class BluetoothProfilePreference extends Preference implements OnClickListener {
|
||||||
|
|
||||||
private static final String TAG = "BluetoothProfilePreference";
|
// private static final String TAG = "BluetoothProfilePreference";
|
||||||
|
|
||||||
private Drawable mProfileDrawable;
|
private Drawable mProfileDrawable;
|
||||||
private boolean mExpanded;
|
private boolean mExpanded;
|
||||||
private ImageView mProfileExpandView;
|
private ImageView mProfileExpandView;
|
||||||
private final Profile mProfile;
|
private final LocalBluetoothProfile mProfile;
|
||||||
|
|
||||||
private OnClickListener mOnExpandClickListener;
|
private OnClickListener mOnExpandClickListener;
|
||||||
|
|
||||||
public BluetoothProfilePreference(Context context, Profile profile) {
|
BluetoothProfilePreference(Context context, LocalBluetoothProfile profile) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
mProfile = profile;
|
mProfile = profile;
|
||||||
@@ -75,14 +74,14 @@ public class BluetoothProfilePreference extends Preference implements OnClickLis
|
|||||||
btProfile.setImageDrawable(mProfileDrawable);
|
btProfile.setImageDrawable(mProfileDrawable);
|
||||||
|
|
||||||
mProfileExpandView = (ImageView) view.findViewById(R.id.profileExpand);
|
mProfileExpandView = (ImageView) view.findViewById(R.id.profileExpand);
|
||||||
if (mProfile == Profile.PAN) {
|
if (mProfile.isAutoConnectable()) {
|
||||||
mProfileExpandView.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
mProfileExpandView.setOnClickListener(this);
|
mProfileExpandView.setOnClickListener(this);
|
||||||
mProfileExpandView.setTag(mProfile);
|
mProfileExpandView.setTag(mProfile);
|
||||||
mProfileExpandView.setImageResource(mExpanded
|
mProfileExpandView.setImageResource(mExpanded
|
||||||
? com.android.internal.R.drawable.expander_open_holo_dark
|
? com.android.internal.R.drawable.expander_open_holo_dark
|
||||||
: com.android.internal.R.drawable.expander_close_holo_dark);
|
: com.android.internal.R.drawable.expander_close_holo_dark);
|
||||||
|
} else {
|
||||||
|
mProfileExpandView.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,15 +16,13 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.preference.CheckBoxPreference;
|
import android.preference.CheckBoxPreference;
|
||||||
import android.preference.ListPreference;
|
import android.preference.ListPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceCategory;
|
import android.preference.PreferenceActivity;
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
@@ -34,9 +32,7 @@ import com.android.settings.R;
|
|||||||
* BluetoothSettings is the Settings screen for Bluetooth configuration and
|
* BluetoothSettings is the Settings screen for Bluetooth configuration and
|
||||||
* connection management.
|
* connection management.
|
||||||
*/
|
*/
|
||||||
public class BluetoothSettings extends DeviceListPreferenceFragment
|
public final class BluetoothSettings extends DeviceListPreferenceFragment {
|
||||||
implements LocalBluetoothManager.Callback, View.OnClickListener {
|
|
||||||
|
|
||||||
private static final String TAG = "BluetoothSettings";
|
private static final String TAG = "BluetoothSettings";
|
||||||
|
|
||||||
private static final String KEY_BT_CHECKBOX = "bt_checkbox";
|
private static final String KEY_BT_CHECKBOX = "bt_checkbox";
|
||||||
@@ -53,13 +49,20 @@ public class BluetoothSettings extends DeviceListPreferenceFragment
|
|||||||
private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES =
|
private static final String BTOPP_ACTION_OPEN_RECEIVED_FILES =
|
||||||
"android.btopp.intent.action.OPEN_RECEIVED_FILES";
|
"android.btopp.intent.action.OPEN_RECEIVED_FILES";
|
||||||
|
|
||||||
void addPreferencesForActivity(Activity activity) {
|
/** Initialize the filter to show bonded devices only. */
|
||||||
|
public BluetoothSettings() {
|
||||||
|
super(BluetoothDeviceFilter.BONDED_DEVICE_FILTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void addPreferencesForActivity() {
|
||||||
addPreferencesFromResource(R.xml.bluetooth_settings);
|
addPreferencesFromResource(R.xml.bluetooth_settings);
|
||||||
|
|
||||||
mEnabler = new BluetoothEnabler(activity,
|
mEnabler = new BluetoothEnabler(getActivity(), mLocalAdapter,
|
||||||
(CheckBoxPreference) findPreference(KEY_BT_CHECKBOX));
|
(CheckBoxPreference) findPreference(KEY_BT_CHECKBOX));
|
||||||
|
|
||||||
mDiscoverableEnabler = new BluetoothDiscoverableEnabler(activity,
|
mDiscoverableEnabler = new BluetoothDiscoverableEnabler(getActivity(),
|
||||||
|
mLocalAdapter,
|
||||||
(CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE),
|
(CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE),
|
||||||
(ListPreference) findPreference(KEY_BT_DISCOVERABLE_TIMEOUT));
|
(ListPreference) findPreference(KEY_BT_DISCOVERABLE_TIMEOUT));
|
||||||
|
|
||||||
@@ -88,6 +91,27 @@ public class BluetoothSettings extends DeviceListPreferenceFragment
|
|||||||
mEnabler.pause();
|
mEnabler.pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final View.OnClickListener mListener = new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
// User clicked on advanced options icon for a device in the list
|
||||||
|
if (v.getTag() instanceof CachedBluetoothDevice) {
|
||||||
|
CachedBluetoothDevice
|
||||||
|
device = (CachedBluetoothDevice) v.getTag();
|
||||||
|
|
||||||
|
Preference pref = new Preference(getActivity());
|
||||||
|
pref.setTitle(device.getName());
|
||||||
|
pref.setFragment(DeviceProfilesSettings.class.getName());
|
||||||
|
pref.getExtras().putParcelable(DeviceProfilesSettings.EXTRA_DEVICE,
|
||||||
|
device.getDevice());
|
||||||
|
((PreferenceActivity) getActivity())
|
||||||
|
.onPreferenceStartFragment(BluetoothSettings.this,
|
||||||
|
pref);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "onClick() called for other View: " + v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
|
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
|
||||||
Preference preference) {
|
Preference preference) {
|
||||||
@@ -105,32 +129,20 @@ public class BluetoothSettings extends DeviceListPreferenceFragment
|
|||||||
if (bondState == BluetoothDevice.BOND_BONDED) {
|
if (bondState == BluetoothDevice.BOND_BONDED) {
|
||||||
// add to "Paired devices" list after remote-initiated pairing
|
// add to "Paired devices" list after remote-initiated pairing
|
||||||
if (mDevicePreferenceMap.get(cachedDevice) == null) {
|
if (mDevicePreferenceMap.get(cachedDevice) == null) {
|
||||||
if (addDevicePreference(cachedDevice)) {
|
|
||||||
createDevicePreference(cachedDevice);
|
createDevicePreference(cachedDevice);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if (bondState == BluetoothDevice.BOND_NONE) {
|
} else if (bondState == BluetoothDevice.BOND_NONE) {
|
||||||
// remove unpaired device from paired devices list
|
// remove unpaired device from paired devices list
|
||||||
onDeviceDeleted(cachedDevice);
|
onDeviceDeleted(cachedDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Additional check to only add paired devices to list.
|
|
||||||
*/
|
|
||||||
boolean addDevicePreference(CachedBluetoothDevice cachedDevice) {
|
|
||||||
if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
|
|
||||||
return super.addDevicePreference(cachedDevice);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a listener, which enables the advanced settings icon.
|
* Add a listener, which enables the advanced settings icon.
|
||||||
* @param preference the newly added preference
|
* @param preference the newly added preference
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
void initDevicePreference(BluetoothDevicePreference preference) {
|
void initDevicePreference(BluetoothDevicePreference preference) {
|
||||||
preference.setOnSettingsClickListener(this);
|
preference.setOnSettingsClickListener(mListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,30 +16,18 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
|
||||||
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
|
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
|
||||||
import android.bluetooth.BluetoothClass;
|
import android.bluetooth.BluetoothClass;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.content.Context;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.ParcelUuid;
|
import android.os.ParcelUuid;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.preference.Preference;
|
|
||||||
import android.preference.PreferenceActivity;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CachedBluetoothDevice represents a remote Bluetooth device. It contains
|
* CachedBluetoothDevice represents a remote Bluetooth device. It contains
|
||||||
@@ -47,27 +35,23 @@ import java.util.Map;
|
|||||||
* functionality that can be performed on the device (connect, pair, disconnect,
|
* functionality that can be performed on the device (connect, pair, disconnect,
|
||||||
* etc.).
|
* etc.).
|
||||||
*/
|
*/
|
||||||
class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
final class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
||||||
private static final String TAG = "CachedBluetoothDevice";
|
private static final String TAG = "CachedBluetoothDevice";
|
||||||
private static final boolean D = LocalBluetoothManager.D;
|
private static final boolean DEBUG = Utils.V;
|
||||||
private static final boolean V = LocalBluetoothManager.V;
|
|
||||||
private static final boolean DEBUG = false;
|
|
||||||
|
|
||||||
|
private final LocalBluetoothAdapter mLocalAdapter;
|
||||||
|
private final LocalBluetoothProfileManager mProfileManager;
|
||||||
private final BluetoothDevice mDevice;
|
private final BluetoothDevice mDevice;
|
||||||
private String mName;
|
private String mName;
|
||||||
private short mRssi;
|
private short mRssi;
|
||||||
private BluetoothClass mBtClass;
|
private BluetoothClass mBtClass;
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
private List<Profile> mProfiles = new ArrayList<Profile>();
|
private final List<LocalBluetoothProfile> mProfiles =
|
||||||
|
new ArrayList<LocalBluetoothProfile>();
|
||||||
|
|
||||||
private boolean mVisible;
|
private boolean mVisible;
|
||||||
|
|
||||||
private final LocalBluetoothManager mLocalManager;
|
private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
|
||||||
|
|
||||||
private AlertDialog mDialog = null;
|
|
||||||
|
|
||||||
private List<Callback> mCallbacks = new ArrayList<Callback>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When we connect to multiple profiles, we only want to display a single
|
* When we connect to multiple profiles, we only want to display a single
|
||||||
@@ -95,186 +79,62 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
* @param profile Profile to describe
|
* @param profile Profile to describe
|
||||||
* @return Description of the device and profile
|
* @return Description of the device and profile
|
||||||
*/
|
*/
|
||||||
private String describe(Profile profile) {
|
private String describe(LocalBluetoothProfile profile) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Address:").append(mDevice);
|
sb.append("Address:").append(mDevice);
|
||||||
if (profile != null) {
|
if (profile != null) {
|
||||||
sb.append(" Profile:").append(profile.name());
|
sb.append(" Profile:").append(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onProfileStateChanged(Profile profile, int newProfileState) {
|
void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
|
||||||
if (D) {
|
if (Utils.D) {
|
||||||
Log.d(TAG, "onProfileStateChanged: profile " + profile.toString() +
|
Log.d(TAG, "onProfileStateChanged: profile " + profile +
|
||||||
" newProfileState " + newProfileState);
|
" newProfileState " + newProfileState);
|
||||||
}
|
}
|
||||||
|
|
||||||
final LocalBluetoothProfileManager pm =
|
if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
|
||||||
LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
|
|
||||||
if (pm == null) return;
|
|
||||||
int newState = pm.convertState(newProfileState);
|
|
||||||
|
|
||||||
if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED) {
|
|
||||||
if (!mProfiles.contains(profile)) {
|
if (!mProfiles.contains(profile)) {
|
||||||
mProfiles.add(profile);
|
mProfiles.add(profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedBluetoothDevice(Context context, BluetoothDevice device) {
|
CachedBluetoothDevice(LocalBluetoothAdapter adapter,
|
||||||
mLocalManager = LocalBluetoothManager.getInstance(context);
|
LocalBluetoothProfileManager profileManager,
|
||||||
if (mLocalManager == null) {
|
BluetoothDevice device) {
|
||||||
throw new IllegalStateException(
|
mLocalAdapter = adapter;
|
||||||
"Cannot use CachedBluetoothDevice without Bluetooth hardware");
|
mProfileManager = profileManager;
|
||||||
}
|
|
||||||
|
|
||||||
mDevice = device;
|
mDevice = device;
|
||||||
mContext = context;
|
|
||||||
|
|
||||||
fillData();
|
fillData();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onClicked() {
|
void disconnect() {
|
||||||
int bondState = getBondState();
|
for (LocalBluetoothProfile profile : mProfiles) {
|
||||||
|
|
||||||
if (isConnected()) {
|
|
||||||
askDisconnect();
|
|
||||||
} else if (bondState == BluetoothDevice.BOND_BONDED) {
|
|
||||||
connect(true);
|
|
||||||
} else if (bondState == BluetoothDevice.BOND_NONE) {
|
|
||||||
pair();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnect() {
|
|
||||||
for (Profile profile : mProfiles) {
|
|
||||||
disconnect(profile);
|
disconnect(profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disconnect(Profile profile) {
|
void disconnect(LocalBluetoothProfile profile) {
|
||||||
LocalBluetoothProfileManager profileManager =
|
if (profile.disconnect(mDevice)) {
|
||||||
LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
|
if (Utils.D) {
|
||||||
if (profileManager.disconnect(mDevice)) {
|
|
||||||
if (D) {
|
|
||||||
Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile));
|
Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void askDisconnect() {
|
void connect(boolean connectAllProfiles) {
|
||||||
Context context = mLocalManager.getForegroundActivity();
|
if (!ensurePaired()) {
|
||||||
if (context == null) {
|
|
||||||
// Cannot ask, since we need an activity context
|
|
||||||
disconnect();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Resources res = context.getResources();
|
|
||||||
|
|
||||||
String name = getName();
|
|
||||||
if (TextUtils.isEmpty(name)) {
|
|
||||||
name = res.getString(R.string.bluetooth_device);
|
|
||||||
}
|
|
||||||
String message = res.getString(R.string.bluetooth_disconnect_blank, name);
|
|
||||||
|
|
||||||
DialogInterface.OnClickListener disconnectListener = new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
disconnect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
showDisconnectDialog(context, disconnectListener, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void askDisconnect(final Profile profile) {
|
|
||||||
Context context = mLocalManager.getForegroundActivity();
|
|
||||||
if (context == null) {
|
|
||||||
// Cannot ask, since we need an activity context
|
|
||||||
disconnect(profile);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Resources res = context.getResources();
|
|
||||||
|
|
||||||
String name = getName();
|
|
||||||
if (TextUtils.isEmpty(name)) {
|
|
||||||
name = res.getString(R.string.bluetooth_device);
|
|
||||||
}
|
|
||||||
int disconnectMessage;
|
|
||||||
switch (profile) {
|
|
||||||
case A2DP:
|
|
||||||
disconnectMessage = R.string.bluetooth_disconnect_a2dp_profile;
|
|
||||||
break;
|
|
||||||
case HEADSET:
|
|
||||||
disconnectMessage = R.string.bluetooth_disconnect_headset_profile;
|
|
||||||
break;
|
|
||||||
case HID:
|
|
||||||
disconnectMessage = R.string.bluetooth_disconnect_hid_profile;
|
|
||||||
break;
|
|
||||||
case PAN:
|
|
||||||
disconnectMessage = R.string.bluetooth_disconnect_pan_profile;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.w(TAG, "askDisconnect: unexpected profile " + profile);
|
|
||||||
disconnectMessage = R.string.bluetooth_disconnect_blank;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
String message = res.getString(disconnectMessage, name);
|
|
||||||
|
|
||||||
DialogInterface.OnClickListener disconnectListener =
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
disconnect(profile);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
showDisconnectDialog(context, disconnectListener, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showDisconnectDialog(Context context,
|
|
||||||
DialogInterface.OnClickListener disconnectListener,
|
|
||||||
String message) {
|
|
||||||
if (mDialog == null) {
|
|
||||||
mDialog = new AlertDialog.Builder(context)
|
|
||||||
.setPositiveButton(android.R.string.ok, disconnectListener)
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.create();
|
|
||||||
} else {
|
|
||||||
if (mDialog.isShowing()) {
|
|
||||||
mDialog.dismiss();
|
|
||||||
}
|
|
||||||
// use disconnectListener for the correct profile(s)
|
|
||||||
CharSequence okText = context.getText(android.R.string.ok);
|
|
||||||
mDialog.setButton(DialogInterface.BUTTON_POSITIVE,
|
|
||||||
okText, disconnectListener);
|
|
||||||
}
|
|
||||||
mDialog.setTitle(getName());
|
|
||||||
mDialog.setMessage(message);
|
|
||||||
mDialog.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void finalize() throws Throwable {
|
|
||||||
if (mDialog != null) {
|
|
||||||
mDialog.dismiss();
|
|
||||||
mDialog = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connect(boolean connectAllProfiles) {
|
|
||||||
if (!ensurePaired()) return;
|
|
||||||
|
|
||||||
mConnectAttempted = SystemClock.elapsedRealtime();
|
mConnectAttempted = SystemClock.elapsedRealtime();
|
||||||
|
|
||||||
connectWithoutResettingTimer(connectAllProfiles);
|
connectWithoutResettingTimer(connectAllProfiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ void onBondingDockConnect() {
|
void onBondingDockConnect() {
|
||||||
// Attempt to connect if UUIDs are available. Otherwise,
|
// Attempt to connect if UUIDs are available. Otherwise,
|
||||||
// we will connect when the ACTION_UUID intent arrives.
|
// we will connect when the ACTION_UUID intent arrives.
|
||||||
connect(false);
|
connect(false);
|
||||||
@@ -282,7 +142,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
|
|
||||||
private void connectWithoutResettingTimer(boolean connectAllProfiles) {
|
private void connectWithoutResettingTimer(boolean connectAllProfiles) {
|
||||||
// Try to initialize the profiles if they were not.
|
// Try to initialize the profiles if they were not.
|
||||||
if (mProfiles.size() == 0) {
|
if (mProfiles.isEmpty()) {
|
||||||
if (!updateProfiles()) {
|
if (!updateProfiles()) {
|
||||||
// If UUIDs are not available yet, connect will be happen
|
// If UUIDs are not available yet, connect will be happen
|
||||||
// upon arrival of the ACTION_UUID intent.
|
// upon arrival of the ACTION_UUID intent.
|
||||||
@@ -295,98 +155,85 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
mIsConnectingErrorPossible = true;
|
mIsConnectingErrorPossible = true;
|
||||||
|
|
||||||
int preferredProfiles = 0;
|
int preferredProfiles = 0;
|
||||||
for (Profile profile : mProfiles) {
|
for (LocalBluetoothProfile profile : mProfiles) {
|
||||||
if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
|
if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
|
||||||
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
|
if (profile.isPreferred(mDevice)) {
|
||||||
.getProfileManager(mLocalManager, profile);
|
|
||||||
if (profileManager.isPreferred(mDevice)) {
|
|
||||||
++preferredProfiles;
|
++preferredProfiles;
|
||||||
connectInt(this, profile);
|
connectInt(profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
|
if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
|
||||||
|
|
||||||
if (preferredProfiles == 0) {
|
if (preferredProfiles == 0) {
|
||||||
connectAllAutoConnectableProfiles();
|
connectAutoConnectableProfiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectAllAutoConnectableProfiles() {
|
private void connectAutoConnectableProfiles() {
|
||||||
if (!ensurePaired()) return;
|
if (!ensurePaired()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Reset the only-show-one-error-dialog tracking variable
|
// Reset the only-show-one-error-dialog tracking variable
|
||||||
mIsConnectingErrorPossible = true;
|
mIsConnectingErrorPossible = true;
|
||||||
|
|
||||||
for (Profile profile : mProfiles) {
|
for (LocalBluetoothProfile profile : mProfiles) {
|
||||||
if (profile.isAutoConnectable()) {
|
if (profile.isAutoConnectable()) {
|
||||||
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
|
profile.setPreferred(mDevice, true);
|
||||||
.getProfileManager(mLocalManager, profile);
|
connectInt(profile);
|
||||||
profileManager.setPreferred(mDevice, true);
|
|
||||||
connectInt(this, profile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(Profile profile) {
|
/**
|
||||||
|
* Connect this device to the specified profile.
|
||||||
|
*
|
||||||
|
* @param profile the profile to use with the remote device
|
||||||
|
*/
|
||||||
|
void connectProfile(LocalBluetoothProfile profile) {
|
||||||
mConnectAttempted = SystemClock.elapsedRealtime();
|
mConnectAttempted = SystemClock.elapsedRealtime();
|
||||||
// Reset the only-show-one-error-dialog tracking variable
|
// Reset the only-show-one-error-dialog tracking variable
|
||||||
mIsConnectingErrorPossible = true;
|
mIsConnectingErrorPossible = true;
|
||||||
connectInt(this, profile);
|
connectInt(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean connectInt(CachedBluetoothDevice cachedDevice, Profile profile) {
|
private void connectInt(LocalBluetoothProfile profile) {
|
||||||
if (!cachedDevice.ensurePaired()) return false;
|
if (!ensurePaired()) {
|
||||||
|
return;
|
||||||
LocalBluetoothProfileManager profileManager =
|
}
|
||||||
LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
|
if (profile.connect(mDevice)) {
|
||||||
|
if (Utils.D) {
|
||||||
if (profileManager.connect(cachedDevice.mDevice)) {
|
|
||||||
if (D) {
|
|
||||||
Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
|
Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
|
||||||
}
|
}
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
Log.i(TAG, "Failed to connect " + profile.toString() + " to " + cachedDevice.mName);
|
Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showConnectingError() {
|
|
||||||
if (!mIsConnectingErrorPossible) return;
|
|
||||||
mIsConnectingErrorPossible = false;
|
|
||||||
|
|
||||||
mLocalManager.showError(mDevice,
|
|
||||||
R.string.bluetooth_connecting_error_message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean ensurePaired() {
|
private boolean ensurePaired() {
|
||||||
if (getBondState() == BluetoothDevice.BOND_NONE) {
|
if (getBondState() == BluetoothDevice.BOND_NONE) {
|
||||||
pair();
|
startPairing();
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pair() {
|
boolean startPairing() {
|
||||||
BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
|
|
||||||
|
|
||||||
// Pairing is unreliable while scanning, so cancel discovery
|
// Pairing is unreliable while scanning, so cancel discovery
|
||||||
if (adapter.isDiscovering()) {
|
if (mLocalAdapter.isDiscovering()) {
|
||||||
adapter.cancelDiscovery();
|
mLocalAdapter.cancelDiscovery();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mDevice.createBond()) {
|
if (!mDevice.createBond()) {
|
||||||
mLocalManager.showError(mDevice,
|
return false;
|
||||||
R.string.bluetooth_pairing_error_message);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mConnectAfterPairing = true; // auto-connect after pairing
|
mConnectAfterPairing = true; // auto-connect after pairing
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unpair() {
|
void unpair() {
|
||||||
disconnect();
|
disconnect();
|
||||||
|
|
||||||
int state = getBondState();
|
int state = getBondState();
|
||||||
@@ -396,14 +243,14 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state != BluetoothDevice.BOND_NONE) {
|
if (state != BluetoothDevice.BOND_NONE) {
|
||||||
final BluetoothDevice dev = getDevice();
|
final BluetoothDevice dev = mDevice;
|
||||||
if (dev != null) {
|
if (dev != null) {
|
||||||
final boolean successful = dev.removeBond();
|
final boolean successful = dev.removeBond();
|
||||||
if (successful) {
|
if (successful) {
|
||||||
if (D) {
|
if (Utils.D) {
|
||||||
Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));
|
Log.d(TAG, "Command sent successfully:REMOVE_BOND " + describe(null));
|
||||||
}
|
}
|
||||||
} else if (V) {
|
} else if (Utils.V) {
|
||||||
Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +
|
Log.v(TAG, "Framework rejected command immediately:REMOVE_BOND " +
|
||||||
describe(null));
|
describe(null));
|
||||||
}
|
}
|
||||||
@@ -411,6 +258,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: do any of these need to run async on a background thread?
|
||||||
private void fillData() {
|
private void fillData() {
|
||||||
fetchName();
|
fetchName();
|
||||||
fetchBtClass();
|
fetchBtClass();
|
||||||
@@ -421,15 +269,15 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
dispatchAttributesChanged();
|
dispatchAttributesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public BluetoothDevice getDevice() {
|
BluetoothDevice getDevice() {
|
||||||
return mDevice;
|
return mDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
String getName() {
|
||||||
return mName;
|
return mName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
void setName(String name) {
|
||||||
if (!mName.equals(name)) {
|
if (!mName.equals(name)) {
|
||||||
if (TextUtils.isEmpty(name)) {
|
if (TextUtils.isEmpty(name)) {
|
||||||
// TODO: use friendly name for unknown device (bug 1181856)
|
// TODO: use friendly name for unknown device (bug 1181856)
|
||||||
@@ -442,7 +290,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshName() {
|
void refreshName() {
|
||||||
fetchName();
|
fetchName();
|
||||||
dispatchAttributesChanged();
|
dispatchAttributesChanged();
|
||||||
}
|
}
|
||||||
@@ -452,15 +300,15 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
|
|
||||||
if (TextUtils.isEmpty(mName)) {
|
if (TextUtils.isEmpty(mName)) {
|
||||||
mName = mDevice.getAddress();
|
mName = mDevice.getAddress();
|
||||||
if (DEBUG) Log.d(TAG, "Default to address. Device has no name (yet) " + mName);
|
if (DEBUG) Log.d(TAG, "Device has no name (yet), use address: " + mName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refresh() {
|
void refresh() {
|
||||||
dispatchAttributesChanged();
|
dispatchAttributesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVisible() {
|
boolean isVisible() {
|
||||||
return mVisible;
|
return mVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,7 +319,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBondState() {
|
int getBondState() {
|
||||||
return mDevice.getBondState();
|
return mDevice.getBondState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,11 +335,10 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
*
|
*
|
||||||
* @return Whether it is connected.
|
* @return Whether it is connected.
|
||||||
*/
|
*/
|
||||||
public boolean isConnected() {
|
boolean isConnected() {
|
||||||
for (Profile profile : mProfiles) {
|
for (LocalBluetoothProfile profile : mProfiles) {
|
||||||
int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
|
int status = profile.getConnectionStatus(mDevice);
|
||||||
.getConnectionStatus(mDevice);
|
if (status == BluetoothProfile.STATE_CONNECTED) {
|
||||||
if (SettingsBtStatus.isConnectionStatusConnected(status)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -499,81 +346,21 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnectedProfile(Profile profile) {
|
boolean isConnectedProfile(LocalBluetoothProfile profile) {
|
||||||
int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
|
int status = profile.getConnectionStatus(mDevice);
|
||||||
.getConnectionStatus(mDevice);
|
return status == BluetoothProfile.STATE_CONNECTED;
|
||||||
if (SettingsBtStatus.isConnectionStatusConnected(status)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
boolean isBusy() {
|
||||||
}
|
for (LocalBluetoothProfile profile : mProfiles) {
|
||||||
|
int status = profile.getConnectionStatus(mDevice);
|
||||||
public boolean isBusy() {
|
if (status == BluetoothProfile.STATE_CONNECTING
|
||||||
for (Profile profile : mProfiles) {
|
|| status == BluetoothProfile.STATE_DISCONNECTING) {
|
||||||
int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
|
|
||||||
.getConnectionStatus(mDevice);
|
|
||||||
if (SettingsBtStatus.isConnectionStatusBusy(status)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return getBondState() == BluetoothDevice.BOND_BONDING;
|
||||||
if (getBondState() == BluetoothDevice.BOND_BONDING) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBtClassDrawable() {
|
|
||||||
if (mBtClass != null) {
|
|
||||||
switch (mBtClass.getMajorDeviceClass()) {
|
|
||||||
case BluetoothClass.Device.Major.COMPUTER:
|
|
||||||
return R.drawable.ic_bt_laptop;
|
|
||||||
|
|
||||||
case BluetoothClass.Device.Major.PHONE:
|
|
||||||
return R.drawable.ic_bt_cellphone;
|
|
||||||
|
|
||||||
case BluetoothClass.Device.Major.PERIPHERAL:
|
|
||||||
return getHidClassDrawable();
|
|
||||||
|
|
||||||
case BluetoothClass.Device.Major.IMAGING:
|
|
||||||
return R.drawable.ic_bt_imaging;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "mBtClass is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mProfiles.size() > 0) {
|
|
||||||
if (mProfiles.contains(Profile.A2DP)) {
|
|
||||||
return R.drawable.ic_bt_headphones_a2dp;
|
|
||||||
} else if (mProfiles.contains(Profile.HEADSET)) {
|
|
||||||
return R.drawable.ic_bt_headset_hfp;
|
|
||||||
}
|
|
||||||
} else if (mBtClass != null) {
|
|
||||||
if (mBtClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
|
|
||||||
return R.drawable.ic_bt_headphones_a2dp;
|
|
||||||
|
|
||||||
}
|
|
||||||
if (mBtClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
|
|
||||||
return R.drawable.ic_bt_headset_hfp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getHidClassDrawable() {
|
|
||||||
switch (mBtClass.getDeviceClass()) {
|
|
||||||
case BluetoothClass.Device.PERIPHERAL_KEYBOARD:
|
|
||||||
case BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING:
|
|
||||||
return R.drawable.ic_bt_keyboard_hid;
|
|
||||||
|
|
||||||
case BluetoothClass.Device.PERIPHERAL_POINTING:
|
|
||||||
return R.drawable.ic_bt_pointing_hid;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return R.drawable.ic_bt_misc_hid;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -587,11 +374,10 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
ParcelUuid[] uuids = mDevice.getUuids();
|
ParcelUuid[] uuids = mDevice.getUuids();
|
||||||
if (uuids == null) return false;
|
if (uuids == null) return false;
|
||||||
|
|
||||||
BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
|
ParcelUuid[] localUuids = mLocalAdapter.getUuids();
|
||||||
ParcelUuid[] localUuids = adapter.getUuids();
|
|
||||||
if (localUuids == null) return false;
|
if (localUuids == null) return false;
|
||||||
|
|
||||||
LocalBluetoothProfileManager.updateProfiles(uuids, localUuids, mProfiles);
|
mProfileManager.updateProfiles(uuids, localUuids, mProfiles);
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.e(TAG, "updating profiles for " + mDevice.getName());
|
Log.e(TAG, "updating profiles for " + mDevice.getName());
|
||||||
@@ -599,8 +385,8 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
|
|
||||||
if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
|
if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
|
||||||
Log.v(TAG, "UUID:");
|
Log.v(TAG, "UUID:");
|
||||||
for (int i = 0; i < uuids.length; i++) {
|
for (ParcelUuid uuid : uuids) {
|
||||||
Log.v(TAG, " " + uuids[i]);
|
Log.v(TAG, " " + uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -610,7 +396,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
* Refreshes the UI for the BT class, including fetching the latest value
|
* Refreshes the UI for the BT class, including fetching the latest value
|
||||||
* for the class.
|
* for the class.
|
||||||
*/
|
*/
|
||||||
public void refreshBtClass() {
|
void refreshBtClass() {
|
||||||
fetchBtClass();
|
fetchBtClass();
|
||||||
dispatchAttributesChanged();
|
dispatchAttributesChanged();
|
||||||
}
|
}
|
||||||
@@ -618,7 +404,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
/**
|
/**
|
||||||
* Refreshes the UI when framework alerts us of a UUID change.
|
* Refreshes the UI when framework alerts us of a UUID change.
|
||||||
*/
|
*/
|
||||||
public void onUuidChanged() {
|
void onUuidChanged() {
|
||||||
updateProfiles();
|
updateProfiles();
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
@@ -630,7 +416,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
* If a connect was attempted earlier without any UUID, we will do the
|
* If a connect was attempted earlier without any UUID, we will do the
|
||||||
* connect now.
|
* connect now.
|
||||||
*/
|
*/
|
||||||
if (mProfiles.size() > 0
|
if (!mProfiles.isEmpty()
|
||||||
&& (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
|
&& (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
|
||||||
.elapsedRealtime()) {
|
.elapsedRealtime()) {
|
||||||
connectWithoutResettingTimer(false);
|
connectWithoutResettingTimer(false);
|
||||||
@@ -638,7 +424,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
dispatchAttributesChanged();
|
dispatchAttributesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onBondingStateChanged(int bondState) {
|
void onBondingStateChanged(int bondState) {
|
||||||
if (bondState == BluetoothDevice.BOND_NONE) {
|
if (bondState == BluetoothDevice.BOND_NONE) {
|
||||||
mProfiles.clear();
|
mProfiles.clear();
|
||||||
mConnectAfterPairing = false; // cancel auto-connect
|
mConnectAfterPairing = false; // cancel auto-connect
|
||||||
@@ -656,52 +442,25 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBtClass(BluetoothClass btClass) {
|
void setBtClass(BluetoothClass btClass) {
|
||||||
if (btClass != null && mBtClass != btClass) {
|
if (btClass != null && mBtClass != btClass) {
|
||||||
mBtClass = btClass;
|
mBtClass = btClass;
|
||||||
dispatchAttributesChanged();
|
dispatchAttributesChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSummary() {
|
BluetoothClass getBtClass() {
|
||||||
for (Profile profile : mProfiles) {
|
return mBtClass;
|
||||||
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
|
|
||||||
.getProfileManager(mLocalManager, profile);
|
|
||||||
int connectionStatus = profileManager.getConnectionStatus(mDevice);
|
|
||||||
|
|
||||||
if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus) ||
|
|
||||||
connectionStatus == SettingsBtStatus.CONNECTION_STATUS_CONNECTING ||
|
|
||||||
connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING) {
|
|
||||||
return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return SettingsBtStatus.getPairingStatusSummary(getBondState());
|
List<LocalBluetoothProfile> getProfiles() {
|
||||||
|
return Collections.unmodifiableList(mProfiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Profile, Drawable> getProfileIcons() {
|
List<LocalBluetoothProfile> getConnectableProfiles() {
|
||||||
Map<Profile, Drawable> drawables = new HashMap<Profile, Drawable>();
|
List<LocalBluetoothProfile> connectableProfiles =
|
||||||
|
new ArrayList<LocalBluetoothProfile>();
|
||||||
for (Profile profile : mProfiles) {
|
for (LocalBluetoothProfile profile : mProfiles) {
|
||||||
int iconResource;
|
|
||||||
if (profile == Profile.HID && mBtClass != null) {
|
|
||||||
iconResource = getHidClassDrawable();
|
|
||||||
} else {
|
|
||||||
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
|
|
||||||
.getProfileManager(mLocalManager, profile);
|
|
||||||
iconResource = profileManager.getDrawableResource();
|
|
||||||
}
|
|
||||||
if (iconResource != 0) {
|
|
||||||
drawables.put(profile, mContext.getResources().getDrawable(iconResource));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return drawables;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Profile> getConnectableProfiles() {
|
|
||||||
ArrayList<Profile> connectableProfiles = new ArrayList<Profile>();
|
|
||||||
for (Profile profile : mProfiles) {
|
|
||||||
if (profile.isConnectable()) {
|
if (profile.isConnectable()) {
|
||||||
connectableProfiles.add(profile);
|
connectableProfiles.add(profile);
|
||||||
}
|
}
|
||||||
@@ -709,36 +468,13 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
return connectableProfiles;
|
return connectableProfiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onClickedAdvancedOptions(SettingsPreferenceFragment fragment) {
|
void registerCallback(Callback callback) {
|
||||||
// TODO: Verify if there really is a case when there's no foreground
|
|
||||||
// activity
|
|
||||||
|
|
||||||
// Intent intent = new Intent();
|
|
||||||
// // Need an activity context to open this in our task
|
|
||||||
// Context context = mLocalManager.getForegroundActivity();
|
|
||||||
// if (context == null) {
|
|
||||||
// // Fallback on application context, and open in a new task
|
|
||||||
// context = mLocalManager.getContext();
|
|
||||||
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
// }
|
|
||||||
// intent.setClass(context, ConnectSpecificProfilesActivity.class);
|
|
||||||
// intent.putExtra(ConnectSpecificProfilesActivity.EXTRA_DEVICE,
|
|
||||||
// mDevice);
|
|
||||||
// context.startActivity(intent);
|
|
||||||
Preference pref = new Preference(fragment.getActivity());
|
|
||||||
pref.setTitle(getName());
|
|
||||||
pref.setFragment(DeviceProfilesSettings.class.getName());
|
|
||||||
pref.getExtras().putParcelable(DeviceProfilesSettings.EXTRA_DEVICE, mDevice);
|
|
||||||
((PreferenceActivity) fragment.getActivity()).onPreferenceStartFragment(fragment, pref);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerCallback(Callback callback) {
|
|
||||||
synchronized (mCallbacks) {
|
synchronized (mCallbacks) {
|
||||||
mCallbacks.add(callback);
|
mCallbacks.add(callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterCallback(Callback callback) {
|
void unregisterCallback(Callback callback) {
|
||||||
synchronized (mCallbacks) {
|
synchronized (mCallbacks) {
|
||||||
mCallbacks.remove(callback);
|
mCallbacks.remove(callback);
|
||||||
}
|
}
|
||||||
@@ -760,9 +496,8 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if ((o == null) || !(o instanceof CachedBluetoothDevice)) {
|
if ((o == null) || !(o instanceof CachedBluetoothDevice)) {
|
||||||
throw new ClassCastException();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mDevice.equals(((CachedBluetoothDevice) o).mDevice);
|
return mDevice.equals(((CachedBluetoothDevice) o).mDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,11 +506,12 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
return mDevice.getAddress().hashCode();
|
return mDevice.getAddress().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This comparison uses non-final fields so the sort order may change
|
||||||
|
// when device attributes change (such as bonding state). Settings
|
||||||
|
// will completely refresh the device list when this happens.
|
||||||
public int compareTo(CachedBluetoothDevice another) {
|
public int compareTo(CachedBluetoothDevice another) {
|
||||||
int comparison;
|
|
||||||
|
|
||||||
// Connected above not connected
|
// Connected above not connected
|
||||||
comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
|
int comparison = (another.isConnected() ? 1 : 0) - (isConnected() ? 1 : 0);
|
||||||
if (comparison != 0) return comparison;
|
if (comparison != 0) return comparison;
|
||||||
|
|
||||||
// Paired above not paired
|
// Paired above not paired
|
||||||
@@ -792,7 +528,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
|
|||||||
if (comparison != 0) return comparison;
|
if (comparison != 0) return comparison;
|
||||||
|
|
||||||
// Fallback on name
|
// Fallback on name
|
||||||
return getName().compareTo(another.getName());
|
return mName.compareTo(another.mName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
|
@@ -16,121 +16,82 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
|
||||||
import android.bluetooth.BluetoothClass;
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.bluetooth.LocalBluetoothManager.Callback;
|
|
||||||
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
|
* CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
|
||||||
*/
|
*/
|
||||||
class CachedBluetoothDeviceManager {
|
final class CachedBluetoothDeviceManager {
|
||||||
private static final String TAG = "CachedBluetoothDeviceManager";
|
// private static final String TAG = "CachedBluetoothDeviceManager";
|
||||||
|
|
||||||
final LocalBluetoothManager mLocalManager;
|
private final List<CachedBluetoothDevice> mCachedDevices =
|
||||||
final List<Callback> mCallbacks;
|
new ArrayList<CachedBluetoothDevice>();
|
||||||
|
|
||||||
final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
|
public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
|
||||||
|
|
||||||
public CachedBluetoothDeviceManager(LocalBluetoothManager localManager) {
|
|
||||||
mLocalManager = localManager;
|
|
||||||
mCallbacks = localManager.getCallbacks();
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized boolean readPairedDevices() {
|
|
||||||
BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
|
|
||||||
Set<BluetoothDevice> bondedDevices = adapter.getBondedDevices();
|
|
||||||
if (bondedDevices == null) return false;
|
|
||||||
|
|
||||||
boolean deviceAdded = false;
|
|
||||||
for (BluetoothDevice device : bondedDevices) {
|
|
||||||
CachedBluetoothDevice cachedDevice = findDevice(device);
|
|
||||||
if (cachedDevice == null) {
|
|
||||||
cachedDevice = new CachedBluetoothDevice(mLocalManager.getContext(), device);
|
|
||||||
mCachedDevices.add(cachedDevice);
|
|
||||||
dispatchDeviceAdded(cachedDevice);
|
|
||||||
deviceAdded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return deviceAdded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized List<CachedBluetoothDevice> getCachedDevicesCopy() {
|
|
||||||
return new ArrayList<CachedBluetoothDevice>(mCachedDevices);
|
return new ArrayList<CachedBluetoothDevice>(mCachedDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBluetoothStateChanged(boolean enabled) {
|
public boolean onDeviceDisappeared(CachedBluetoothDevice cachedDevice) {
|
||||||
if (enabled) {
|
|
||||||
readPairedDevices();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void onDeviceAppeared(BluetoothDevice device, short rssi,
|
|
||||||
BluetoothClass btClass, String name) {
|
|
||||||
boolean deviceAdded = false;
|
|
||||||
|
|
||||||
CachedBluetoothDevice cachedDevice = findDevice(device);
|
|
||||||
if (cachedDevice == null) {
|
|
||||||
cachedDevice = new CachedBluetoothDevice(mLocalManager.getContext(), device);
|
|
||||||
mCachedDevices.add(cachedDevice);
|
|
||||||
deviceAdded = true;
|
|
||||||
}
|
|
||||||
cachedDevice.setRssi(rssi);
|
|
||||||
cachedDevice.setBtClass(btClass);
|
|
||||||
cachedDevice.setName(name);
|
|
||||||
cachedDevice.setVisible(true);
|
|
||||||
|
|
||||||
if (deviceAdded) {
|
|
||||||
dispatchDeviceAdded(cachedDevice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void onDeviceDisappeared(BluetoothDevice device) {
|
|
||||||
CachedBluetoothDevice cachedDevice = findDevice(device);
|
|
||||||
if (cachedDevice == null) return;
|
|
||||||
|
|
||||||
cachedDevice.setVisible(false);
|
cachedDevice.setVisible(false);
|
||||||
checkForDeviceRemoval(cachedDevice);
|
return checkForDeviceRemoval(cachedDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkForDeviceRemoval(CachedBluetoothDevice cachedDevice) {
|
private boolean checkForDeviceRemoval(
|
||||||
|
CachedBluetoothDevice cachedDevice) {
|
||||||
if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
|
if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
|
||||||
!cachedDevice.isVisible()) {
|
!cachedDevice.isVisible()) {
|
||||||
// If device isn't paired, remove it altogether
|
// If device isn't paired, remove it altogether
|
||||||
mCachedDevices.remove(cachedDevice);
|
mCachedDevices.remove(cachedDevice);
|
||||||
dispatchDeviceDeleted(cachedDevice);
|
return true; // dispatch device deleted
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void onDeviceNameUpdated(BluetoothDevice device) {
|
public void onDeviceNameUpdated(BluetoothDevice device) {
|
||||||
CachedBluetoothDevice cachedDevice = findDevice(device);
|
CachedBluetoothDevice cachedDevice = findDevice(device);
|
||||||
if (cachedDevice != null) {
|
if (cachedDevice != null) {
|
||||||
cachedDevice.refreshName();
|
cachedDevice.refreshName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized CachedBluetoothDevice findDevice(BluetoothDevice device) {
|
/**
|
||||||
|
* Search for existing {@link CachedBluetoothDevice} or return null
|
||||||
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
|
* if this device isn't in the cache. Use {@link #addDevice}
|
||||||
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
|
* to create and return a new {@link CachedBluetoothDevice} for
|
||||||
|
* a newly discovered {@link BluetoothDevice}.
|
||||||
|
*
|
||||||
|
* @param device the address of the Bluetooth device
|
||||||
|
* @return the cached device object for this device, or null if it has
|
||||||
|
* not been previously seen
|
||||||
|
*/
|
||||||
|
CachedBluetoothDevice findDevice(BluetoothDevice device) {
|
||||||
|
for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
|
||||||
if (cachedDevice.getDevice().equals(device)) {
|
if (cachedDevice.getDevice().equals(device)) {
|
||||||
return cachedDevice;
|
return cachedDevice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and return a new {@link CachedBluetoothDevice}. This assumes
|
||||||
|
* that {@link #findDevice} has already been called and returned null.
|
||||||
|
* @param device the address of the new Bluetooth device
|
||||||
|
* @return the newly created CachedBluetoothDevice object
|
||||||
|
*/
|
||||||
|
CachedBluetoothDevice addDevice(LocalBluetoothAdapter adapter,
|
||||||
|
LocalBluetoothProfileManager profileManager,
|
||||||
|
BluetoothDevice device) {
|
||||||
|
CachedBluetoothDevice newDevice = new CachedBluetoothDevice(adapter, profileManager,
|
||||||
|
device);
|
||||||
|
mCachedDevices.add(newDevice);
|
||||||
|
return newDevice;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to get the name of a remote device, otherwise returns the address.
|
* Attempts to get the name of a remote device, otherwise returns the address.
|
||||||
*
|
*
|
||||||
@@ -139,122 +100,23 @@ class CachedBluetoothDeviceManager {
|
|||||||
*/
|
*/
|
||||||
public String getName(BluetoothDevice device) {
|
public String getName(BluetoothDevice device) {
|
||||||
CachedBluetoothDevice cachedDevice = findDevice(device);
|
CachedBluetoothDevice cachedDevice = findDevice(device);
|
||||||
if (cachedDevice != null) return cachedDevice.getName();
|
if (cachedDevice != null) {
|
||||||
|
return cachedDevice.getName();
|
||||||
|
}
|
||||||
|
|
||||||
String name = device.getName();
|
String name = device.getName();
|
||||||
if (name != null) return name;
|
if (name != null) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
return device.getAddress();
|
return device.getAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
|
|
||||||
synchronized (mCallbacks) {
|
|
||||||
for (Callback callback : mCallbacks) {
|
|
||||||
callback.onDeviceAdded(cachedDevice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: divider between prev paired/connected and scanned
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dispatchDeviceDeleted(CachedBluetoothDevice cachedDevice) {
|
|
||||||
synchronized (mCallbacks) {
|
|
||||||
for (Callback callback : mCallbacks) {
|
|
||||||
callback.onDeviceDeleted(cachedDevice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dispatchDeviceBondStateChanged(
|
|
||||||
CachedBluetoothDevice cachedDevice, int bondState) {
|
|
||||||
synchronized (mCallbacks) {
|
|
||||||
for (Callback callback : mCallbacks) {
|
|
||||||
callback.onDeviceBondStateChanged(cachedDevice, bondState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void onBondingStateChanged(BluetoothDevice device, int bondState) {
|
|
||||||
CachedBluetoothDevice cachedDevice = findDevice(device);
|
|
||||||
if (cachedDevice == null) {
|
|
||||||
if (!readPairedDevices()) {
|
|
||||||
Log.e(TAG, "Got bonding state changed for " + device +
|
|
||||||
", but we have no record of that device.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cachedDevice = findDevice(device);
|
|
||||||
if (cachedDevice == null) {
|
|
||||||
Log.e(TAG, "Got bonding state changed for " + device +
|
|
||||||
", but device not added in cache.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatchDeviceBondStateChanged(cachedDevice, bondState);
|
|
||||||
cachedDevice.onBondingStateChanged(bondState);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when we have reached the un-bond state.
|
|
||||||
*
|
|
||||||
* @param device The remote device.
|
|
||||||
* @param reason The reason, one of the error reasons from
|
|
||||||
* BluetoothDevice.UNBOND_REASON_*
|
|
||||||
*/
|
|
||||||
public synchronized void showUnbondMessage(BluetoothDevice device, int reason) {
|
|
||||||
int errorMsg;
|
|
||||||
|
|
||||||
switch(reason) {
|
|
||||||
case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
|
|
||||||
errorMsg = R.string.bluetooth_pairing_pin_error_message;
|
|
||||||
mLocalManager.showError(device, errorMsg);
|
|
||||||
break;
|
|
||||||
case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
|
|
||||||
errorMsg = R.string.bluetooth_pairing_rejected_error_message;
|
|
||||||
mLocalManager.showError(device, errorMsg);
|
|
||||||
break;
|
|
||||||
case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
|
|
||||||
errorMsg = R.string.bluetooth_pairing_device_down_error_message;
|
|
||||||
mLocalManager.showError(device, errorMsg);
|
|
||||||
break;
|
|
||||||
case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
|
|
||||||
case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
|
|
||||||
case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
|
|
||||||
case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
|
|
||||||
errorMsg = R.string.bluetooth_pairing_error_message;
|
|
||||||
mLocalManager.showError(device, errorMsg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.w(TAG, "showUnbondMessage: Not displaying any message for reason:" + reason);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void onProfileStateChanged(BluetoothDevice device, Profile profile,
|
|
||||||
int newProfileState) {
|
|
||||||
CachedBluetoothDevice cachedDevice = findDevice(device);
|
|
||||||
if (cachedDevice == null) return;
|
|
||||||
|
|
||||||
cachedDevice.onProfileStateChanged(profile, newProfileState);
|
|
||||||
cachedDevice.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void onConnectingError(BluetoothDevice device) {
|
|
||||||
CachedBluetoothDevice cachedDevice = findDevice(device);
|
|
||||||
if (cachedDevice == null) return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Go through the device's delegate so we don't spam the user with
|
|
||||||
* errors connecting to different profiles, and instead make sure the
|
|
||||||
* user sees a single error for his single 'connect' action.
|
|
||||||
*/
|
|
||||||
cachedDevice.showConnectingError();
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void onScanningStateChanged(boolean started) {
|
public synchronized void onScanningStateChanged(boolean started) {
|
||||||
if (!started) return;
|
if (!started) return;
|
||||||
|
|
||||||
// If starting a new scan, clear old visibility
|
// If starting a new scan, clear old visibility
|
||||||
|
// Iterate in reverse order since devices may be removed.
|
||||||
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
|
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
|
||||||
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
|
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
|
||||||
cachedDevice.setVisible(false);
|
cachedDevice.setVisible(false);
|
||||||
|
@@ -16,28 +16,18 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothClass;
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothDevicePicker;
|
|
||||||
import android.bluetooth.BluetoothUuid;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.ParcelUuid;
|
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceCategory;
|
import android.preference.PreferenceCategory;
|
||||||
import android.preference.PreferenceScreen;
|
import android.preference.PreferenceScreen;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import com.android.settings.ProgressCategory;
|
import com.android.settings.ProgressCategory;
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,105 +38,96 @@ import java.util.WeakHashMap;
|
|||||||
* @see DevicePickerFragment
|
* @see DevicePickerFragment
|
||||||
* @see BluetoothFindNearby
|
* @see BluetoothFindNearby
|
||||||
*/
|
*/
|
||||||
public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFragment
|
public abstract class DeviceListPreferenceFragment extends
|
||||||
implements LocalBluetoothManager.Callback, View.OnClickListener {
|
SettingsPreferenceFragment implements BluetoothCallback {
|
||||||
|
|
||||||
private static final String TAG = "DeviceListPreferenceFragment";
|
private static final String TAG = "DeviceListPreferenceFragment";
|
||||||
|
|
||||||
static final String KEY_BT_DEVICE_LIST = "bt_device_list";
|
private static final String KEY_BT_DEVICE_LIST = "bt_device_list";
|
||||||
static final String KEY_BT_SCAN = "bt_scan";
|
private static final String KEY_BT_SCAN = "bt_scan";
|
||||||
|
|
||||||
int mFilterType;
|
private BluetoothDeviceFilter.Filter mFilter;
|
||||||
|
|
||||||
BluetoothDevice mSelectedDevice = null;
|
BluetoothDevice mSelectedDevice;
|
||||||
|
|
||||||
|
LocalBluetoothAdapter mLocalAdapter;
|
||||||
LocalBluetoothManager mLocalManager;
|
LocalBluetoothManager mLocalManager;
|
||||||
|
|
||||||
private PreferenceCategory mDeviceList;
|
private PreferenceCategory mDeviceList;
|
||||||
|
|
||||||
WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
|
final WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
|
||||||
new WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference>();
|
new WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference>();
|
||||||
|
|
||||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
DeviceListPreferenceFragment() {
|
||||||
@Override
|
mFilter = BluetoothDeviceFilter.ALL_FILTER;
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
|
|
||||||
onBluetoothStateChanged(mLocalManager.getBluetoothState());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeviceListPreferenceFragment(BluetoothDeviceFilter.Filter filter) {
|
||||||
|
mFilter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
final void setFilter(int filterType) {
|
||||||
|
mFilter = BluetoothDeviceFilter.getFilter(filterType);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
// We delay calling super.onActivityCreated(). See WifiSettings.java for more info.
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
final Activity activity = getActivity();
|
mLocalManager = LocalBluetoothManager.getInstance(getActivity());
|
||||||
mLocalManager = LocalBluetoothManager.getInstance(activity);
|
|
||||||
if (mLocalManager == null) {
|
if (mLocalManager == null) {
|
||||||
finish();
|
Log.e(TAG, "Bluetooth is not supported on this device");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
mLocalAdapter = mLocalManager.getBluetoothAdapter();
|
||||||
|
|
||||||
mFilterType = BluetoothDevicePicker.FILTER_TYPE_ALL;
|
addPreferencesForActivity();
|
||||||
|
|
||||||
if (getPreferenceScreen() != null) getPreferenceScreen().removeAll();
|
|
||||||
|
|
||||||
addPreferencesForActivity(activity);
|
|
||||||
|
|
||||||
mDeviceList = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST);
|
mDeviceList = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST);
|
||||||
|
if (mDeviceList == null) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
Log.e(TAG, "Could not find device list preference object!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add preferences from the subclass. */
|
/** Add preferences from the subclass. */
|
||||||
abstract void addPreferencesForActivity(Activity activity);
|
abstract void addPreferencesForActivity();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
mLocalManager.registerCallback(this);
|
|
||||||
|
|
||||||
updateProgressUi(mLocalManager.getBluetoothAdapter().isDiscovering());
|
|
||||||
|
|
||||||
IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
|
|
||||||
getActivity().registerReceiver(mReceiver, intentFilter);
|
|
||||||
mLocalManager.setForegroundActivity(getActivity());
|
mLocalManager.setForegroundActivity(getActivity());
|
||||||
|
mLocalManager.getEventManager().registerCallback(this);
|
||||||
|
|
||||||
|
updateProgressUi(mLocalAdapter.isDiscovering());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
mLocalManager.stopScanning();
|
|
||||||
|
mLocalAdapter.stopScanning();
|
||||||
mLocalManager.setForegroundActivity(null);
|
mLocalManager.setForegroundActivity(null);
|
||||||
|
mLocalManager.getEventManager().unregisterCallback(this);
|
||||||
|
|
||||||
mDevicePreferenceMap.clear();
|
mDevicePreferenceMap.clear();
|
||||||
mDeviceList.removeAll();
|
mDeviceList.removeAll();
|
||||||
getActivity().unregisterReceiver(mReceiver);
|
|
||||||
|
|
||||||
mLocalManager.unregisterCallback(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addDevices() {
|
void addDevices() {
|
||||||
List<CachedBluetoothDevice> cachedDevices =
|
Collection<CachedBluetoothDevice> cachedDevices =
|
||||||
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
|
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
|
||||||
for (CachedBluetoothDevice cachedDevice : cachedDevices) {
|
for (CachedBluetoothDevice cachedDevice : cachedDevices) {
|
||||||
onDeviceAdded(cachedDevice);
|
onDeviceAdded(cachedDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onClick(View v) {
|
|
||||||
// User clicked on advanced options icon for a device in the list
|
|
||||||
if (v.getTag() instanceof CachedBluetoothDevice) {
|
|
||||||
CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag();
|
|
||||||
device.onClickedAdvancedOptions(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
|
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
|
||||||
Preference preference) {
|
Preference preference) {
|
||||||
|
|
||||||
if (KEY_BT_SCAN.equals(preference.getKey())) {
|
if (KEY_BT_SCAN.equals(preference.getKey())) {
|
||||||
mLocalManager.startScanning(true);
|
mLocalAdapter.startScanning(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +143,7 @@ public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFra
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
|
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
|
||||||
btPreference.getCachedDevice().onClicked();
|
btPreference.onClicked();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
|
public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
|
||||||
@@ -171,71 +152,11 @@ public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFra
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addDevicePreference(cachedDevice)) {
|
if (mFilter.matches(cachedDevice.getDevice())) {
|
||||||
createDevicePreference(cachedDevice);
|
createDevicePreference(cachedDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether to add the new device to the list.
|
|
||||||
* @param cachedDevice the newly discovered device
|
|
||||||
* @return true if the device should be added; false otherwise
|
|
||||||
*/
|
|
||||||
boolean addDevicePreference(CachedBluetoothDevice cachedDevice) {
|
|
||||||
ParcelUuid[] uuids = cachedDevice.getDevice().getUuids();
|
|
||||||
BluetoothClass bluetoothClass = cachedDevice.getDevice().getBluetoothClass();
|
|
||||||
|
|
||||||
switch(mFilterType) {
|
|
||||||
case BluetoothDevicePicker.FILTER_TYPE_TRANSFER:
|
|
||||||
if (uuids != null) {
|
|
||||||
if (BluetoothUuid.containsAnyUuid(uuids,
|
|
||||||
LocalBluetoothProfileManager.OPP_PROFILE_UUIDS)) return true;
|
|
||||||
}
|
|
||||||
if (bluetoothClass != null
|
|
||||||
&& bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_OPP)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BluetoothDevicePicker.FILTER_TYPE_AUDIO:
|
|
||||||
if (uuids != null) {
|
|
||||||
if (BluetoothUuid.containsAnyUuid(uuids,
|
|
||||||
LocalBluetoothProfileManager.A2DP_SINK_PROFILE_UUIDS)) return true;
|
|
||||||
|
|
||||||
if (BluetoothUuid.containsAnyUuid(uuids,
|
|
||||||
LocalBluetoothProfileManager.HEADSET_PROFILE_UUIDS)) return true;
|
|
||||||
} else if (bluetoothClass != null) {
|
|
||||||
if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) return true;
|
|
||||||
|
|
||||||
if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BluetoothDevicePicker.FILTER_TYPE_PANU:
|
|
||||||
if (uuids != null) {
|
|
||||||
if (BluetoothUuid.containsAnyUuid(uuids,
|
|
||||||
LocalBluetoothProfileManager.PANU_PROFILE_UUIDS)) return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
if (bluetoothClass != null
|
|
||||||
&& bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_PANU)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BluetoothDevicePicker.FILTER_TYPE_NAP:
|
|
||||||
if (uuids != null) {
|
|
||||||
if (BluetoothUuid.containsAnyUuid(uuids,
|
|
||||||
LocalBluetoothProfileManager.NAP_PROFILE_UUIDS)) return true;
|
|
||||||
}
|
|
||||||
if (bluetoothClass != null
|
|
||||||
&& bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_NAP)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void createDevicePreference(CachedBluetoothDevice cachedDevice) {
|
void createDevicePreference(CachedBluetoothDevice cachedDevice) {
|
||||||
BluetoothDevicePreference preference = new BluetoothDevicePreference(
|
BluetoothDevicePreference preference = new BluetoothDevicePreference(
|
||||||
getActivity(), cachedDevice);
|
getActivity(), cachedDevice);
|
||||||
@@ -252,7 +173,8 @@ public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFra
|
|||||||
void initDevicePreference(BluetoothDevicePreference preference) { }
|
void initDevicePreference(BluetoothDevicePreference preference) { }
|
||||||
|
|
||||||
public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
|
public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
|
||||||
BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
|
BluetoothDevicePreference preference = mDevicePreferenceMap.remove(
|
||||||
|
cachedDevice);
|
||||||
if (preference != null) {
|
if (preference != null) {
|
||||||
mDeviceList.removePreference(preference);
|
mDeviceList.removePreference(preference);
|
||||||
}
|
}
|
||||||
@@ -268,15 +190,9 @@ public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBluetoothStateChanged(int bluetoothState) {
|
public void onBluetoothStateChanged(int bluetoothState) {
|
||||||
if (bluetoothState == BluetoothAdapter.STATE_OFF) {
|
if (bluetoothState == BluetoothAdapter.STATE_OFF) {
|
||||||
updateProgressUi(false);
|
updateProgressUi(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendDevicePickedIntent(BluetoothDevice device) {
|
|
||||||
Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
|
|
||||||
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
|
|
||||||
getActivity().sendBroadcast(intent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ import android.os.Bundle;
|
|||||||
* Activity for Bluetooth device picker dialog. The device picker logic
|
* Activity for Bluetooth device picker dialog. The device picker logic
|
||||||
* is implemented in the {@link BluetoothSettings} fragment.
|
* is implemented in the {@link BluetoothSettings} fragment.
|
||||||
*/
|
*/
|
||||||
public class DevicePickerActivity extends Activity {
|
public final class DevicePickerActivity extends Activity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@@ -16,12 +16,11 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothDevicePicker;
|
import android.bluetooth.BluetoothDevicePicker;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.util.Log;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
|
||||||
@@ -29,36 +28,42 @@ import com.android.settings.R;
|
|||||||
* BluetoothSettings is the Settings screen for Bluetooth configuration and
|
* BluetoothSettings is the Settings screen for Bluetooth configuration and
|
||||||
* connection management.
|
* connection management.
|
||||||
*/
|
*/
|
||||||
public class DevicePickerFragment extends DeviceListPreferenceFragment {
|
public final class DevicePickerFragment extends DeviceListPreferenceFragment {
|
||||||
|
|
||||||
private static final String TAG = "BluetoothDevicePicker";
|
|
||||||
|
|
||||||
private boolean mNeedAuth;
|
private boolean mNeedAuth;
|
||||||
private String mLaunchPackage;
|
private String mLaunchPackage;
|
||||||
private String mLaunchClass;
|
private String mLaunchClass;
|
||||||
|
|
||||||
void addPreferencesForActivity(Activity activity) {
|
@Override
|
||||||
Intent intent = activity.getIntent();
|
void addPreferencesForActivity() {
|
||||||
|
addPreferencesFromResource(R.xml.device_picker);
|
||||||
|
|
||||||
|
Intent intent = getActivity().getIntent();
|
||||||
mNeedAuth = intent.getBooleanExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
|
mNeedAuth = intent.getBooleanExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
|
||||||
mFilterType = intent.getIntExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
|
setFilter(intent.getIntExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
|
||||||
BluetoothDevicePicker.FILTER_TYPE_ALL);
|
BluetoothDevicePicker.FILTER_TYPE_ALL));
|
||||||
mLaunchPackage = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE);
|
mLaunchPackage = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE);
|
||||||
mLaunchClass = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS);
|
mLaunchClass = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS);
|
||||||
|
}
|
||||||
|
|
||||||
activity.setTitle(activity.getString(R.string.device_picker));
|
@Override
|
||||||
addPreferencesFromResource(R.xml.device_picker);
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
getActivity().setTitle(getString(R.string.device_picker));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
addDevices();
|
addDevices();
|
||||||
mLocalManager.startScanning(true);
|
mLocalAdapter.startScanning(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
|
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
|
||||||
mLocalManager.stopScanning();
|
mLocalAdapter.stopScanning();
|
||||||
mLocalManager.persistSelectedDeviceInPicker(mSelectedDevice.getAddress());
|
LocalBluetoothPreferences.persistSelectedDeviceInPicker(
|
||||||
|
getActivity(), mSelectedDevice.getAddress());
|
||||||
if ((btPreference.getCachedDevice().getBondState() ==
|
if ((btPreference.getCachedDevice().getBondState() ==
|
||||||
BluetoothDevice.BOND_BONDED) || !mNeedAuth) {
|
BluetoothDevice.BOND_BONDED) || !mNeedAuth) {
|
||||||
sendDevicePickedIntent(mSelectedDevice);
|
sendDevicePickedIntent(mSelectedDevice);
|
||||||
@@ -79,15 +84,16 @@ public class DevicePickerFragment extends DeviceListPreferenceFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBluetoothStateChanged(int bluetoothState) {
|
@Override
|
||||||
|
public void onBluetoothStateChanged(int bluetoothState) {
|
||||||
super.onBluetoothStateChanged(bluetoothState);
|
super.onBluetoothStateChanged(bluetoothState);
|
||||||
|
|
||||||
if (bluetoothState == BluetoothAdapter.STATE_ON) {
|
if (bluetoothState == BluetoothAdapter.STATE_ON) {
|
||||||
mLocalManager.startScanning(false);
|
mLocalAdapter.startScanning(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendDevicePickedIntent(BluetoothDevice device) {
|
private void sendDevicePickedIntent(BluetoothDevice device) {
|
||||||
Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
|
Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
|
||||||
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
|
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
|
||||||
if (mLaunchPackage != null && mLaunchClass != null) {
|
if (mLaunchPackage != null && mLaunchClass != null) {
|
||||||
|
@@ -16,15 +16,12 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import android.app.AlertDialog;
|
||||||
import com.android.settings.SettingsPreferenceFragment;
|
|
||||||
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
|
|
||||||
|
|
||||||
import android.bluetooth.BluetoothClass;
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothUuid;
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.ParcelUuid;
|
|
||||||
import android.preference.CheckBoxPreference;
|
import android.preference.CheckBoxPreference;
|
||||||
import android.preference.EditTextPreference;
|
import android.preference.EditTextPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
@@ -34,6 +31,9 @@ import android.text.TextUtils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,7 +41,7 @@ import java.util.HashMap;
|
|||||||
* for a particular device, and allows them to be individually connected
|
* for a particular device, and allows them to be individually connected
|
||||||
* (or disconnected).
|
* (or disconnected).
|
||||||
*/
|
*/
|
||||||
public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
public final class DeviceProfilesSettings extends SettingsPreferenceFragment
|
||||||
implements CachedBluetoothDevice.Callback, Preference.OnPreferenceChangeListener,
|
implements CachedBluetoothDevice.Callback, Preference.OnPreferenceChangeListener,
|
||||||
View.OnClickListener {
|
View.OnClickListener {
|
||||||
private static final String TAG = "DeviceProfilesSettings";
|
private static final String TAG = "DeviceProfilesSettings";
|
||||||
@@ -56,11 +56,15 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
|
|
||||||
private LocalBluetoothManager mManager;
|
private LocalBluetoothManager mManager;
|
||||||
private CachedBluetoothDevice mCachedDevice;
|
private CachedBluetoothDevice mCachedDevice;
|
||||||
|
private LocalBluetoothProfileManager mProfileManager;
|
||||||
|
|
||||||
private PreferenceGroup mProfileContainer;
|
private PreferenceGroup mProfileContainer;
|
||||||
private EditTextPreference mDeviceNamePref;
|
private EditTextPreference mDeviceNamePref;
|
||||||
private final HashMap<String,CheckBoxPreference> mAutoConnectPrefs
|
|
||||||
= new HashMap<String,CheckBoxPreference>();
|
private final HashMap<LocalBluetoothProfile, CheckBoxPreference> mAutoConnectPrefs
|
||||||
|
= new HashMap<LocalBluetoothProfile, CheckBoxPreference>();
|
||||||
|
|
||||||
|
private AlertDialog mDisconnectDialog;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -74,51 +78,54 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
device = args.getParcelable(EXTRA_DEVICE);
|
device = args.getParcelable(EXTRA_DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addPreferencesFromResource(R.xml.bluetooth_device_advanced);
|
||||||
|
getPreferenceScreen().setOrderingAsAdded(false);
|
||||||
|
mProfileContainer = (PreferenceGroup) findPreference(KEY_PROFILE_CONTAINER);
|
||||||
|
mDeviceNamePref = (EditTextPreference) findPreference(KEY_RENAME_DEVICE);
|
||||||
|
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
Log.w(TAG, "Activity started without a remote Bluetooth device");
|
Log.w(TAG, "Activity started without a remote Bluetooth device");
|
||||||
finish();
|
finish();
|
||||||
return;
|
return; // TODO: test this failure path
|
||||||
}
|
}
|
||||||
|
|
||||||
mManager = LocalBluetoothManager.getInstance(getActivity());
|
mManager = LocalBluetoothManager.getInstance(getActivity());
|
||||||
mCachedDevice = mManager.getCachedDeviceManager().findDevice(device);
|
CachedBluetoothDeviceManager deviceManager =
|
||||||
|
mManager.getCachedDeviceManager();
|
||||||
|
mProfileManager = mManager.getProfileManager();
|
||||||
|
mCachedDevice = deviceManager.findDevice(device);
|
||||||
if (mCachedDevice == null) {
|
if (mCachedDevice == null) {
|
||||||
Log.w(TAG, "Device not found, cannot connect to it");
|
Log.w(TAG, "Device not found, cannot connect to it");
|
||||||
finish();
|
finish();
|
||||||
return;
|
return; // TODO: test this failure path
|
||||||
}
|
}
|
||||||
|
|
||||||
addPreferencesFromResource(R.xml.bluetooth_device_advanced);
|
String deviceName = mCachedDevice.getName();
|
||||||
getPreferenceScreen().setOrderingAsAdded(false);
|
mDeviceNamePref.setSummary(deviceName);
|
||||||
|
mDeviceNamePref.setText(deviceName);
|
||||||
mProfileContainer = (PreferenceGroup) findPreference(KEY_PROFILE_CONTAINER);
|
|
||||||
|
|
||||||
mDeviceNamePref = (EditTextPreference) findPreference(KEY_RENAME_DEVICE);
|
|
||||||
mDeviceNamePref.setSummary(mCachedDevice.getName());
|
|
||||||
mDeviceNamePref.setText(mCachedDevice.getName());
|
|
||||||
mDeviceNamePref.setOnPreferenceChangeListener(this);
|
mDeviceNamePref.setOnPreferenceChangeListener(this);
|
||||||
|
|
||||||
// Set the title of the screen
|
// Set the title of the screen
|
||||||
findPreference(KEY_TITLE).setTitle(getResources()
|
findPreference(KEY_TITLE).setTitle(
|
||||||
.getString(R.string.bluetooth_device_advanced_title, mCachedDevice.getName()));
|
getString(R.string.bluetooth_device_advanced_title,
|
||||||
|
deviceName));
|
||||||
|
|
||||||
// Add a preference for each profile
|
// Add a preference for each profile
|
||||||
addPreferencesForProfiles();
|
addPreferencesForProfiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isObjectPushSupported(BluetoothDevice device) {
|
@Override
|
||||||
ParcelUuid[] uuids = device.getUuids();
|
public void onDestroy() {
|
||||||
BluetoothClass bluetoothClass = device.getBluetoothClass();
|
super.onDestroy();
|
||||||
return (uuids != null && BluetoothUuid.containsAnyUuid(uuids,
|
if (mDisconnectDialog != null) {
|
||||||
LocalBluetoothProfileManager.OPP_PROFILE_UUIDS)) ||
|
mDisconnectDialog.dismiss();
|
||||||
(bluetoothClass != null && bluetoothClass.doesClassMatch(
|
mDisconnectDialog = null;
|
||||||
BluetoothClass.PROFILE_OPP));
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSaveInstanceState(Bundle outState) {
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
super.onSaveInstanceState(outState);
|
super.onSaveInstanceState(outState);
|
||||||
|
|
||||||
outState.putParcelable(EXTRA_DEVICE, mCachedDevice.getDevice());
|
outState.putParcelable(EXTRA_DEVICE, mCachedDevice.getDevice());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,7 +148,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addPreferencesForProfiles() {
|
private void addPreferencesForProfiles() {
|
||||||
for (Profile profile : mCachedDevice.getConnectableProfiles()) {
|
for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
|
||||||
Preference pref = createProfilePreference(profile);
|
Preference pref = createProfilePreference(profile);
|
||||||
mProfileContainer.addPreference(pref);
|
mProfileContainer.addPreference(pref);
|
||||||
}
|
}
|
||||||
@@ -155,21 +162,18 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
* @return A preference that allows the user to choose whether this profile
|
* @return A preference that allows the user to choose whether this profile
|
||||||
* will be connected to.
|
* will be connected to.
|
||||||
*/
|
*/
|
||||||
private Preference createProfilePreference(Profile profile) {
|
private Preference createProfilePreference(LocalBluetoothProfile profile) {
|
||||||
BluetoothProfilePreference pref = new BluetoothProfilePreference(getActivity(), profile);
|
BluetoothProfilePreference pref = new BluetoothProfilePreference(getActivity(), profile);
|
||||||
pref.setKey(profile.toString());
|
pref.setKey(profile.toString());
|
||||||
pref.setTitle(profile.localizedString);
|
pref.setTitle(profile.getNameResource());
|
||||||
pref.setExpanded(false);
|
pref.setExpanded(false);
|
||||||
pref.setPersistent(false);
|
pref.setPersistent(false);
|
||||||
pref.setOrder(getProfilePreferenceIndex(profile));
|
pref.setOrder(getProfilePreferenceIndex(profile.getOrdinal()));
|
||||||
pref.setOnExpandClickListener(this);
|
pref.setOnExpandClickListener(this);
|
||||||
|
|
||||||
LocalBluetoothProfileManager profileManager =
|
int iconResource = profile.getDrawableResource(null); // FIXME: get BT class for this?
|
||||||
LocalBluetoothProfileManager.getProfileManager(mManager, profile);
|
|
||||||
int iconResource = profileManager.getDrawableResource();
|
|
||||||
if (iconResource != 0) {
|
if (iconResource != 0) {
|
||||||
pref.setProfileDrawable(mManager.getContext()
|
pref.setProfileDrawable(getResources().getDrawable(iconResource));
|
||||||
.getResources().getDrawable(iconResource));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,7 +190,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
|
public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
|
||||||
String key = preference.getKey();
|
String key = preference.getKey();
|
||||||
if (preference instanceof BluetoothProfilePreference) {
|
if (preference instanceof BluetoothProfilePreference) {
|
||||||
onProfileClicked(Profile.valueOf(key));
|
onProfileClicked(mProfileManager.getProfileByName(key));
|
||||||
return true;
|
return true;
|
||||||
} else if (key.equals(KEY_UNPAIR)) {
|
} else if (key.equals(KEY_UNPAIR)) {
|
||||||
unpairDevice();
|
unpairDevice();
|
||||||
@@ -194,7 +198,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return super.onPreferenceTreeClick(screen, preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||||
@@ -202,10 +206,8 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
mCachedDevice.setName((String) newValue);
|
mCachedDevice.setName((String) newValue);
|
||||||
} else if (preference instanceof CheckBoxPreference) {
|
} else if (preference instanceof CheckBoxPreference) {
|
||||||
boolean autoConnect = (Boolean) newValue;
|
boolean autoConnect = (Boolean) newValue;
|
||||||
Profile prof = getProfileOf(preference);
|
LocalBluetoothProfile prof = getProfileOf(preference);
|
||||||
LocalBluetoothProfileManager
|
prof.setPreferred(mCachedDevice.getDevice(),
|
||||||
.getProfileManager(mManager, prof)
|
|
||||||
.setPreferred(mCachedDevice.getDevice(),
|
|
||||||
autoConnect);
|
autoConnect);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -215,22 +217,46 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onProfileClicked(Profile profile) {
|
private void onProfileClicked(LocalBluetoothProfile profile) {
|
||||||
BluetoothDevice device = mCachedDevice.getDevice();
|
BluetoothDevice device = mCachedDevice.getDevice();
|
||||||
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
|
|
||||||
.getProfileManager(mManager, profile);
|
|
||||||
|
|
||||||
int status = profileManager.getConnectionStatus(device);
|
int status = profile.getConnectionStatus(device);
|
||||||
boolean isConnected =
|
boolean isConnected =
|
||||||
SettingsBtStatus.isConnectionStatusConnected(status);
|
status == BluetoothProfile.STATE_CONNECTED;
|
||||||
|
|
||||||
if (isConnected) {
|
if (isConnected) {
|
||||||
mCachedDevice.askDisconnect(profile);
|
askDisconnect(getActivity(), profile);
|
||||||
} else {
|
} else {
|
||||||
mCachedDevice.connect(profile);
|
mCachedDevice.connectProfile(profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void askDisconnect(Context context,
|
||||||
|
final LocalBluetoothProfile profile) {
|
||||||
|
// local reference for callback
|
||||||
|
final CachedBluetoothDevice device = mCachedDevice;
|
||||||
|
String name = device.getName();
|
||||||
|
if (TextUtils.isEmpty(name)) {
|
||||||
|
name = context.getString(R.string.bluetooth_device);
|
||||||
|
}
|
||||||
|
int disconnectMessage = profile.getDisconnectResource();
|
||||||
|
if (disconnectMessage == 0) {
|
||||||
|
Log.w(TAG, "askDisconnect: unexpected profile " + profile);
|
||||||
|
disconnectMessage = R.string.bluetooth_disconnect_blank;
|
||||||
|
}
|
||||||
|
String message = context.getString(disconnectMessage, name);
|
||||||
|
|
||||||
|
DialogInterface.OnClickListener disconnectListener =
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
device.disconnect(profile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mDisconnectDialog = Utils.showDisconnectDialog(context,
|
||||||
|
mDisconnectDialog, disconnectListener, name, message);
|
||||||
|
}
|
||||||
|
|
||||||
public void onDeviceAttributesChanged() {
|
public void onDeviceAttributesChanged() {
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
@@ -242,7 +268,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
// transaction.setBreadCrumbTitle(deviceName);
|
// transaction.setBreadCrumbTitle(deviceName);
|
||||||
// transaction.commit();
|
// transaction.commit();
|
||||||
|
|
||||||
findPreference(KEY_TITLE).setTitle(getResources().getString(
|
findPreference(KEY_TITLE).setTitle(getString(
|
||||||
R.string.bluetooth_device_advanced_title,
|
R.string.bluetooth_device_advanced_title,
|
||||||
deviceName));
|
deviceName));
|
||||||
mDeviceNamePref = (EditTextPreference) findPreference(KEY_RENAME_DEVICE);
|
mDeviceNamePref = (EditTextPreference) findPreference(KEY_RENAME_DEVICE);
|
||||||
@@ -253,7 +279,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void refreshProfiles() {
|
private void refreshProfiles() {
|
||||||
for (Profile profile : mCachedDevice.getConnectableProfiles()) {
|
for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
|
||||||
Preference profilePref = findPreference(profile.toString());
|
Preference profilePref = findPreference(profile.toString());
|
||||||
if (profilePref == null) {
|
if (profilePref == null) {
|
||||||
profilePref = createProfilePreference(profile);
|
profilePref = createProfilePreference(profile);
|
||||||
@@ -264,78 +290,43 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshProfilePreference(Preference profilePref, Profile profile) {
|
private void refreshProfilePreference(Preference profilePref, LocalBluetoothProfile profile) {
|
||||||
BluetoothDevice device = mCachedDevice.getDevice();
|
BluetoothDevice device = mCachedDevice.getDevice();
|
||||||
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
|
|
||||||
.getProfileManager(mManager, profile);
|
|
||||||
|
|
||||||
int connectionStatus = profileManager.getConnectionStatus(device);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gray out checkbox while connecting and disconnecting
|
* Gray out checkbox while connecting and disconnecting
|
||||||
*/
|
*/
|
||||||
profilePref.setEnabled(!mCachedDevice.isBusy());
|
profilePref.setEnabled(!mCachedDevice.isBusy());
|
||||||
profilePref.setSummary(getProfileSummary(profileManager, profile, device,
|
profilePref.setSummary(profile.getSummaryResourceForDevice(device));
|
||||||
connectionStatus, isDeviceOnline()));
|
|
||||||
// TODO:
|
|
||||||
//profilePref.setChecked(profileManager.isPreferred(device));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Profile getProfileOf(Preference pref) {
|
private LocalBluetoothProfile getProfileOf(Preference pref) {
|
||||||
if (!(pref instanceof CheckBoxPreference)) return null;
|
if (!(pref instanceof CheckBoxPreference)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
String key = pref.getKey();
|
String key = pref.getKey();
|
||||||
if (TextUtils.isEmpty(key)) return null;
|
if (TextUtils.isEmpty(key)) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return Profile.valueOf(pref.getKey());
|
return mProfileManager.getProfileByName(pref.getKey());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException ignored) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int getProfileSummary(LocalBluetoothProfileManager profileManager,
|
|
||||||
Profile profile, BluetoothDevice device, int connectionStatus, boolean onlineMode) {
|
|
||||||
if (!onlineMode || connectionStatus == SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED) {
|
|
||||||
return getProfileSummaryForSettingPreference(profile);
|
|
||||||
} else {
|
|
||||||
return profileManager.getSummary(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the summary that describes when checked, it will become a preferred profile.
|
|
||||||
*
|
|
||||||
* @param profile The profile to get the summary for.
|
|
||||||
* @return The summary.
|
|
||||||
*/
|
|
||||||
private static final int getProfileSummaryForSettingPreference(Profile profile) {
|
|
||||||
switch (profile) {
|
|
||||||
case A2DP:
|
|
||||||
return R.string.bluetooth_a2dp_profile_summary_use_for;
|
|
||||||
case HEADSET:
|
|
||||||
return R.string.bluetooth_headset_profile_summary_use_for;
|
|
||||||
case HID:
|
|
||||||
return R.string.bluetooth_hid_profile_summary_use_for;
|
|
||||||
case PAN:
|
|
||||||
return R.string.bluetooth_pan_profile_summary_use_for;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (v.getTag() instanceof Profile) {
|
if (v.getTag() instanceof LocalBluetoothProfile) {
|
||||||
Profile prof = (Profile) v.getTag();
|
LocalBluetoothProfile prof = (LocalBluetoothProfile) v.getTag();
|
||||||
CheckBoxPreference autoConnectPref = mAutoConnectPrefs.get(prof.toString());
|
CheckBoxPreference autoConnectPref = mAutoConnectPrefs.get(prof);
|
||||||
if (autoConnectPref == null) {
|
if (autoConnectPref == null) {
|
||||||
autoConnectPref = new CheckBoxPreference(getActivity());
|
autoConnectPref = new CheckBoxPreference(getActivity());
|
||||||
autoConnectPref.setLayoutResource(com.android.internal.R.layout.preference_child);
|
autoConnectPref.setLayoutResource(com.android.internal.R.layout.preference_child);
|
||||||
autoConnectPref.setKey(prof.toString());
|
autoConnectPref.setKey(prof.toString());
|
||||||
autoConnectPref.setTitle(R.string.bluetooth_auto_connect);
|
autoConnectPref.setTitle(R.string.bluetooth_auto_connect);
|
||||||
autoConnectPref.setOrder(getProfilePreferenceIndex(prof) + 1);
|
autoConnectPref.setOrder(getProfilePreferenceIndex(prof.getOrdinal()) + 1);
|
||||||
autoConnectPref.setChecked(getAutoConnect(prof));
|
autoConnectPref.setChecked(getAutoConnect(prof));
|
||||||
autoConnectPref.setOnPreferenceChangeListener(this);
|
autoConnectPref.setOnPreferenceChangeListener(this);
|
||||||
mAutoConnectPrefs.put(prof.name(), autoConnectPref);
|
mAutoConnectPrefs.put(prof, autoConnectPref);
|
||||||
}
|
}
|
||||||
BluetoothProfilePreference profilePref =
|
BluetoothProfilePreference profilePref =
|
||||||
(BluetoothProfilePreference) findPreference(prof.toString());
|
(BluetoothProfilePreference) findPreference(prof.toString());
|
||||||
@@ -349,19 +340,14 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getProfilePreferenceIndex(Profile prof) {
|
private int getProfilePreferenceIndex(int profIndex) {
|
||||||
return mProfileContainer.getOrder() + prof.ordinal() * 10;
|
return mProfileContainer.getOrder() + profIndex * 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unpairDevice() {
|
private void unpairDevice() {
|
||||||
mCachedDevice.unpair();
|
mCachedDevice.unpair();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDeviceOnline() {
|
|
||||||
// TODO: Verify
|
|
||||||
return mCachedDevice.isConnected() || mCachedDevice.isBusy();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setIncomingFileTransfersAllowed(boolean allow) {
|
private void setIncomingFileTransfersAllowed(boolean allow) {
|
||||||
// TODO: make an IPC call into BluetoothOpp to update
|
// TODO: make an IPC call into BluetoothOpp to update
|
||||||
Log.d(TAG, "Set allow incoming = " + allow);
|
Log.d(TAG, "Set allow incoming = " + allow);
|
||||||
@@ -372,8 +358,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean getAutoConnect(Profile prof) {
|
private boolean getAutoConnect(LocalBluetoothProfile prof) {
|
||||||
return LocalBluetoothProfileManager.getProfileManager(mManager, prof)
|
return prof.isPreferred(mCachedDevice.getDevice());
|
||||||
.isPreferred(mCachedDevice.getDevice());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@ import android.content.Intent;
|
|||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class DockEventReceiver extends BroadcastReceiver {
|
public final class DockEventReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
private static final boolean DEBUG = DockService.DEBUG;
|
private static final boolean DEBUG = DockService.DEBUG;
|
||||||
|
|
||||||
@@ -39,11 +39,9 @@ public class DockEventReceiver extends BroadcastReceiver {
|
|||||||
|
|
||||||
private static final int EXTRA_INVALID = -1234;
|
private static final int EXTRA_INVALID = -1234;
|
||||||
|
|
||||||
private static final Object mStartingServiceSync = new Object();
|
private static final Object sStartingServiceSync = new Object();
|
||||||
|
|
||||||
private static final long WAKELOCK_TIMEOUT = 5000;
|
private static PowerManager.WakeLock sStartingService;
|
||||||
|
|
||||||
private static PowerManager.WakeLock mStartingService;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
@@ -75,7 +73,7 @@ public class DockEventReceiver extends BroadcastReceiver {
|
|||||||
beginStartingService(context, i);
|
beginStartingService(context, i);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (DEBUG) Log.e(TAG, "Unknown state");
|
Log.e(TAG, "Unknown state: " + state);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction()) ||
|
} else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction()) ||
|
||||||
@@ -118,15 +116,15 @@ public class DockEventReceiver extends BroadcastReceiver {
|
|||||||
* Start the service to process the current event notifications, acquiring
|
* Start the service to process the current event notifications, acquiring
|
||||||
* the wake lock before returning to ensure that the service will run.
|
* the wake lock before returning to ensure that the service will run.
|
||||||
*/
|
*/
|
||||||
public static void beginStartingService(Context context, Intent intent) {
|
private static void beginStartingService(Context context, Intent intent) {
|
||||||
synchronized (mStartingServiceSync) {
|
synchronized (sStartingServiceSync) {
|
||||||
if (mStartingService == null) {
|
if (sStartingService == null) {
|
||||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||||
mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
sStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
|
||||||
"StartingDockService");
|
"StartingDockService");
|
||||||
}
|
}
|
||||||
|
|
||||||
mStartingService.acquire(WAKELOCK_TIMEOUT);
|
sStartingService.acquire();
|
||||||
|
|
||||||
if (context.startService(intent) == null) {
|
if (context.startService(intent) == null) {
|
||||||
Log.e(TAG, "Can't start DockService");
|
Log.e(TAG, "Can't start DockService");
|
||||||
@@ -139,10 +137,13 @@ public class DockEventReceiver extends BroadcastReceiver {
|
|||||||
* releasing the wake lock if the service is now stopping.
|
* releasing the wake lock if the service is now stopping.
|
||||||
*/
|
*/
|
||||||
public static void finishStartingService(Service service, int startId) {
|
public static void finishStartingService(Service service, int startId) {
|
||||||
synchronized (mStartingServiceSync) {
|
synchronized (sStartingServiceSync) {
|
||||||
if (mStartingService != null) {
|
if (sStartingService != null) {
|
||||||
if (DEBUG) Log.d(TAG, "stopSelf id = "+ startId);
|
if (DEBUG) Log.d(TAG, "stopSelf id = " + startId);
|
||||||
service.stopSelfResult(startId);
|
if (service.stopSelfResult(startId)) {
|
||||||
|
Log.d(TAG, "finishStartingService: stopping service");
|
||||||
|
sStartingService.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,7 +17,6 @@
|
|||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
|
|
||||||
import com.android.settings.bluetooth.LocalBluetoothProfileManager.ServiceListener;
|
import com.android.settings.bluetooth.LocalBluetoothProfileManager.ServiceListener;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
@@ -27,7 +26,7 @@ import android.bluetooth.BluetoothA2dp;
|
|||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothHeadset;
|
import android.bluetooth.BluetoothHeadset;
|
||||||
import android.content.Context;
|
import android.bluetooth.BluetoothProfile;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
@@ -44,12 +43,11 @@ import android.view.WindowManager;
|
|||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class DockService extends Service implements AlertDialog.OnMultiChoiceClickListener,
|
public final class DockService extends Service implements ServiceListener {
|
||||||
DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
|
|
||||||
CompoundButton.OnCheckedChangeListener, ServiceListener {
|
|
||||||
|
|
||||||
private static final String TAG = "DockService";
|
private static final String TAG = "DockService";
|
||||||
|
|
||||||
@@ -82,14 +80,11 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
|
|
||||||
private static final String SHARED_PREFERENCES_NAME = "dock_settings";
|
private static final String SHARED_PREFERENCES_NAME = "dock_settings";
|
||||||
|
|
||||||
private static final String SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED =
|
private static final String KEY_DISABLE_BT_WHEN_UNDOCKED = "disable_bt_when_undock";
|
||||||
"disable_bt_when_undock";
|
|
||||||
|
|
||||||
private static final String SHARED_PREFERENCES_KEY_DISABLE_BT =
|
private static final String KEY_DISABLE_BT = "disable_bt";
|
||||||
"disable_bt";
|
|
||||||
|
|
||||||
private static final String SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT =
|
private static final String KEY_CONNECT_RETRY_COUNT = "connect_retry_count";
|
||||||
"connect_retry_count";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If disconnected unexpectedly, reconnect up to 6 times. Each profile counts
|
* If disconnected unexpectedly, reconnect up to 6 times. Each profile counts
|
||||||
@@ -103,8 +98,9 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
private volatile Looper mServiceLooper;
|
private volatile Looper mServiceLooper;
|
||||||
private volatile ServiceHandler mServiceHandler;
|
private volatile ServiceHandler mServiceHandler;
|
||||||
private Runnable mRunnable;
|
private Runnable mRunnable;
|
||||||
private DockService mContext;
|
private LocalBluetoothAdapter mLocalAdapter;
|
||||||
private LocalBluetoothManager mBtManager;
|
private CachedBluetoothDeviceManager mDeviceManager;
|
||||||
|
private LocalBluetoothProfileManager mProfileManager;
|
||||||
|
|
||||||
// Normally set after getting a docked event and unset when the connection
|
// Normally set after getting a docked event and unset when the connection
|
||||||
// is severed. One exception is that mDevice could be null if the service
|
// is severed. One exception is that mDevice could be null if the service
|
||||||
@@ -113,7 +109,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
|
|
||||||
// Created and used for the duration of the dialog
|
// Created and used for the duration of the dialog
|
||||||
private AlertDialog mDialog;
|
private AlertDialog mDialog;
|
||||||
private Profile[] mProfiles;
|
private LocalBluetoothProfile[] mProfiles;
|
||||||
private boolean[] mCheckedItems;
|
private boolean[] mCheckedItems;
|
||||||
private int mStartIdAssociatedWithDialog;
|
private int mStartIdAssociatedWithDialog;
|
||||||
|
|
||||||
@@ -127,8 +123,19 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
if (DEBUG) Log.d(TAG, "onCreate");
|
if (DEBUG) Log.d(TAG, "onCreate");
|
||||||
|
|
||||||
mBtManager = LocalBluetoothManager.getInstance(this);
|
LocalBluetoothManager manager = LocalBluetoothManager.getInstance(this);
|
||||||
mContext = this;
|
if (manager == null) {
|
||||||
|
Log.e(TAG, "Can't get LocalBluetoothManager: exiting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLocalAdapter = manager.getBluetoothAdapter();
|
||||||
|
mDeviceManager = manager.getCachedDeviceManager();
|
||||||
|
mProfileManager = manager.getProfileManager();
|
||||||
|
if (mProfileManager == null) {
|
||||||
|
Log.e(TAG, "Can't get LocalBluetoothProfileManager: exiting");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
HandlerThread thread = new HandlerThread("DockService");
|
HandlerThread thread = new HandlerThread("DockService");
|
||||||
thread.start();
|
thread.start();
|
||||||
@@ -141,23 +148,37 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
if (DEBUG) Log.d(TAG, "onDestroy");
|
if (DEBUG) Log.d(TAG, "onDestroy");
|
||||||
mRunnable = null;
|
mRunnable = null;
|
||||||
LocalBluetoothProfileManager.removeServiceListener(this);
|
|
||||||
if (mDialog != null) {
|
if (mDialog != null) {
|
||||||
mDialog.dismiss();
|
mDialog.dismiss();
|
||||||
mDialog = null;
|
mDialog = null;
|
||||||
}
|
}
|
||||||
|
if (mProfileManager != null) {
|
||||||
|
mProfileManager.removeServiceListener(this);
|
||||||
|
}
|
||||||
|
if (mServiceLooper != null) {
|
||||||
mServiceLooper.quit();
|
mServiceLooper.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLocalAdapter = null;
|
||||||
|
mDeviceManager = null;
|
||||||
|
mProfileManager = null;
|
||||||
|
mServiceLooper = null;
|
||||||
|
mServiceHandler = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind(Intent intent) {
|
||||||
// not supported
|
// not supported
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SharedPreferences getPrefs() {
|
||||||
|
return getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
if (DEBUG) Log.d(TAG, "onStartCommand startId:" + startId + " flags: " + flags);
|
if (DEBUG) Log.d(TAG, "onStartCommand startId: " + startId + " flags: " + flags);
|
||||||
|
|
||||||
if (intent == null) {
|
if (intent == null) {
|
||||||
// Nothing to process, stop.
|
// Nothing to process, stop.
|
||||||
@@ -178,24 +199,24 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
* This assumes that the intent sender has checked that this is a dock
|
* This assumes that the intent sender has checked that this is a dock
|
||||||
* and that the intent is for a disconnect
|
* and that the intent is for a disconnect
|
||||||
*/
|
*/
|
||||||
|
final SharedPreferences prefs = getPrefs();
|
||||||
if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
|
if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
|
||||||
BluetoothDevice disconnectedDevice = intent
|
BluetoothDevice disconnectedDevice = intent
|
||||||
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
int retryCount = prefs.getInt(KEY_CONNECT_RETRY_COUNT, 0);
|
||||||
int retryCount = getSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, 0);
|
|
||||||
if (retryCount < MAX_CONNECT_RETRY) {
|
if (retryCount < MAX_CONNECT_RETRY) {
|
||||||
setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
|
prefs.edit().putInt(KEY_CONNECT_RETRY_COUNT, retryCount + 1).apply();
|
||||||
handleUnexpectedDisconnect(disconnectedDevice, Profile.HEADSET, startId);
|
handleUnexpectedDisconnect(disconnectedDevice, mProfileManager.getHeadsetProfile(), startId);
|
||||||
}
|
}
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
} else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
|
} else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
|
||||||
BluetoothDevice disconnectedDevice = intent
|
BluetoothDevice disconnectedDevice = intent
|
||||||
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
|
|
||||||
int retryCount = getSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, 0);
|
int retryCount = prefs.getInt(KEY_CONNECT_RETRY_COUNT, 0);
|
||||||
if (retryCount < MAX_CONNECT_RETRY) {
|
if (retryCount < MAX_CONNECT_RETRY) {
|
||||||
setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
|
prefs.edit().putInt(KEY_CONNECT_RETRY_COUNT, retryCount + 1).apply();
|
||||||
handleUnexpectedDisconnect(disconnectedDevice, Profile.A2DP, startId);
|
handleUnexpectedDisconnect(disconnectedDevice, mProfileManager.getA2dpProfile(), startId);
|
||||||
}
|
}
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
@@ -209,7 +230,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (msg.what == MSG_TYPE_DOCKED) {
|
if (msg.what == MSG_TYPE_DOCKED) {
|
||||||
removeSetting(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT);
|
prefs.edit().remove(KEY_CONNECT_RETRY_COUNT).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.arg2 = startId;
|
msg.arg2 = startId;
|
||||||
@@ -219,7 +240,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final class ServiceHandler extends Handler {
|
private final class ServiceHandler extends Handler {
|
||||||
public ServiceHandler(Looper looper) {
|
private ServiceHandler(Looper looper) {
|
||||||
super(looper);
|
super(looper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +255,6 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
int msgType = msg.what;
|
int msgType = msg.what;
|
||||||
final int state = msg.arg1;
|
final int state = msg.arg1;
|
||||||
final int startId = msg.arg2;
|
final int startId = msg.arg2;
|
||||||
boolean deferFinishCall = false;
|
|
||||||
BluetoothDevice device = null;
|
BluetoothDevice device = null;
|
||||||
if (msg.obj != null) {
|
if (msg.obj != null) {
|
||||||
device = (BluetoothDevice) msg.obj;
|
device = (BluetoothDevice) msg.obj;
|
||||||
@@ -243,18 +263,97 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
if(DEBUG) Log.d(TAG, "processMessage: " + msgType + " state: " + state + " device = "
|
if(DEBUG) Log.d(TAG, "processMessage: " + msgType + " state: " + state + " device = "
|
||||||
+ (device == null ? "null" : device.toString()));
|
+ (device == null ? "null" : device.toString()));
|
||||||
|
|
||||||
|
boolean deferFinishCall = false;
|
||||||
|
|
||||||
switch (msgType) {
|
switch (msgType) {
|
||||||
case MSG_TYPE_SHOW_UI:
|
case MSG_TYPE_SHOW_UI:
|
||||||
if (mDialog != null) {
|
createDialog(device, state, startId);
|
||||||
// Shouldn't normally happen
|
|
||||||
mDialog.dismiss();
|
|
||||||
mDialog = null;
|
|
||||||
}
|
|
||||||
mDevice = device;
|
|
||||||
createDialog(mContext, mDevice, state, startId);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MSG_TYPE_DOCKED:
|
case MSG_TYPE_DOCKED:
|
||||||
|
deferFinishCall = msgTypeDocked(device, state, startId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_TYPE_UNDOCKED_PERMANENT:
|
||||||
|
deferFinishCall = msgTypeUndockedPermanent(device, startId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_TYPE_UNDOCKED_TEMPORARY:
|
||||||
|
msgTypeUndockedTemporary(device, state, startId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_TYPE_DISABLE_BT:
|
||||||
|
deferFinishCall = msgTypeDisableBluetooth(startId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDialog == null && mPendingDevice == null && msgType != MSG_TYPE_UNDOCKED_TEMPORARY
|
||||||
|
&& !deferFinishCall) {
|
||||||
|
// NOTE: We MUST not call stopSelf() directly, since we need to
|
||||||
|
// make sure the wake lock acquired by the Receiver is released.
|
||||||
|
DockEventReceiver.finishStartingService(this, startId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean msgTypeDisableBluetooth(int startId) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "BT DISABLE");
|
||||||
|
}
|
||||||
|
final SharedPreferences prefs = getPrefs();
|
||||||
|
if (mLocalAdapter.disable()) {
|
||||||
|
prefs.edit().remove(KEY_DISABLE_BT_WHEN_UNDOCKED).apply();
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// disable() returned an error. Persist a flag to disable BT later
|
||||||
|
prefs.edit().putBoolean(KEY_DISABLE_BT, true).apply();
|
||||||
|
mPendingTurnOffStartId = startId;
|
||||||
|
if(DEBUG) {
|
||||||
|
Log.d(TAG, "disable failed. try again later " + startId);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void msgTypeUndockedTemporary(BluetoothDevice device, int state,
|
||||||
|
int startId) {
|
||||||
|
// Undocked event received. Queue a delayed msg to sever connection
|
||||||
|
Message newMsg = mServiceHandler.obtainMessage(MSG_TYPE_UNDOCKED_PERMANENT, state,
|
||||||
|
startId, device);
|
||||||
|
mServiceHandler.sendMessageDelayed(newMsg, UNDOCKED_GRACE_PERIOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean msgTypeUndockedPermanent(BluetoothDevice device, int startId) {
|
||||||
|
// Grace period passed. Disconnect.
|
||||||
|
handleUndocked(device);
|
||||||
|
final SharedPreferences prefs = getPrefs();
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "DISABLE_BT_WHEN_UNDOCKED = "
|
||||||
|
+ prefs.getBoolean(KEY_DISABLE_BT_WHEN_UNDOCKED, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefs.getBoolean(KEY_DISABLE_BT_WHEN_UNDOCKED, false)) {
|
||||||
|
if (hasOtherConnectedDevices(device)) {
|
||||||
|
// Don't disable BT if something is connected
|
||||||
|
prefs.edit().remove(KEY_DISABLE_BT_WHEN_UNDOCKED).apply();
|
||||||
|
} else {
|
||||||
|
// BT was disabled when we first docked
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "QUEUED BT DISABLE");
|
||||||
|
}
|
||||||
|
// Queue a delayed msg to disable BT
|
||||||
|
Message newMsg = mServiceHandler.obtainMessage(
|
||||||
|
MSG_TYPE_DISABLE_BT, 0, startId, null);
|
||||||
|
mServiceHandler.sendMessageDelayed(newMsg,
|
||||||
|
DISABLE_BT_GRACE_PERIOD);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean msgTypeDocked(BluetoothDevice device, final int state,
|
||||||
|
final int startId) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
// TODO figure out why hasMsg always returns false if device
|
// TODO figure out why hasMsg always returns false if device
|
||||||
// is supplied
|
// is supplied
|
||||||
@@ -266,12 +365,12 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
|
|
||||||
mServiceHandler.removeMessages(MSG_TYPE_UNDOCKED_PERMANENT);
|
mServiceHandler.removeMessages(MSG_TYPE_UNDOCKED_PERMANENT);
|
||||||
mServiceHandler.removeMessages(MSG_TYPE_DISABLE_BT);
|
mServiceHandler.removeMessages(MSG_TYPE_DISABLE_BT);
|
||||||
removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
|
getPrefs().edit().remove(KEY_DISABLE_BT).apply();
|
||||||
|
|
||||||
if (!device.equals(mDevice)) {
|
if (device != null && !device.equals(mDevice)) {
|
||||||
if (mDevice != null) {
|
if (mDevice != null) {
|
||||||
// Not expected. Cleanup/undock existing
|
// Not expected. Cleanup/undock existing
|
||||||
handleUndocked(mContext, mBtManager, mDevice);
|
handleUndocked(mDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
mDevice = device;
|
mDevice = device;
|
||||||
@@ -279,91 +378,40 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
// Register first in case LocalBluetoothProfileManager
|
// Register first in case LocalBluetoothProfileManager
|
||||||
// becomes ready after isManagerReady is called and it
|
// becomes ready after isManagerReady is called and it
|
||||||
// would be too late to register a service listener.
|
// would be too late to register a service listener.
|
||||||
LocalBluetoothProfileManager.addServiceListener(this);
|
mProfileManager.addServiceListener(this);
|
||||||
if (LocalBluetoothProfileManager.isManagerReady()) {
|
if (mProfileManager.isManagerReady()) {
|
||||||
handleDocked(device, state, startId);
|
handleDocked(device, state, startId);
|
||||||
// Not needed after all
|
// Not needed after all
|
||||||
LocalBluetoothProfileManager.removeServiceListener(this);
|
mProfileManager.removeServiceListener(this);
|
||||||
} else {
|
} else {
|
||||||
final BluetoothDevice d = device;
|
final BluetoothDevice d = device;
|
||||||
mRunnable = new Runnable() {
|
mRunnable = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
handleDocked(d, state, startId);
|
handleDocked(d, state, startId); // FIXME: WTF runnable here?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
deferFinishCall = true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case MSG_TYPE_UNDOCKED_PERMANENT:
|
|
||||||
// Grace period passed. Disconnect.
|
|
||||||
handleUndocked(mContext, mBtManager, device);
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "DISABLE_BT_WHEN_UNDOCKED = "
|
|
||||||
+ getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED)) {
|
|
||||||
// BT was disabled when we first docked
|
|
||||||
if (!hasOtherConnectedDevices(device)) {
|
|
||||||
if(DEBUG) Log.d(TAG, "QUEUED BT DISABLE");
|
|
||||||
// Queue a delayed msg to disable BT
|
|
||||||
Message newMsg = mServiceHandler.obtainMessage(MSG_TYPE_DISABLE_BT, 0,
|
|
||||||
startId, null);
|
|
||||||
mServiceHandler.sendMessageDelayed(newMsg, DISABLE_BT_GRACE_PERIOD);
|
|
||||||
deferFinishCall = true;
|
|
||||||
} else {
|
|
||||||
// Don't disable BT if something is connected
|
|
||||||
removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MSG_TYPE_UNDOCKED_TEMPORARY:
|
|
||||||
// Undocked event received. Queue a delayed msg to sever connection
|
|
||||||
Message newMsg = mServiceHandler.obtainMessage(MSG_TYPE_UNDOCKED_PERMANENT, state,
|
|
||||||
startId, device);
|
|
||||||
mServiceHandler.sendMessageDelayed(newMsg, UNDOCKED_GRACE_PERIOD);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MSG_TYPE_DISABLE_BT:
|
|
||||||
if(DEBUG) Log.d(TAG, "BT DISABLE");
|
|
||||||
if (mBtManager.getBluetoothAdapter().disable()) {
|
|
||||||
removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED);
|
|
||||||
} else {
|
|
||||||
// disable() returned an error. Persist a flag to disable BT later
|
|
||||||
setSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT, true);
|
|
||||||
mPendingTurnOffStartId = startId;
|
|
||||||
deferFinishCall = true;
|
|
||||||
if(DEBUG) Log.d(TAG, "disable failed. try again later " + startId);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mDialog == null && mPendingDevice == null && msgType != MSG_TYPE_UNDOCKED_TEMPORARY
|
|
||||||
&& !deferFinishCall) {
|
|
||||||
// NOTE: We MUST not call stopSelf() directly, since we need to
|
|
||||||
// make sure the wake lock acquired by the Receiver is released.
|
|
||||||
DockEventReceiver.finishStartingService(DockService.this, startId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized boolean hasOtherConnectedDevices(BluetoothDevice dock) {
|
|
||||||
List<CachedBluetoothDevice> cachedDevices = mBtManager.getCachedDeviceManager()
|
|
||||||
.getCachedDevicesCopy();
|
|
||||||
Set<BluetoothDevice> btDevices = mBtManager.getBluetoothAdapter().getBondedDevices();
|
|
||||||
if (btDevices == null || cachedDevices == null || btDevices.size() == 0) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(DEBUG) Log.d(TAG, "btDevices = " + btDevices.size());
|
|
||||||
if(DEBUG) Log.d(TAG, "cachedDevices = " + cachedDevices.size());
|
|
||||||
|
|
||||||
for (CachedBluetoothDevice device : cachedDevices) {
|
synchronized boolean hasOtherConnectedDevices(BluetoothDevice dock) {
|
||||||
BluetoothDevice btDevice = device.getDevice();
|
Collection<CachedBluetoothDevice> cachedDevices = mDeviceManager.getCachedDevicesCopy();
|
||||||
if (!btDevice.equals(dock) && btDevices.contains(btDevice) && device.isConnected()) {
|
Set<BluetoothDevice> btDevices = mLocalAdapter.getBondedDevices();
|
||||||
if(DEBUG) Log.d(TAG, "connected device = " + device.getName());
|
if (btDevices == null || cachedDevices == null || btDevices.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(DEBUG) {
|
||||||
|
Log.d(TAG, "btDevices = " + btDevices.size());
|
||||||
|
Log.d(TAG, "cachedDeviceUIs = " + cachedDevices.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (CachedBluetoothDevice deviceUI : cachedDevices) {
|
||||||
|
BluetoothDevice btDevice = deviceUI.getDevice();
|
||||||
|
if (!btDevice.equals(dock) && btDevices.contains(btDevice) && deviceUI
|
||||||
|
.isConnected()) {
|
||||||
|
if(DEBUG) Log.d(TAG, "connected deviceUI = " + deviceUI.getName());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -404,96 +452,128 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
return mServiceHandler.obtainMessage(msgType, state, 0, device);
|
return mServiceHandler.obtainMessage(msgType, state, 0, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean createDialog(DockService service, BluetoothDevice device, int state,
|
private void createDialog(BluetoothDevice device,
|
||||||
int startId) {
|
int state, int startId) {
|
||||||
|
if (mDialog != null) {
|
||||||
|
// Shouldn't normally happen
|
||||||
|
mDialog.dismiss();
|
||||||
|
mDialog = null;
|
||||||
|
}
|
||||||
|
mDevice = device;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Intent.EXTRA_DOCK_STATE_CAR:
|
case Intent.EXTRA_DOCK_STATE_CAR:
|
||||||
case Intent.EXTRA_DOCK_STATE_DESK:
|
case Intent.EXTRA_DOCK_STATE_DESK:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
startForeground(0, new Notification());
|
startForeground(0, new Notification());
|
||||||
|
|
||||||
// Device in a new dock.
|
// Device in a new dock.
|
||||||
boolean firstTime = !mBtManager.hasDockAutoConnectSetting(device.getAddress());
|
boolean firstTime = !LocalBluetoothPreferences.hasDockAutoConnectSetting(this, device.getAddress());
|
||||||
|
|
||||||
CharSequence[] items = initBtSettings(service, device, state, firstTime);
|
CharSequence[] items = initBtSettings(device, state, firstTime);
|
||||||
|
|
||||||
final AlertDialog.Builder ab = new AlertDialog.Builder(service);
|
final AlertDialog.Builder ab = new AlertDialog.Builder(this);
|
||||||
ab.setTitle(service.getString(R.string.bluetooth_dock_settings_title));
|
ab.setTitle(getString(R.string.bluetooth_dock_settings_title));
|
||||||
|
|
||||||
// Profiles
|
// Profiles
|
||||||
ab.setMultiChoiceItems(items, mCheckedItems, service);
|
ab.setMultiChoiceItems(items, mCheckedItems, mMultiClickListener);
|
||||||
|
|
||||||
// Remember this settings
|
// Remember this settings
|
||||||
LayoutInflater inflater = (LayoutInflater) service
|
LayoutInflater inflater = (LayoutInflater)
|
||||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
getSystemService(LAYOUT_INFLATER_SERVICE);
|
||||||
float pixelScaleFactor = service.getResources().getDisplayMetrics().density;
|
float pixelScaleFactor = getResources().getDisplayMetrics().density;
|
||||||
View view = inflater.inflate(R.layout.remember_dock_setting, null);
|
View view = inflater.inflate(R.layout.remember_dock_setting, null);
|
||||||
CheckBox rememberCheckbox = (CheckBox) view.findViewById(R.id.remember);
|
CheckBox rememberCheckbox = (CheckBox) view.findViewById(R.id.remember);
|
||||||
|
|
||||||
// check "Remember setting" by default if no value was saved
|
// check "Remember setting" by default if no value was saved
|
||||||
boolean checked = firstTime || mBtManager.getDockAutoConnectSetting(device.getAddress());
|
boolean checked = firstTime || LocalBluetoothPreferences.getDockAutoConnectSetting(this, device.getAddress());
|
||||||
rememberCheckbox.setChecked(checked);
|
rememberCheckbox.setChecked(checked);
|
||||||
rememberCheckbox.setOnCheckedChangeListener(this);
|
rememberCheckbox.setOnCheckedChangeListener(mCheckedChangeListener);
|
||||||
int viewSpacingLeft = (int) (14 * pixelScaleFactor);
|
int viewSpacingLeft = (int) (14 * pixelScaleFactor);
|
||||||
int viewSpacingRight = (int) (14 * pixelScaleFactor);
|
int viewSpacingRight = (int) (14 * pixelScaleFactor);
|
||||||
ab.setView(view, viewSpacingLeft, 0 /* top */, viewSpacingRight, 0 /* bottom */);
|
ab.setView(view, viewSpacingLeft, 0 /* top */, viewSpacingRight, 0 /* bottom */);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Auto connect = "
|
Log.d(TAG, "Auto connect = "
|
||||||
+ mBtManager.getDockAutoConnectSetting(device.getAddress()));
|
+ LocalBluetoothPreferences.getDockAutoConnectSetting(this, device.getAddress()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok Button
|
// Ok Button
|
||||||
ab.setPositiveButton(service.getString(android.R.string.ok), service);
|
ab.setPositiveButton(getString(android.R.string.ok), mClickListener);
|
||||||
|
|
||||||
mStartIdAssociatedWithDialog = startId;
|
mStartIdAssociatedWithDialog = startId;
|
||||||
mDialog = ab.create();
|
mDialog = ab.create();
|
||||||
mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
|
mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
|
||||||
mDialog.setOnDismissListener(service);
|
mDialog.setOnDismissListener(mDismissListener);
|
||||||
mDialog.show();
|
mDialog.show();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the individual bt profiles are clicked.
|
// Called when the individual bt profiles are clicked.
|
||||||
|
private final DialogInterface.OnMultiChoiceClickListener mMultiClickListener =
|
||||||
|
new DialogInterface.OnMultiChoiceClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
|
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
|
||||||
if (DEBUG) Log.d(TAG, "Item " + which + " changed to " + isChecked);
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "Item " + which + " changed to " + isChecked);
|
||||||
|
}
|
||||||
mCheckedItems[which] = isChecked;
|
mCheckedItems[which] = isChecked;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Called when the "Remember" Checkbox is clicked
|
// Called when the "Remember" Checkbox is clicked
|
||||||
|
private final CompoundButton.OnCheckedChangeListener mCheckedChangeListener =
|
||||||
|
new CompoundButton.OnCheckedChangeListener() {
|
||||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
if (DEBUG) Log.d(TAG, "onCheckedChanged: Remember Settings = " + isChecked);
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "onCheckedChanged: Remember Settings = " + isChecked);
|
||||||
|
}
|
||||||
if (mDevice != null) {
|
if (mDevice != null) {
|
||||||
mBtManager.saveDockAutoConnectSetting(mDevice.getAddress(), isChecked);
|
LocalBluetoothPreferences.saveDockAutoConnectSetting(
|
||||||
|
DockService.this, mDevice.getAddress(), isChecked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Called when the dialog is dismissed
|
// Called when the dialog is dismissed
|
||||||
|
private final DialogInterface.OnDismissListener mDismissListener =
|
||||||
|
new DialogInterface.OnDismissListener() {
|
||||||
public void onDismiss(DialogInterface dialog) {
|
public void onDismiss(DialogInterface dialog) {
|
||||||
// NOTE: We MUST not call stopSelf() directly, since we need to
|
// NOTE: We MUST not call stopSelf() directly, since we need to
|
||||||
// make sure the wake lock acquired by the Receiver is released.
|
// make sure the wake lock acquired by the Receiver is released.
|
||||||
if (mPendingDevice == null) {
|
if (mPendingDevice == null) {
|
||||||
DockEventReceiver.finishStartingService(mContext, mStartIdAssociatedWithDialog);
|
DockEventReceiver.finishStartingService(
|
||||||
|
DockService.this, mStartIdAssociatedWithDialog);
|
||||||
}
|
}
|
||||||
mContext.stopForeground(true);
|
stopForeground(true);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Called when clicked on the OK button
|
// Called when clicked on the OK button
|
||||||
|
private final DialogInterface.OnClickListener mClickListener =
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
if (which == DialogInterface.BUTTON_POSITIVE && mDevice != null) {
|
if (which == DialogInterface.BUTTON_POSITIVE
|
||||||
if (!mBtManager.hasDockAutoConnectSetting(mDevice.getAddress())) {
|
&& mDevice != null) {
|
||||||
mBtManager.saveDockAutoConnectSetting(mDevice.getAddress(), true);
|
if (!LocalBluetoothPreferences
|
||||||
|
.hasDockAutoConnectSetting(
|
||||||
|
DockService.this,
|
||||||
|
mDevice.getAddress())) {
|
||||||
|
LocalBluetoothPreferences
|
||||||
|
.saveDockAutoConnectSetting(
|
||||||
|
DockService.this,
|
||||||
|
mDevice.getAddress(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
applyBtSettings(mDevice, mStartIdAssociatedWithDialog);
|
applyBtSettings(mDevice, mStartIdAssociatedWithDialog);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private CharSequence[] initBtSettings(DockService service, BluetoothDevice device, int state,
|
private CharSequence[] initBtSettings(BluetoothDevice device,
|
||||||
boolean firstTime) {
|
int state, boolean firstTime) {
|
||||||
// TODO Avoid hardcoding dock and profiles. Read from system properties
|
// TODO Avoid hardcoding dock and profiles. Read from system properties
|
||||||
int numOfProfiles;
|
int numOfProfiles;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
@@ -507,63 +587,95 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
mProfiles = new Profile[numOfProfiles];
|
mProfiles = new LocalBluetoothProfile[numOfProfiles];
|
||||||
mCheckedItems = new boolean[numOfProfiles];
|
mCheckedItems = new boolean[numOfProfiles];
|
||||||
CharSequence[] items = new CharSequence[numOfProfiles];
|
CharSequence[] items = new CharSequence[numOfProfiles];
|
||||||
|
|
||||||
|
// FIXME: convert switch to something else
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case Intent.EXTRA_DOCK_STATE_CAR:
|
case Intent.EXTRA_DOCK_STATE_CAR:
|
||||||
items[0] = service.getString(R.string.bluetooth_dock_settings_headset);
|
items[0] = getString(R.string.bluetooth_dock_settings_headset);
|
||||||
items[1] = service.getString(R.string.bluetooth_dock_settings_a2dp);
|
items[1] = getString(R.string.bluetooth_dock_settings_a2dp);
|
||||||
mProfiles[0] = Profile.HEADSET;
|
mProfiles[0] = mProfileManager.getHeadsetProfile();
|
||||||
mProfiles[1] = Profile.A2DP;
|
mProfiles[1] = mProfileManager.getA2dpProfile();
|
||||||
if (firstTime) {
|
if (firstTime) {
|
||||||
// Enable by default for car dock
|
// Enable by default for car dock
|
||||||
mCheckedItems[0] = true;
|
mCheckedItems[0] = true;
|
||||||
mCheckedItems[1] = true;
|
mCheckedItems[1] = true;
|
||||||
} else {
|
} else {
|
||||||
mCheckedItems[0] = LocalBluetoothProfileManager.getProfileManager(mBtManager,
|
mCheckedItems[0] = mProfiles[0].isPreferred(device);
|
||||||
Profile.HEADSET).isPreferred(device);
|
mCheckedItems[1] = mProfiles[1].isPreferred(device);
|
||||||
mCheckedItems[1] = LocalBluetoothProfileManager.getProfileManager(mBtManager,
|
|
||||||
Profile.A2DP).isPreferred(device);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Intent.EXTRA_DOCK_STATE_DESK:
|
case Intent.EXTRA_DOCK_STATE_DESK:
|
||||||
items[0] = service.getString(R.string.bluetooth_dock_settings_a2dp);
|
items[0] = getString(R.string.bluetooth_dock_settings_a2dp);
|
||||||
mProfiles[0] = Profile.A2DP;
|
mProfiles[0] = mProfileManager.getA2dpProfile();
|
||||||
if (firstTime) {
|
if (firstTime) {
|
||||||
// Disable by default for desk dock
|
// Disable by default for desk dock
|
||||||
mCheckedItems[0] = false;
|
mCheckedItems[0] = false;
|
||||||
} else {
|
} else {
|
||||||
mCheckedItems[0] = LocalBluetoothProfileManager.getProfileManager(mBtManager,
|
mCheckedItems[0] = mProfiles[0].isPreferred(device);
|
||||||
Profile.A2DP).isPreferred(device);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: move to background thread to fix strict mode warnings
|
||||||
private void handleBtStateChange(Intent intent, int startId) {
|
private void handleBtStateChange(Intent intent, int startId) {
|
||||||
int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
|
int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if(DEBUG) Log.d(TAG, "BtState = " + btState + " mPendingDevice = " + mPendingDevice);
|
if(DEBUG) Log.d(TAG, "BtState = " + btState + " mPendingDevice = " + mPendingDevice);
|
||||||
if (btState == BluetoothAdapter.STATE_ON) {
|
if (btState == BluetoothAdapter.STATE_ON) {
|
||||||
|
handleBluetoothStateOn(startId);
|
||||||
|
} else if (btState == BluetoothAdapter.STATE_TURNING_OFF) {
|
||||||
|
// Remove the flag to disable BT if someone is turning off bt.
|
||||||
|
// The rational is that:
|
||||||
|
// a) if BT is off at undock time, no work needs to be done
|
||||||
|
// b) if BT is on at undock time, the user wants it on.
|
||||||
|
getPrefs().edit().remove(KEY_DISABLE_BT_WHEN_UNDOCKED).apply();
|
||||||
|
DockEventReceiver.finishStartingService(this, startId);
|
||||||
|
} else if (btState == BluetoothAdapter.STATE_OFF) {
|
||||||
|
// Bluetooth was turning off as we were trying to turn it on.
|
||||||
|
// Let's try again
|
||||||
|
if(DEBUG) Log.d(TAG, "Bluetooth = OFF mPendingDevice = " + mPendingDevice);
|
||||||
|
|
||||||
|
if (mPendingTurnOffStartId != INVALID_STARTID) {
|
||||||
|
DockEventReceiver.finishStartingService(this, mPendingTurnOffStartId);
|
||||||
|
getPrefs().edit().remove(KEY_DISABLE_BT).apply();
|
||||||
|
mPendingTurnOffStartId = INVALID_STARTID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPendingDevice != null) {
|
||||||
|
mLocalAdapter.enable();
|
||||||
|
mPendingTurnOnStartId = startId;
|
||||||
|
} else {
|
||||||
|
DockEventReceiver.finishStartingService(this, startId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleBluetoothStateOn(int startId) {
|
||||||
if (mPendingDevice != null) {
|
if (mPendingDevice != null) {
|
||||||
if (mPendingDevice.equals(mDevice)) {
|
if (mPendingDevice.equals(mDevice)) {
|
||||||
if(DEBUG) Log.d(TAG, "applying settings");
|
if(DEBUG) {
|
||||||
|
Log.d(TAG, "applying settings");
|
||||||
|
}
|
||||||
applyBtSettings(mPendingDevice, mPendingStartId);
|
applyBtSettings(mPendingDevice, mPendingStartId);
|
||||||
} else if(DEBUG) {
|
} else if(DEBUG) {
|
||||||
Log.d(TAG, "mPendingDevice (" + mPendingDevice + ") != mDevice ("
|
Log.d(TAG, "mPendingDevice (" + mPendingDevice + ") != mDevice ("
|
||||||
+ mDevice + ")");
|
+ mDevice + ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
mPendingDevice = null;
|
mPendingDevice = null;
|
||||||
DockEventReceiver.finishStartingService(mContext, mPendingStartId);
|
DockEventReceiver.finishStartingService(this, mPendingStartId);
|
||||||
} else {
|
} else {
|
||||||
|
final SharedPreferences prefs = getPrefs();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "A DISABLE_BT_WHEN_UNDOCKED = "
|
Log.d(TAG, "A DISABLE_BT_WHEN_UNDOCKED = "
|
||||||
+ getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
|
+ prefs.getBoolean(KEY_DISABLE_BT_WHEN_UNDOCKED, false));
|
||||||
}
|
}
|
||||||
// Reconnect if docked and bluetooth was enabled by user.
|
// Reconnect if docked and bluetooth was enabled by user.
|
||||||
Intent i = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
|
Intent i = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
|
||||||
@@ -576,10 +688,10 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
if (device != null) {
|
if (device != null) {
|
||||||
connectIfEnabled(device);
|
connectIfEnabled(device);
|
||||||
}
|
}
|
||||||
} else if (getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT)
|
} else if (prefs.getBoolean(KEY_DISABLE_BT, false)
|
||||||
&& mBtManager.getBluetoothAdapter().disable()) {
|
&& mLocalAdapter.disable()) {
|
||||||
mPendingTurnOffStartId = startId;
|
mPendingTurnOffStartId = startId;
|
||||||
removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
|
prefs.edit().remove(KEY_DISABLE_BT).apply();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -591,53 +703,28 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
}
|
}
|
||||||
|
|
||||||
DockEventReceiver.finishStartingService(this, startId);
|
DockEventReceiver.finishStartingService(this, startId);
|
||||||
} else if (btState == BluetoothAdapter.STATE_TURNING_OFF) {
|
|
||||||
// Remove the flag to disable BT if someone is turning off bt.
|
|
||||||
// The rational is that:
|
|
||||||
// a) if BT is off at undock time, no work needs to be done
|
|
||||||
// b) if BT is on at undock time, the user wants it on.
|
|
||||||
removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED);
|
|
||||||
DockEventReceiver.finishStartingService(this, startId);
|
|
||||||
} else if (btState == BluetoothAdapter.STATE_OFF) {
|
|
||||||
// Bluetooth was turning off as we were trying to turn it on.
|
|
||||||
// Let's try again
|
|
||||||
if(DEBUG) Log.d(TAG, "Bluetooth = OFF mPendingDevice = " + mPendingDevice);
|
|
||||||
|
|
||||||
if (mPendingTurnOffStartId != INVALID_STARTID) {
|
|
||||||
DockEventReceiver.finishStartingService(this, mPendingTurnOffStartId);
|
|
||||||
removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
|
|
||||||
mPendingTurnOffStartId = INVALID_STARTID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mPendingDevice != null) {
|
private synchronized void handleUnexpectedDisconnect(BluetoothDevice disconnectedDevice,
|
||||||
mBtManager.getBluetoothAdapter().enable();
|
LocalBluetoothProfile profile, int startId) {
|
||||||
mPendingTurnOnStartId = startId;
|
if (DEBUG) {
|
||||||
} else {
|
Log.d(TAG, "handling failed connect for " + disconnectedDevice);
|
||||||
DockEventReceiver.finishStartingService(this, startId);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleUnexpectedDisconnect(BluetoothDevice disconnectedDevice, Profile profile,
|
|
||||||
int startId) {
|
|
||||||
synchronized (this) {
|
|
||||||
if (DEBUG) Log.d(TAG, "handling failed connect for " + disconnectedDevice);
|
|
||||||
|
|
||||||
// Reconnect if docked.
|
// Reconnect if docked.
|
||||||
if (disconnectedDevice != null) {
|
if (disconnectedDevice != null) {
|
||||||
// registerReceiver can't be called from a BroadcastReceiver
|
// registerReceiver can't be called from a BroadcastReceiver
|
||||||
Intent i = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
|
Intent intent = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
|
||||||
if (i != null) {
|
if (intent != null) {
|
||||||
int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE,
|
int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
|
||||||
Intent.EXTRA_DOCK_STATE_UNDOCKED);
|
Intent.EXTRA_DOCK_STATE_UNDOCKED);
|
||||||
if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
|
if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
|
||||||
BluetoothDevice dockedDevice = i
|
BluetoothDevice dockedDevice = intent
|
||||||
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
||||||
if (dockedDevice != null && dockedDevice.equals(disconnectedDevice)) {
|
if (dockedDevice != null && dockedDevice.equals(disconnectedDevice)) {
|
||||||
CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext,
|
CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(
|
||||||
mBtManager, dockedDevice);
|
dockedDevice);
|
||||||
cachedDevice.connect(profile);
|
cachedDevice.connectProfile(profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -645,48 +732,38 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
|
|
||||||
DockEventReceiver.finishStartingService(this, startId);
|
DockEventReceiver.finishStartingService(this, startId);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void connectIfEnabled(BluetoothDevice device) {
|
private synchronized void connectIfEnabled(BluetoothDevice device) {
|
||||||
CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext, mBtManager, device);
|
CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(
|
||||||
List<Profile> profiles = cachedDevice.getConnectableProfiles();
|
device);
|
||||||
for (int i = 0; i < profiles.size(); i++) {
|
List<LocalBluetoothProfile> profiles = cachedDevice.getConnectableProfiles();
|
||||||
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
|
for (LocalBluetoothProfile profile : profiles) {
|
||||||
.getProfileManager(mBtManager, profiles.get(i));
|
if (profile.getPreferred(device) == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
|
||||||
int auto;
|
|
||||||
if (Profile.A2DP == profiles.get(i)) {
|
|
||||||
auto = BluetoothA2dp.PRIORITY_AUTO_CONNECT;
|
|
||||||
} else if (Profile.HEADSET == profiles.get(i)) {
|
|
||||||
auto = BluetoothHeadset.PRIORITY_AUTO_CONNECT;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (profileManager.getPreferred(device) == auto) {
|
|
||||||
cachedDevice.connect(false);
|
cachedDevice.connect(false);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void applyBtSettings(final BluetoothDevice device, int startId) {
|
private synchronized void applyBtSettings(BluetoothDevice device, int startId) {
|
||||||
if (device == null || mProfiles == null || mCheckedItems == null)
|
if (device == null || mProfiles == null || mCheckedItems == null
|
||||||
|
|| mLocalAdapter == null) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Turn on BT if something is enabled
|
// Turn on BT if something is enabled
|
||||||
synchronized (this) {
|
|
||||||
for (boolean enable : mCheckedItems) {
|
for (boolean enable : mCheckedItems) {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
int btState = mBtManager.getBluetoothState();
|
int btState = mLocalAdapter.getBluetoothState();
|
||||||
if(DEBUG) Log.d(TAG, "BtState = " + btState);
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "BtState = " + btState);
|
||||||
|
}
|
||||||
// May have race condition as the phone comes in and out and in the dock.
|
// May have race condition as the phone comes in and out and in the dock.
|
||||||
// Always turn on BT
|
// Always turn on BT
|
||||||
mBtManager.getBluetoothAdapter().enable();
|
mLocalAdapter.enable();
|
||||||
|
|
||||||
switch (btState) {
|
// if adapter was previously OFF, TURNING_OFF, or TURNING_ON
|
||||||
case BluetoothAdapter.STATE_OFF:
|
if (btState != BluetoothAdapter.STATE_ON) {
|
||||||
case BluetoothAdapter.STATE_TURNING_OFF:
|
|
||||||
case BluetoothAdapter.STATE_TURNING_ON:
|
|
||||||
if (mPendingDevice != null && mPendingDevice.equals(mDevice)) {
|
if (mPendingDevice != null && mPendingDevice.equals(mDevice)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -694,40 +771,37 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
mPendingDevice = device;
|
mPendingDevice = device;
|
||||||
mPendingStartId = startId;
|
mPendingStartId = startId;
|
||||||
if (btState != BluetoothAdapter.STATE_TURNING_ON) {
|
if (btState != BluetoothAdapter.STATE_TURNING_ON) {
|
||||||
setSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED,
|
getPrefs().edit().putBoolean(
|
||||||
true);
|
KEY_DISABLE_BT_WHEN_UNDOCKED, true).apply();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mPendingDevice = null;
|
mPendingDevice = null;
|
||||||
|
|
||||||
boolean callConnect = false;
|
boolean callConnect = false;
|
||||||
CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext, mBtManager,
|
CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(
|
||||||
device);
|
device);
|
||||||
for (int i = 0; i < mProfiles.length; i++) {
|
for (int i = 0; i < mProfiles.length; i++) {
|
||||||
LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
|
LocalBluetoothProfile profile = mProfiles[i];
|
||||||
.getProfileManager(mBtManager, mProfiles[i]);
|
if (DEBUG) Log.d(TAG, profile.toString() + " = " + mCheckedItems[i]);
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, mProfiles[i].toString() + " = " + mCheckedItems[i]);
|
|
||||||
|
|
||||||
if (mCheckedItems[i]) {
|
if (mCheckedItems[i]) {
|
||||||
// Checked but not connected
|
// Checked but not connected
|
||||||
callConnect = true;
|
callConnect = true;
|
||||||
} else if (!mCheckedItems[i]) {
|
} else if (!mCheckedItems[i]) {
|
||||||
// Unchecked, may or may not be connected.
|
// Unchecked, may or may not be connected.
|
||||||
int status = profileManager.getConnectionStatus(cachedDevice.getDevice());
|
int status = profile.getConnectionStatus(cachedDevice.getDevice());
|
||||||
if (SettingsBtStatus.isConnectionStatusConnected(status)) {
|
if (status == BluetoothProfile.STATE_CONNECTED) {
|
||||||
if (DEBUG) Log.d(TAG, "applyBtSettings - Disconnecting");
|
if (DEBUG) Log.d(TAG, "applyBtSettings - Disconnecting");
|
||||||
cachedDevice.disconnect(mProfiles[i]);
|
cachedDevice.disconnect(mProfiles[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
profileManager.setPreferred(device, mCheckedItems[i]);
|
profile.setPreferred(device, mCheckedItems[i]);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
if (mCheckedItems[i] != profileManager.isPreferred(device)) {
|
if (mCheckedItems[i] != profile.isPreferred(device)) {
|
||||||
Log.e(TAG, "Can't save preferred value");
|
Log.e(TAG, "Can't save preferred value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -739,84 +813,47 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void handleDocked(final BluetoothDevice device, final int state,
|
private synchronized void handleDocked(BluetoothDevice device, int state,
|
||||||
final int startId) {
|
int startId) {
|
||||||
if (mBtManager.getDockAutoConnectSetting(device.getAddress())) {
|
if (LocalBluetoothPreferences.getDockAutoConnectSetting(this, device.getAddress())) {
|
||||||
// Setting == auto connect
|
// Setting == auto connect
|
||||||
initBtSettings(mContext, device, state, false);
|
initBtSettings(device, state, false);
|
||||||
applyBtSettings(mDevice, startId);
|
applyBtSettings(mDevice, startId);
|
||||||
} else {
|
} else {
|
||||||
createDialog(mContext, device, state, startId);
|
createDialog(device, state, startId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void handleUndocked(Context context, LocalBluetoothManager localManager,
|
private synchronized void handleUndocked(BluetoothDevice device) {
|
||||||
BluetoothDevice device) {
|
|
||||||
mRunnable = null;
|
mRunnable = null;
|
||||||
LocalBluetoothProfileManager.removeServiceListener(this);
|
mProfileManager.removeServiceListener(this);
|
||||||
if (mDialog != null) {
|
if (mDialog != null) {
|
||||||
mDialog.dismiss();
|
mDialog.dismiss();
|
||||||
mDialog = null;
|
mDialog = null;
|
||||||
}
|
}
|
||||||
mDevice = null;
|
mDevice = null;
|
||||||
mPendingDevice = null;
|
mPendingDevice = null;
|
||||||
CachedBluetoothDevice cachedBluetoothDevice = getCachedBluetoothDevice(context,
|
CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(device);
|
||||||
localManager, device);
|
cachedDevice.disconnect();
|
||||||
cachedBluetoothDevice.disconnect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CachedBluetoothDevice getCachedBluetoothDevice(Context context,
|
private CachedBluetoothDevice getCachedBluetoothDevice(BluetoothDevice device) {
|
||||||
LocalBluetoothManager localManager, BluetoothDevice device) {
|
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
|
||||||
CachedBluetoothDeviceManager cachedDeviceManager = localManager.getCachedDeviceManager();
|
if (cachedDevice == null) {
|
||||||
CachedBluetoothDevice cachedBluetoothDevice = cachedDeviceManager.findDevice(device);
|
cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
|
||||||
if (cachedBluetoothDevice == null) {
|
|
||||||
cachedBluetoothDevice = new CachedBluetoothDevice(context, device);
|
|
||||||
}
|
}
|
||||||
return cachedBluetoothDevice;
|
return cachedDevice;
|
||||||
}
|
|
||||||
|
|
||||||
private boolean getSettingBool(String key) {
|
|
||||||
SharedPreferences sharedPref = getSharedPreferences(SHARED_PREFERENCES_NAME,
|
|
||||||
Context.MODE_PRIVATE);
|
|
||||||
return sharedPref.getBoolean(key, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getSettingInt(String key, int defaultValue) {
|
|
||||||
SharedPreferences sharedPref = getSharedPreferences(SHARED_PREFERENCES_NAME,
|
|
||||||
Context.MODE_PRIVATE);
|
|
||||||
return sharedPref.getInt(key, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSettingBool(String key, boolean bool) {
|
|
||||||
SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFERENCES_NAME,
|
|
||||||
Context.MODE_PRIVATE).edit();
|
|
||||||
editor.putBoolean(key, bool);
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSettingInt(String key, int value) {
|
|
||||||
SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFERENCES_NAME,
|
|
||||||
Context.MODE_PRIVATE).edit();
|
|
||||||
editor.putInt(key, value);
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeSetting(String key) {
|
|
||||||
SharedPreferences sharedPref = getSharedPreferences(SHARED_PREFERENCES_NAME,
|
|
||||||
Context.MODE_PRIVATE);
|
|
||||||
SharedPreferences.Editor editor = sharedPref.edit();
|
|
||||||
editor.remove(key);
|
|
||||||
editor.apply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void onServiceConnected() {
|
public synchronized void onServiceConnected() {
|
||||||
if (mRunnable != null) {
|
if (mRunnable != null) {
|
||||||
mRunnable.run();
|
mRunnable.run();
|
||||||
mRunnable = null;
|
mRunnable = null;
|
||||||
LocalBluetoothProfileManager.removeServiceListener(this);
|
mProfileManager.removeServiceListener(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceDisconnected() {
|
public void onServiceDisconnected() {
|
||||||
|
// FIXME: shouldn't I do something on service disconnected too?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
198
src/com/android/settings/bluetooth/HeadsetProfile.java
Normal file
198
src/com/android/settings/bluetooth/HeadsetProfile.java
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothHeadset;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.bluetooth.BluetoothUuid;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HeadsetProfile handles Bluetooth HFP and Headset profiles.
|
||||||
|
*/
|
||||||
|
final class HeadsetProfile implements LocalBluetoothProfile {
|
||||||
|
private static final String TAG = "HeadsetProfile";
|
||||||
|
|
||||||
|
private BluetoothHeadset mService;
|
||||||
|
private boolean mProfileReady;
|
||||||
|
|
||||||
|
private final LocalBluetoothAdapter mLocalAdapter;
|
||||||
|
private final CachedBluetoothDeviceManager mDeviceManager;
|
||||||
|
private final LocalBluetoothProfileManager mProfileManager;
|
||||||
|
|
||||||
|
static final ParcelUuid[] UUIDS = {
|
||||||
|
BluetoothUuid.HSP,
|
||||||
|
BluetoothUuid.Handsfree,
|
||||||
|
};
|
||||||
|
|
||||||
|
static final String NAME = "HEADSET";
|
||||||
|
|
||||||
|
// Order of this profile in device profiles list
|
||||||
|
private static final int ORDINAL = 0;
|
||||||
|
|
||||||
|
// These callbacks run on the main thread.
|
||||||
|
private final class HeadsetServiceListener
|
||||||
|
implements BluetoothProfile.ServiceListener {
|
||||||
|
|
||||||
|
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||||
|
mService = (BluetoothHeadset) proxy;
|
||||||
|
mProfileReady = true;
|
||||||
|
// We just bound to the service, so refresh the UI of the
|
||||||
|
// headset device.
|
||||||
|
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
|
||||||
|
if (deviceList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BluetoothDevice firstDevice = deviceList.get(0);
|
||||||
|
CachedBluetoothDevice device = mDeviceManager.findDevice(firstDevice);
|
||||||
|
// we may add a new device here, but generally this should not happen
|
||||||
|
if (device == null) {
|
||||||
|
Log.w(TAG, "HeadsetProfile found new device: " + firstDevice);
|
||||||
|
device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, firstDevice);
|
||||||
|
}
|
||||||
|
device.onProfileStateChanged(HeadsetProfile.this,
|
||||||
|
BluetoothProfile.STATE_CONNECTED);
|
||||||
|
|
||||||
|
mProfileManager.callServiceConnectedListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(int profile) {
|
||||||
|
mProfileReady = false;
|
||||||
|
mService = null;
|
||||||
|
mProfileManager.callServiceDisconnectedListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(): The calls must get queued if mService becomes null.
|
||||||
|
// It can happen when the phone app crashes for some reason.
|
||||||
|
// All callers should have service listeners. Dock Service is the only
|
||||||
|
// one right now.
|
||||||
|
HeadsetProfile(Context context, LocalBluetoothAdapter adapter,
|
||||||
|
CachedBluetoothDeviceManager deviceManager,
|
||||||
|
LocalBluetoothProfileManager profileManager) {
|
||||||
|
mLocalAdapter = adapter;
|
||||||
|
mDeviceManager = deviceManager;
|
||||||
|
mProfileManager = profileManager;
|
||||||
|
mLocalAdapter.getProfileProxy(context, new HeadsetServiceListener(),
|
||||||
|
BluetoothProfile.HEADSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAutoConnectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connect(BluetoothDevice device) {
|
||||||
|
List<BluetoothDevice> sinks = mService.getConnectedDevices();
|
||||||
|
if (sinks != null) {
|
||||||
|
for (BluetoothDevice sink : sinks) {
|
||||||
|
mService.disconnect(sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mService.connect(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean disconnect(BluetoothDevice device) {
|
||||||
|
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
|
||||||
|
if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
|
||||||
|
// Downgrade priority as user is disconnecting the headset.
|
||||||
|
if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
|
||||||
|
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
|
||||||
|
}
|
||||||
|
return mService.disconnect(device);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectionStatus(BluetoothDevice device) {
|
||||||
|
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
|
||||||
|
|
||||||
|
return !deviceList.isEmpty() && deviceList.get(0).equals(device)
|
||||||
|
? mService.getConnectionState(device)
|
||||||
|
: BluetoothProfile.STATE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPreferred(BluetoothDevice device) {
|
||||||
|
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPreferred(BluetoothDevice device) {
|
||||||
|
return mService.getPriority(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreferred(BluetoothDevice device, boolean preferred) {
|
||||||
|
if (preferred) {
|
||||||
|
if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
|
||||||
|
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean isProfileReady() {
|
||||||
|
return mProfileReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrdinal() {
|
||||||
|
return ORDINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameResource() {
|
||||||
|
return R.string.bluetooth_profile_headset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisconnectResource() {
|
||||||
|
return R.string.bluetooth_disconnect_headset_profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSummaryResourceForDevice(BluetoothDevice device) {
|
||||||
|
int state = mService.getConnectionState(device);
|
||||||
|
switch (state) {
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
return R.string.bluetooth_headset_profile_summary_use_for;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
return R.string.bluetooth_headset_profile_summary_connected;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Utils.getConnectionStateSummary(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDrawableResource(BluetoothClass btClass) {
|
||||||
|
return R.drawable.ic_bt_headset_hfp;
|
||||||
|
}
|
||||||
|
}
|
155
src/com/android/settings/bluetooth/HidProfile.java
Normal file
155
src/com/android/settings/bluetooth/HidProfile.java
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothInputDevice;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HidProfile handles Bluetooth HID profile.
|
||||||
|
*/
|
||||||
|
final class HidProfile implements LocalBluetoothProfile {
|
||||||
|
private BluetoothInputDevice mService;
|
||||||
|
private boolean mProfileReady;
|
||||||
|
|
||||||
|
static final String NAME = "HID";
|
||||||
|
|
||||||
|
// Order of this profile in device profiles list
|
||||||
|
private static final int ORDINAL = 3;
|
||||||
|
|
||||||
|
// These callbacks run on the main thread.
|
||||||
|
private final class InputDeviceServiceListener
|
||||||
|
implements BluetoothProfile.ServiceListener {
|
||||||
|
|
||||||
|
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||||
|
mService = (BluetoothInputDevice) proxy;
|
||||||
|
mProfileReady = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(int profile) {
|
||||||
|
mProfileReady = false;
|
||||||
|
mService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HidProfile(Context context, LocalBluetoothAdapter adapter) {
|
||||||
|
adapter.getProfileProxy(context, new InputDeviceServiceListener(),
|
||||||
|
BluetoothProfile.INPUT_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAutoConnectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connect(BluetoothDevice device) {
|
||||||
|
return mService.connect(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean disconnect(BluetoothDevice device) {
|
||||||
|
return mService.disconnect(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectionStatus(BluetoothDevice device) {
|
||||||
|
List<BluetoothDevice> deviceList = mService.getConnectedDevices();
|
||||||
|
|
||||||
|
return !deviceList.isEmpty() && deviceList.get(0).equals(device)
|
||||||
|
? mService.getConnectionState(device)
|
||||||
|
: BluetoothProfile.STATE_DISCONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPreferred(BluetoothDevice device) {
|
||||||
|
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPreferred(BluetoothDevice device) {
|
||||||
|
return mService.getPriority(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreferred(BluetoothDevice device, boolean preferred) {
|
||||||
|
if (preferred) {
|
||||||
|
if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
|
||||||
|
mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isProfileReady() {
|
||||||
|
return mProfileReady;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrdinal() {
|
||||||
|
return ORDINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameResource() {
|
||||||
|
return R.string.bluetooth_profile_hid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisconnectResource() {
|
||||||
|
return R.string.bluetooth_disconnect_hid_profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSummaryResourceForDevice(BluetoothDevice device) {
|
||||||
|
int state = mService.getConnectionState(device);
|
||||||
|
switch (state) {
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
return R.string.bluetooth_hid_profile_summary_use_for;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
return R.string.bluetooth_hid_profile_summary_connected;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Utils.getConnectionStateSummary(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDrawableResource(BluetoothClass btClass) {
|
||||||
|
if (btClass == null) {
|
||||||
|
return R.drawable.ic_bt_keyboard_hid;
|
||||||
|
}
|
||||||
|
return getHidClassDrawable(btClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getHidClassDrawable(BluetoothClass btClass) {
|
||||||
|
switch (btClass.getDeviceClass()) {
|
||||||
|
case BluetoothClass.Device.PERIPHERAL_KEYBOARD:
|
||||||
|
case BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING:
|
||||||
|
return R.drawable.ic_bt_keyboard_hid;
|
||||||
|
case BluetoothClass.Device.PERIPHERAL_POINTING:
|
||||||
|
return R.drawable.ic_bt_pointing_hid;
|
||||||
|
default:
|
||||||
|
return R.drawable.ic_bt_misc_hid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
216
src/com/android/settings/bluetooth/LocalBluetoothAdapter.java
Normal file
216
src/com/android/settings/bluetooth/LocalBluetoothAdapter.java
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.ParcelUuid;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LocalBluetoothAdapter provides an interface between the Settings app
|
||||||
|
* and the functionality of the local {@link BluetoothAdapter}, specifically
|
||||||
|
* those related to state transitions of the adapter itself.
|
||||||
|
*
|
||||||
|
* <p>Connection and bonding state changes affecting specific devices
|
||||||
|
* are handled by {@link CachedBluetoothDeviceManager},
|
||||||
|
* {@link BluetoothEventManager}, and {@link LocalBluetoothProfileManager}.
|
||||||
|
*/
|
||||||
|
public final class LocalBluetoothAdapter {
|
||||||
|
private static final String TAG = "LocalBluetoothAdapter";
|
||||||
|
|
||||||
|
/** This class does not allow direct access to the BluetoothAdapter. */
|
||||||
|
private final BluetoothAdapter mAdapter;
|
||||||
|
|
||||||
|
private LocalBluetoothProfileManager mProfileManager;
|
||||||
|
|
||||||
|
private static LocalBluetoothAdapter sInstance;
|
||||||
|
|
||||||
|
private int mState = BluetoothAdapter.ERROR;
|
||||||
|
|
||||||
|
private static final int SCAN_EXPIRATION_MS = 5 * 60 * 1000; // 5 mins
|
||||||
|
|
||||||
|
private long mLastScan;
|
||||||
|
|
||||||
|
private LocalBluetoothAdapter(BluetoothAdapter adapter) {
|
||||||
|
mAdapter = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProfileManager(LocalBluetoothProfileManager manager) {
|
||||||
|
mProfileManager = manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the singleton instance of the LocalBluetoothAdapter. If this device
|
||||||
|
* doesn't support Bluetooth, then null will be returned. Callers must be
|
||||||
|
* prepared to handle a null return value.
|
||||||
|
* @return the LocalBluetoothAdapter object, or null if not supported
|
||||||
|
*/
|
||||||
|
static synchronized LocalBluetoothAdapter getInstance() {
|
||||||
|
if (sInstance == null) {
|
||||||
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
if (adapter != null) {
|
||||||
|
sInstance = new LocalBluetoothAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass-through BluetoothAdapter methods that we can intercept if necessary
|
||||||
|
|
||||||
|
void cancelDiscovery() {
|
||||||
|
mAdapter.cancelDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean enable() {
|
||||||
|
return mAdapter.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean disable() {
|
||||||
|
return mAdapter.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getProfileProxy(Context context,
|
||||||
|
BluetoothProfile.ServiceListener listener, int profile) {
|
||||||
|
mAdapter.getProfileProxy(context, listener, profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<BluetoothDevice> getBondedDevices() {
|
||||||
|
return mAdapter.getBondedDevices();
|
||||||
|
}
|
||||||
|
|
||||||
|
String getName() {
|
||||||
|
return mAdapter.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getScanMode() {
|
||||||
|
return mAdapter.getScanMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
int getState() {
|
||||||
|
return mAdapter.getState();
|
||||||
|
}
|
||||||
|
|
||||||
|
ParcelUuid[] getUuids() {
|
||||||
|
return mAdapter.getUuids();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isDiscovering() {
|
||||||
|
return mAdapter.isDiscovering();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isEnabled() {
|
||||||
|
return mAdapter.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDiscoverableTimeout(int timeout) {
|
||||||
|
mAdapter.setDiscoverableTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setName(String name) {
|
||||||
|
mAdapter.setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setScanMode(int mode) {
|
||||||
|
mAdapter.setScanMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean setScanMode(int mode, int duration) {
|
||||||
|
return mAdapter.setScanMode(mode, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startScanning(boolean force) {
|
||||||
|
// Only start if we're not already scanning
|
||||||
|
if (!mAdapter.isDiscovering()) {
|
||||||
|
if (!force) {
|
||||||
|
// Don't scan more than frequently than SCAN_EXPIRATION_MS,
|
||||||
|
// unless forced
|
||||||
|
if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are playing music, don't scan unless forced.
|
||||||
|
A2dpProfile a2dp = mProfileManager.getA2dpProfile();
|
||||||
|
if (a2dp != null && a2dp.isA2dpPlaying()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAdapter.startDiscovery()) {
|
||||||
|
mLastScan = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopScanning() {
|
||||||
|
if (mAdapter.isDiscovering()) {
|
||||||
|
mAdapter.cancelDiscovery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized int getBluetoothState() {
|
||||||
|
// Always sync state, in case it changed while paused
|
||||||
|
syncBluetoothState();
|
||||||
|
return mState;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized void setBluetoothStateInt(int state) {
|
||||||
|
mState = state;
|
||||||
|
|
||||||
|
if (state == BluetoothAdapter.STATE_ON) {
|
||||||
|
// if mProfileManager hasn't been constructed yet, it will
|
||||||
|
// get the adapter UUIDs in its constructor when it is.
|
||||||
|
if (mProfileManager != null) {
|
||||||
|
mProfileManager.setBluetoothStateOn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the state changed; false otherwise.
|
||||||
|
boolean syncBluetoothState() {
|
||||||
|
int currentState = mAdapter.getState();
|
||||||
|
if (currentState != mState) {
|
||||||
|
setBluetoothStateInt(mAdapter.getState());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBluetoothEnabled(boolean enabled) {
|
||||||
|
boolean success = enabled
|
||||||
|
? mAdapter.enable()
|
||||||
|
: mAdapter.disable();
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
setBluetoothStateInt(enabled
|
||||||
|
? BluetoothAdapter.STATE_TURNING_ON
|
||||||
|
: BluetoothAdapter.STATE_TURNING_OFF);
|
||||||
|
} else {
|
||||||
|
if (Utils.V) {
|
||||||
|
Log.v(TAG, "setBluetoothEnabled call, manager didn't return " +
|
||||||
|
"success for enabled: " + enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
syncBluetoothState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -16,376 +16,96 @@
|
|||||||
|
|
||||||
package com.android.settings.bluetooth;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.bluetooth.BluetoothA2dp;
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
|
||||||
import android.bluetooth.BluetoothProfile;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.ParcelUuid;
|
|
||||||
import android.util.Config;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
// TODO: have some notion of shutting down. Maybe a minute after they leave BT settings?
|
|
||||||
/**
|
/**
|
||||||
* LocalBluetoothManager provides a simplified interface on top of a subset of
|
* LocalBluetoothManager provides a simplified interface on top of a subset of
|
||||||
* the Bluetooth API.
|
* the Bluetooth API. Note that {@link #getInstance} will return null
|
||||||
|
* if there is no Bluetooth adapter on this device, and callers must be
|
||||||
|
* prepared to handle this case.
|
||||||
*/
|
*/
|
||||||
public class LocalBluetoothManager {
|
public final class LocalBluetoothManager {
|
||||||
private static final String TAG = "LocalBluetoothManager";
|
private static final String TAG = "LocalBluetoothManager";
|
||||||
static final boolean V = Config.LOGV;
|
|
||||||
static final boolean D = Config.LOGD;
|
|
||||||
|
|
||||||
private static final String SHARED_PREFERENCES_NAME = "bluetooth_settings";
|
|
||||||
|
|
||||||
/** Singleton instance. */
|
/** Singleton instance. */
|
||||||
private static LocalBluetoothManager sInstance;
|
private static LocalBluetoothManager sInstance;
|
||||||
|
|
||||||
private Context mContext;
|
private final Context mContext;
|
||||||
|
|
||||||
/** If a BT-related activity is in the foreground, this will be it. */
|
/** If a BT-related activity is in the foreground, this will be it. */
|
||||||
private Activity mForegroundActivity;
|
private Context mForegroundActivity;
|
||||||
private AlertDialog mErrorDialog = null;
|
|
||||||
|
|
||||||
private BluetoothAdapter mAdapter;
|
private final LocalBluetoothAdapter mLocalAdapter;
|
||||||
|
|
||||||
private CachedBluetoothDeviceManager mCachedDeviceManager;
|
private final CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||||
private BluetoothA2dp mBluetoothA2dp;
|
|
||||||
|
|
||||||
private int mState = BluetoothAdapter.ERROR;
|
/** The Bluetooth profile manager. */
|
||||||
|
private final LocalBluetoothProfileManager mProfileManager;
|
||||||
|
|
||||||
private final List<Callback> mCallbacks = new ArrayList<Callback>();
|
/** The broadcast receiver event manager. */
|
||||||
|
private final BluetoothEventManager mEventManager;
|
||||||
|
|
||||||
private static final int SCAN_EXPIRATION_MS = 5 * 60 * 1000; // 5 mins
|
public static synchronized LocalBluetoothManager getInstance(Context context) {
|
||||||
|
|
||||||
// If a device was picked from the device picker or was in discoverable mode
|
|
||||||
// in the last 60 seconds, show the pairing dialogs in foreground instead
|
|
||||||
// of raising notifications
|
|
||||||
private static final int GRACE_PERIOD_TO_SHOW_DIALOGS_IN_FOREGROUND = 60 * 1000;
|
|
||||||
|
|
||||||
public static final String SHARED_PREFERENCES_KEY_DISCOVERING_TIMESTAMP =
|
|
||||||
"last_discovering_time";
|
|
||||||
|
|
||||||
private static final String SHARED_PREFERENCES_KEY_LAST_SELECTED_DEVICE =
|
|
||||||
"last_selected_device";
|
|
||||||
|
|
||||||
private static final String SHARED_PREFERENCES_KEY_LAST_SELECTED_DEVICE_TIME =
|
|
||||||
"last_selected_device_time";
|
|
||||||
|
|
||||||
private static final String SHARED_PREFERENCES_KEY_DOCK_AUTO_CONNECT = "auto_connect_to_dock";
|
|
||||||
|
|
||||||
private long mLastScan;
|
|
||||||
|
|
||||||
private LocalBluetoothManager() { }
|
|
||||||
|
|
||||||
public static LocalBluetoothManager getInstance(Context context) {
|
|
||||||
synchronized (LocalBluetoothManager.class) {
|
|
||||||
if (sInstance == null) {
|
if (sInstance == null) {
|
||||||
sInstance = new LocalBluetoothManager();
|
LocalBluetoothAdapter adapter = LocalBluetoothAdapter.getInstance();
|
||||||
if (!sInstance.init(context)) {
|
if (adapter == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
LocalBluetoothProfileManager.init(sInstance);
|
// This will be around as long as this process is
|
||||||
|
Context appContext = context.getApplicationContext();
|
||||||
|
sInstance = new LocalBluetoothManager(adapter, appContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context) {
|
||||||
|
mContext = context;
|
||||||
|
mLocalAdapter = adapter;
|
||||||
|
|
||||||
|
mCachedDeviceManager = new CachedBluetoothDeviceManager();
|
||||||
|
mEventManager = new BluetoothEventManager(mLocalAdapter,
|
||||||
|
mCachedDeviceManager);
|
||||||
|
mProfileManager = new LocalBluetoothProfileManager(context,
|
||||||
|
mLocalAdapter, mCachedDeviceManager, mEventManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean init(Context context) {
|
public LocalBluetoothAdapter getBluetoothAdapter() {
|
||||||
// This will be around as long as this process is
|
return mLocalAdapter;
|
||||||
mContext = context.getApplicationContext();
|
|
||||||
|
|
||||||
mAdapter = BluetoothAdapter.getDefaultAdapter();
|
|
||||||
if (mAdapter == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mCachedDeviceManager = new CachedBluetoothDeviceManager(this);
|
|
||||||
|
|
||||||
new BluetoothEventRedirector(this).registerReceiver();
|
|
||||||
|
|
||||||
mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final BluetoothProfile.ServiceListener mProfileListener =
|
|
||||||
new BluetoothProfile.ServiceListener() {
|
|
||||||
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
|
||||||
mBluetoothA2dp = (BluetoothA2dp) proxy;
|
|
||||||
}
|
|
||||||
public void onServiceDisconnected(int profile) {
|
|
||||||
mBluetoothA2dp = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public BluetoothAdapter getBluetoothAdapter() {
|
|
||||||
return mAdapter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
return mContext;
|
return mContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Activity getForegroundActivity() {
|
boolean isForegroundActivity() {
|
||||||
return mForegroundActivity;
|
return mForegroundActivity != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setForegroundActivity(Activity activity) {
|
synchronized void setForegroundActivity(Context context) {
|
||||||
if (mErrorDialog != null) {
|
if (context != null) {
|
||||||
mErrorDialog.dismiss();
|
Log.d(TAG, "setting foreground activity to non-null context");
|
||||||
mErrorDialog = null;
|
mForegroundActivity = context;
|
||||||
|
mEventManager.resume(context);
|
||||||
|
} else {
|
||||||
|
if (mForegroundActivity != null) {
|
||||||
|
Log.d(TAG, "setting foreground activity to null");
|
||||||
|
mEventManager.pause(mForegroundActivity);
|
||||||
|
mForegroundActivity = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mForegroundActivity = activity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SharedPreferences getSharedPreferences() {
|
CachedBluetoothDeviceManager getCachedDeviceManager() {
|
||||||
return mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CachedBluetoothDeviceManager getCachedDeviceManager() {
|
|
||||||
return mCachedDeviceManager;
|
return mCachedDeviceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Callback> getCallbacks() {
|
BluetoothEventManager getEventManager() {
|
||||||
return mCallbacks;
|
return mEventManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerCallback(Callback callback) {
|
LocalBluetoothProfileManager getProfileManager() {
|
||||||
synchronized (mCallbacks) {
|
return mProfileManager;
|
||||||
mCallbacks.add(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unregisterCallback(Callback callback) {
|
|
||||||
synchronized (mCallbacks) {
|
|
||||||
mCallbacks.remove(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startScanning(boolean force) {
|
|
||||||
if (mAdapter.isDiscovering()) {
|
|
||||||
/*
|
|
||||||
* Already discovering, but give the callback that information.
|
|
||||||
* Note: we only call the callbacks, not the same path as if the
|
|
||||||
* scanning state had really changed (in that case the device
|
|
||||||
* manager would clear its list of unpaired scanned devices).
|
|
||||||
*/
|
|
||||||
dispatchScanningStateChanged(true);
|
|
||||||
} else {
|
|
||||||
if (!force) {
|
|
||||||
// Don't scan more than frequently than SCAN_EXPIRATION_MS,
|
|
||||||
// unless forced
|
|
||||||
if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are playing music, don't scan unless forced.
|
|
||||||
if (mBluetoothA2dp != null) {
|
|
||||||
List<BluetoothDevice> sinks = mBluetoothA2dp.getConnectedDevices();
|
|
||||||
if (sinks.size() > 0) {
|
|
||||||
if (mBluetoothA2dp.isA2dpPlaying(sinks.get(0))) return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mAdapter.startDiscovery()) {
|
|
||||||
mLastScan = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopScanning() {
|
|
||||||
if (mAdapter.isDiscovering()) {
|
|
||||||
mAdapter.cancelDiscovery();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBluetoothState() {
|
|
||||||
|
|
||||||
if (mState == BluetoothAdapter.ERROR) {
|
|
||||||
syncBluetoothState();
|
|
||||||
}
|
|
||||||
|
|
||||||
return mState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBluetoothStateInt(int state) {
|
|
||||||
mState = state;
|
|
||||||
|
|
||||||
if (state == BluetoothAdapter.STATE_ON) {
|
|
||||||
ParcelUuid[] uuids = mAdapter.getUuids();
|
|
||||||
LocalBluetoothProfileManager.updateLocalProfiles(getInstance(mContext), uuids);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state == BluetoothAdapter.STATE_ON ||
|
|
||||||
state == BluetoothAdapter.STATE_OFF) {
|
|
||||||
mCachedDeviceManager.onBluetoothStateChanged(state ==
|
|
||||||
BluetoothAdapter.STATE_ON);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void syncBluetoothState() {
|
|
||||||
int bluetoothState;
|
|
||||||
|
|
||||||
if (mAdapter != null) {
|
|
||||||
bluetoothState = mAdapter.isEnabled()
|
|
||||||
? BluetoothAdapter.STATE_ON
|
|
||||||
: BluetoothAdapter.STATE_OFF;
|
|
||||||
} else {
|
|
||||||
bluetoothState = BluetoothAdapter.ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
setBluetoothStateInt(bluetoothState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBluetoothEnabled(boolean enabled) {
|
|
||||||
boolean wasSetStateSuccessful = enabled
|
|
||||||
? mAdapter.enable()
|
|
||||||
: mAdapter.disable();
|
|
||||||
|
|
||||||
if (wasSetStateSuccessful) {
|
|
||||||
setBluetoothStateInt(enabled
|
|
||||||
? BluetoothAdapter.STATE_TURNING_ON
|
|
||||||
: BluetoothAdapter.STATE_TURNING_OFF);
|
|
||||||
} else {
|
|
||||||
if (V) {
|
|
||||||
Log.v(TAG,
|
|
||||||
"setBluetoothEnabled call, manager didn't return success for enabled: "
|
|
||||||
+ enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
syncBluetoothState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param started True if scanning started, false if scanning finished.
|
|
||||||
*/
|
|
||||||
void onScanningStateChanged(boolean started) {
|
|
||||||
// TODO: have it be a callback (once we switch bluetooth state changed to callback)
|
|
||||||
mCachedDeviceManager.onScanningStateChanged(started);
|
|
||||||
dispatchScanningStateChanged(started);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dispatchScanningStateChanged(boolean started) {
|
|
||||||
synchronized (mCallbacks) {
|
|
||||||
for (Callback callback : mCallbacks) {
|
|
||||||
callback.onScanningStateChanged(started);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void showError(BluetoothDevice device, int messageResId) {
|
|
||||||
CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(device);
|
|
||||||
String name = null;
|
|
||||||
if (cachedDevice == null) {
|
|
||||||
if (device != null) name = device.getName();
|
|
||||||
|
|
||||||
if (name == null) {
|
|
||||||
name = mContext.getString(R.string.bluetooth_remote_device);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
name = cachedDevice.getName();
|
|
||||||
}
|
|
||||||
String message = mContext.getString(messageResId, name);
|
|
||||||
|
|
||||||
if (mForegroundActivity != null) {
|
|
||||||
// Need an activity context to show a dialog
|
|
||||||
mErrorDialog = new AlertDialog.Builder(mForegroundActivity)
|
|
||||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
|
||||||
.setTitle(R.string.bluetooth_error_title)
|
|
||||||
.setMessage(message)
|
|
||||||
.setPositiveButton(android.R.string.ok, null)
|
|
||||||
.show();
|
|
||||||
} else {
|
|
||||||
// Fallback on a toast
|
|
||||||
Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface Callback {
|
|
||||||
void onScanningStateChanged(boolean started);
|
|
||||||
void onDeviceAdded(CachedBluetoothDevice cachedDevice);
|
|
||||||
void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
|
|
||||||
void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean shouldShowDialogInForeground(String deviceAddress) {
|
|
||||||
// If Bluetooth Settings is visible
|
|
||||||
if (mForegroundActivity != null) return true;
|
|
||||||
|
|
||||||
long currentTimeMillis = System.currentTimeMillis();
|
|
||||||
SharedPreferences sharedPreferences = getSharedPreferences();
|
|
||||||
|
|
||||||
// If the device was in discoverABLE mode recently
|
|
||||||
long lastDiscoverableEndTime = sharedPreferences.getLong(
|
|
||||||
BluetoothDiscoverableEnabler.SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP, 0);
|
|
||||||
if ((lastDiscoverableEndTime + GRACE_PERIOD_TO_SHOW_DIALOGS_IN_FOREGROUND)
|
|
||||||
> currentTimeMillis) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the device was discoverING recently
|
|
||||||
if (mAdapter != null && mAdapter.isDiscovering()) {
|
|
||||||
return true;
|
|
||||||
} else if ((sharedPreferences.getLong(SHARED_PREFERENCES_KEY_DISCOVERING_TIMESTAMP, 0) +
|
|
||||||
GRACE_PERIOD_TO_SHOW_DIALOGS_IN_FOREGROUND) > currentTimeMillis) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the device was picked in the device picker recently
|
|
||||||
if (deviceAddress != null) {
|
|
||||||
String lastSelectedDevice = sharedPreferences.getString(
|
|
||||||
SHARED_PREFERENCES_KEY_LAST_SELECTED_DEVICE, null);
|
|
||||||
|
|
||||||
if (deviceAddress.equals(lastSelectedDevice)) {
|
|
||||||
long lastDeviceSelectedTime = sharedPreferences.getLong(
|
|
||||||
SHARED_PREFERENCES_KEY_LAST_SELECTED_DEVICE_TIME, 0);
|
|
||||||
if ((lastDeviceSelectedTime + GRACE_PERIOD_TO_SHOW_DIALOGS_IN_FOREGROUND)
|
|
||||||
> currentTimeMillis) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void persistSelectedDeviceInPicker(String deviceAddress) {
|
|
||||||
SharedPreferences.Editor editor = getSharedPreferences().edit();
|
|
||||||
editor.putString(LocalBluetoothManager.SHARED_PREFERENCES_KEY_LAST_SELECTED_DEVICE,
|
|
||||||
deviceAddress);
|
|
||||||
editor.putLong(LocalBluetoothManager.SHARED_PREFERENCES_KEY_LAST_SELECTED_DEVICE_TIME,
|
|
||||||
System.currentTimeMillis());
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasDockAutoConnectSetting(String addr) {
|
|
||||||
return getSharedPreferences().contains(SHARED_PREFERENCES_KEY_DOCK_AUTO_CONNECT + addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getDockAutoConnectSetting(String addr) {
|
|
||||||
return getSharedPreferences().getBoolean(SHARED_PREFERENCES_KEY_DOCK_AUTO_CONNECT + addr,
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveDockAutoConnectSetting(String addr, boolean autoConnect) {
|
|
||||||
SharedPreferences.Editor editor = getSharedPreferences().edit();
|
|
||||||
editor.putBoolean(SHARED_PREFERENCES_KEY_DOCK_AUTO_CONNECT + addr, autoConnect);
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeDockAutoConnectSetting(String addr) {
|
|
||||||
SharedPreferences.Editor editor = getSharedPreferences().edit();
|
|
||||||
editor.remove(SHARED_PREFERENCES_KEY_DOCK_AUTO_CONNECT + addr);
|
|
||||||
editor.apply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2008 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.app.QueuedWork;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LocalBluetoothPreferences provides an interface to the preferences
|
||||||
|
* related to Bluetooth.
|
||||||
|
*/
|
||||||
|
final class LocalBluetoothPreferences {
|
||||||
|
// private static final String TAG = "LocalBluetoothPreferences";
|
||||||
|
|
||||||
|
private static final String SHARED_PREFERENCES_NAME = "bluetooth_settings";
|
||||||
|
|
||||||
|
// If a device was picked from the device picker or was in discoverable mode
|
||||||
|
// in the last 60 seconds, show the pairing dialogs in foreground instead
|
||||||
|
// of raising notifications
|
||||||
|
private static final int GRACE_PERIOD_TO_SHOW_DIALOGS_IN_FOREGROUND = 60 * 1000;
|
||||||
|
|
||||||
|
private static final String KEY_DISCOVERING_TIMESTAMP = "last_discovering_time";
|
||||||
|
|
||||||
|
private static final String KEY_LAST_SELECTED_DEVICE = "last_selected_device";
|
||||||
|
|
||||||
|
private static final String KEY_LAST_SELECTED_DEVICE_TIME = "last_selected_device_time";
|
||||||
|
|
||||||
|
private static final String KEY_DOCK_AUTO_CONNECT = "auto_connect_to_dock";
|
||||||
|
|
||||||
|
private static final String KEY_DISCOVERABLE_END_TIMESTAMP = "discoverable_end_timestamp";
|
||||||
|
|
||||||
|
private LocalBluetoothPreferences() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SharedPreferences getSharedPreferences(Context context) {
|
||||||
|
return context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long getDiscoverableEndTimestamp(Context context) {
|
||||||
|
return getSharedPreferences(context).getLong(
|
||||||
|
KEY_DISCOVERABLE_END_TIMESTAMP, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean shouldShowDialogInForeground(Context context,
|
||||||
|
String deviceAddress) {
|
||||||
|
LocalBluetoothManager manager = LocalBluetoothManager.getInstance(context);
|
||||||
|
if (manager == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Bluetooth Settings is visible
|
||||||
|
if (manager.isForegroundActivity()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
long currentTimeMillis = System.currentTimeMillis();
|
||||||
|
SharedPreferences sharedPreferences = getSharedPreferences(context);
|
||||||
|
|
||||||
|
// If the device was in discoverABLE mode recently
|
||||||
|
long lastDiscoverableEndTime = sharedPreferences.getLong(
|
||||||
|
KEY_DISCOVERABLE_END_TIMESTAMP, 0);
|
||||||
|
if ((lastDiscoverableEndTime + GRACE_PERIOD_TO_SHOW_DIALOGS_IN_FOREGROUND)
|
||||||
|
> currentTimeMillis) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the device was discoverING recently
|
||||||
|
LocalBluetoothAdapter adapter = manager.getBluetoothAdapter();
|
||||||
|
if (adapter != null && adapter.isDiscovering()) {
|
||||||
|
return true;
|
||||||
|
} else if ((sharedPreferences.getLong(KEY_DISCOVERING_TIMESTAMP, 0) +
|
||||||
|
GRACE_PERIOD_TO_SHOW_DIALOGS_IN_FOREGROUND) > currentTimeMillis) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the device was picked in the device picker recently
|
||||||
|
if (deviceAddress != null) {
|
||||||
|
String lastSelectedDevice = sharedPreferences.getString(
|
||||||
|
KEY_LAST_SELECTED_DEVICE, null);
|
||||||
|
|
||||||
|
if (deviceAddress.equals(lastSelectedDevice)) {
|
||||||
|
long lastDeviceSelectedTime = sharedPreferences.getLong(
|
||||||
|
KEY_LAST_SELECTED_DEVICE_TIME, 0);
|
||||||
|
if ((lastDeviceSelectedTime + GRACE_PERIOD_TO_SHOW_DIALOGS_IN_FOREGROUND)
|
||||||
|
> currentTimeMillis) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void persistSelectedDeviceInPicker(Context context, String deviceAddress) {
|
||||||
|
SharedPreferences.Editor editor = getSharedPreferences(context).edit();
|
||||||
|
editor.putString(KEY_LAST_SELECTED_DEVICE,
|
||||||
|
deviceAddress);
|
||||||
|
editor.putLong(KEY_LAST_SELECTED_DEVICE_TIME,
|
||||||
|
System.currentTimeMillis());
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void persistDiscoverableEndTimestamp(Context context, long endTimestamp) {
|
||||||
|
SharedPreferences.Editor editor = getSharedPreferences(context).edit();
|
||||||
|
editor.putLong(KEY_DISCOVERABLE_END_TIMESTAMP, endTimestamp);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void persistDiscoveringTimestamp(final Context context) {
|
||||||
|
// Load the shared preferences and edit it on a background
|
||||||
|
// thread (but serialized!).
|
||||||
|
QueuedWork.singleThreadExecutor().submit(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
SharedPreferences.Editor editor = getSharedPreferences(context).edit();
|
||||||
|
editor.putLong(
|
||||||
|
KEY_DISCOVERING_TIMESTAMP,
|
||||||
|
System.currentTimeMillis());
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean hasDockAutoConnectSetting(Context context, String addr) {
|
||||||
|
return getSharedPreferences(context).contains(KEY_DOCK_AUTO_CONNECT + addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean getDockAutoConnectSetting(Context context, String addr) {
|
||||||
|
return getSharedPreferences(context).getBoolean(KEY_DOCK_AUTO_CONNECT + addr,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void saveDockAutoConnectSetting(Context context, String addr, boolean autoConnect) {
|
||||||
|
SharedPreferences.Editor editor = getSharedPreferences(context).edit();
|
||||||
|
editor.putBoolean(KEY_DOCK_AUTO_CONNECT + addr, autoConnect);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void removeDockAutoConnectSetting(Context context, String addr) {
|
||||||
|
SharedPreferences.Editor editor = getSharedPreferences(context).edit();
|
||||||
|
editor.remove(KEY_DOCK_AUTO_CONNECT + addr);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LocalBluetoothProfile is an interface defining the basic
|
||||||
|
* functionality related to a Bluetooth profile.
|
||||||
|
*/
|
||||||
|
interface LocalBluetoothProfile {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the user can initiate a connection, false otherwise.
|
||||||
|
*/
|
||||||
|
boolean isConnectable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the user can enable auto connection for this profile.
|
||||||
|
*/
|
||||||
|
boolean isAutoConnectable();
|
||||||
|
|
||||||
|
boolean connect(BluetoothDevice device);
|
||||||
|
|
||||||
|
boolean disconnect(BluetoothDevice device);
|
||||||
|
|
||||||
|
int getConnectionStatus(BluetoothDevice device);
|
||||||
|
|
||||||
|
boolean isPreferred(BluetoothDevice device);
|
||||||
|
|
||||||
|
int getPreferred(BluetoothDevice device);
|
||||||
|
|
||||||
|
void setPreferred(BluetoothDevice device, boolean preferred);
|
||||||
|
|
||||||
|
boolean isProfileReady();
|
||||||
|
|
||||||
|
/** Display order for device profile settings. */
|
||||||
|
int getOrdinal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the string resource ID for the localized name for this profile.
|
||||||
|
*/
|
||||||
|
int getNameResource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the string resource ID for the disconnect confirmation text
|
||||||
|
* for this profile.
|
||||||
|
*/
|
||||||
|
int getDisconnectResource();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the string resource ID for the summary text for this profile
|
||||||
|
* for the specified device, e.g. "Use for media audio" or
|
||||||
|
* "Connected to media audio".
|
||||||
|
* @param device the device to query for profile connection status
|
||||||
|
* @return a string resource ID for the profile summary text
|
||||||
|
*/
|
||||||
|
int getSummaryResourceForDevice(BluetoothDevice device);
|
||||||
|
|
||||||
|
int getDrawableResource(BluetoothClass btClass);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
92
src/com/android/settings/bluetooth/OppProfile.java
Normal file
92
src/com/android/settings/bluetooth/OppProfile.java
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OppProfile handles Bluetooth OPP.
|
||||||
|
*/
|
||||||
|
final class OppProfile implements LocalBluetoothProfile {
|
||||||
|
|
||||||
|
static final String NAME = "OPP";
|
||||||
|
|
||||||
|
// Order of this profile in device profiles list
|
||||||
|
private static final int ORDINAL = 2;
|
||||||
|
|
||||||
|
public boolean isConnectable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAutoConnectable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connect(BluetoothDevice device) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean disconnect(BluetoothDevice device) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectionStatus(BluetoothDevice device) {
|
||||||
|
return -1; // FIXME: change to DISCONNECTED?
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPreferred(BluetoothDevice device) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPreferred(BluetoothDevice device) {
|
||||||
|
return -1; // FIXME: is this correct?
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreferred(BluetoothDevice device, boolean preferred) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isProfileReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrdinal() {
|
||||||
|
return ORDINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameResource() {
|
||||||
|
return R.string.bluetooth_profile_opp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisconnectResource() {
|
||||||
|
return 0; // user must use notification to disconnect OPP transfer.
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSummaryResourceForDevice(BluetoothDevice device) {
|
||||||
|
return 0; // OPP profile not displayed in UI
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDrawableResource(BluetoothClass btClass) {
|
||||||
|
return 0; // no icon for OPP
|
||||||
|
}
|
||||||
|
}
|
135
src/com/android/settings/bluetooth/PanProfile.java
Normal file
135
src/com/android/settings/bluetooth/PanProfile.java
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothDevice;
|
||||||
|
import android.bluetooth.BluetoothPan;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PanProfile handles Bluetooth PAN profile.
|
||||||
|
*/
|
||||||
|
final class PanProfile implements LocalBluetoothProfile {
|
||||||
|
private BluetoothPan mService;
|
||||||
|
|
||||||
|
static final String NAME = "PAN";
|
||||||
|
|
||||||
|
// Order of this profile in device profiles list
|
||||||
|
private static final int ORDINAL = 4;
|
||||||
|
|
||||||
|
// These callbacks run on the main thread.
|
||||||
|
private final class PanServiceListener
|
||||||
|
implements BluetoothProfile.ServiceListener {
|
||||||
|
|
||||||
|
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||||
|
mService = (BluetoothPan) proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onServiceDisconnected(int profile) {
|
||||||
|
mService = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PanProfile(Context context) {
|
||||||
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
adapter.getProfileProxy(context, new PanServiceListener(),
|
||||||
|
BluetoothProfile.PAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnectable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAutoConnectable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean connect(BluetoothDevice device) {
|
||||||
|
List<BluetoothDevice> sinks = mService.getConnectedDevices();
|
||||||
|
if (sinks != null) {
|
||||||
|
for (BluetoothDevice sink : sinks) {
|
||||||
|
mService.disconnect(sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mService.connect(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean disconnect(BluetoothDevice device) {
|
||||||
|
return mService.disconnect(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectionStatus(BluetoothDevice device) {
|
||||||
|
return mService.getConnectionState(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPreferred(BluetoothDevice device) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPreferred(BluetoothDevice device) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreferred(BluetoothDevice device, boolean preferred) {
|
||||||
|
// ignore: isPreferred is always true for PAN
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isProfileReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrdinal() {
|
||||||
|
return ORDINAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameResource() {
|
||||||
|
return R.string.bluetooth_profile_pan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDisconnectResource() {
|
||||||
|
return R.string.bluetooth_disconnect_pan_profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSummaryResourceForDevice(BluetoothDevice device) {
|
||||||
|
int state = mService.getConnectionState(device);
|
||||||
|
switch (state) {
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
return R.string.bluetooth_pan_profile_summary_use_for;
|
||||||
|
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
return R.string.bluetooth_pan_profile_summary_connected;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Utils.getConnectionStateSummary(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDrawableResource(BluetoothClass btClass) {
|
||||||
|
return R.drawable.ic_bt_network_pan;
|
||||||
|
}
|
||||||
|
}
|
@@ -27,7 +27,6 @@ import android.content.Context;
|
|||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
|
|
||||||
private static final int REQUEST_CODE_START_BT = 1;
|
private static final int REQUEST_CODE_START_BT = 1;
|
||||||
|
|
||||||
private LocalBluetoothManager mLocalManager;
|
private LocalBluetoothAdapter mLocalAdapter;
|
||||||
|
|
||||||
private int mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT;
|
private int mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT;
|
||||||
|
|
||||||
@@ -66,18 +65,19 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
|
|
||||||
// True if requesting BT to be turned on
|
// True if requesting BT to be turned on
|
||||||
// False if requesting BT to be turned on + discoverable mode
|
// False if requesting BT to be turned on + discoverable mode
|
||||||
private boolean mEnableOnly = false;
|
private boolean mEnableOnly;
|
||||||
|
|
||||||
private boolean mUserConfirmed = false;
|
private boolean mUserConfirmed;
|
||||||
|
|
||||||
private AlertDialog mDialog = null;
|
private AlertDialog mDialog;
|
||||||
|
|
||||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (intent == null)
|
if (intent == null) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if (mNeededToEnableBluetooth
|
if (mNeededToEnableBluetooth
|
||||||
&& BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
|
&& BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
|
||||||
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothDevice.ERROR);
|
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothDevice.ERROR);
|
||||||
@@ -94,12 +94,13 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Note: initializes mLocalAdapter and returns true on error
|
||||||
if (parseIntent()) {
|
if (parseIntent()) {
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int btState = mLocalManager.getBluetoothState();
|
int btState = mLocalAdapter.getState();
|
||||||
|
|
||||||
switch (btState) {
|
switch (btState) {
|
||||||
case BluetoothAdapter.STATE_OFF:
|
case BluetoothAdapter.STATE_OFF:
|
||||||
@@ -120,28 +121,29 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
*/
|
*/
|
||||||
registerReceiver(mReceiver,
|
registerReceiver(mReceiver,
|
||||||
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
|
||||||
Intent i = new Intent();
|
Intent intent = new Intent();
|
||||||
i.setClass(this, RequestPermissionHelperActivity.class);
|
intent.setClass(this, RequestPermissionHelperActivity.class);
|
||||||
if (mEnableOnly) {
|
if (mEnableOnly) {
|
||||||
i.setAction(RequestPermissionHelperActivity.ACTION_INTERNAL_REQUEST_BT_ON);
|
intent.setAction(RequestPermissionHelperActivity.ACTION_INTERNAL_REQUEST_BT_ON);
|
||||||
} else {
|
} else {
|
||||||
i.setAction(RequestPermissionHelperActivity.
|
intent.setAction(RequestPermissionHelperActivity.
|
||||||
ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE);
|
ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE);
|
||||||
i.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, mTimeout);
|
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, mTimeout);
|
||||||
}
|
}
|
||||||
startActivityForResult(i, REQUEST_CODE_START_BT);
|
startActivityForResult(intent, REQUEST_CODE_START_BT);
|
||||||
mNeededToEnableBluetooth = true;
|
mNeededToEnableBluetooth = true;
|
||||||
break;
|
break;
|
||||||
case BluetoothAdapter.STATE_ON:
|
case BluetoothAdapter.STATE_ON:
|
||||||
if (mEnableOnly) {
|
if (mEnableOnly) {
|
||||||
// Nothing to do. Already enabled.
|
// Nothing to do. Already enabled.
|
||||||
proceedAndFinish();
|
proceedAndFinish();
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
// Ask the user about enabling discovery mode
|
// Ask the user about enabling discovery mode
|
||||||
createDialog();
|
createDialog();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.e(TAG, "Unknown adapter state: " + btState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,8 +178,8 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (requestCode != REQUEST_CODE_START_BT) {
|
if (requestCode != REQUEST_CODE_START_BT) {
|
||||||
Log.e(TAG, "Unexpected onActivityResult " + requestCode + " " + resultCode);
|
Log.e(TAG, "Unexpected onActivityResult " + requestCode + ' ' + resultCode);
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -191,7 +193,7 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
// BT and discoverable mode.
|
// BT and discoverable mode.
|
||||||
mUserConfirmed = true;
|
mUserConfirmed = true;
|
||||||
|
|
||||||
if (mLocalManager.getBluetoothState() == BluetoothAdapter.STATE_ON) {
|
if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
|
||||||
proceedAndFinish();
|
proceedAndFinish();
|
||||||
} else {
|
} else {
|
||||||
// If BT is not up yet, show "Turning on Bluetooth..."
|
// If BT is not up yet, show "Turning on Bluetooth..."
|
||||||
@@ -206,7 +208,7 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case DialogInterface.BUTTON_NEGATIVE:
|
case DialogInterface.BUTTON_NEGATIVE:
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
finish();
|
finish();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -217,18 +219,19 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
|
|
||||||
if (mEnableOnly) {
|
if (mEnableOnly) {
|
||||||
// BT enabled. Done
|
// BT enabled. Done
|
||||||
returnCode = Activity.RESULT_OK;
|
returnCode = RESULT_OK;
|
||||||
} else if (mLocalManager.getBluetoothAdapter().setScanMode(
|
} else if (mLocalAdapter.setScanMode(
|
||||||
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, mTimeout)) {
|
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, mTimeout)) {
|
||||||
// If already in discoverable mode, this will extend the timeout.
|
// If already in discoverable mode, this will extend the timeout.
|
||||||
persistDiscoverableEndTimestamp(System.currentTimeMillis() + mTimeout * 1000);
|
LocalBluetoothPreferences.persistDiscoverableEndTimestamp(
|
||||||
|
this, System.currentTimeMillis() + (long) mTimeout * 1000);
|
||||||
returnCode = mTimeout;
|
returnCode = mTimeout;
|
||||||
// Activity.RESULT_FIRST_USER should be 1
|
// Activity.RESULT_FIRST_USER should be 1
|
||||||
if (returnCode < Activity.RESULT_FIRST_USER) {
|
if (returnCode < RESULT_FIRST_USER) {
|
||||||
returnCode = Activity.RESULT_FIRST_USER;
|
returnCode = RESULT_FIRST_USER;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
returnCode = Activity.RESULT_CANCELED;
|
returnCode = RESULT_CANCELED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDialog != null) {
|
if (mDialog != null) {
|
||||||
@@ -239,6 +242,10 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the received Intent and initialize mLocalBluetoothAdapter.
|
||||||
|
* @return true if an error occurred; false otherwise
|
||||||
|
*/
|
||||||
private boolean parseIntent() {
|
private boolean parseIntent() {
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
if (intent != null && intent.getAction().equals(BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
|
if (intent != null && intent.getAction().equals(BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
|
||||||
@@ -257,16 +264,17 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
Log.e(TAG, "Error: this activity may be started only with intent "
|
Log.e(TAG, "Error: this activity may be started only with intent "
|
||||||
+ BluetoothAdapter.ACTION_REQUEST_ENABLE + " or "
|
+ BluetoothAdapter.ACTION_REQUEST_ENABLE + " or "
|
||||||
+ BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
|
+ BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLocalManager = LocalBluetoothManager.getInstance(this);
|
LocalBluetoothManager manager = LocalBluetoothManager.getInstance(this);
|
||||||
if (mLocalManager == null) {
|
if (manager == null) {
|
||||||
Log.e(TAG, "Error: there's a problem starting bluetooth");
|
Log.e(TAG, "Error: there's a problem starting Bluetooth");
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
mLocalAdapter = manager.getBluetoothAdapter();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -274,20 +282,14 @@ public class RequestPermissionActivity extends Activity implements
|
|||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
if (mNeededToEnableBluetooth) unregisterReceiver(mReceiver);
|
if (mNeededToEnableBluetooth) {
|
||||||
|
unregisterReceiver(mReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void persistDiscoverableEndTimestamp(long endTimestamp) {
|
|
||||||
SharedPreferences.Editor editor = mLocalManager.getSharedPreferences().edit();
|
|
||||||
editor.putLong(
|
|
||||||
BluetoothDiscoverableEnabler.SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP,
|
|
||||||
endTimestamp);
|
|
||||||
editor.apply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -43,7 +43,7 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
|
|||||||
public static final String ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE =
|
public static final String ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE =
|
||||||
"com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE";
|
"com.android.settings.bluetooth.ACTION_INTERNAL_REQUEST_BT_ON_AND_DISCOVERABLE";
|
||||||
|
|
||||||
private LocalBluetoothManager mLocalManager;
|
private LocalBluetoothAdapter mLocalAdapter;
|
||||||
|
|
||||||
private int mTimeout;
|
private int mTimeout;
|
||||||
|
|
||||||
@@ -55,6 +55,7 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Note: initializes mLocalAdapter and returns true on error
|
||||||
if (parseIntent()) {
|
if (parseIntent()) {
|
||||||
finish();
|
finish();
|
||||||
return;
|
return;
|
||||||
@@ -92,32 +93,33 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
|
|||||||
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
int returnCode;
|
int returnCode;
|
||||||
|
// FIXME: fix this ugly switch logic!
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case DialogInterface.BUTTON_POSITIVE:
|
case BUTTON_POSITIVE:
|
||||||
int btState = 0;
|
int btState = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// TODO There's a better way.
|
// TODO There's a better way.
|
||||||
int retryCount = 30;
|
int retryCount = 30;
|
||||||
do {
|
do {
|
||||||
btState = mLocalManager.getBluetoothState();
|
btState = mLocalAdapter.getBluetoothState();
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
} while (btState == BluetoothAdapter.STATE_TURNING_OFF && --retryCount > 0);
|
} while (btState == BluetoothAdapter.STATE_TURNING_OFF && --retryCount > 0);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException ignored) {
|
||||||
// don't care
|
// don't care
|
||||||
}
|
}
|
||||||
|
|
||||||
if (btState == BluetoothAdapter.STATE_TURNING_ON
|
if (btState == BluetoothAdapter.STATE_TURNING_ON
|
||||||
|| btState == BluetoothAdapter.STATE_ON
|
|| btState == BluetoothAdapter.STATE_ON
|
||||||
|| mLocalManager.getBluetoothAdapter().enable()) {
|
|| mLocalAdapter.enable()) {
|
||||||
returnCode = RequestPermissionActivity.RESULT_BT_STARTING_OR_STARTED;
|
returnCode = RequestPermissionActivity.RESULT_BT_STARTING_OR_STARTED;
|
||||||
} else {
|
} else {
|
||||||
returnCode = Activity.RESULT_CANCELED;
|
returnCode = RESULT_CANCELED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DialogInterface.BUTTON_NEGATIVE:
|
case BUTTON_NEGATIVE:
|
||||||
returnCode = Activity.RESULT_CANCELED;
|
returnCode = RESULT_CANCELED;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
@@ -125,6 +127,10 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
|
|||||||
setResult(returnCode);
|
setResult(returnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the received Intent and initialize mLocalBluetoothAdapter.
|
||||||
|
* @return true if an error occurred; false otherwise
|
||||||
|
*/
|
||||||
private boolean parseIntent() {
|
private boolean parseIntent() {
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
if (intent != null && intent.getAction().equals(ACTION_INTERNAL_REQUEST_BT_ON)) {
|
if (intent != null && intent.getAction().equals(ACTION_INTERNAL_REQUEST_BT_ON)) {
|
||||||
@@ -136,23 +142,24 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
|
|||||||
mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
|
mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
|
||||||
BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT);
|
BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT);
|
||||||
} else {
|
} else {
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLocalManager = LocalBluetoothManager.getInstance(this);
|
LocalBluetoothManager manager = LocalBluetoothManager.getInstance(this);
|
||||||
if (mLocalManager == null) {
|
if (manager == null) {
|
||||||
Log.e(TAG, "Error: there's a problem starting bluetooth");
|
Log.e(TAG, "Error: there's a problem starting Bluetooth");
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
mLocalAdapter = manager.getBluetoothAdapter();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.bluetooth;
|
|
||||||
|
|
||||||
import android.bluetooth.BluetoothDevice;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SettingsBtStatus is a helper class that contains constants for various status
|
|
||||||
* codes.
|
|
||||||
*/
|
|
||||||
class SettingsBtStatus {
|
|
||||||
private static final String TAG = "SettingsBtStatus";
|
|
||||||
|
|
||||||
// Connection status
|
|
||||||
|
|
||||||
public static final int CONNECTION_STATUS_UNKNOWN = 0;
|
|
||||||
public static final int CONNECTION_STATUS_ACTIVE = 1;
|
|
||||||
/** Use {@link #isConnectionStatusConnected} to check for the connected state */
|
|
||||||
public static final int CONNECTION_STATUS_CONNECTED = 2;
|
|
||||||
public static final int CONNECTION_STATUS_CONNECTING = 3;
|
|
||||||
public static final int CONNECTION_STATUS_DISCONNECTED = 4;
|
|
||||||
public static final int CONNECTION_STATUS_DISCONNECTING = 5;
|
|
||||||
|
|
||||||
public static final int getConnectionStatusSummary(int connectionStatus) {
|
|
||||||
switch (connectionStatus) {
|
|
||||||
case CONNECTION_STATUS_ACTIVE:
|
|
||||||
return R.string.bluetooth_connected;
|
|
||||||
case CONNECTION_STATUS_CONNECTED:
|
|
||||||
return R.string.bluetooth_connected;
|
|
||||||
case CONNECTION_STATUS_CONNECTING:
|
|
||||||
return R.string.bluetooth_connecting;
|
|
||||||
case CONNECTION_STATUS_DISCONNECTED:
|
|
||||||
return R.string.bluetooth_disconnected;
|
|
||||||
case CONNECTION_STATUS_DISCONNECTING:
|
|
||||||
return R.string.bluetooth_disconnecting;
|
|
||||||
case CONNECTION_STATUS_UNKNOWN:
|
|
||||||
return R.string.bluetooth_unknown;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final boolean isConnectionStatusConnected(int connectionStatus) {
|
|
||||||
return connectionStatus == CONNECTION_STATUS_ACTIVE
|
|
||||||
|| connectionStatus == CONNECTION_STATUS_CONNECTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final boolean isConnectionStatusBusy(int connectionStatus) {
|
|
||||||
return connectionStatus == CONNECTION_STATUS_CONNECTING
|
|
||||||
|| connectionStatus == CONNECTION_STATUS_DISCONNECTING;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final int getPairingStatusSummary(int bondState) {
|
|
||||||
switch (bondState) {
|
|
||||||
case BluetoothDevice.BOND_BONDED:
|
|
||||||
return R.string.bluetooth_paired;
|
|
||||||
case BluetoothDevice.BOND_BONDING:
|
|
||||||
return R.string.bluetooth_pairing;
|
|
||||||
case BluetoothDevice.BOND_NONE:
|
|
||||||
return R.string.bluetooth_not_connected;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
84
src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java
Normal file
84
src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.text.InputFilter;
|
||||||
|
import android.text.Spanned;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This filter will constrain edits so that the text length is not
|
||||||
|
* greater than the specified number of bytes using UTF-8 encoding.
|
||||||
|
* <p>The JNI method used by {@link android.server.BluetoothService}
|
||||||
|
* to convert UTF-16 to UTF-8 doesn't support surrogate pairs,
|
||||||
|
* therefore code points outside of the basic multilingual plane
|
||||||
|
* (0000-FFFF) will be encoded as a pair of 3-byte UTF-8 characters,
|
||||||
|
* rather than a single 4-byte UTF-8 encoding. Dalvik implements this
|
||||||
|
* conversion in {@code convertUtf16ToUtf8()} in
|
||||||
|
* {@code dalvik/vm/UtfString.c}.
|
||||||
|
* <p>This JNI method is unlikely to change in the future due to
|
||||||
|
* backwards compatibility requirements. It's also unclear whether
|
||||||
|
* the installed base of Bluetooth devices would correctly handle the
|
||||||
|
* encoding of surrogate pairs in UTF-8 as 4 bytes rather than 6.
|
||||||
|
* However, this filter will still work in scenarios where surrogate
|
||||||
|
* pairs are encoded as 4 bytes, with the caveat that the maximum
|
||||||
|
* length will be constrained more conservatively than necessary.
|
||||||
|
*/
|
||||||
|
class Utf8ByteLengthFilter implements InputFilter {
|
||||||
|
private final int mMaxBytes;
|
||||||
|
|
||||||
|
Utf8ByteLengthFilter(int maxBytes) {
|
||||||
|
mMaxBytes = maxBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence filter(CharSequence source, int start, int end,
|
||||||
|
Spanned dest, int dstart, int dend) {
|
||||||
|
int srcByteCount = 0;
|
||||||
|
// count UTF-8 bytes in source substring
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
char c = source.charAt(i);
|
||||||
|
srcByteCount += (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3);
|
||||||
|
}
|
||||||
|
int destLen = dest.length();
|
||||||
|
int destByteCount = 0;
|
||||||
|
// count UTF-8 bytes in destination excluding replaced section
|
||||||
|
for (int i = 0; i < destLen; i++) {
|
||||||
|
if (i < dstart || i >= dend) {
|
||||||
|
char c = dest.charAt(i);
|
||||||
|
destByteCount += (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int keepBytes = mMaxBytes - destByteCount;
|
||||||
|
if (keepBytes <= 0) {
|
||||||
|
return "";
|
||||||
|
} else if (keepBytes >= srcByteCount) {
|
||||||
|
return null; // use original dest string
|
||||||
|
} else {
|
||||||
|
// find end position of largest sequence that fits in keepBytes
|
||||||
|
for (int i = start; i < end; i++) {
|
||||||
|
char c = source.charAt(i);
|
||||||
|
keepBytes -= (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3);
|
||||||
|
if (keepBytes < 0) {
|
||||||
|
return source.subSequence(start, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the entire substring fits, we should have returned null
|
||||||
|
// above, so this line should not be reached. If for some
|
||||||
|
// reason it is, return null to use the original dest string.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
src/com/android/settings/bluetooth/Utils.java
Normal file
99
src/com/android/settings/bluetooth/Utils.java
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.bluetooth.BluetoothClass;
|
||||||
|
import android.bluetooth.BluetoothProfile;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utils is a helper class that contains constants for various
|
||||||
|
* Android resource IDs, debug logging flags, and static methods
|
||||||
|
* for creating dialogs.
|
||||||
|
*/
|
||||||
|
final class Utils {
|
||||||
|
static final boolean V = false; // verbose logging
|
||||||
|
static final boolean D = true; // regular logging
|
||||||
|
|
||||||
|
private Utils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getConnectionStateSummary(int connectionState) {
|
||||||
|
switch (connectionState) {
|
||||||
|
case BluetoothProfile.STATE_CONNECTED:
|
||||||
|
return R.string.bluetooth_connected;
|
||||||
|
case BluetoothProfile.STATE_CONNECTING:
|
||||||
|
return R.string.bluetooth_connecting;
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTED:
|
||||||
|
return R.string.bluetooth_disconnected;
|
||||||
|
case BluetoothProfile.STATE_DISCONNECTING:
|
||||||
|
return R.string.bluetooth_disconnecting;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create (or recycle existing) and show disconnect dialog.
|
||||||
|
static AlertDialog showDisconnectDialog(Context context,
|
||||||
|
AlertDialog dialog,
|
||||||
|
DialogInterface.OnClickListener disconnectListener,
|
||||||
|
CharSequence title, CharSequence message) {
|
||||||
|
if (dialog == null) {
|
||||||
|
dialog = new AlertDialog.Builder(context)
|
||||||
|
.setPositiveButton(android.R.string.ok, disconnectListener)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.create();
|
||||||
|
} else {
|
||||||
|
if (dialog.isShowing()) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
// use disconnectListener for the correct profile(s)
|
||||||
|
CharSequence okText = context.getText(android.R.string.ok);
|
||||||
|
dialog.setButton(DialogInterface.BUTTON_POSITIVE,
|
||||||
|
okText, disconnectListener);
|
||||||
|
}
|
||||||
|
dialog.setTitle(title);
|
||||||
|
dialog.setMessage(message);
|
||||||
|
dialog.show();
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: wire this up to show connection errors...
|
||||||
|
static void showConnectingError(Context context, String name) {
|
||||||
|
// if (!mIsConnectingErrorPossible) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// mIsConnectingErrorPossible = false;
|
||||||
|
|
||||||
|
showError(context, name, R.string.bluetooth_connecting_error_message);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showError(Context context, String name, int messageResId) {
|
||||||
|
String message = context.getString(messageResId, name);
|
||||||
|
new AlertDialog.Builder(context)
|
||||||
|
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||||
|
.setTitle(R.string.bluetooth_error_title)
|
||||||
|
.setMessage(message)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
@@ -38,6 +38,7 @@ import android.provider.Settings;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.bluetooth.LocalBluetoothAdapter;
|
||||||
import com.android.settings.bluetooth.LocalBluetoothManager;
|
import com.android.settings.bluetooth.LocalBluetoothManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,7 +51,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
|
|||||||
new ComponentName("com.android.settings",
|
new ComponentName("com.android.settings",
|
||||||
"com.android.settings.widget.SettingsAppWidgetProvider");
|
"com.android.settings.widget.SettingsAppWidgetProvider");
|
||||||
|
|
||||||
private static LocalBluetoothManager sLocalBluetoothManager = null;
|
private static LocalBluetoothAdapter sLocalBluetoothAdapter = null;
|
||||||
|
|
||||||
private static final int BUTTON_WIFI = 0;
|
private static final int BUTTON_WIFI = 0;
|
||||||
private static final int BUTTON_BRIGHTNESS = 1;
|
private static final int BUTTON_BRIGHTNESS = 1;
|
||||||
@@ -411,18 +412,19 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getActualState(Context context) {
|
public int getActualState(Context context) {
|
||||||
if (sLocalBluetoothManager == null) {
|
if (sLocalBluetoothAdapter == null) {
|
||||||
sLocalBluetoothManager = LocalBluetoothManager.getInstance(context);
|
LocalBluetoothManager manager = LocalBluetoothManager.getInstance(context);
|
||||||
if (sLocalBluetoothManager == null) {
|
if (manager == null) {
|
||||||
return STATE_UNKNOWN; // On emulator?
|
return STATE_UNKNOWN; // On emulator?
|
||||||
}
|
}
|
||||||
|
sLocalBluetoothAdapter = manager.getBluetoothAdapter();
|
||||||
}
|
}
|
||||||
return bluetoothStateToFiveState(sLocalBluetoothManager.getBluetoothState());
|
return bluetoothStateToFiveState(sLocalBluetoothAdapter.getBluetoothState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void requestStateChange(Context context, final boolean desiredState) {
|
protected void requestStateChange(Context context, final boolean desiredState) {
|
||||||
if (sLocalBluetoothManager == null) {
|
if (sLocalBluetoothAdapter == null) {
|
||||||
Log.d(TAG, "No LocalBluetoothManager");
|
Log.d(TAG, "No LocalBluetoothManager");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -433,7 +435,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
|
|||||||
new AsyncTask<Void, Void, Void>() {
|
new AsyncTask<Void, Void, Void>() {
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... args) {
|
protected Void doInBackground(Void... args) {
|
||||||
sLocalBluetoothManager.setBluetoothEnabled(desiredState);
|
sLocalBluetoothAdapter.setBluetoothEnabled(desiredState);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
@@ -584,7 +586,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
|
|||||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
|
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
|
||||||
int[] appWidgetIds) {
|
int[] appWidgetIds) {
|
||||||
// Update each requested appWidgetId
|
// Update each requested appWidgetId
|
||||||
RemoteViews view = buildUpdate(context, -1);
|
RemoteViews view = buildUpdate(context);
|
||||||
|
|
||||||
for (int i = 0; i < appWidgetIds.length; i++) {
|
for (int i = 0; i < appWidgetIds.length; i++) {
|
||||||
appWidgetManager.updateAppWidget(appWidgetIds[i], view);
|
appWidgetManager.updateAppWidget(appWidgetIds[i], view);
|
||||||
@@ -613,22 +615,22 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
|
|||||||
/**
|
/**
|
||||||
* Load image for given widget and build {@link RemoteViews} for it.
|
* Load image for given widget and build {@link RemoteViews} for it.
|
||||||
*/
|
*/
|
||||||
static RemoteViews buildUpdate(Context context, int appWidgetId) {
|
static RemoteViews buildUpdate(Context context) {
|
||||||
RemoteViews views = new RemoteViews(context.getPackageName(),
|
RemoteViews views = new RemoteViews(context.getPackageName(),
|
||||||
R.layout.widget);
|
R.layout.widget);
|
||||||
views.setOnClickPendingIntent(R.id.btn_wifi, getLaunchPendingIntent(context, appWidgetId,
|
views.setOnClickPendingIntent(R.id.btn_wifi, getLaunchPendingIntent(context,
|
||||||
BUTTON_WIFI));
|
BUTTON_WIFI));
|
||||||
views.setOnClickPendingIntent(R.id.btn_brightness,
|
views.setOnClickPendingIntent(R.id.btn_brightness,
|
||||||
getLaunchPendingIntent(context,
|
getLaunchPendingIntent(context,
|
||||||
appWidgetId, BUTTON_BRIGHTNESS));
|
BUTTON_BRIGHTNESS));
|
||||||
views.setOnClickPendingIntent(R.id.btn_sync,
|
views.setOnClickPendingIntent(R.id.btn_sync,
|
||||||
getLaunchPendingIntent(context,
|
getLaunchPendingIntent(context,
|
||||||
appWidgetId, BUTTON_SYNC));
|
BUTTON_SYNC));
|
||||||
views.setOnClickPendingIntent(R.id.btn_gps,
|
views.setOnClickPendingIntent(R.id.btn_gps,
|
||||||
getLaunchPendingIntent(context, appWidgetId, BUTTON_GPS));
|
getLaunchPendingIntent(context, BUTTON_GPS));
|
||||||
views.setOnClickPendingIntent(R.id.btn_bluetooth,
|
views.setOnClickPendingIntent(R.id.btn_bluetooth,
|
||||||
getLaunchPendingIntent(context,
|
getLaunchPendingIntent(context,
|
||||||
appWidgetId, BUTTON_BLUETOOTH));
|
BUTTON_BLUETOOTH));
|
||||||
|
|
||||||
updateButtons(views, context);
|
updateButtons(views, context);
|
||||||
return views;
|
return views;
|
||||||
@@ -640,7 +642,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
|
|||||||
* @param context
|
* @param context
|
||||||
*/
|
*/
|
||||||
public static void updateWidget(Context context) {
|
public static void updateWidget(Context context) {
|
||||||
RemoteViews views = buildUpdate(context, -1);
|
RemoteViews views = buildUpdate(context);
|
||||||
// Update specific list of appWidgetIds if given, otherwise default to all
|
// Update specific list of appWidgetIds if given, otherwise default to all
|
||||||
final AppWidgetManager gm = AppWidgetManager.getInstance(context);
|
final AppWidgetManager gm = AppWidgetManager.getInstance(context);
|
||||||
gm.updateAppWidget(THIS_APPWIDGET, views);
|
gm.updateAppWidget(THIS_APPWIDGET, views);
|
||||||
@@ -680,10 +682,9 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
|
|||||||
* Creates PendingIntent to notify the widget of a button click.
|
* Creates PendingIntent to notify the widget of a button click.
|
||||||
*
|
*
|
||||||
* @param context
|
* @param context
|
||||||
* @param appWidgetId
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static PendingIntent getLaunchPendingIntent(Context context, int appWidgetId,
|
private static PendingIntent getLaunchPendingIntent(Context context,
|
||||||
int buttonId) {
|
int buttonId) {
|
||||||
Intent launchIntent = new Intent();
|
Intent launchIntent = new Intent();
|
||||||
launchIntent.setClass(context, SettingsAppWidgetProvider.class);
|
launchIntent.setClass(context, SettingsAppWidgetProvider.class);
|
||||||
|
@@ -93,7 +93,7 @@ public class SettingsHookTests extends ActivityInstrumentationTestCase2<Settings
|
|||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue("Intent-filer not found", result);
|
assertTrue("Intent-filter not found", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,7 +111,7 @@ public class SettingsHookTests extends ActivityInstrumentationTestCase2<Settings
|
|||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue("Intent-filer not found", result);
|
assertTrue("Intent-filter not found", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,6 +119,7 @@ public class SettingsHookTests extends ActivityInstrumentationTestCase2<Settings
|
|||||||
* application.
|
* application.
|
||||||
*/
|
*/
|
||||||
public void testOperatorPreferenceAvailable() {
|
public void testOperatorPreferenceAvailable() {
|
||||||
|
// TODO: fix this test case to work with fragments
|
||||||
// PreferenceGroup root = (PreferenceGroup)mSettings.findPreference(KEY_SETTINGS_ROOT);
|
// PreferenceGroup root = (PreferenceGroup)mSettings.findPreference(KEY_SETTINGS_ROOT);
|
||||||
// Preference operatorPreference = root.findPreference(KEY_SETTINGS_OPERATOR);
|
// Preference operatorPreference = root.findPreference(KEY_SETTINGS_OPERATOR);
|
||||||
// assertNotNull(operatorPreference);
|
// assertNotNull(operatorPreference);
|
||||||
@@ -129,6 +130,7 @@ public class SettingsHookTests extends ActivityInstrumentationTestCase2<Settings
|
|||||||
* application.
|
* application.
|
||||||
*/
|
*/
|
||||||
public void testManufacturerPreferenceAvailable() {
|
public void testManufacturerPreferenceAvailable() {
|
||||||
|
// TODO: fix this test case to work with fragments
|
||||||
// PreferenceGroup root = (PreferenceGroup)mSettings.findPreference(KEY_SETTINGS_ROOT);
|
// PreferenceGroup root = (PreferenceGroup)mSettings.findPreference(KEY_SETTINGS_ROOT);
|
||||||
// Preference manufacturerHook = root.findPreference(KEY_SETTINGS_MANUFACTURER);
|
// Preference manufacturerHook = root.findPreference(KEY_SETTINGS_MANUFACTURER);
|
||||||
// assertNotNull(manufacturerHook);
|
// assertNotNull(manufacturerHook);
|
||||||
|
@@ -14,20 +14,20 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.settings.tests;
|
package com.android.settings.bluetooth;
|
||||||
|
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
import android.text.InputFilter;
|
import android.text.InputFilter;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
|
|
||||||
import com.android.settings.bluetooth.BluetoothNamePreference;
|
import com.android.settings.bluetooth.Utf8ByteLengthFilter;
|
||||||
|
|
||||||
import dalvik.annotation.TestLevel;
|
import dalvik.annotation.TestLevel;
|
||||||
import dalvik.annotation.TestTargetClass;
|
import dalvik.annotation.TestTargetClass;
|
||||||
import dalvik.annotation.TestTargetNew;
|
import dalvik.annotation.TestTargetNew;
|
||||||
import dalvik.annotation.TestTargets;
|
import dalvik.annotation.TestTargets;
|
||||||
|
|
||||||
@TestTargetClass(BluetoothNamePreference.Utf8ByteLengthFilter.class)
|
@TestTargetClass(Utf8ByteLengthFilter.class)
|
||||||
public class Utf8ByteLengthFilterTest extends AndroidTestCase {
|
public class Utf8ByteLengthFilterTest extends AndroidTestCase {
|
||||||
|
|
||||||
@TestTargets({
|
@TestTargets({
|
||||||
@@ -39,7 +39,7 @@ public class Utf8ByteLengthFilterTest extends AndroidTestCase {
|
|||||||
),
|
),
|
||||||
@TestTargetNew(
|
@TestTargetNew(
|
||||||
level = TestLevel.COMPLETE,
|
level = TestLevel.COMPLETE,
|
||||||
method = "BluetoothNamePreference.Utf8ByteLengthFilter",
|
method = "Utf8ByteLengthFilter",
|
||||||
args = {int.class}
|
args = {int.class}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -48,7 +48,7 @@ public class Utf8ByteLengthFilterTest extends AndroidTestCase {
|
|||||||
CharSequence source;
|
CharSequence source;
|
||||||
SpannableStringBuilder dest;
|
SpannableStringBuilder dest;
|
||||||
// Constructor to create a LengthFilter
|
// Constructor to create a LengthFilter
|
||||||
BluetoothNamePreference.Utf8ByteLengthFilter lengthFilter = new BluetoothNamePreference.Utf8ByteLengthFilter(10);
|
InputFilter lengthFilter = new Utf8ByteLengthFilter(10);
|
||||||
InputFilter[] filters = {lengthFilter};
|
InputFilter[] filters = {lengthFilter};
|
||||||
|
|
||||||
// filter() implicitly invoked. If the total length > filter length, the filter will
|
// filter() implicitly invoked. If the total length > filter length, the filter will
|
Reference in New Issue
Block a user