Improve Bluetooth tethering UI usability.

- Updated hint text for BT tethering checkbox to
  "[Sharing|not sharing] this [tablet|phone]'s mobile data connection".
- Show correct hint text when user enters tethering screen.
- Show correct status after user enables tethering when Bluetooth is off.
  When BluetoothPan.setBluetoothTethering(true) is called with BT off,
  BluetoothPanProfileHandler will add a broadcast receiver to enable
  tethering after BT turns on. This happens too late to show the correct
  status when TetherSettings gets the adapter state changed event, so set
  a flag (mBluetoothEnableForTether) instead, and call setBluetoothTethering
  ourselves after the state changes to ON. Also, clear the flag if the
  adapter state changes to OFF or ERROR.
- Show correct status when user enables tethering, then disables Bluetooth,
  then returns to the tethering screen. Previously it would show
  Bluetooth tethering enabled, even though adapter state was OFF.
- Show the number of connected devices in tethering preference screen.
- Distinguish between PANU and NAP in device profiles screen, and show
  appropriate text to clarify the direction of tethering.
- Remove profiles from device profiles list when the device removes the UUID
  (e.g. Mac OS X turning NAP on/off) and after a NAP disconnection when the
  remote device only supports PANU.

Bug: 3414575
Change-Id: I2c0830876d5b9bddb293e57c4d3ca74f105911b8
This commit is contained in:
Jake Hamby
2011-03-04 15:37:39 -08:00
parent 2715376cfa
commit c777ee29c8
11 changed files with 184 additions and 64 deletions

View File

@@ -142,7 +142,7 @@ final class A2dpProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_a2dp;
}
public int getDisconnectResource() {
public int getDisconnectResource(BluetoothDevice device) {
return R.string.bluetooth_disconnect_a2dp_profile;
}

View File

@@ -51,6 +51,13 @@ final class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
private final List<LocalBluetoothProfile> mProfiles =
new ArrayList<LocalBluetoothProfile>();
// List of profiles that were previously in mProfiles, but have been removed
private final List<LocalBluetoothProfile> mRemovedProfiles =
new ArrayList<LocalBluetoothProfile>();
// Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
private boolean mLocalNapRoleConnected;
private boolean mVisible;
private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
@@ -100,8 +107,21 @@ final class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
mProfileConnectionState.put(profile, newProfileState);
if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
if (!mProfiles.contains(profile)) {
mRemovedProfiles.remove(profile);
mProfiles.add(profile);
if (profile instanceof PanProfile &&
((PanProfile) profile).isLocalRoleNap(mDevice)) {
// Device doesn't support NAP, so remove PanProfile on disconnect
mLocalNapRoleConnected = true;
}
}
} else if (mLocalNapRoleConnected && profile instanceof PanProfile &&
((PanProfile) profile).isLocalRoleNap(mDevice) &&
newProfileState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d(TAG, "Removing PanProfile from device after NAP disconnect");
mProfiles.remove(profile);
mRemovedProfiles.add(profile);
mLocalNapRoleConnected = false;
}
}
@@ -391,7 +411,7 @@ final class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
ParcelUuid[] localUuids = mLocalAdapter.getUuids();
if (localUuids == null) return false;
mProfileManager.updateProfiles(uuids, localUuids, mProfiles);
mProfileManager.updateProfiles(uuids, localUuids, mProfiles, mRemovedProfiles);
if (DEBUG) {
Log.e(TAG, "updating profiles for " + mDevice.getName());
@@ -482,6 +502,10 @@ final class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
return connectableProfiles;
}
List<LocalBluetoothProfile> getRemovedProfiles() {
return mRemovedProfiles;
}
void registerCallback(Callback callback) {
synchronized (mCallbacks) {
mCallbacks.add(callback);

View File

@@ -239,7 +239,7 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment
if (TextUtils.isEmpty(name)) {
name = context.getString(R.string.bluetooth_device);
}
int disconnectMessage = profile.getDisconnectResource();
int disconnectMessage = profile.getDisconnectResource(device.getDevice());
if (disconnectMessage == 0) {
Log.w(TAG, "askDisconnect: unexpected profile " + profile);
disconnectMessage = R.string.bluetooth_disconnect_blank;
@@ -288,6 +288,13 @@ public final class DeviceProfilesSettings extends SettingsPreferenceFragment
refreshProfilePreference(profilePref, profile);
}
}
for (LocalBluetoothProfile profile : mCachedDevice.getRemovedProfiles()) {
Preference profilePref = findPreference(profile.toString());
if (profilePref != null) {
Log.d(TAG, "Removing " + profile.toString() + " from profile list");
mProfileContainer.removePreference(profilePref);
}
}
}
private void refreshProfilePreference(Preference profilePref, LocalBluetoothProfile profile) {

View File

@@ -22,13 +22,11 @@ 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;
/**
@@ -176,7 +174,7 @@ final class HeadsetProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_headset;
}
public int getDisconnectResource() {
public int getDisconnectResource(BluetoothDevice device) {
return R.string.bluetooth_disconnect_headset_profile;
}

View File

@@ -116,7 +116,7 @@ final class HidProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_hid;
}
public int getDisconnectResource() {
public int getDisconnectResource(BluetoothDevice device) {
return R.string.bluetooth_disconnect_hid_profile;
}

View File

@@ -60,8 +60,9 @@ interface LocalBluetoothProfile {
/**
* Returns the string resource ID for the disconnect confirmation text
* for this profile.
* @param device
*/
int getDisconnectResource();
int getDisconnectResource(BluetoothDevice device);
/**
* Returns the string resource ID for the summary text for this profile

View File

@@ -110,7 +110,9 @@ final class LocalBluetoothProfileManager {
BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
mPanProfile = new PanProfile(context);
addProfile(mPanProfile, PanProfile.NAME, BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
addPanProfile(mPanProfile, PanProfile.NAME,
BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
Log.d(TAG, "LocalBluetoothProfileManager construction complete");
}
@@ -173,6 +175,13 @@ final class LocalBluetoothProfileManager {
mProfileNameMap.put(profileName, profile);
}
private void addPanProfile(LocalBluetoothProfile profile,
String profileName, String stateChangedAction) {
mEventManager.addProfileHandler(stateChangedAction,
new PanStateChangedHandler(profile));
mProfileNameMap.put(profileName, profile);
}
LocalBluetoothProfile getProfileByName(String name) {
return mProfileNameMap.get(name);
}
@@ -190,7 +199,7 @@ final class LocalBluetoothProfileManager {
* Generic handler for connection state change events for the specified profile.
*/
private class StateChangedHandler implements BluetoothEventManager.Handler {
private final LocalBluetoothProfile mProfile;
final LocalBluetoothProfile mProfile;
StateChangedHandler(LocalBluetoothProfile profile) {
mProfile = profile;
@@ -215,6 +224,22 @@ final class LocalBluetoothProfileManager {
}
}
/** State change handler for NAP and PANU profiles. */
private class PanStateChangedHandler extends StateChangedHandler {
PanStateChangedHandler(LocalBluetoothProfile profile) {
super(profile);
}
@Override
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
PanProfile panProfile = (PanProfile) mProfile;
int role = intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, 0);
panProfile.setLocalRole(device, role);
super.onReceive(context, intent, device);
}
}
// called from DockService
void addServiceListener(ServiceListener l) {
mServiceListeners.add(l);
@@ -269,9 +294,14 @@ final class LocalBluetoothProfileManager {
* @param uuids of the remote device
* @param localUuids UUIDs of the local device
* @param profiles The list of profiles to fill
* @param removedProfiles list of profiles that were removed
*/
synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
Collection<LocalBluetoothProfile> profiles) {
Collection<LocalBluetoothProfile> profiles,
Collection<LocalBluetoothProfile> removedProfiles) {
// Copy previous profile list into removedProfiles
removedProfiles.clear();
removedProfiles.addAll(profiles);
profiles.clear();
if (uuids == null) {
@@ -280,31 +310,36 @@ final class LocalBluetoothProfileManager {
if (mHeadsetProfile != null) {
if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
(BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
profiles.add(mHeadsetProfile);
BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
(BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
profiles.add(mHeadsetProfile);
removedProfiles.remove(mHeadsetProfile);
}
}
if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
mA2dpProfile != null) {
profiles.add(mA2dpProfile);
removedProfiles.remove(mA2dpProfile);
}
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
mOppProfile != null) {
profiles.add(mOppProfile);
removedProfiles.remove(mOppProfile);
}
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) &&
mHidProfile != null) {
profiles.add(mHidProfile);
removedProfiles.remove(mHidProfile);
}
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
mPanProfile != null) {
profiles.add(mPanProfile);
removedProfiles.remove(mPanProfile);
}
}
}

