diff --git a/res/values/strings.xml b/res/values/strings.xml index c84565cc80a..4c6210209f7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -950,16 +950,20 @@ Input Device - Tethering + Internet access - + %1$s will be disconnected from media audio. - + %1$s will be disconnected from handsfree audio. - + %1$s will be disconnected from input device. - - %1$s will be disconnected from tethering. + + Internet access via %1$s will be disconnected. + + %1$s will be disconnected from sharing this tablet\'s Internet connection. + + %1$s will be disconnected from sharing this phone\'s Internet connection. %1$s options @@ -985,8 +989,10 @@ Not connected to file transfer server Connected to input device - - Tethered + + Connected to device for Internet access + + Sharing local Internet connection with device Use for media audio @@ -1863,11 +1869,21 @@ Bluetooth tethering - Bluetooth tethering on, but not connected - - Bluetooth tethering on and connected + Sharing this tablet\'s Internet connection + + Sharing this phone\'s Internet connection + + Sharing this tablet\'s Internet connection to 1 device + + Sharing this phone\'s Internet connection to 1 device + + Sharing this tablet\'s Internet connection to %1$d devices + + Sharing this phone\'s Internet connection to %1$d devices - Bluetooth tethering off + Not sharing this tablet\'s Internet connection + + Not sharing this phone\'s Internet connection Bluetooth tethering error diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java index 39f05356ffb..1513d43cf91 100644 --- a/src/com/android/settings/TetherSettings.java +++ b/src/com/android/settings/TetherSettings.java @@ -95,6 +95,8 @@ public class TetherSettings extends SettingsPreferenceFragment private WifiManager mWifiManager; private WifiConfiguration mWifiConfig = null; + private boolean mBluetoothEnableForTether; + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -239,7 +241,8 @@ public class TetherSettings extends SettingsPreferenceFragment private class TetherChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context content, Intent intent) { - if (intent.getAction().equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) { + String action = intent.getAction(); + if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) { // TODO - this should understand the interface types ArrayList available = intent.getStringArrayListExtra( ConnectivityManager.EXTRA_AVAILABLE_TETHER); @@ -250,10 +253,27 @@ public class TetherSettings extends SettingsPreferenceFragment updateState(available.toArray(new String[available.size()]), active.toArray(new String[active.size()]), errored.toArray(new String[errored.size()])); - } else if (intent.getAction().equals(Intent.ACTION_MEDIA_SHARED) || - intent.getAction().equals(Intent.ACTION_MEDIA_UNSHARED)) { + } else if (action.equals(Intent.ACTION_MEDIA_SHARED) || + action.equals(Intent.ACTION_MEDIA_UNSHARED)) { updateState(); - } else if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { + if (mBluetoothEnableForTether) { + switch (intent + .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) { + case BluetoothAdapter.STATE_ON: + mBluetoothPan.setBluetoothTethering(true); + mBluetoothEnableForTether = false; + break; + + case BluetoothAdapter.STATE_OFF: + case BluetoothAdapter.ERROR: + mBluetoothEnableForTether = false; + break; + + default: + // ignore transition states + } + } updateState(); } } @@ -281,6 +301,8 @@ public class TetherSettings extends SettingsPreferenceFragment if (intent != null) mTetherChangeReceiver.onReceive(activity, intent); mWifiApEnabler.resume(); + + updateState(); } @Override @@ -368,22 +390,10 @@ public class TetherSettings extends SettingsPreferenceFragment private void updateBluetoothState(String[] available, String[] tethered, String[] errored) { - ConnectivityManager cm = - (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); - int bluetoothError = ConnectivityManager.TETHER_ERROR_NO_ERROR; - for (String s : available) { - for (String regex : mBluetoothRegexs) { - if (s.matches(regex)) { - if (bluetoothError == ConnectivityManager.TETHER_ERROR_NO_ERROR) { - bluetoothError = cm.getLastTetherError(s); - } - } - } - } - boolean bluetoothTethered = false; + int bluetoothTethered = 0; for (String s : tethered) { for (String regex : mBluetoothRegexs) { - if (s.matches(regex)) bluetoothTethered = true; + if (s.matches(regex)) bluetoothTethered++; } } boolean bluetoothErrored = false; @@ -401,17 +411,19 @@ public class TetherSettings extends SettingsPreferenceFragment } else if (btState == BluetoothAdapter.STATE_TURNING_ON) { mBluetoothTether.setEnabled(false); mBluetoothTether.setSummary(R.string.bluetooth_turning_on); - } else if (mBluetoothPan.isTetheringOn()) { + } else if (btState == BluetoothAdapter.STATE_ON && mBluetoothPan.isTetheringOn()) { mBluetoothTether.setChecked(true); - if (btState == BluetoothAdapter.STATE_ON) { - mBluetoothTether.setEnabled(true); - if (bluetoothTethered) { - mBluetoothTether.setSummary(R.string.bluetooth_tethering_connected_subtext); - } else if (bluetoothErrored) { - mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext); - } else { - mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext); - } + mBluetoothTether.setEnabled(true); + if (bluetoothTethered > 1) { + String summary = getString( + R.string.bluetooth_tethering_devices_connected_subtext, bluetoothTethered); + mBluetoothTether.setSummary(summary); + } else if (bluetoothTethered == 1) { + mBluetoothTether.setSummary(R.string.bluetooth_tethering_device_connected_subtext); + } else if (bluetoothErrored) { + mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext); + } else { + mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext); } } else { mBluetoothTether.setEnabled(true); @@ -456,20 +468,21 @@ public class TetherSettings extends SettingsPreferenceFragment } mUsbTether.setSummary(""); } - } else if(preference == mBluetoothTether) { + } else if (preference == mBluetoothTether) { boolean bluetoothTetherState = mBluetoothTether.isChecked(); if (bluetoothTetherState) { // turn on Bluetooth first BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter.getState() == BluetoothAdapter.STATE_OFF) { + mBluetoothEnableForTether = true; adapter.enable(); mBluetoothTether.setSummary(R.string.bluetooth_turning_on); mBluetoothTether.setEnabled(false); + } else { + mBluetoothPan.setBluetoothTethering(true); + mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext); } - - mBluetoothPan.setBluetoothTethering(true); - mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext); } else { boolean errored = false; diff --git a/src/com/android/settings/bluetooth/A2dpProfile.java b/src/com/android/settings/bluetooth/A2dpProfile.java index 96225d87c3e..e8582f3799e 100644 --- a/src/com/android/settings/bluetooth/A2dpProfile.java +++ b/src/com/android/settings/bluetooth/A2dpProfile.java @@ -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; } diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java index 56e96b4d003..71a5c01740c 100644 --- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java +++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java @@ -51,6 +51,13 @@ final class CachedBluetoothDevice implements Comparable { private final List mProfiles = new ArrayList(); + // List of profiles that were previously in mProfiles, but have been removed + private final List mRemovedProfiles = + new ArrayList(); + + // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP + private boolean mLocalNapRoleConnected; + private boolean mVisible; private final Collection mCallbacks = new ArrayList(); @@ -100,8 +107,21 @@ final class CachedBluetoothDevice implements Comparable { 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 { 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 { return connectableProfiles; } + List getRemovedProfiles() { + return mRemovedProfiles; + } + void registerCallback(Callback callback) { synchronized (mCallbacks) { mCallbacks.add(callback); diff --git a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java index 307125cb5d5..9db4baf4e11 100644 --- a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java +++ b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java @@ -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) { diff --git a/src/com/android/settings/bluetooth/HeadsetProfile.java b/src/com/android/settings/bluetooth/HeadsetProfile.java index e9c52ef0bb4..13dce335cf2 100644 --- a/src/com/android/settings/bluetooth/HeadsetProfile.java +++ b/src/com/android/settings/bluetooth/HeadsetProfile.java @@ -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; } diff --git a/src/com/android/settings/bluetooth/HidProfile.java b/src/com/android/settings/bluetooth/HidProfile.java index 9185059d6b9..13d3db9dffe 100644 --- a/src/com/android/settings/bluetooth/HidProfile.java +++ b/src/com/android/settings/bluetooth/HidProfile.java @@ -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; } diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfile.java b/src/com/android/settings/bluetooth/LocalBluetoothProfile.java index 936231a1c14..878a032b443 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothProfile.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothProfile.java @@ -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 diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java index ee3cb66a067..f3143f0b8e7 100644 --- a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java +++ b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java @@ -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 profiles) { + Collection profiles, + Collection 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); } } } diff --git a/src/com/android/settings/bluetooth/OppProfile.java b/src/com/android/settings/bluetooth/OppProfile.java index 3f7df38532b..eb5900e0e14 100644 --- a/src/com/android/settings/bluetooth/OppProfile.java +++ b/src/com/android/settings/bluetooth/OppProfile.java @@ -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. } diff --git a/src/com/android/settings/bluetooth/PanProfile.java b/src/com/android/settings/bluetooth/PanProfile.java index 3f456e45bc5..6cb1991efbd 100644 --- a/src/com/android/settings/bluetooth/PanProfile.java +++ b/src/com/android/settings/bluetooth/PanProfile.java @@ -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 mDeviceRoleMap = + new HashMap(); + 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; + } + } }