View File

@@ -79,7 +79,7 @@ final class OppProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_opp;
}
public int getDisconnectResource() {
public int getDisconnectResource(BluetoothDevice device) {
return 0; // user must use notification to disconnect OPP transfer.
}

View File

@@ -25,14 +25,19 @@ import android.content.Context;
import com.android.settings.R;
import java.util.HashMap;
import java.util.List;
/**
* PanProfile handles Bluetooth PAN profile.
* PanProfile handles Bluetooth PAN profile (NAP and PANU).
*/
final class PanProfile implements LocalBluetoothProfile {
private BluetoothPan mService;
// Tethering direction for each device
private final HashMap<BluetoothDevice, Integer> mDeviceRoleMap =
new HashMap<BluetoothDevice, Integer>();
static final String NAME = "PAN";
// Order of this profile in device profiles list
@@ -111,8 +116,12 @@ final class PanProfile implements LocalBluetoothProfile {
return R.string.bluetooth_profile_pan;
}
public int getDisconnectResource() {
return R.string.bluetooth_disconnect_pan_profile;
public int getDisconnectResource(BluetoothDevice device) {
if (isLocalRoleNap(device)) {
return R.string.bluetooth_disconnect_pan_nap_profile;
} else {
return R.string.bluetooth_disconnect_pan_user_profile;
}
}
public int getSummaryResourceForDevice(BluetoothDevice device) {
@@ -122,7 +131,11 @@ final class PanProfile implements LocalBluetoothProfile {
return R.string.bluetooth_pan_profile_summary_use_for;
case BluetoothProfile.STATE_CONNECTED:
return R.string.bluetooth_pan_profile_summary_connected;
if (isLocalRoleNap(device)) {
return R.string.bluetooth_pan_nap_profile_summary_connected;
} else {
return R.string.bluetooth_pan_user_profile_summary_connected;
}
default:
return Utils.getConnectionStateSummary(state);
@@ -132,4 +145,17 @@ final class PanProfile implements LocalBluetoothProfile {
public int getDrawableResource(BluetoothClass btClass) {
return R.drawable.ic_bt_network_pan;
}
// Tethering direction determines UI strings.
void setLocalRole(BluetoothDevice device, int role) {
mDeviceRoleMap.put(device, role);
}
boolean isLocalRoleNap(BluetoothDevice device) {
if (mDeviceRoleMap.containsKey(device)) {
return mDeviceRoleMap.get(device) == BluetoothPan.LOCAL_NAP_ROLE;
} else {
return false;
}
}
}