summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/settings/ApnEditor.java44
-rw-r--r--src/com/android/settings/BrightnessPreference.java89
-rw-r--r--src/com/android/settings/ChooseLockPassword.java2
-rw-r--r--src/com/android/settings/ChooseLockPattern.java2
-rw-r--r--src/com/android/settings/ChooseLockPatternTutorial.java1
-rw-r--r--src/com/android/settings/CryptKeeper.java119
-rw-r--r--src/com/android/settings/DateTimeSettingsSetupWizard.java2
-rw-r--r--src/com/android/settings/DisplaySettings.java23
-rw-r--r--src/com/android/settings/RadioInfo.java2
-rw-r--r--src/com/android/settings/TetherSettings.java89
-rw-r--r--src/com/android/settings/WirelessSettings.java5
-rw-r--r--src/com/android/settings/accounts/SyncStateCheckBoxPreference.java13
-rw-r--r--src/com/android/settings/applications/ApplicationsState.java17
-rw-r--r--src/com/android/settings/applications/InstalledAppDetails.java39
-rw-r--r--src/com/android/settings/applications/InterestingConfigChanges.java37
-rw-r--r--src/com/android/settings/applications/RunningState.java30
-rw-r--r--src/com/android/settings/bluetooth/A2dpProfile.java166
-rw-r--r--src/com/android/settings/bluetooth/BluetoothCallback.java29
-rw-r--r--src/com/android/settings/bluetooth/BluetoothDeviceFilter.java159
-rw-r--r--src/com/android/settings/bluetooth/BluetoothDevicePreference.java210
-rw-r--r--src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java123
-rw-r--r--src/com/android/settings/bluetooth/BluetoothDiscoveryReceiver.java47
-rw-r--r--src/com/android/settings/bluetooth/BluetoothEnabler.java22
-rw-r--r--src/com/android/settings/bluetooth/BluetoothEventManager.java398
-rw-r--r--src/com/android/settings/bluetooth/BluetoothEventRedirector.java259
-rw-r--r--src/com/android/settings/bluetooth/BluetoothFindNearby.java29
-rw-r--r--src/com/android/settings/bluetooth/BluetoothNamePreference.java92
-rw-r--r--src/com/android/settings/bluetooth/BluetoothPairingDialog.java260
-rw-r--r--src/com/android/settings/bluetooth/BluetoothPairingRequest.java36
-rw-r--r--src/com/android/settings/bluetooth/BluetoothProfilePreference.java19
-rw-r--r--src/com/android/settings/bluetooth/BluetoothSettings.java63
-rw-r--r--src/com/android/settings/bluetooth/CachedBluetoothDevice.java487
-rw-r--r--src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java232
-rw-r--r--src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java174
-rw-r--r--src/com/android/settings/bluetooth/DevicePickerActivity.java2
-rw-r--r--src/com/android/settings/bluetooth/DevicePickerFragment.java40
-rw-r--r--src/com/android/settings/bluetooth/DeviceProfilesSettings.java211
-rw-r--r--src/com/android/settings/bluetooth/DockEventReceiver.java31
-rw-r--r--src/com/android/settings/bluetooth/DockService.java721
-rw-r--r--src/com/android/settings/bluetooth/HeadsetProfile.java198
-rw-r--r--src/com/android/settings/bluetooth/HidProfile.java155
-rw-r--r--src/com/android/settings/bluetooth/LocalBluetoothAdapter.java216
-rw-r--r--src/com/android/settings/bluetooth/LocalBluetoothManager.java382
-rw-r--r--src/com/android/settings/bluetooth/LocalBluetoothPreferences.java157
-rw-r--r--src/com/android/settings/bluetooth/LocalBluetoothProfile.java76
-rw-r--r--src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java875
-rw-r--r--src/com/android/settings/bluetooth/OppProfile.java93
-rw-r--r--src/com/android/settings/bluetooth/PanProfile.java135
-rw-r--r--src/com/android/settings/bluetooth/RequestPermissionActivity.java99
-rw-r--r--src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java41
-rw-r--r--src/com/android/settings/bluetooth/SettingsBtStatus.java81
-rw-r--r--src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java84
-rw-r--r--src/com/android/settings/bluetooth/Utils.java99
-rw-r--r--src/com/android/settings/deviceinfo/Constants.java102
-rw-r--r--src/com/android/settings/deviceinfo/FileItemInfoLayout.java78
-rw-r--r--src/com/android/settings/deviceinfo/Memory.java133
-rw-r--r--src/com/android/settings/deviceinfo/MemoryMeasurement.java120
-rw-r--r--src/com/android/settings/deviceinfo/MiscFilesHandler.java278
-rw-r--r--src/com/android/settings/deviceinfo/Status.java2
-rw-r--r--src/com/android/settings/fuelgauge/PowerUsageSummary.java3
-rw-r--r--src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java22
-rw-r--r--src/com/android/settings/inputmethod/InputMethodConfig.java45
-rw-r--r--src/com/android/settings/vpn/VpnEditor.java15
-rw-r--r--src/com/android/settings/vpn/VpnSettings.java6
-rw-r--r--src/com/android/settings/widget/SettingsAppWidgetProvider.java35
-rw-r--r--src/com/android/settings/wifi/WifiApDialog.java59
-rw-r--r--src/com/android/settings/wifi/WifiApEnabler.java4
-rw-r--r--src/com/android/settings/wifi/WifiApSettings.java13
-rw-r--r--src/com/android/settings/wifi/WifiConfigController.java98
-rw-r--r--src/com/android/settings/wifi/WifiSettings.java81
-rw-r--r--src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java490
71 files changed, 5101 insertions, 3498 deletions
diff --git a/src/com/android/settings/ApnEditor.java b/src/com/android/settings/ApnEditor.java
index 9328c149b..f738823f0 100644
--- a/src/com/android/settings/ApnEditor.java
+++ b/src/com/android/settings/ApnEditor.java
@@ -48,6 +48,7 @@ public class ApnEditor extends PreferenceActivity
private final static String SAVED_POS = "pos";
private final static String KEY_AUTH_TYPE = "auth_type";
+ private final static String KEY_PROTOCOL = "apn_protocol";
private static final int MENU_DELETE = Menu.FIRST;
private static final int MENU_SAVE = Menu.FIRST + 1;
@@ -69,6 +70,7 @@ public class ApnEditor extends PreferenceActivity
private EditTextPreference mMmsPort;
private ListPreference mAuthType;
private EditTextPreference mApnType;
+ private ListPreference mProtocol;
private String mCurMnc;
private String mCurMcc;
@@ -99,6 +101,7 @@ public class ApnEditor extends PreferenceActivity
Telephony.Carriers.MMSPORT, // 13
Telephony.Carriers.AUTH_TYPE, // 14
Telephony.Carriers.TYPE, // 15
+ Telephony.Carriers.PROTOCOL, // 16
};
private static final int ID_INDEX = 0;
@@ -116,6 +119,7 @@ public class ApnEditor extends PreferenceActivity
private static final int MMSPORT_INDEX = 13;
private static final int AUTH_TYPE_INDEX = 14;
private static final int TYPE_INDEX = 15;
+ private static final int PROTOCOL_INDEX = 16;
@Override
@@ -139,9 +143,12 @@ public class ApnEditor extends PreferenceActivity
mMnc = (EditTextPreference) findPreference("apn_mnc");
mApnType = (EditTextPreference) findPreference("apn_type");
- mAuthType = (ListPreference) findPreference("auth_type");
+ mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE);
mAuthType.setOnPreferenceChangeListener(this);
+ mProtocol = (ListPreference) findPreference(KEY_PROTOCOL);
+ mProtocol.setOnPreferenceChangeListener(this);
+
mRes = getResources();
final Intent intent = getIntent();
@@ -238,6 +245,7 @@ public class ApnEditor extends PreferenceActivity
mAuthType.setValue(null);
}
+ mProtocol.setValue(mCursor.getString(PROTOCOL_INDEX));
}
mName.setSummary(checkNull(mName.getText()));
@@ -264,6 +272,28 @@ public class ApnEditor extends PreferenceActivity
} else {
mAuthType.setSummary(sNotSet);
}
+
+ mProtocol.setSummary(
+ checkNull(protocolDescription(mProtocol.getValue())));
+ }
+
+ /**
+ * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given
+ * raw value of the protocol preference (e.g., "IPV4V6"). If unknown,
+ * return null.
+ */
+ private String protocolDescription(String raw) {
+ int protocolIndex = mProtocol.findIndexOfValue(raw);
+ if (protocolIndex == -1) {
+ return null;
+ } else {
+ String[] values = mRes.getStringArray(R.array.apn_protocol_entries);
+ try {
+ return values[protocolIndex];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ }
}
public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -278,6 +308,16 @@ public class ApnEditor extends PreferenceActivity
} catch (NumberFormatException e) {
return false;
}
+ return true;
+ }
+
+ if (KEY_PROTOCOL.equals(key)) {
+ String protocol = protocolDescription((String) newValue);
+ if (protocol == null) {
+ return false;
+ }
+ mProtocol.setSummary(protocol);
+ mProtocol.setValue((String) newValue);
}
return true;
}
@@ -389,6 +429,8 @@ public class ApnEditor extends PreferenceActivity
values.put(Telephony.Carriers.AUTH_TYPE, Integer.parseInt(authVal));
}
+ values.put(Telephony.Carriers.PROTOCOL, checkNotSet(mProtocol.getValue()));
+
values.put(Telephony.Carriers.TYPE, checkNotSet(mApnType.getText()));
values.put(Telephony.Carriers.MCC, mcc);
diff --git a/src/com/android/settings/BrightnessPreference.java b/src/com/android/settings/BrightnessPreference.java
index 00f5debc2..9bbb66ac4 100644
--- a/src/com/android/settings/BrightnessPreference.java
+++ b/src/com/android/settings/BrightnessPreference.java
@@ -16,25 +16,25 @@
package com.android.settings;
+import android.content.ContentResolver;
import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IPowerManager;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
-import android.os.IPowerManager;
import android.os.ServiceManager;
import android.preference.SeekBarPreference;
-import android.preference.Preference.BaseSavedState;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.SeekBar;
-import java.util.Map;
-
public class BrightnessPreference extends SeekBarPreference implements
SeekBar.OnSeekBarChangeListener, CheckBox.OnCheckedChangeListener {
@@ -53,6 +53,20 @@ public class BrightnessPreference extends SeekBarPreference implements
private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10;
private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
+ private ContentObserver mBrightnessObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ onBrightnessChanged();
+ }
+ };
+
+ private ContentObserver mBrightnessModeObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ onBrightnessModeChanged();
+ }
+ };
+
public BrightnessPreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -64,28 +78,32 @@ public class BrightnessPreference extends SeekBarPreference implements
}
@Override
+ protected void showDialog(Bundle state) {
+ super.showDialog(state);
+
+ getContext().getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS), true,
+ mBrightnessObserver);
+
+ getContext().getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), true,
+ mBrightnessModeObserver);
+ mRestoredOldState = false;
+ }
+
+ @Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
mSeekBar = getSeekBar(view);
mSeekBar.setMax(MAXIMUM_BACKLIGHT - MINIMUM_BACKLIGHT);
- try {
- mOldBrightness = Settings.System.getInt(getContext().getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS);
- } catch (SettingNotFoundException snfe) {
- mOldBrightness = MAXIMUM_BACKLIGHT;
- }
+ mOldBrightness = getBrightness(0);
mSeekBar.setProgress(mOldBrightness - MINIMUM_BACKLIGHT);
mCheckBox = (CheckBox)view.findViewById(R.id.automatic_mode);
if (mAutomaticAvailable) {
mCheckBox.setOnCheckedChangeListener(this);
- try {
- mOldAutomatic = Settings.System.getInt(getContext().getContentResolver(),
- Settings.System.SCREEN_BRIGHTNESS_MODE);
- } catch (SettingNotFoundException snfe) {
- mOldAutomatic = 0;
- }
+ mOldAutomatic = getBrightnessMode(0);
mCheckBox.setChecked(mOldAutomatic != 0);
} else {
mCheckBox.setVisibility(View.GONE);
@@ -114,17 +132,52 @@ public class BrightnessPreference extends SeekBarPreference implements
}
}
+ private int getBrightness(int defaultValue) {
+ int brightness = defaultValue;
+ try {
+ brightness = Settings.System.getInt(getContext().getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS);
+ } catch (SettingNotFoundException snfe) {
+ }
+ return brightness;
+ }
+
+ private int getBrightnessMode(int defaultValue) {
+ int brightnessMode = defaultValue;
+ try {
+ brightnessMode = Settings.System.getInt(getContext().getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE);
+ } catch (SettingNotFoundException snfe) {
+ }
+ return brightnessMode;
+ }
+
+ private void onBrightnessChanged() {
+ int brightness = getBrightness(MAXIMUM_BACKLIGHT);
+ mSeekBar.setProgress(brightness - MINIMUM_BACKLIGHT);
+ }
+
+ private void onBrightnessModeChanged() {
+ boolean checked = getBrightnessMode(0) != 0;
+ mCheckBox.setChecked(checked);
+ }
+
@Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
+ final ContentResolver resolver = getContext().getContentResolver();
+
if (positiveResult) {
- Settings.System.putInt(getContext().getContentResolver(),
+ Settings.System.putInt(resolver,
Settings.System.SCREEN_BRIGHTNESS,
mSeekBar.getProgress() + MINIMUM_BACKLIGHT);
} else {
restoreOldState();
}
+
+ resolver.unregisterContentObserver(mBrightnessObserver);
+ resolver.unregisterContentObserver(mBrightnessModeObserver);
}
private void restoreOldState() {
diff --git a/src/com/android/settings/ChooseLockPassword.java b/src/com/android/settings/ChooseLockPassword.java
index 018dfd2aa..a0f23465e 100644
--- a/src/com/android/settings/ChooseLockPassword.java
+++ b/src/com/android/settings/ChooseLockPassword.java
@@ -274,7 +274,7 @@ public class ChooseLockPassword extends PreferenceActivity {
if (password.length() > mPasswordMaxLength) {
return getString(mIsAlphaMode ?
R.string.lockpassword_password_too_long
- : R.string.lockpassword_pin_too_long, mPasswordMaxLength);
+ : R.string.lockpassword_pin_too_long, mPasswordMaxLength + 1);
}
int letters = 0;
int numbers = 0;
diff --git a/src/com/android/settings/ChooseLockPattern.java b/src/com/android/settings/ChooseLockPattern.java
index 17d3de24b..55f62546a 100644
--- a/src/com/android/settings/ChooseLockPattern.java
+++ b/src/com/android/settings/ChooseLockPattern.java
@@ -248,7 +248,7 @@ public class ChooseLockPattern extends PreferenceActivity {
LeftButtonMode.Retry, RightButtonMode.Continue, ID_EMPTY_MESSAGE, false),
NeedToConfirm(
R.string.lockpattern_need_to_confirm,
- LeftButtonMode.CancelDisabled, RightButtonMode.ConfirmDisabled,
+ LeftButtonMode.Cancel, RightButtonMode.ConfirmDisabled,
ID_EMPTY_MESSAGE, true),
ConfirmWrong(
R.string.lockpattern_need_to_unlock_wrong,
diff --git a/src/com/android/settings/ChooseLockPatternTutorial.java b/src/com/android/settings/ChooseLockPatternTutorial.java
index 2e596dfb7..4ad16fbdc 100644
--- a/src/com/android/settings/ChooseLockPatternTutorial.java
+++ b/src/com/android/settings/ChooseLockPatternTutorial.java
@@ -102,6 +102,7 @@ public class ChooseLockPatternTutorial extends PreferenceActivity {
Intent intent = new Intent(getActivity(), ChooseLockPattern.class);
intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(intent);
+ getActivity().overridePendingTransition(0, 0); // no animation
getActivity().finish();
}
}
diff --git a/src/com/android/settings/CryptKeeper.java b/src/com/android/settings/CryptKeeper.java
index 3d752a32b..edf00d525 100644
--- a/src/com/android/settings/CryptKeeper.java
+++ b/src/com/android/settings/CryptKeeper.java
@@ -25,7 +25,9 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.graphics.Rect;
import android.inputmethodservice.KeyboardView;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -35,11 +37,14 @@ import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.storage.IMountService;
import android.text.TextUtils;
+import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
@@ -59,6 +64,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
private int mCooldown;
PowerManager.WakeLock mWakeLock;
+ private EditText mPasswordEntry;
/**
* Used to propagate state through configuration changes (e.g. screen rotation)
@@ -80,6 +86,76 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
}
}
+ // Use a custom EditText to prevent the input method from showing.
+ public static class CryptEditText extends EditText {
+ InputMethodManager imm;
+
+ public CryptEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ imm = ((InputMethodManager) getContext().
+ getSystemService(Context.INPUT_METHOD_SERVICE));
+ }
+
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+
+ if (focused && imm != null && imm.isActive(this)) {
+ imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean handled = super.onTouchEvent(event);
+
+ if (imm != null && imm.isActive(this)) {
+ imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+ }
+
+ return handled;
+ }
+ }
+
+ private class DecryptTask extends AsyncTask<String, Void, Integer> {
+ @Override
+ protected Integer doInBackground(String... params) {
+ IMountService service = getMountService();
+ try {
+ return service.decryptStorage(params[0]);
+ } catch (Exception e) {
+ Log.e(TAG, "Error while decrypting...", e);
+ return -1;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Integer failedAttempts) {
+ if (failedAttempts == 0) {
+ // The password was entered successfully. Start the Blank activity
+ // so this activity animates to black before the devices starts. Note
+ // It has 1 second to complete the animation or it will be frozen
+ // until the boot animation comes back up.
+ Intent intent = new Intent(CryptKeeper.this, Blank.class);
+ finish();
+ startActivity(intent);
+ } else if (failedAttempts == MAX_FAILED_ATTEMPTS) {
+ // Factory reset the device.
+ sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
+ } else if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) {
+ mCooldown = COOL_DOWN_INTERVAL;
+ cooldown();
+ } else {
+ TextView tv = (TextView) findViewById(R.id.status);
+ tv.setText(R.string.try_again);
+ tv.setVisibility(View.VISIBLE);
+
+ // Reenable the password entry
+ mPasswordEntry.setEnabled(true);
+ }
+ }
+ }
+
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -245,8 +321,7 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
if (mCooldown <= 0) {
// Re-enable the password entry
- EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
- passwordEntry.setEnabled(true);
+ mPasswordEntry.setEnabled(true);
tv.setVisibility(View.GONE);
} else {
@@ -262,13 +337,13 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
}
private void passwordEntryInit() {
- TextView passwordEntry = (TextView) findViewById(R.id.passwordEntry);
- passwordEntry.setOnEditorActionListener(this);
+ mPasswordEntry = (EditText) findViewById(R.id.passwordEntry);
+ mPasswordEntry.setOnEditorActionListener(this);
KeyboardView keyboardView = (PasswordEntryKeyboardView) findViewById(R.id.keyboard);
PasswordEntryKeyboardHelper keyboardHelper = new PasswordEntryKeyboardHelper(this,
- keyboardView, passwordEntry, false);
+ keyboardView, mPasswordEntry, false);
keyboardHelper.setKeyboardMode(PasswordEntryKeyboardHelper.KEYBOARD_MODE_ALPHA);
}
@@ -293,34 +368,12 @@ public class CryptKeeper extends Activity implements TextView.OnEditorActionList
// Now that we have the password clear the password field.
v.setText(null);
- IMountService service = getMountService();
- try {
- int failedAttempts = service.decryptStorage(password);
-
- if (failedAttempts == 0) {
- // The password was entered successfully. Start the Blank activity
- // so this activity animates to black before the devices starts. Note
- // It has 1 second to complete the animation or it will be frozen
- // until the boot animation comes back up.
- Intent intent = new Intent(this, Blank.class);
- finish();
- startActivity(intent);
- } else if (failedAttempts == MAX_FAILED_ATTEMPTS) {
- // Factory reset the device.
- sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
- } else if ((failedAttempts % COOL_DOWN_ATTEMPTS) == 0) {
- mCooldown = COOL_DOWN_INTERVAL;
- EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
- passwordEntry.setEnabled(false);
- cooldown();
- } else {
- TextView tv = (TextView) findViewById(R.id.status);
- tv.setText(R.string.try_again);
- tv.setVisibility(View.VISIBLE);
- }
- } catch (Exception e) {
- Log.e(TAG, "Error while decrypting...", e);
- }
+ // Disable the password entry while checking the password. This
+ // we either be reenabled if the password was wrong or after the
+ // cooldown period.
+ mPasswordEntry.setEnabled(false);
+
+ new DecryptTask().execute(password);
return true;
}
diff --git a/src/com/android/settings/DateTimeSettingsSetupWizard.java b/src/com/android/settings/DateTimeSettingsSetupWizard.java
index c3d3bfdec..bfbb601ed 100644
--- a/src/com/android/settings/DateTimeSettingsSetupWizard.java
+++ b/src/com/android/settings/DateTimeSettingsSetupWizard.java
@@ -95,7 +95,7 @@ public class DateTimeSettingsSetupWizard extends Activity
mAutoTimeZoneButton.setText(autoTimeZoneEnabled ? R.string.zone_auto_summaryOn :
R.string.zone_auto_summaryOff);*/
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
final TimeZone tz = TimeZone.getDefault();
mSelectedTimeZone = tz;
diff --git a/src/com/android/settings/DisplaySettings.java b/src/com/android/settings/DisplaySettings.java
index 0e17810c0..cdb0147c1 100644
--- a/src/com/android/settings/DisplaySettings.java
+++ b/src/com/android/settings/DisplaySettings.java
@@ -21,7 +21,9 @@ import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import android.app.admin.DevicePolicyManager;
import android.content.ContentResolver;
import android.content.Context;
+import android.database.ContentObserver;
import android.os.Bundle;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.preference.CheckBoxPreference;
@@ -53,6 +55,13 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
private ListPreference mScreenTimeoutPreference;
+ private ContentObserver mAccelerometerRotationObserver = new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAccelerometerRotationCheckbox();
+ }
+ };
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -131,6 +140,16 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
super.onResume();
updateState(true);
+ getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION), true,
+ mAccelerometerRotationObserver);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ getContentResolver().unregisterContentObserver(mAccelerometerRotationObserver);
}
private void updateState(boolean force) {
@@ -159,6 +178,10 @@ public class DisplaySettings extends SettingsPreferenceFragment implements
}
mAnimations.setValueIndex(idx);
updateAnimationsSummary(mAnimations.getValue());
+ updateAccelerometerRotationCheckbox();
+ }
+
+ private void updateAccelerometerRotationCheckbox() {
mAccelerometer.setChecked(Settings.System.getInt(
getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION, 0) != 0);
diff --git a/src/com/android/settings/RadioInfo.java b/src/com/android/settings/RadioInfo.java
index beea51ec3..4f8b374a9 100644
--- a/src/com/android/settings/RadioInfo.java
+++ b/src/com/android/settings/RadioInfo.java
@@ -427,7 +427,7 @@ public class RadioInfo extends Activity {
if (-1 == signalDbm) signalDbm = 0;
- int signalAsu = mPhoneStateReceiver.getSignalStrength();
+ int signalAsu = mPhoneStateReceiver.getSignalStrengthLevelAsu();
if (-1 == signalAsu) signalAsu = 0;
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index 085a089bb..af831576b 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -23,6 +23,7 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -34,8 +35,6 @@ import android.os.Environment;
import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceScreen;
-import android.view.LayoutInflater;
-import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.webkit.WebView;
@@ -48,7 +47,6 @@ import java.util.Locale;
* Displays preferences for Tethering.
*/
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 ENABLE_WIFI_AP = "enable_wifi_ap";
@@ -65,8 +63,6 @@ public class TetherSettings extends SettingsPreferenceFragment {
private WebView mView;
private CheckBoxPreference mUsbTether;
- private CheckBoxPreference mEnableWifiAp;
- private PreferenceScreen mWifiApSettings;
private WifiApEnabler mWifiApEnabler;
private CheckBoxPreference mBluetoothTether;
@@ -86,18 +82,17 @@ public class TetherSettings extends SettingsPreferenceFragment {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.tether_prefs);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = super.onCreateView(inflater, container, savedInstanceState);
final Activity activity = getActivity();
- mBluetoothPan = new BluetoothPan(activity);
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener,
+ BluetoothProfile.PAN);
+ }
- mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
- mWifiApSettings = (PreferenceScreen) findPreference(WIFI_AP_SETTINGS);
+ CheckBoxPreference enableWifiAp =
+ (CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
+ Preference wifiApSettings = findPreference(WIFI_AP_SETTINGS);
mUsbTether = (CheckBoxPreference) findPreference(USB_TETHER_SETTINGS);
mBluetoothTether = (CheckBoxPreference) findPreference(ENABLE_BLUETOOTH_TETHERING);
mTetherHelp = (PreferenceScreen) findPreference(TETHERING_HELP);
@@ -109,49 +104,43 @@ public class TetherSettings extends SettingsPreferenceFragment {
mWifiRegexs = cm.getTetherableWifiRegexs();
mBluetoothRegexs = cm.getTetherableBluetoothRegexs();
- boolean usbAvailable = mUsbRegexs.length != 0;
- boolean wifiAvailable = mWifiRegexs.length != 0;
- boolean bluetoothAvailable = mBluetoothRegexs.length != 0;
+ final boolean usbAvailable = mUsbRegexs.length != 0;
+ final boolean wifiAvailable = mWifiRegexs.length != 0;
+ final boolean bluetoothAvailable = mBluetoothRegexs.length != 0;
if (!usbAvailable || Utils.isMonkeyRunning()) {
getPreferenceScreen().removePreference(mUsbTether);
}
+
if (!wifiAvailable) {
- getPreferenceScreen().removePreference(mEnableWifiAp);
- getPreferenceScreen().removePreference(mWifiApSettings);
+ getPreferenceScreen().removePreference(enableWifiAp);
+ getPreferenceScreen().removePreference(wifiApSettings);
}
+
if (!bluetoothAvailable) {
getPreferenceScreen().removePreference(mBluetoothTether);
} else {
- if (mBluetoothPan.isTetheringOn()) {
+ if (mBluetoothPan != null && mBluetoothPan.isTetheringOn()) {
mBluetoothTether.setChecked(true);
} else {
mBluetoothTether.setChecked(false);
}
}
- /* Don't change the title for two-pane settings
- if (wifiAvailable && usbAvailable && bluetoothAvailable){
- activity.setTitle(R.string.tether_settings_title_all);
- } else if (wifiAvailable && usbAvailable){
- activity.setTitle(R.string.tether_settings_title_all);
- } else if (wifiAvailable && bluetoothAvailable){
- activity.setTitle(R.string.tether_settings_title_all);
- } else if (wifiAvailable) {
- activity.setTitle(R.string.tether_settings_title_wifi);
- } else if (usbAvailable && bluetoothAvailable) {
- activity.setTitle(R.string.tether_settings_title_usb_bluetooth);
- } else if (usbAvailable) {
- activity.setTitle(R.string.tether_settings_title_usb);
- } else {
- activity.setTitle(R.string.tether_settings_title_bluetooth);
- }
- */
- mWifiApEnabler = new WifiApEnabler(activity, mEnableWifiAp);
- mView = new WebView(activity);
- return view;
+ mWifiApEnabler = new WifiApEnabler(activity, enableWifiAp);
+ mView = new WebView(activity);
}
+ private BluetoothProfile.ServiceListener mProfileServiceListener =
+ new BluetoothProfile.ServiceListener() {
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ mBluetoothPan = (BluetoothPan) proxy;
+ }
+ public void onServiceDisconnected(int profile) {
+ mBluetoothPan = null;
+ }
+ };
+
@Override
public Dialog onCreateDialog(int id) {
if (id == DIALOG_TETHER_HELP) {
@@ -160,22 +149,22 @@ public class TetherSettings extends SettingsPreferenceFragment {
// check for the full language + country resource, if not there, try just language
final AssetManager am = getActivity().getAssets();
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;
InputStream is = null;
try {
is = am.open(path);
- } catch (Exception e) {
+ } catch (Exception ignored) {
useCountry = false;
} finally {
if (is != null) {
try {
is.close();
- } catch (Exception e) {}
+ } catch (Exception ignored) {}
}
}
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)) {
url = url.replace("%x", USB_HELP_MODIFIER);
} else if ((mWifiRegexs.length != 0) && (mUsbRegexs.length == 0)) {
@@ -277,10 +266,8 @@ public class TetherSettings extends SettingsPreferenceFragment {
String[] errored) {
ConnectivityManager cm =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
- boolean usbTethered = false;
boolean usbAvailable = false;
int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
- boolean usbErrored = false;
boolean massStorageActive =
Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
for (String s : available) {
@@ -293,11 +280,13 @@ public class TetherSettings extends SettingsPreferenceFragment {
}
}
}
+ boolean usbTethered = false;
for (String s : tethered) {
for (String regex : mUsbRegexs) {
if (s.matches(regex)) usbTethered = true;
}
}
+ boolean usbErrored = false;
for (String s: errored) {
for (String regex : mUsbRegexs) {
if (s.matches(regex)) usbErrored = true;
@@ -335,25 +324,23 @@ public class TetherSettings extends SettingsPreferenceFragment {
String[] errored) {
ConnectivityManager cm =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
- boolean bluetoothTethered = false;
- boolean bluetoothAvailable = false;
int bluetoothError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
- boolean bluetoothErrored = false;
for (String s : available) {
for (String regex : mBluetoothRegexs) {
if (s.matches(regex)) {
- bluetoothAvailable = true;
if (bluetoothError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
bluetoothError = cm.getLastTetherError(s);
}
}
}
}
+ boolean bluetoothTethered = false;
for (String s : tethered) {
for (String regex : mBluetoothRegexs) {
if (s.matches(regex)) bluetoothTethered = true;
}
}
+ boolean bluetoothErrored = false;
for (String s: errored) {
for (String regex : mBluetoothRegexs) {
if (s.matches(regex)) bluetoothErrored = true;
@@ -464,7 +451,7 @@ public class TetherSettings extends SettingsPreferenceFragment {
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 regex : regexes) {
if (iface.matches(regex)) {
diff --git a/src/com/android/settings/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java
index b1b7e6ec1..4b927491c 100644
--- a/src/com/android/settings/WirelessSettings.java
+++ b/src/com/android/settings/WirelessSettings.java
@@ -19,6 +19,8 @@ package com.android.settings;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
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.nfc.NfcEnabler;
@@ -103,7 +105,8 @@ public class WirelessSettings extends SettingsPreferenceFragment {
mAirplaneModeEnabler = new AirplaneModeEnabler(activity, airplane);
mAirplaneModePreference = (CheckBoxPreference) findPreference(KEY_TOGGLE_AIRPLANE);
mWifiEnabler = new WifiEnabler(activity, wifi);
- mBtEnabler = new BluetoothEnabler(activity, bt);
+ mBtEnabler = new BluetoothEnabler(activity,
+ LocalBluetoothManager.getInstance(activity).getBluetoothAdapter(), bt);
mNfcEnabler = new NfcEnabler(activity, nfc);
String toggleable = Settings.System.getString(activity.getContentResolver(),
diff --git a/src/com/android/settings/accounts/SyncStateCheckBoxPreference.java b/src/com/android/settings/accounts/SyncStateCheckBoxPreference.java
index b200eb67d..4b9ca81b2 100644
--- a/src/com/android/settings/accounts/SyncStateCheckBoxPreference.java
+++ b/src/com/android/settings/accounts/SyncStateCheckBoxPreference.java
@@ -20,6 +20,7 @@ import com.android.settings.R;
import android.content.Context;
import android.graphics.drawable.AnimationDrawable;
+import android.os.Handler;
import android.preference.CheckBoxPreference;
import android.util.AttributeSet;
import android.view.View;
@@ -40,7 +41,7 @@ public class SyncStateCheckBoxPreference extends CheckBoxPreference {
* toggling whether the provider will do autosync.
*/
private boolean mOneTimeSyncMode = false;
-
+
public SyncStateCheckBoxPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setWidgetLayoutResource(R.layout.preference_widget_sync_toggle);
@@ -67,7 +68,7 @@ public class SyncStateCheckBoxPreference extends CheckBoxPreference {
boolean showError;
boolean showPending;
if (mIsActive) {
- syncActiveView.post(new Runnable() {
+ new Handler(getContext().getMainLooper()).post(new Runnable() {
public void run() {
anim.start();
}
@@ -87,11 +88,11 @@ public class SyncStateCheckBoxPreference extends CheckBoxPreference {
syncFailedView.setVisibility(showError ? View.VISIBLE : View.GONE);
syncPendingView.setVisibility((showPending && !mIsActive) ? View.VISIBLE : View.GONE);
-
+
View checkBox = view.findViewById(android.R.id.checkbox);
if (mOneTimeSyncMode) {
checkBox.setVisibility(View.GONE);
-
+
/*
* Override the summary. Fill in the %1$s with the existing summary
* (what ends up happening is the old summary is shown on the next
@@ -138,7 +139,7 @@ public class SyncStateCheckBoxPreference extends CheckBoxPreference {
mOneTimeSyncMode = oneTimeSyncMode;
notifyChanged();
}
-
+
/**
* Gets whether the preference is in one-time sync mode.
*/
@@ -152,7 +153,7 @@ public class SyncStateCheckBoxPreference extends CheckBoxPreference {
// checkbox state
if (!mOneTimeSyncMode) {
super.onClick();
- }
+ }
}
public Account getAccount() {
diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java
index 0168396fa..e56db743b 100644
--- a/src/com/android/settings/applications/ApplicationsState.java
+++ b/src/com/android/settings/applications/ApplicationsState.java
@@ -5,11 +5,14 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
@@ -200,6 +203,7 @@ public class ApplicationsState {
// Information about all applications. Synchronize on mAppEntries
// to protect access to these.
+ final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
final HashMap<String, AppEntry> mEntriesMap = new HashMap<String, AppEntry>();
final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
@@ -376,9 +380,18 @@ public class ApplicationsState {
if (mApplications == null) {
mApplications = new ArrayList<ApplicationInfo>();
}
- for (int i=0; i<mAppEntries.size(); i++) {
- mAppEntries.get(i).sizeStale = true;
+
+ if (mInterestingConfigChanges.applyNewConfig(mContext.getResources())) {
+ // If an interesting part of the configuration has changed, we
+ // should completely reload the app entries.
+ mEntriesMap.clear();
+ mAppEntries.clear();
+ } else {
+ for (int i=0; i<mAppEntries.size(); i++) {
+ mAppEntries.get(i).sizeStale = true;
+ }
}
+
for (int i=0; i<mApplications.size(); i++) {
final ApplicationInfo info = mApplications.get(i);
final AppEntry entry = mEntriesMap.get(info.packageName);
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index 06d97fc87..aafd3a174 100644
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -37,13 +37,16 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.hardware.usb.IUsbManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.preference.PreferenceActivity;
import android.text.format.Formatter;
import android.util.Log;
@@ -79,6 +82,7 @@ public class InstalledAppDetails extends Fragment
public static final String ARG_PACKAGE_NAME = "package";
private PackageManager mPm;
+ private IUsbManager mUsbManager;
private ApplicationsState mState;
private ApplicationsState.AppEntry mAppEntry;
private PackageInfo mPackageInfo;
@@ -305,7 +309,9 @@ public class InstalledAppDetails extends Fragment
mState = ApplicationsState.getInstance(getActivity().getApplication());
mPm = getActivity().getPackageManager();
-
+ IBinder b = ServiceManager.getService(Context.USB_SERVICE);
+ mUsbManager = IUsbManager.Stub.asInterface(b);
+
mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
}
@@ -444,8 +450,14 @@ public class InstalledAppDetails extends Fragment
List<IntentFilter> intentList = new ArrayList<IntentFilter>();
mPm.getPreferredActivities(intentList, prefActList, packageName);
if(localLOGV) Log.i(TAG, "Have "+prefActList.size()+" number of activities in prefered list");
+ boolean hasUsbDefaults = false;
+ try {
+ hasUsbDefaults = mUsbManager.hasDefaults(packageName, mAppEntry.info.uid);
+ } catch (RemoteException e) {
+ Log.e(TAG, "mUsbManager.hasDefaults", e);
+ }
TextView autoLaunchView = (TextView)mRootView.findViewById(R.id.auto_launch);
- if (prefActList.size() <= 0) {
+ if (prefActList.size() <= 0 && !hasUsbDefaults) {
// Disable clear activities button
autoLaunchView.setText(R.string.auto_launch_disable_text);
mActivitiesButton.setEnabled(false);
@@ -730,18 +742,28 @@ public class InstalledAppDetails extends Fragment
private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- mForceStopButton.setEnabled(getResultCode() != Activity.RESULT_CANCELED);
- mForceStopButton.setOnClickListener(InstalledAppDetails.this);
+ updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED);
}
};
+
+ private void updateForceStopButton(boolean enabled) {
+ mForceStopButton.setEnabled(enabled);
+ mForceStopButton.setOnClickListener(InstalledAppDetails.this);
+ }
private void checkForceStop() {
Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
Uri.fromParts("package", mAppEntry.info.packageName, null));
intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
- getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
- Activity.RESULT_CANCELED, null, null);
+ if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
+ // If the app isn't explicitly stopped, then always show the
+ // force stop button.
+ updateForceStopButton(true);
+ } else {
+ getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
+ Activity.RESULT_CANCELED, null, null);
+ }
}
static class DisableChanger extends AsyncTask<Object, Object, Object> {
@@ -784,6 +806,11 @@ public class InstalledAppDetails extends Fragment
}
} else if(v == mActivitiesButton) {
mPm.clearPackagePreferredActivities(packageName);
+ try {
+ mUsbManager.clearDefaults(packageName, mAppEntry.info.uid);
+ } catch (RemoteException e) {
+ Log.e(TAG, "mUsbManager.clearDefaults", e);
+ }
mActivitiesButton.setEnabled(false);
} else if(v == mClearDataButton) {
if (mAppEntry.info.manageSpaceActivityName != null) {
diff --git a/src/com/android/settings/applications/InterestingConfigChanges.java b/src/com/android/settings/applications/InterestingConfigChanges.java
new file mode 100644
index 000000000..816d16986
--- /dev/null
+++ b/src/com/android/settings/applications/InterestingConfigChanges.java
@@ -0,0 +1,37 @@
+/*
+ * 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.applications;
+
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+
+class InterestingConfigChanges {
+ final Configuration mLastConfiguration = new Configuration();
+ int mLastDensity;
+
+ boolean applyNewConfig(Resources res) {
+ int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
+ boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
+ if (densityChanged || (configChanges&(ActivityInfo.CONFIG_LOCALE
+ |ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0) {
+ mLastDensity = res.getDisplayMetrics().densityDpi;
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/settings/applications/RunningState.java b/src/com/android/settings/applications/RunningState.java
index dbe4a64c0..e7e3af4df 100644
--- a/src/com/android/settings/applications/RunningState.java
+++ b/src/com/android/settings/applications/RunningState.java
@@ -34,7 +34,6 @@ import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.text.format.Formatter;
import android.util.Log;
import android.util.SparseArray;
@@ -54,9 +53,10 @@ public class RunningState {
static Object sGlobalLock = new Object();
static RunningState sInstance;
- static final int MSG_UPDATE_CONTENTS = 1;
- static final int MSG_REFRESH_UI = 2;
- static final int MSG_UPDATE_TIME = 3;
+ static final int MSG_RESET_CONTENTS = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+ static final int MSG_REFRESH_UI = 3;
+ static final int MSG_UPDATE_TIME = 4;
static final long TIME_UPDATE_DELAY = 1000;
static final long CONTENTS_UPDATE_DELAY = 2000;
@@ -69,6 +69,8 @@ public class RunningState {
OnRefreshUiListener mRefreshUiListener;
+ final InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
+
// Processes that are hosting a service we are interested in, organized
// by uid and name. Note that this mapping does not change even across
// service restarts, and during a restart there will still be a process
@@ -133,6 +135,9 @@ public class RunningState {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case MSG_RESET_CONTENTS:
+ reset();
+ break;
case MSG_UPDATE_CONTENTS:
synchronized (mLock) {
if (!mResumed) {
@@ -561,6 +566,12 @@ public class RunningState {
synchronized (mLock) {
mResumed = true;
mRefreshUiListener = listener;
+ if (mInterestingConfigChanges.applyNewConfig(mApplicationContext.getResources())) {
+ mHaveData = false;
+ mBackgroundHandler.removeMessages(MSG_RESET_CONTENTS);
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendEmptyMessage(MSG_RESET_CONTENTS);
+ }
if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) {
mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
}
@@ -613,6 +624,15 @@ public class RunningState {
return false;
}
+ private void reset() {
+ mServiceProcessesByName.clear();
+ mServiceProcessesByPid.clear();
+ mInterestingProcesses.clear();
+ mRunningProcesses.clear();
+ mProcessItems.clear();
+ mAllProcessItems.clear();
+ }
+
private boolean update(Context context, ActivityManager am) {
final PackageManager pm = context.getPackageManager();
@@ -975,7 +995,7 @@ public class RunningState {
}
if (newBackgroundItems == null) {
- // One or more at the bottom may no longer exit.
+ // One or more at the bottom may no longer exist.
if (mBackgroundItems.size() > numBackgroundProcesses) {
newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
for (int bgi=0; bgi<numBackgroundProcesses; bgi++) {
diff --git a/src/com/android/settings/bluetooth/A2dpProfile.java b/src/com/android/settings/bluetooth/A2dpProfile.java
new file mode 100644
index 000000000..96225d87c
--- /dev/null
+++ b/src/com/android/settings/bluetooth/A2dpProfile.java
@@ -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;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothCallback.java b/src/com/android/settings/bluetooth/BluetoothCallback.java
new file mode 100644
index 000000000..3ce9adfef
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothCallback.java
@@ -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);
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java b/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java
new file mode 100644
index 000000000..00e342ced
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java
@@ -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);
+ }
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
index 6a0b8811c..5f791d942 100644
--- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java
@@ -16,27 +16,32 @@
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.DialogInterface;
import android.graphics.drawable.Drawable;
import android.preference.Preference;
+import android.text.TextUtils;
+import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
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
* Bluetooth device in the Bluetooth Settings screen.
*/
-public class BluetoothDevicePreference extends Preference implements
+public final class BluetoothDevicePreference extends Preference implements
CachedBluetoothDevice.Callback, OnClickListener {
private static final String TAG = "BluetoothDevicePreference";
@@ -48,11 +53,7 @@ public class BluetoothDevicePreference extends Preference implements
private OnClickListener mOnSettingsClickListener;
- /**
- * Cached local copy of whether the device is busy. This is only updated
- * from {@link #onDeviceAttributesChanged()}.
- */
- private boolean mIsBusy;
+ private AlertDialog mDisconnectDialog;
public BluetoothDevicePreference(Context context, CachedBluetoothDevice cachedDevice) {
super(context);
@@ -60,19 +61,19 @@ public class BluetoothDevicePreference extends Preference implements
if (sDimAlpha == Integer.MIN_VALUE) {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
- sDimAlpha = (int) ((outValue.getFloat() * 255) * 0.5);
+ sDimAlpha = (int) (outValue.getFloat() * 255);
}
mCachedDevice = cachedDevice;
setWidgetLayoutResource(R.layout.preference_bluetooth);
- cachedDevice.registerCallback(this);
+ mCachedDevice.registerCallback(this);
onDeviceAttributesChanged();
}
- public CachedBluetoothDevice getCachedDevice() {
+ CachedBluetoothDevice getCachedDevice() {
return mCachedDevice;
}
@@ -84,42 +85,30 @@ public class BluetoothDevicePreference extends Preference implements
protected void onPrepareForRemoval() {
super.onPrepareForRemoval();
mCachedDevice.unregisterCallback(this);
+ if (mDisconnectDialog != null) {
+ mDisconnectDialog.dismiss();
+ mDisconnectDialog = null;
+ }
}
public void onDeviceAttributesChanged() {
-
/*
* 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());
- /*
- * 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());
+ setSummary(getConnectionSummary());
// Used to gray out the item
- mIsBusy = mCachedDevice.isBusy();
-
- // Data has changed
- notifyChanged();
+ setEnabled(!mCachedDevice.isBusy());
- // This could affect ordering, so notify that also
+ // This could affect ordering, so notify that
notifyHierarchyChanged();
}
@Override
- public boolean isEnabled() {
- // Temp fix until we have 2053751 fixed in the framework
- setEnabled(true);
- return super.isEnabled() && !mIsBusy;
- }
-
- @Override
protected void onBindView(View view) {
// Disable this view if the bluetooth enable/disable preference view is off
if (null != findPreferenceInHierarchy("bt_checkbox")) {
@@ -129,17 +118,17 @@ public class BluetoothDevicePreference extends Preference implements
super.onBindView(view);
ImageView btClass = (ImageView) view.findViewById(android.R.id.icon);
- btClass.setImageResource(mCachedDevice.getBtClassDrawable());
- btClass.setAlpha(!mIsBusy ? 255 : sDimAlpha);
+ btClass.setImageResource(getBtClassDrawable());
+ btClass.setAlpha(isEnabled() ? 255 : sDimAlpha);
mDeviceSettings = (ImageView) view.findViewById(R.id.deviceDetails);
if (mOnSettingsClickListener != null) {
mDeviceSettings.setOnClickListener(this);
mDeviceSettings.setTag(mCachedDevice);
- mDeviceSettings.setAlpha(!mIsBusy ? 255 : sDimAlpha);
+ mDeviceSettings.setAlpha(isEnabled() ? 255 : sDimAlpha);
} else { // Hide the settings icon and divider
mDeviceSettings.setVisibility(View.GONE);
- ImageView divider = (ImageView) view.findViewById(R.id.divider);
+ View divider = view.findViewById(R.id.divider);
if (divider != null) {
divider.setVisibility(View.GONE);
}
@@ -148,24 +137,40 @@ public class BluetoothDevicePreference extends Preference implements
LayoutInflater inflater = (LayoutInflater)
getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup profilesGroup = (ViewGroup) view.findViewById(R.id.profileIcons);
- Map<Profile, Drawable> profileIcons = mCachedDevice.getProfileIcons();
- for (Profile profile : profileIcons.keySet()) {
- Drawable icon = profileIcons.get(profile);
- inflater.inflate(R.layout.profile_icon_small, profilesGroup, true);
- ImageView imageView =
- (ImageView) profilesGroup.getChildAt(profilesGroup.getChildCount() - 1);
- imageView.setImageDrawable(icon);
- boolean profileEnabled = mCachedDevice.isConnectedProfile(profile);
- imageView.setAlpha(profileEnabled ? 255 : sDimAlpha);
+ for (LocalBluetoothProfile profile : mCachedDevice.getProfiles()) {
+ int iconResource = profile.getDrawableResource(mCachedDevice.getBtClass());
+ if (iconResource != 0) {
+ Drawable icon = getContext().getResources().getDrawable(iconResource);
+ inflater.inflate(R.layout.profile_icon_small, profilesGroup, true);
+ ImageView imageView =
+ (ImageView) profilesGroup.getChildAt(profilesGroup.getChildCount() - 1);
+ imageView.setImageDrawable(icon);
+ boolean profileEnabled = mCachedDevice.isConnectedProfile(profile);
+ imageView.setAlpha(profileEnabled ? 255 : sDimAlpha);
+ }
}
}
public void onClick(View v) {
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
public int compareTo(Preference another) {
if (!(another instanceof BluetoothDevicePreference)) {
@@ -173,7 +178,112 @@ public class BluetoothDevicePreference extends Preference implements
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;
+ }
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java b/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java
index 37e48ff9f..40bf5bc3a 100644
--- a/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java
+++ b/src/com/android/settings/bluetooth/BluetoothDiscoverableEnabler.java
@@ -16,39 +16,47 @@
package com.android.settings.bluetooth;
-import com.android.settings.R;
-
import android.bluetooth.BluetoothAdapter;
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.os.SystemProperties;
-import android.preference.Preference;
import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+
+import com.android.settings.R;
/**
* BluetoothDiscoverableEnabler is a helper to manage the "Discoverable"
* checkbox. It sets/unsets discoverability and keeps track of how much time
* until the the discoverability is automatically turned off.
*/
-public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChangeListener {
- private static final String TAG = "BluetoothDiscoverableEnabler";
+final class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChangeListener {
private static final String SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT =
"debug.bt.discoverable_time";
- /* package */ static final int DEFAULT_DISCOVERABLE_TIMEOUT = 120;
- /* package */ static final String SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP =
- "discoverable_end_timestamp";
+ private static final int DISCOVERABLE_TIMEOUT_TWO_MINUTES = 120;
+ private static final int DISCOVERABLE_TIMEOUT_FIVE_MINUTES = 300;
+ private static final int DISCOVERABLE_TIMEOUT_ONE_HOUR = 3600;
+ static final int DISCOVERABLE_TIMEOUT_NEVER = 0;
+
+ 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_ONE_HOUR = "onehour";
+ private static final String VALUE_DISCOVERABLE_TIMEOUT_NEVER = "never";
+
+ static final int DEFAULT_DISCOVERABLE_TIMEOUT = DISCOVERABLE_TIMEOUT_TWO_MINUTES;
private final Context mContext;
private final Handler mUiHandler;
private final CheckBoxPreference mCheckBoxPreference;
+ private final ListPreference mTimeoutListPreference;
- private final LocalBluetoothManager mLocalManager;
+ private final LocalBluetoothAdapter mLocalAdapter;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -69,103 +77,125 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
}
};
- public BluetoothDiscoverableEnabler(Context context, CheckBoxPreference checkBoxPreference) {
+ BluetoothDiscoverableEnabler(Context context, LocalBluetoothAdapter adapter,
+ CheckBoxPreference checkBoxPreference, ListPreference timeoutListPreference) {
mContext = context;
mUiHandler = new Handler();
mCheckBoxPreference = checkBoxPreference;
+ mTimeoutListPreference = timeoutListPreference;
checkBoxPreference.setPersistent(false);
+ // we actually want to persist this since can't infer from BT device state
+ mTimeoutListPreference.setPersistent(true);
- mLocalManager = LocalBluetoothManager.getInstance(context);
- if (mLocalManager == null) {
+ mLocalAdapter = adapter;
+ if (adapter == null) {
// Bluetooth not supported
checkBoxPreference.setEnabled(false);
}
}
public void resume() {
- if (mLocalManager == null) {
+ if (mLocalAdapter == null) {
return;
}
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
mContext.registerReceiver(mReceiver, filter);
mCheckBoxPreference.setOnPreferenceChangeListener(this);
-
- handleModeChanged(mLocalManager.getBluetoothAdapter().getScanMode());
+ mTimeoutListPreference.setOnPreferenceChangeListener(this);
+ handleModeChanged(mLocalAdapter.getScanMode());
}
public void pause() {
- if (mLocalManager == null) {
+ if (mLocalAdapter == null) {
return;
}
mUiHandler.removeCallbacks(mUpdateCountdownSummaryRunnable);
mCheckBoxPreference.setOnPreferenceChangeListener(null);
+ mTimeoutListPreference.setOnPreferenceChangeListener(null);
mContext.unregisterReceiver(mReceiver);
}
public boolean onPreferenceChange(Preference preference, Object value) {
- // Turn on/off BT discoverability
- setEnabled((Boolean) value);
+ if (preference == mCheckBoxPreference) {
+ // Turn on/off BT discoverability
+ setEnabled((Boolean) value);
+ } else if (preference == mTimeoutListPreference) {
+ mTimeoutListPreference.setValue((String) value);
+ setEnabled(true);
+ }
return true;
}
- private void setEnabled(final boolean enable) {
- BluetoothAdapter manager = mLocalManager.getBluetoothAdapter();
-
+ private void setEnabled(boolean enable) {
if (enable) {
-
int timeout = getDiscoverableTimeout();
- manager.setDiscoverableTimeout(timeout);
+ mLocalAdapter.setDiscoverableTimeout(timeout);
- mCheckBoxPreference.setSummaryOn(
- mContext.getResources().getString(R.string.bluetooth_is_discoverable, timeout));
+ long endTimestamp = System.currentTimeMillis() + timeout * 1000L;
+ LocalBluetoothPreferences.persistDiscoverableEndTimestamp(mContext, endTimestamp);
- long endTimestamp = System.currentTimeMillis() + timeout * 1000;
- persistDiscoverableEndTimestamp(endTimestamp);
+ updateCountdownSummary();
- manager.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+ mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, timeout);
} else {
- manager.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
+ }
+ }
+
+ private void updateTimerDisplay(int timeout) {
+ if (getDiscoverableTimeout() == DISCOVERABLE_TIMEOUT_NEVER) {
+ mCheckBoxPreference.setSummaryOn(
+ mContext.getString(R.string.bluetooth_is_discoverable_always));
+ } else {
+ mCheckBoxPreference.setSummaryOn(
+ mContext.getString(R.string.bluetooth_is_discoverable, timeout));
}
}
private int getDiscoverableTimeout() {
int timeout = SystemProperties.getInt(SYSTEM_PROPERTY_DISCOVERABLE_TIMEOUT, -1);
- if (timeout <= 0) {
- timeout = DEFAULT_DISCOVERABLE_TIMEOUT;
+ if (timeout < 0) {
+ String timeoutValue = mTimeoutListPreference.getValue();
+ if (timeoutValue == null) {
+ mTimeoutListPreference.setValue(VALUE_DISCOVERABLE_TIMEOUT_TWO_MINUTES);
+ return DISCOVERABLE_TIMEOUT_TWO_MINUTES;
+ }
+
+ if (timeoutValue.equals(VALUE_DISCOVERABLE_TIMEOUT_NEVER)) {
+ timeout = DISCOVERABLE_TIMEOUT_NEVER;
+ } else if (timeoutValue.equals(VALUE_DISCOVERABLE_TIMEOUT_ONE_HOUR)) {
+ timeout = DISCOVERABLE_TIMEOUT_ONE_HOUR;
+ } else if (timeoutValue.equals(VALUE_DISCOVERABLE_TIMEOUT_FIVE_MINUTES)) {
+ timeout = DISCOVERABLE_TIMEOUT_FIVE_MINUTES;
+ } else {
+ timeout = DISCOVERABLE_TIMEOUT_TWO_MINUTES;
+ }
}
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) {
if (mode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
mCheckBoxPreference.setChecked(true);
updateCountdownSummary();
-
} else {
mCheckBoxPreference.setChecked(false);
}
}
private void updateCountdownSummary() {
- int mode = mLocalManager.getBluetoothAdapter().getScanMode();
+ int mode = mLocalAdapter.getScanMode();
if (mode != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
return;
}
long currentTimestamp = System.currentTimeMillis();
- long endTimestamp = mLocalManager.getSharedPreferences().getLong(
- SHARED_PREFERENCES_KEY_DISCOVERABLE_END_TIMESTAMP, 0);
+ long endTimestamp = LocalBluetoothPreferences.getDiscoverableEndTimestamp(mContext);
if (currentTimestamp > endTimestamp) {
// We're still in discoverable mode, but maybe there isn't a timeout.
@@ -173,17 +203,12 @@ public class BluetoothDiscoverableEnabler implements Preference.OnPreferenceChan
return;
}
- String formattedTimeLeft = String.valueOf((endTimestamp - currentTimestamp) / 1000);
-
- mCheckBoxPreference.setSummaryOn(
- mContext.getResources().getString(R.string.bluetooth_is_discoverable,
- formattedTimeLeft));
+ int timeLeft = (int) ((endTimestamp - currentTimestamp) / 1000L);
+ updateTimerDisplay(timeLeft);
synchronized (this) {
mUiHandler.removeCallbacks(mUpdateCountdownSummaryRunnable);
mUiHandler.postDelayed(mUpdateCountdownSummaryRunnable, 1000);
}
}
-
-
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDiscoveryReceiver.java b/src/com/android/settings/bluetooth/BluetoothDiscoveryReceiver.java
new file mode 100644
index 000000000..fbb682756
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDiscoveryReceiver.java
@@ -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);
+ }
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothEnabler.java b/src/com/android/settings/bluetooth/BluetoothEnabler.java
index a7b9d713e..9aeb1b939 100644
--- a/src/com/android/settings/bluetooth/BluetoothEnabler.java
+++ b/src/com/android/settings/bluetooth/BluetoothEnabler.java
@@ -34,13 +34,14 @@ import android.widget.Toast;
* preference. It turns on/off Bluetooth and ensures the summary of the
* preference reflects the current state.
*/
-public class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
+public final class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
private final Context mContext;
private final CheckBoxPreference mCheckBox;
private final CharSequence mOriginalSummary;
- private final LocalBluetoothManager mLocalManager;
+ private final LocalBluetoothAdapter mLocalAdapter;
private final IntentFilter mIntentFilter;
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
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;
mCheckBox = checkBox;
mOriginalSummary = checkBox.getSummary();
checkBox.setPersistent(false);
- mLocalManager = LocalBluetoothManager.getInstance(context);
- if (mLocalManager == null) {
+ mLocalAdapter = adapter;
+ if (adapter == null) {
// Bluetooth is not supported
checkBox.setEnabled(false);
}
@@ -64,19 +66,19 @@ public class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
}
public void resume() {
- if (mLocalManager == null) {
+ if (mLocalAdapter == null) {
return;
}
// Bluetooth state is not sticky, so set it manually
- handleStateChanged(mLocalManager.getBluetoothState());
+ handleStateChanged(mLocalAdapter.getBluetoothState());
mContext.registerReceiver(mReceiver, mIntentFilter);
mCheckBox.setOnPreferenceChangeListener(this);
}
public void pause() {
- if (mLocalManager == null) {
+ if (mLocalAdapter == null) {
return;
}
@@ -95,14 +97,14 @@ public class BluetoothEnabler implements Preference.OnPreferenceChangeListener {
return false;
}
- mLocalManager.setBluetoothEnabled(enable);
+ mLocalAdapter.setBluetoothEnabled(enable);
mCheckBox.setEnabled(false);
// Don't update UI to opposite state until we're sure
return false;
}
- /* package */ void handleStateChanged(int state) {
+ void handleStateChanged(int state) {
switch (state) {
case BluetoothAdapter.STATE_TURNING_ON:
mCheckBox.setSummary(R.string.wifi_starting);
diff --git a/src/com/android/settings/bluetooth/BluetoothEventManager.java b/src/com/android/settings/bluetooth/BluetoothEventManager.java
new file mode 100644
index 000000000..70e35f984
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothEventManager.java
@@ -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;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java b/src/com/android/settings/bluetooth/BluetoothEventRedirector.java
deleted file mode 100644
index 3023dafb7..000000000
--- a/src/com/android/settings/bluetooth/BluetoothEventRedirector.java
+++ /dev/null
@@ -1,259 +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_INPUT_DEVICE_STATE_CHANGED)) {
- final int newState = intent.getIntExtra(
- BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0);
- final int oldState = intent.getIntExtra(
- BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0);
- if (newState == BluetoothInputDevice.STATE_DISCONNECTED &&
- oldState == BluetoothInputDevice.STATE_CONNECTING) {
- Log.i(TAG, "Failed to connect BT HID");
- }
-
- mManager.getCachedDeviceManager().onProfileStateChanged(device,
- Profile.HID, newState);
-
- } else if (action.equals(BluetoothPan.ACTION_PAN_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_PAN_STATE, 0);
- final int oldState = intent.getIntExtra(
- BluetoothPan.EXTRA_PREVIOUS_PAN_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_PAN_STATE_CHANGED);
- filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_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();
- }
- });
- }
- });
- }
-}
diff --git a/src/com/android/settings/bluetooth/BluetoothFindNearby.java b/src/com/android/settings/bluetooth/BluetoothFindNearby.java
index f1b876e76..066f4f611 100644
--- a/src/com/android/settings/bluetooth/BluetoothFindNearby.java
+++ b/src/com/android/settings/bluetooth/BluetoothFindNearby.java
@@ -16,21 +16,18 @@
package com.android.settings.bluetooth;
-import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.util.Log;
import com.android.settings.R;
/**
* 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";
-
- void addPreferencesForActivity(Activity activity) {
+ @Override
+ void addPreferencesForActivity() {
addPreferencesFromResource(R.xml.device_picker);
}
@@ -38,35 +35,37 @@ public class BluetoothFindNearby extends DeviceListPreferenceFragment {
public void onResume() {
super.onResume();
if (mSelectedDevice != null) {
- CachedBluetoothDevice device =
- mLocalManager.getCachedDeviceManager().findDevice(mSelectedDevice);
- if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
+ CachedBluetoothDeviceManager manager = mLocalManager.getCachedDeviceManager();
+ CachedBluetoothDevice device = manager.findDevice(mSelectedDevice);
+ if (device != null && device.getBondState() == BluetoothDevice.BOND_BONDED) {
// selected device was paired, so return from this screen
finish();
return;
}
}
- mLocalManager.startScanning(true);
+ mLocalAdapter.startScanning(true);
}
+ @Override
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
- mLocalManager.stopScanning();
+ mLocalAdapter.stopScanning();
super.onDevicePreferenceClick(btPreference);
}
- public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice,
- int bondState) {
+ public void onDeviceBondStateChanged(CachedBluetoothDevice
+ cachedDevice, int bondState) {
if (bondState == BluetoothDevice.BOND_BONDED) {
// return from scan screen after successful auto-pairing
finish();
}
}
- void onBluetoothStateChanged(int bluetoothState) {
+ @Override
+ public void onBluetoothStateChanged(int bluetoothState) {
super.onBluetoothStateChanged(bluetoothState);
if (bluetoothState == BluetoothAdapter.STATE_ON) {
- mLocalManager.startScanning(false);
+ mLocalAdapter.startScanning(false);
}
}
}
diff --git a/src/com/android/settings/bluetooth/BluetoothNamePreference.java b/src/com/android/settings/bluetooth/BluetoothNamePreference.java
index c99ab4c99..f41689ef7 100644
--- a/src/com/android/settings/bluetooth/BluetoothNamePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothNamePreference.java
@@ -22,12 +22,12 @@ import android.app.Dialog;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.preference.EditTextPreference;
import android.text.Editable;
import android.text.InputFilter;
-import android.text.Spanned;
import android.text.TextWatcher;
import android.util.AttributeSet;
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 API.
*/
-public class BluetoothNamePreference extends EditTextPreference implements TextWatcher {
- private static final String TAG = "BluetoothNamePreference";
+public final class BluetoothNamePreference extends EditTextPreference implements TextWatcher {
+// private static final String TAG = "BluetoothNamePreference";
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
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -61,7 +61,7 @@ public class BluetoothNamePreference extends EditTextPreference implements TextW
public BluetoothNamePreference(Context context, AttributeSet attrs) {
super(context, attrs);
- mLocalManager = LocalBluetoothManager.getInstance(context);
+ mLocalAdapter = LocalBluetoothManager.getInstance(context).getBluetoothAdapter();
setSummaryToName();
}
@@ -97,16 +97,17 @@ public class BluetoothNamePreference extends EditTextPreference implements TextW
}
private void setSummaryToName() {
- BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
- if (adapter.isEnabled()) {
- setSummary(adapter.getName());
+ if (mLocalAdapter != null && mLocalAdapter.isEnabled()) {
+ setSummary(mLocalAdapter.getName());
}
}
@Override
protected boolean persistString(String value) {
- BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
- adapter.setName(value);
+ // Persist with Bluez instead of shared preferences
+ if (mLocalAdapter != null) {
+ mLocalAdapter.setName(value);
+ }
return true;
}
@@ -116,8 +117,8 @@ public class BluetoothNamePreference extends EditTextPreference implements TextW
// The dialog should be created by now
EditText et = getEditText();
- if (et != null) {
- et.setText(mLocalManager.getBluetoothAdapter().getName());
+ if (et != null && mLocalAdapter != null) {
+ et.setText(mLocalAdapter.getName());
}
}
@@ -125,7 +126,7 @@ public class BluetoothNamePreference extends EditTextPreference implements TextW
public void afterTextChanged(Editable s) {
Dialog d = getDialog();
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
}
- /**
- * 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;
- }
- }
- }
}
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
index 1822e73f8..1b443c497 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingDialog.java
@@ -42,33 +42,37 @@ import com.android.settings.R;
* 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.
*/
-public class BluetoothPairingDialog extends AlertActivity implements DialogInterface.OnClickListener,
+public final class BluetoothPairingDialog extends AlertActivity implements DialogInterface.OnClickListener,
TextWatcher {
private static final String TAG = "BluetoothPairingDialog";
private static final int BLUETOOTH_PIN_MAX_LENGTH = 16;
private static final int BLUETOOTH_PASSKEY_MAX_LENGTH = 6;
- private LocalBluetoothManager mLocalManager;
private BluetoothDevice mDevice;
private int mType;
private String mPairingKey;
private EditText mPairingView;
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
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,
BluetoothDevice.ERROR);
if (bondState == BluetoothDevice.BOND_BONDED ||
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);
if (device == null || device.equals(mDevice)) {
- dismissDialog();
+ dismiss();
}
}
}
@@ -81,48 +85,63 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
Intent intent = getIntent();
if (!intent.getAction().equals(BluetoothDevice.ACTION_PAIRING_REQUEST))
{
- Log.e(TAG,
- "Error: this activity may be started only with intent " +
+ Log.e(TAG, "Error: this activity may be started only with intent " +
BluetoothDevice.ACTION_PAIRING_REQUEST);
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);
mType = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.ERROR);
- if (mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
- createUserEntryDialog();
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY) {
- createUserEntryDialog();
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION){
- int passkey =
- intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
- if (passkey == BluetoothDevice.ERROR) {
- Log.e(TAG, "Invalid ConfirmationPasskey received, not showing any dialog");
- return;
- }
- mPairingKey = String.format("%06d", passkey);
- createConfirmationDialog();
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
- createConsentDialog();
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY ||
- mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
- int pairingKey =
- intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
- if (pairingKey == BluetoothDevice.ERROR) {
- Log.e(TAG, "Invalid Confirmation Passkey or PIN received, not showing any dialog");
- return;
- }
- if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
- mPairingKey = String.format("%06d", pairingKey);
- } else {
- mPairingKey = String.format("%04d", pairingKey);
- }
- createDisplayPasskeyOrPinDialog();
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT) {
- createConsentDialog();
- } else {
- Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
+
+ switch (mType) {
+ case BluetoothDevice.PAIRING_VARIANT_PIN:
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+ createUserEntryDialog(deviceManager);
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+ int passkey =
+ intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
+ if (passkey == BluetoothDevice.ERROR) {
+ Log.e(TAG, "Invalid Confirmation Passkey received, not showing any dialog");
+ return;
+ }
+ mPairingKey = String.format("%06d", passkey);
+ createConfirmationDialog(deviceManager);
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_CONSENT:
+ case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+ createConsentDialog(deviceManager);
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
+ case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
+ int pairingKey =
+ intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_KEY, BluetoothDevice.ERROR);
+ if (pairingKey == BluetoothDevice.ERROR) {
+ Log.e(TAG, "Invalid Confirmation Passkey or PIN received, not showing any dialog");
+ return;
+ }
+ if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
+ mPairingKey = String.format("%06d", pairingKey);
+ } else {
+ mPairingKey = String.format("%04d", pairingKey);
+ }
+ createDisplayPasskeyOrPinDialog(deviceManager);
+ break;
+
+ default:
+ 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));
}
- private void createUserEntryDialog() {
+ private void createUserEntryDialog(CachedBluetoothDeviceManager deviceManager) {
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pairing_request);
- p.mView = createView();
+ p.mView = createView(deviceManager);
p.mPositiveButtonText = getString(android.R.string.ok);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(android.R.string.cancel);
p.mNegativeButtonListener = this;
setupAlert();
- mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+ mOkButton = mAlert.getButton(BUTTON_POSITIVE);
mOkButton.setEnabled(false);
}
- private View createView() {
+ private View createView(CachedBluetoothDeviceManager deviceManager) {
View view = getLayoutInflater().inflate(R.layout.bluetooth_pin_entry, null);
-
- String name = mLocalManager.getCachedDeviceManager().getName(mDevice);
+ String name = deviceManager.getName(mDevice);
TextView messageView = (TextView) view.findViewById(R.id.message);
mPairingView = (EditText) view.findViewById(R.id.text);
mPairingView.addTextChangedListener(this);
- if (mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
- messageView.setText(getString(R.string.bluetooth_enter_pin_msg, name));
- // Maximum of 16 characters in a PIN adb sync
- mPairingView.setFilters(new InputFilter[] {
- new LengthFilter(BLUETOOTH_PIN_MAX_LENGTH) });
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY){
- messageView.setText(getString(R.string.bluetooth_enter_passkey_msg, name));
- // Maximum of 6 digits for passkey
- mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER |
- InputType.TYPE_NUMBER_FLAG_SIGNED);
- mPairingView.setFilters(new InputFilter[] {
- new LengthFilter(BLUETOOTH_PASSKEY_MAX_LENGTH)});
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
- mPairingView.setVisibility(View.GONE);
- messageView.setText(getString(R.string.bluetooth_confirm_passkey_msg, name,
- mPairingKey));
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
- mPairingView.setVisibility(View.GONE);
- messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY ||
- mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
- mPairingView.setVisibility(View.GONE);
- messageView.setText(getString(R.string.bluetooth_display_passkey_pin_msg, name,
- mPairingKey));
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT) {
- mPairingView.setVisibility(View.GONE);
- messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
- } else {
- Log.e(TAG, "Incorrect pairing type received, not creating view");
+ switch (mType) {
+ case BluetoothDevice.PAIRING_VARIANT_PIN:
+ messageView.setText(getString(R.string.bluetooth_enter_pin_msg, name));
+ // Maximum of 16 characters in a PIN adb sync
+ mPairingView.setFilters(new InputFilter[] {
+ new LengthFilter(BLUETOOTH_PIN_MAX_LENGTH) });
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+ messageView.setText(getString(R.string.bluetooth_enter_passkey_msg, name));
+ // Maximum of 6 digits for passkey
+ mPairingView.setInputType(InputType.TYPE_CLASS_NUMBER |
+ InputType.TYPE_NUMBER_FLAG_SIGNED);
+ mPairingView.setFilters(new InputFilter[] {
+ new LengthFilter(BLUETOOTH_PASSKEY_MAX_LENGTH)});
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+ mPairingView.setVisibility(View.GONE);
+ messageView.setText(getString(R.string.bluetooth_confirm_passkey_msg, name,
+ mPairingKey));
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_CONSENT:
+ mPairingView.setVisibility(View.GONE);
+ messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
+ case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
+ mPairingView.setVisibility(View.GONE);
+ messageView.setText(getString(R.string.bluetooth_display_passkey_pin_msg, name,
+ mPairingKey));
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+ mPairingView.setVisibility(View.GONE);
+ messageView.setText(getString(R.string.bluetooth_incoming_pairing_msg, name));
+ break;
+
+ default:
+ Log.e(TAG, "Incorrect pairing type received, not creating view");
}
return view;
}
- private void createConfirmationDialog() {
+ private void createConfirmationDialog(CachedBluetoothDeviceManager deviceManager) {
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pairing_request);
- p.mView = createView();
+ p.mView = createView(deviceManager);
p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
@@ -201,11 +232,11 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
setupAlert();
}
- private void createConsentDialog() {
+ private void createConsentDialog(CachedBluetoothDeviceManager deviceManager) {
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pairing_request);
- p.mView = createView();
+ p.mView = createView(deviceManager);
p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
p.mPositiveButtonListener = this;
p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
@@ -213,11 +244,12 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
setupAlert();
}
- private void createDisplayPasskeyOrPinDialog() {
+ private void createDisplayPasskeyOrPinDialog(
+ CachedBluetoothDeviceManager deviceManager) {
final AlertController.AlertParams p = mAlertParams;
p.mIconId = android.R.drawable.ic_dialog_info;
p.mTitle = getString(R.string.bluetooth_pairing_request);
- p.mView = createView();
+ p.mView = createView(deviceManager);
p.mNegativeButtonText = getString(android.R.string.cancel);
p.mNegativeButtonListener = this;
setupAlert();
@@ -244,32 +276,37 @@ public class BluetoothPairingDialog extends AlertActivity implements DialogInter
}
}
- private void dismissDialog() {
- this.dismiss();
- }
-
private void onPair(String value) {
- if (mType == BluetoothDevice.PAIRING_VARIANT_PIN) {
- byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
- if (pinBytes == null) {
- return;
- }
- mDevice.setPin(pinBytes);
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY) {
- int passkey = Integer.parseInt(value);
- mDevice.setPasskey(passkey);
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION) {
- mDevice.setPairingConfirmation(true);
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
- mDevice.setPairingConfirmation(true);
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
- // Do Nothing.
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN) {
- // Do Nothing
- } else if (mType == BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT) {
- mDevice.setRemoteOutOfBandData();
- } else {
- Log.e(TAG, "Incorrect pairing type received");
+ switch (mType) {
+ case BluetoothDevice.PAIRING_VARIANT_PIN:
+ byte[] pinBytes = BluetoothDevice.convertPinToBytes(value);
+ if (pinBytes == null) {
+ return;
+ }
+ mDevice.setPin(pinBytes);
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
+ int passkey = Integer.parseInt(value);
+ mDevice.setPasskey(passkey);
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
+ case BluetoothDevice.PAIRING_VARIANT_CONSENT:
+ mDevice.setPairingConfirmation(true);
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
+ case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
+ // Do nothing.
+ break;
+
+ case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
+ mDevice.setRemoteOutOfBandData();
+ break;
+
+ default:
+ 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) {
switch (which) {
- case DialogInterface.BUTTON_POSITIVE:
+ case BUTTON_POSITIVE:
onPair(mPairingView.getText().toString());
break;
- case DialogInterface.BUTTON_NEGATIVE:
+ case BUTTON_NEGATIVE:
+ default:
onCancel();
break;
}
diff --git a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java
index 6037c82a9..de96d7119 100644
--- a/src/com/android/settings/bluetooth/BluetoothPairingRequest.java
+++ b/src/com/android/settings/bluetooth/BluetoothPairingRequest.java
@@ -35,17 +35,15 @@ import android.os.PowerManager;
* confirmation entry dialog. Otherwise it puts a Notification in the status bar, which can
* 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
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(BluetoothDevice.ACTION_PAIRING_REQUEST)) {
-
- LocalBluetoothManager localManager = LocalBluetoothManager.getInstance(context);
-
+ // convert broadcast intent into activity intent (same action string)
BluetoothDevice device =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
int type = intent.getIntExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
@@ -68,39 +66,35 @@ public class BluetoothPairingRequest extends BroadcastReceiver {
(PowerManager)context.getSystemService(Context.POWER_SERVICE);
String deviceAddress = device != null ? device.getAddress() : null;
if (powerManager.isScreenOn() &&
- localManager.shouldShowDialogInForeground(deviceAddress)) {
+ LocalBluetoothPreferences.shouldShowDialogInForeground(context, deviceAddress)) {
// Since the screen is on and the BT-related activity is in the foreground,
// just open the dialog
context.startActivity(pairingIntent);
-
} else {
-
// Put up a notification that leads to the dialog
Resources res = context.getResources();
- Notification notification = new Notification(
- android.R.drawable.stat_sys_data_bluetooth,
- res.getString(R.string.bluetooth_notif_ticker),
- System.currentTimeMillis());
+ Notification.Builder builder = new Notification.Builder(context)
+ .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
+ .setTicker(res.getString(R.string.bluetooth_notif_ticker));
PendingIntent pending = PendingIntent.getActivity(context, 0,
pairingIntent, PendingIntent.FLAG_ONE_SHOT);
String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME);
if (TextUtils.isEmpty(name)) {
- name = (device != null ? device.getName() :
- context.getString(android.R.string.unknownName));
+ name = device != null ? device.getName() :
+ context.getString(android.R.string.unknownName);
}
- notification.setLatestEventInfo(context,
- res.getString(R.string.bluetooth_notif_title),
- res.getString(R.string.bluetooth_notif_message, name),
- pending);
- notification.flags |= Notification.FLAG_AUTO_CANCEL;
- notification.defaults |= Notification.DEFAULT_SOUND;
+ builder.setContentTitle(res.getString(R.string.bluetooth_notif_title))
+ .setContentText(res.getString(R.string.bluetooth_notif_message, name))
+ .setContentIntent(pending)
+ .setAutoCancel(true)
+ .setDefaults(Notification.DEFAULT_SOUND);
NotificationManager manager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
- manager.notify(NOTIFICATION_ID, notification);
+ manager.notify(NOTIFICATION_ID, builder.getNotification());
}
} else if (action.equals(BluetoothDevice.ACTION_PAIRING_CANCEL)) {
diff --git a/src/com/android/settings/bluetooth/BluetoothProfilePreference.java b/src/com/android/settings/bluetooth/BluetoothProfilePreference.java
index c74012a20..e334867ec 100644
--- a/src/com/android/settings/bluetooth/BluetoothProfilePreference.java
+++ b/src/com/android/settings/bluetooth/BluetoothProfilePreference.java
@@ -16,9 +16,6 @@
package com.android.settings.bluetooth;
-import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
-import com.android.settings.R;
-
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.preference.Preference;
@@ -26,22 +23,24 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
+import com.android.settings.R;
+
/**
* BluetoothProfilePreference is the preference type used to display each profile for a
* 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 boolean mExpanded;
private ImageView mProfileExpandView;
- private final Profile mProfile;
+ private final LocalBluetoothProfile mProfile;
private OnClickListener mOnExpandClickListener;
- public BluetoothProfilePreference(Context context, Profile profile) {
+ BluetoothProfilePreference(Context context, LocalBluetoothProfile profile) {
super(context);
mProfile = profile;
@@ -75,14 +74,14 @@ public class BluetoothProfilePreference extends Preference implements OnClickLis
btProfile.setImageDrawable(mProfileDrawable);
mProfileExpandView = (ImageView) view.findViewById(R.id.profileExpand);
- if (mProfile == Profile.PAN) {
- mProfileExpandView.setVisibility(View.GONE);
- } else {
+ if (mProfile.isAutoConnectable()) {
mProfileExpandView.setOnClickListener(this);
mProfileExpandView.setTag(mProfile);
mProfileExpandView.setImageResource(mExpanded
? com.android.internal.R.drawable.expander_open_holo_dark
: com.android.internal.R.drawable.expander_close_holo_dark);
+ } else {
+ mProfileExpandView.setVisibility(View.GONE);
}
}
diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java
index c1fda6bfc..c6ba9af56 100644
--- a/src/com/android/settings/bluetooth/BluetoothSettings.java
+++ b/src/com/android/settings/bluetooth/BluetoothSettings.java
@@ -16,11 +16,12 @@
package com.android.settings.bluetooth;
-import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
import android.preference.Preference;
+import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.util.Log;
import android.view.View;
@@ -31,13 +32,12 @@ import com.android.settings.R;
* BluetoothSettings is the Settings screen for Bluetooth configuration and
* connection management.
*/
-public class BluetoothSettings extends DeviceListPreferenceFragment
- implements LocalBluetoothManager.Callback, View.OnClickListener {
-
+public final class BluetoothSettings extends DeviceListPreferenceFragment {
private static final String TAG = "BluetoothSettings";
private static final String KEY_BT_CHECKBOX = "bt_checkbox";
private static final String KEY_BT_DISCOVERABLE = "bt_discoverable";
+ private static final String KEY_BT_DISCOVERABLE_TIMEOUT = "bt_discoverable_timeout";
private static final String KEY_BT_NAME = "bt_name";
private static final String KEY_BT_SHOW_RECEIVED = "bt_show_received_files";
@@ -49,14 +49,22 @@ public class BluetoothSettings extends DeviceListPreferenceFragment
private static final String BTOPP_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);
- mEnabler = new BluetoothEnabler(activity,
+ mEnabler = new BluetoothEnabler(getActivity(), mLocalAdapter,
(CheckBoxPreference) findPreference(KEY_BT_CHECKBOX));
- mDiscoverableEnabler = new BluetoothDiscoverableEnabler(activity,
- (CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE));
+ mDiscoverableEnabler = new BluetoothDiscoverableEnabler(getActivity(),
+ mLocalAdapter,
+ (CheckBoxPreference) findPreference(KEY_BT_DISCOVERABLE),
+ (ListPreference) findPreference(KEY_BT_DISCOVERABLE_TIMEOUT));
mNamePreference = (BluetoothNamePreference) findPreference(KEY_BT_NAME);
}
@@ -83,6 +91,27 @@ public class BluetoothSettings extends DeviceListPreferenceFragment
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
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
@@ -100,9 +129,7 @@ public class BluetoothSettings extends DeviceListPreferenceFragment
if (bondState == BluetoothDevice.BOND_BONDED) {
// add to "Paired devices" list after remote-initiated pairing
if (mDevicePreferenceMap.get(cachedDevice) == null) {
- if (addDevicePreference(cachedDevice)) {
- createDevicePreference(cachedDevice);
- }
+ createDevicePreference(cachedDevice);
}
} else if (bondState == BluetoothDevice.BOND_NONE) {
// remove unpaired device from paired devices list
@@ -111,21 +138,11 @@ public class BluetoothSettings extends DeviceListPreferenceFragment
}
/**
- * 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.
* @param preference the newly added preference
*/
+ @Override
void initDevicePreference(BluetoothDevicePreference preference) {
- preference.setOnSettingsClickListener(this);
+ preference.setOnSettingsClickListener(mListener);
}
}
diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
index db20411bc..0bc816c9d 100644
--- a/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
+++ b/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
@@ -16,30 +16,18 @@
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.BluetoothDevice;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
+import android.bluetooth.BluetoothProfile;
import android.os.ParcelUuid;
import android.os.SystemClock;
-import android.preference.Preference;
-import android.preference.PreferenceActivity;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
/**
* 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,
* etc.).
*/
-class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
+final class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
private static final String TAG = "CachedBluetoothDevice";
- private static final boolean D = LocalBluetoothManager.D;
- private static final boolean V = LocalBluetoothManager.V;
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Utils.V;
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final LocalBluetoothProfileManager mProfileManager;
private final BluetoothDevice mDevice;
private String mName;
private short mRssi;
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 final LocalBluetoothManager mLocalManager;
-
- private AlertDialog mDialog = null;
-
- private List<Callback> mCallbacks = new ArrayList<Callback>();
+ private final Collection<Callback> mCallbacks = new ArrayList<Callback>();
/**
* 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
* @return Description of the device and profile
*/
- private String describe(Profile profile) {
+ private String describe(LocalBluetoothProfile profile) {
StringBuilder sb = new StringBuilder();
sb.append("Address:").append(mDevice);
if (profile != null) {
- sb.append(" Profile:").append(profile.name());
+ sb.append(" Profile:").append(profile);
}
return sb.toString();
}
- public void onProfileStateChanged(Profile profile, int newProfileState) {
- if (D) {
- Log.d(TAG, "onProfileStateChanged: profile " + profile.toString() +
+ void onProfileStateChanged(LocalBluetoothProfile profile, int newProfileState) {
+ if (Utils.D) {
+ Log.d(TAG, "onProfileStateChanged: profile " + profile +
" newProfileState " + newProfileState);
}
- final LocalBluetoothProfileManager pm =
- LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
- if (pm == null) return;
- int newState = pm.convertState(newProfileState);
-
- if (newState == SettingsBtStatus.CONNECTION_STATUS_CONNECTED) {
+ if (newProfileState == BluetoothProfile.STATE_CONNECTED) {
if (!mProfiles.contains(profile)) {
mProfiles.add(profile);
}
}
}
- CachedBluetoothDevice(Context context, BluetoothDevice device) {
- mLocalManager = LocalBluetoothManager.getInstance(context);
- if (mLocalManager == null) {
- throw new IllegalStateException(
- "Cannot use CachedBluetoothDevice without Bluetooth hardware");
- }
-
+ CachedBluetoothDevice(LocalBluetoothAdapter adapter,
+ LocalBluetoothProfileManager profileManager,
+ BluetoothDevice device) {
+ mLocalAdapter = adapter;
+ mProfileManager = profileManager;
mDevice = device;
- mContext = context;
-
fillData();
}
- public void onClicked() {
- int bondState = getBondState();
-
- 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) {
+ void disconnect() {
+ for (LocalBluetoothProfile profile : mProfiles) {
disconnect(profile);
}
}
- public void disconnect(Profile profile) {
- LocalBluetoothProfileManager profileManager =
- LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
- if (profileManager.disconnect(mDevice)) {
- if (D) {
+ void disconnect(LocalBluetoothProfile profile) {
+ if (profile.disconnect(mDevice)) {
+ if (Utils.D) {
Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile));
}
}
}
- public void askDisconnect() {
- Context context = mLocalManager.getForegroundActivity();
- if (context == null) {
- // Cannot ask, since we need an activity context
- disconnect();
+ void connect(boolean connectAllProfiles) {
+ if (!ensurePaired()) {
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();
-
connectWithoutResettingTimer(connectAllProfiles);
}
- /*package*/ void onBondingDockConnect() {
+ void onBondingDockConnect() {
// Attempt to connect if UUIDs are available. Otherwise,
// we will connect when the ACTION_UUID intent arrives.
connect(false);
@@ -282,7 +142,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
private void connectWithoutResettingTimer(boolean connectAllProfiles) {
// Try to initialize the profiles if they were not.
- if (mProfiles.size() == 0) {
+ if (mProfiles.isEmpty()) {
if (!updateProfiles()) {
// If UUIDs are not available yet, connect will be happen
// upon arrival of the ACTION_UUID intent.
@@ -295,98 +155,85 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
mIsConnectingErrorPossible = true;
int preferredProfiles = 0;
- for (Profile profile : mProfiles) {
+ for (LocalBluetoothProfile profile : mProfiles) {
if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
- LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
- .getProfileManager(mLocalManager, profile);
- if (profileManager.isPreferred(mDevice)) {
+ if (profile.isPreferred(mDevice)) {
++preferredProfiles;
- connectInt(this, profile);
+ connectInt(profile);
}
}
}
if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);
if (preferredProfiles == 0) {
- connectAllAutoConnectableProfiles();
+ connectAutoConnectableProfiles();
}
}
- private void connectAllAutoConnectableProfiles() {
- if (!ensurePaired()) return;
-
+ private void connectAutoConnectableProfiles() {
+ if (!ensurePaired()) {
+ return;
+ }
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
- for (Profile profile : mProfiles) {
+ for (LocalBluetoothProfile profile : mProfiles) {
if (profile.isAutoConnectable()) {
- LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
- .getProfileManager(mLocalManager, profile);
- profileManager.setPreferred(mDevice, true);
- connectInt(this, profile);
+ profile.setPreferred(mDevice, true);
+ connectInt(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();
// Reset the only-show-one-error-dialog tracking variable
mIsConnectingErrorPossible = true;
- connectInt(this, profile);
+ connectInt(profile);
}
- private boolean connectInt(CachedBluetoothDevice cachedDevice, Profile profile) {
- if (!cachedDevice.ensurePaired()) return false;
-
- LocalBluetoothProfileManager profileManager =
- LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile);
-
- if (profileManager.connect(cachedDevice.mDevice)) {
- if (D) {
+ private void connectInt(LocalBluetoothProfile profile) {
+ if (!ensurePaired()) {
+ return;
+ }
+ if (profile.connect(mDevice)) {
+ if (Utils.D) {
Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));
}
- return true;
+ return;
}
- Log.i(TAG, "Failed to connect " + profile.toString() + " to " + cachedDevice.mName);
-
- return false;
- }
-
- public void showConnectingError() {
- if (!mIsConnectingErrorPossible) return;
- mIsConnectingErrorPossible = false;
-
- mLocalManager.showError(mDevice,
- R.string.bluetooth_connecting_error_message);
+ Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);
}
private boolean ensurePaired() {
if (getBondState() == BluetoothDevice.BOND_NONE) {
- pair();
+ startPairing();
return false;
} else {
return true;
}
}
- public void pair() {
- BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
-
+ boolean startPairing() {
// Pairing is unreliable while scanning, so cancel discovery
- if (adapter.isDiscovering()) {
- adapter.cancelDiscovery();
+ if (mLocalAdapter.isDiscovering()) {
+ mLocalAdapter.cancelDiscovery();
}
if (!mDevice.createBond()) {
- mLocalManager.showError(mDevice,
- R.string.bluetooth_pairing_error_message);
- return;
+ return false;
}
mConnectAfterPairing = true; // auto-connect after pairing
+ return true;
}
- public void unpair() {
+ void unpair() {
disconnect();
int state = getBondState();
@@ -396,14 +243,14 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
}
if (state != BluetoothDevice.BOND_NONE) {
- final BluetoothDevice dev = getDevice();
+ final BluetoothDevice dev = mDevice;
if (dev != null) {
final boolean successful = dev.removeBond();
if (successful) {
- if (D) {
+ if (Utils.D) {
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 " +
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() {
fetchName();
fetchBtClass();
@@ -421,15 +269,15 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
dispatchAttributesChanged();
}
- public BluetoothDevice getDevice() {
+ BluetoothDevice getDevice() {
return mDevice;
}
- public String getName() {
+ String getName() {
return mName;
}
- public void setName(String name) {
+ void setName(String name) {
if (!mName.equals(name)) {
if (TextUtils.isEmpty(name)) {
// TODO: use friendly name for unknown device (bug 1181856)
@@ -442,7 +290,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
}
}
- public void refreshName() {
+ void refreshName() {
fetchName();
dispatchAttributesChanged();
}
@@ -452,15 +300,15 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
if (TextUtils.isEmpty(mName)) {
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();
}
- public boolean isVisible() {
+ boolean isVisible() {
return mVisible;
}
@@ -471,7 +319,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
}
}
- public int getBondState() {
+ int getBondState() {
return mDevice.getBondState();
}
@@ -487,11 +335,10 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
*
* @return Whether it is connected.
*/
- public boolean isConnected() {
- for (Profile profile : mProfiles) {
- int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
- .getConnectionStatus(mDevice);
- if (SettingsBtStatus.isConnectionStatusConnected(status)) {
+ boolean isConnected() {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ int status = profile.getConnectionStatus(mDevice);
+ if (status == BluetoothProfile.STATE_CONNECTED) {
return true;
}
}
@@ -499,78 +346,21 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
return false;
}
- public boolean isConnectedProfile(Profile profile) {
- int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
- .getConnectionStatus(mDevice);
- if (SettingsBtStatus.isConnectionStatusConnected(status)) {
- return true;
- }
+ boolean isConnectedProfile(LocalBluetoothProfile profile) {
+ int status = profile.getConnectionStatus(mDevice);
+ return status == BluetoothProfile.STATE_CONNECTED;
- return false;
}
- public boolean isBusy() {
- for (Profile profile : mProfiles) {
- int status = LocalBluetoothProfileManager.getProfileManager(mLocalManager, profile)
- .getConnectionStatus(mDevice);
- if (SettingsBtStatus.isConnectionStatusBusy(status)) {
+ boolean isBusy() {
+ for (LocalBluetoothProfile profile : mProfiles) {
+ int status = profile.getConnectionStatus(mDevice);
+ if (status == BluetoothProfile.STATE_CONNECTING
+ || status == BluetoothProfile.STATE_DISCONNECTING) {
return true;
}
}
-
- 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();
- }
- } 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;
- }
+ return getBondState() == BluetoothDevice.BOND_BONDING;
}
/**
@@ -584,11 +374,10 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
ParcelUuid[] uuids = mDevice.getUuids();
if (uuids == null) return false;
- BluetoothAdapter adapter = mLocalManager.getBluetoothAdapter();
- ParcelUuid[] localUuids = adapter.getUuids();
+ ParcelUuid[] localUuids = mLocalAdapter.getUuids();
if (localUuids == null) return false;
- LocalBluetoothProfileManager.updateProfiles(uuids, localUuids, mProfiles);
+ mProfileManager.updateProfiles(uuids, localUuids, mProfiles);
if (DEBUG) {
Log.e(TAG, "updating profiles for " + mDevice.getName());
@@ -596,8 +385,8 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
if (bluetoothClass != null) Log.v(TAG, "Class: " + bluetoothClass.toString());
Log.v(TAG, "UUID:");
- for (int i = 0; i < uuids.length; i++) {
- Log.v(TAG, " " + uuids[i]);
+ for (ParcelUuid uuid : uuids) {
+ Log.v(TAG, " " + uuid);
}
}
return true;
@@ -607,7 +396,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
* Refreshes the UI for the BT class, including fetching the latest value
* for the class.
*/
- public void refreshBtClass() {
+ void refreshBtClass() {
fetchBtClass();
dispatchAttributesChanged();
}
@@ -615,7 +404,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
/**
* Refreshes the UI when framework alerts us of a UUID change.
*/
- public void onUuidChanged() {
+ void onUuidChanged() {
updateProfiles();
if (DEBUG) {
@@ -627,7 +416,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
* If a connect was attempted earlier without any UUID, we will do the
* connect now.
*/
- if (mProfiles.size() > 0
+ if (!mProfiles.isEmpty()
&& (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
.elapsedRealtime()) {
connectWithoutResettingTimer(false);
@@ -635,7 +424,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
dispatchAttributesChanged();
}
- public void onBondingStateChanged(int bondState) {
+ void onBondingStateChanged(int bondState) {
if (bondState == BluetoothDevice.BOND_NONE) {
mProfiles.clear();
mConnectAfterPairing = false; // cancel auto-connect
@@ -653,52 +442,25 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
}
}
- public void setBtClass(BluetoothClass btClass) {
+ void setBtClass(BluetoothClass btClass) {
if (btClass != null && mBtClass != btClass) {
mBtClass = btClass;
dispatchAttributesChanged();
}
}
- public int getSummary() {
- for (Profile profile : mProfiles) {
- 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());
+ BluetoothClass getBtClass() {
+ return mBtClass;
}
- public Map<Profile, Drawable> getProfileIcons() {
- Map<Profile, Drawable> drawables = new HashMap<Profile, Drawable>();
-
- for (Profile 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;
+ List<LocalBluetoothProfile> getProfiles() {
+ return Collections.unmodifiableList(mProfiles);
}
- public List<Profile> getConnectableProfiles() {
- ArrayList<Profile> connectableProfiles = new ArrayList<Profile>();
- for (Profile profile : mProfiles) {
+ List<LocalBluetoothProfile> getConnectableProfiles() {
+ List<LocalBluetoothProfile> connectableProfiles =
+ new ArrayList<LocalBluetoothProfile>();
+ for (LocalBluetoothProfile profile : mProfiles) {
if (profile.isConnectable()) {
connectableProfiles.add(profile);
}
@@ -706,36 +468,13 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
return connectableProfiles;
}
- public void onClickedAdvancedOptions(SettingsPreferenceFragment fragment) {
- // 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) {
+ void registerCallback(Callback callback) {
synchronized (mCallbacks) {
mCallbacks.add(callback);
}
}
- public void unregisterCallback(Callback callback) {
+ void unregisterCallback(Callback callback) {
synchronized (mCallbacks) {
mCallbacks.remove(callback);
}
@@ -757,9 +496,8 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
@Override
public boolean equals(Object o) {
if ((o == null) || !(o instanceof CachedBluetoothDevice)) {
- throw new ClassCastException();
+ return false;
}
-
return mDevice.equals(((CachedBluetoothDevice) o).mDevice);
}
@@ -768,11 +506,12 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
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) {
- int comparison;
-
// 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;
// Paired above not paired
@@ -789,7 +528,7 @@ class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> {
if (comparison != 0) return comparison;
// Fallback on name
- return getName().compareTo(another.getName());
+ return mName.compareTo(another.mName);
}
public interface Callback {
diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java b/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java
index 3ee8bc26d..ab71ece7e 100644
--- a/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java
+++ b/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java
@@ -16,122 +16,83 @@
package com.android.settings.bluetooth;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothClass;
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.Collection;
import java.util.List;
-import java.util.Set;
/**
* CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
*/
-class CachedBluetoothDeviceManager {
- private static final String TAG = "CachedBluetoothDeviceManager";
-
- final LocalBluetoothManager mLocalManager;
- final List<Callback> mCallbacks;
-
- final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
-
- public CachedBluetoothDeviceManager(LocalBluetoothManager localManager) {
- mLocalManager = localManager;
- mCallbacks = localManager.getCallbacks();
- }
+final class CachedBluetoothDeviceManager {
+// private static final String TAG = "CachedBluetoothDeviceManager";
- 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;
- }
+ private final List<CachedBluetoothDevice> mCachedDevices =
+ new ArrayList<CachedBluetoothDevice>();
- public synchronized List<CachedBluetoothDevice> getCachedDevicesCopy() {
+ public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
return new ArrayList<CachedBluetoothDevice>(mCachedDevices);
}
- void onBluetoothStateChanged(boolean enabled) {
- 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;
-
+ public boolean onDeviceDisappeared(CachedBluetoothDevice cachedDevice) {
cachedDevice.setVisible(false);
- checkForDeviceRemoval(cachedDevice);
+ return checkForDeviceRemoval(cachedDevice);
}
- private void checkForDeviceRemoval(CachedBluetoothDevice cachedDevice) {
+ private boolean checkForDeviceRemoval(
+ CachedBluetoothDevice cachedDevice) {
if (cachedDevice.getBondState() == BluetoothDevice.BOND_NONE &&
!cachedDevice.isVisible()) {
// If device isn't paired, remove it altogether
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);
if (cachedDevice != null) {
cachedDevice.refreshName();
}
}
- public synchronized CachedBluetoothDevice findDevice(BluetoothDevice device) {
-
- for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
- CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
-
+ /**
+ * Search for existing {@link CachedBluetoothDevice} or return null
+ * if this device isn't in the cache. Use {@link #addDevice}
+ * 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)) {
return cachedDevice;
}
}
-
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.
*
* @param device The remote device.
@@ -139,122 +100,23 @@ class CachedBluetoothDeviceManager {
*/
public String getName(BluetoothDevice device) {
CachedBluetoothDevice cachedDevice = findDevice(device);
- if (cachedDevice != null) return cachedDevice.getName();
-
- String name = device.getName();
- if (name != null) return name;
-
- 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;
- }
+ if (cachedDevice != null) {
+ return cachedDevice.getName();
}
- 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;
+ String name = device.getName();
+ if (name != null) {
+ return name;
}
- }
- 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();
+ return device.getAddress();
}
public synchronized void onScanningStateChanged(boolean started) {
if (!started) return;
// 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--) {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
cachedDevice.setVisible(false);
diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
index 46fff6e01..a978e2392 100644
--- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
+++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java
@@ -16,28 +16,18 @@
package com.android.settings.bluetooth;
-import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothClass;
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.ParcelUuid;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceScreen;
import android.util.Log;
-import android.view.View;
import com.android.settings.ProgressCategory;
import com.android.settings.SettingsPreferenceFragment;
-import java.util.List;
+import java.util.Collection;
import java.util.WeakHashMap;
/**
@@ -48,105 +38,96 @@ import java.util.WeakHashMap;
* @see DevicePickerFragment
* @see BluetoothFindNearby
*/
-public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFragment
- implements LocalBluetoothManager.Callback, View.OnClickListener {
+public abstract class DeviceListPreferenceFragment extends
+ SettingsPreferenceFragment implements BluetoothCallback {
private static final String TAG = "DeviceListPreferenceFragment";
- static final String KEY_BT_DEVICE_LIST = "bt_device_list";
- static final String KEY_BT_SCAN = "bt_scan";
+ private static final String KEY_BT_DEVICE_LIST = "bt_device_list";
+ private static final String KEY_BT_SCAN = "bt_scan";
- int mFilterType;
+ private BluetoothDeviceFilter.Filter mFilter;
- BluetoothDevice mSelectedDevice = null;
+ BluetoothDevice mSelectedDevice;
+ LocalBluetoothAdapter mLocalAdapter;
LocalBluetoothManager mLocalManager;
private PreferenceCategory mDeviceList;
- WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
+ final WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
new WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference>();
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
- onBluetoothStateChanged(mLocalManager.getBluetoothState());
- }
- }
- };
+ DeviceListPreferenceFragment() {
+ mFilter = BluetoothDeviceFilter.ALL_FILTER;
+ }
+
+ DeviceListPreferenceFragment(BluetoothDeviceFilter.Filter filter) {
+ mFilter = filter;
+ }
+
+ final void setFilter(int filterType) {
+ mFilter = BluetoothDeviceFilter.getFilter(filterType);
+ }
@Override
- public void onActivityCreated(Bundle savedInstanceState) {
- // We delay calling super.onActivityCreated(). See WifiSettings.java for more info.
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
- final Activity activity = getActivity();
- mLocalManager = LocalBluetoothManager.getInstance(activity);
+ mLocalManager = LocalBluetoothManager.getInstance(getActivity());
if (mLocalManager == null) {
- finish();
+ Log.e(TAG, "Bluetooth is not supported on this device");
+ return;
}
+ mLocalAdapter = mLocalManager.getBluetoothAdapter();
- mFilterType = BluetoothDevicePicker.FILTER_TYPE_ALL;
-
- if (getPreferenceScreen() != null) getPreferenceScreen().removeAll();
-
- addPreferencesForActivity(activity);
+ addPreferencesForActivity();
mDeviceList = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST);
-
- super.onActivityCreated(savedInstanceState);
+ if (mDeviceList == null) {
+ Log.e(TAG, "Could not find device list preference object!");
+ }
}
/** Add preferences from the subclass. */
- abstract void addPreferencesForActivity(Activity activity);
+ abstract void addPreferencesForActivity();
@Override
public void 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.getEventManager().registerCallback(this);
+
+ updateProgressUi(mLocalAdapter.isDiscovering());
}
@Override
public void onPause() {
super.onPause();
- mLocalManager.stopScanning();
+
+ mLocalAdapter.stopScanning();
mLocalManager.setForegroundActivity(null);
+ mLocalManager.getEventManager().unregisterCallback(this);
+
mDevicePreferenceMap.clear();
mDeviceList.removeAll();
- getActivity().unregisterReceiver(mReceiver);
-
- mLocalManager.unregisterCallback(this);
}
void addDevices() {
- List<CachedBluetoothDevice> cachedDevices =
+ Collection<CachedBluetoothDevice> cachedDevices =
mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
for (CachedBluetoothDevice cachedDevice : cachedDevices) {
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
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
if (KEY_BT_SCAN.equals(preference.getKey())) {
- mLocalManager.startScanning(true);
+ mLocalAdapter.startScanning(true);
return true;
}
@@ -162,7 +143,7 @@ public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFra
}
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
- btPreference.getCachedDevice().onClicked();
+ btPreference.onClicked();
}
public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
@@ -171,71 +152,11 @@ public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFra
return;
}
- if (addDevicePreference(cachedDevice)) {
+ if (mFilter.matches(cachedDevice.getDevice())) {
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) {
BluetoothDevicePreference preference = new BluetoothDevicePreference(
getActivity(), cachedDevice);
@@ -252,7 +173,8 @@ public abstract class DeviceListPreferenceFragment extends SettingsPreferenceFra
void initDevicePreference(BluetoothDevicePreference preference) { }
public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
- BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
+ BluetoothDevicePreference preference = mDevicePreferenceMap.remove(
+ cachedDevice);
if (preference != null) {
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) {
updateProgressUi(false);
}
}
-
- void sendDevicePickedIntent(BluetoothDevice device) {
- Intent intent = new Intent(BluetoothDevicePicker.ACTION_DEVICE_SELECTED);
- intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- getActivity().sendBroadcast(intent);
- }
}
diff --git a/src/com/android/settings/bluetooth/DevicePickerActivity.java b/src/com/android/settings/bluetooth/DevicePickerActivity.java
index c29fafb64..8f6e0dfe3 100644
--- a/src/com/android/settings/bluetooth/DevicePickerActivity.java
+++ b/src/com/android/settings/bluetooth/DevicePickerActivity.java
@@ -25,7 +25,7 @@ import android.os.Bundle;
* Activity for Bluetooth device picker dialog. The device picker logic
* is implemented in the {@link BluetoothSettings} fragment.
*/
-public class DevicePickerActivity extends Activity {
+public final class DevicePickerActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java
index d3e3d6925..126df022c 100644
--- a/src/com/android/settings/bluetooth/DevicePickerFragment.java
+++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java
@@ -16,12 +16,11 @@
package com.android.settings.bluetooth;
-import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothDevicePicker;
import android.content.Intent;
-import android.util.Log;
+import android.os.Bundle;
import com.android.settings.R;
@@ -29,36 +28,42 @@ import com.android.settings.R;
* BluetoothSettings is the Settings screen for Bluetooth configuration and
* connection management.
*/
-public class DevicePickerFragment extends DeviceListPreferenceFragment {
-
- private static final String TAG = "BluetoothDevicePicker";
+public final class DevicePickerFragment extends DeviceListPreferenceFragment {
private boolean mNeedAuth;
private String mLaunchPackage;
private String mLaunchClass;
- void addPreferencesForActivity(Activity activity) {
- Intent intent = activity.getIntent();
+ @Override
+ void addPreferencesForActivity() {
+ addPreferencesFromResource(R.xml.device_picker);
+
+ Intent intent = getActivity().getIntent();
mNeedAuth = intent.getBooleanExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false);
- mFilterType = intent.getIntExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
- BluetoothDevicePicker.FILTER_TYPE_ALL);
+ setFilter(intent.getIntExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
+ BluetoothDevicePicker.FILTER_TYPE_ALL));
mLaunchPackage = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE);
mLaunchClass = intent.getStringExtra(BluetoothDevicePicker.EXTRA_LAUNCH_CLASS);
+ }
- activity.setTitle(activity.getString(R.string.device_picker));
- addPreferencesFromResource(R.xml.device_picker);
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getActivity().setTitle(getString(R.string.device_picker));
}
@Override
public void onResume() {
super.onResume();
addDevices();
- mLocalManager.startScanning(true);
+ mLocalAdapter.startScanning(true);
}
+ @Override
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
- mLocalManager.stopScanning();
- mLocalManager.persistSelectedDeviceInPicker(mSelectedDevice.getAddress());
+ mLocalAdapter.stopScanning();
+ LocalBluetoothPreferences.persistSelectedDeviceInPicker(
+ getActivity(), mSelectedDevice.getAddress());
if ((btPreference.getCachedDevice().getBondState() ==
BluetoothDevice.BOND_BONDED) || !mNeedAuth) {
sendDevicePickedIntent(mSelectedDevice);
@@ -79,15 +84,16 @@ public class DevicePickerFragment extends DeviceListPreferenceFragment {
}
}
- void onBluetoothStateChanged(int bluetoothState) {
+ @Override
+ public void onBluetoothStateChanged(int bluetoothState) {
super.onBluetoothStateChanged(bluetoothState);
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.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
if (mLaunchPackage != null && mLaunchClass != null) {
diff --git a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
index f39eabdc4..307125cb5 100644
--- a/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
+++ b/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
@@ -16,15 +16,12 @@
package com.android.settings.bluetooth;
-import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
-
-import android.bluetooth.BluetoothClass;
+import android.app.AlertDialog;
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.ParcelUuid;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
@@ -34,6 +31,9 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+
import java.util.HashMap;
/**
@@ -41,7 +41,7 @@ import java.util.HashMap;
* for a particular device, and allows them to be individually connected
* (or disconnected).
*/
-public class DeviceProfilesSettings extends SettingsPreferenceFragment
+public final class DeviceProfilesSettings extends SettingsPreferenceFragment
implements CachedBluetoothDevice.Callback, Preference.OnPreferenceChangeListener,
View.OnClickListener {
private static final String TAG = "DeviceProfilesSettings";
@@ -56,11 +56,15 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
private LocalBluetoothManager mManager;
private CachedBluetoothDevice mCachedDevice;
+ private LocalBluetoothProfileManager mProfileManager;
private PreferenceGroup mProfileContainer;
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
public void onCreate(Bundle savedInstanceState) {
@@ -74,51 +78,54 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
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) {
Log.w(TAG, "Activity started without a remote Bluetooth device");
finish();
- return;
+ return; // TODO: test this failure path
}
mManager = LocalBluetoothManager.getInstance(getActivity());
- mCachedDevice = mManager.getCachedDeviceManager().findDevice(device);
+ CachedBluetoothDeviceManager deviceManager =
+ mManager.getCachedDeviceManager();
+ mProfileManager = mManager.getProfileManager();
+ mCachedDevice = deviceManager.findDevice(device);
if (mCachedDevice == null) {
Log.w(TAG, "Device not found, cannot connect to it");
finish();
- return;
+ return; // TODO: test this failure path
}
- addPreferencesFromResource(R.xml.bluetooth_device_advanced);
- getPreferenceScreen().setOrderingAsAdded(false);
-
- mProfileContainer = (PreferenceGroup) findPreference(KEY_PROFILE_CONTAINER);
-
- mDeviceNamePref = (EditTextPreference) findPreference(KEY_RENAME_DEVICE);
- mDeviceNamePref.setSummary(mCachedDevice.getName());
- mDeviceNamePref.setText(mCachedDevice.getName());
+ String deviceName = mCachedDevice.getName();
+ mDeviceNamePref.setSummary(deviceName);
+ mDeviceNamePref.setText(deviceName);
mDeviceNamePref.setOnPreferenceChangeListener(this);
// Set the title of the screen
- findPreference(KEY_TITLE).setTitle(getResources()
- .getString(R.string.bluetooth_device_advanced_title, mCachedDevice.getName()));
+ findPreference(KEY_TITLE).setTitle(
+ getString(R.string.bluetooth_device_advanced_title,
+ deviceName));
// Add a preference for each profile
addPreferencesForProfiles();
}
- private boolean isObjectPushSupported(BluetoothDevice device) {
- ParcelUuid[] uuids = device.getUuids();
- BluetoothClass bluetoothClass = device.getBluetoothClass();
- return (uuids != null && BluetoothUuid.containsAnyUuid(uuids,
- LocalBluetoothProfileManager.OPP_PROFILE_UUIDS)) ||
- (bluetoothClass != null && bluetoothClass.doesClassMatch(
- BluetoothClass.PROFILE_OPP));
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mDisconnectDialog != null) {
+ mDisconnectDialog.dismiss();
+ mDisconnectDialog = null;
+ }
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
-
outState.putParcelable(EXTRA_DEVICE, mCachedDevice.getDevice());
}
@@ -141,7 +148,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
}
private void addPreferencesForProfiles() {
- for (Profile profile : mCachedDevice.getConnectableProfiles()) {
+ for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
Preference pref = createProfilePreference(profile);
mProfileContainer.addPreference(pref);
}
@@ -155,21 +162,18 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
* @return A preference that allows the user to choose whether this profile
* will be connected to.
*/
- private Preference createProfilePreference(Profile profile) {
+ private Preference createProfilePreference(LocalBluetoothProfile profile) {
BluetoothProfilePreference pref = new BluetoothProfilePreference(getActivity(), profile);
pref.setKey(profile.toString());
- pref.setTitle(profile.localizedString);
+ pref.setTitle(profile.getNameResource());
pref.setExpanded(false);
pref.setPersistent(false);
- pref.setOrder(getProfilePreferenceIndex(profile));
+ pref.setOrder(getProfilePreferenceIndex(profile.getOrdinal()));
pref.setOnExpandClickListener(this);
- LocalBluetoothProfileManager profileManager =
- LocalBluetoothProfileManager.getProfileManager(mManager, profile);
- int iconResource = profileManager.getDrawableResource();
+ int iconResource = profile.getDrawableResource(null); // FIXME: get BT class for this?
if (iconResource != 0) {
- pref.setProfileDrawable(mManager.getContext()
- .getResources().getDrawable(iconResource));
+ pref.setProfileDrawable(getResources().getDrawable(iconResource));
}
/**
@@ -186,7 +190,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
String key = preference.getKey();
if (preference instanceof BluetoothProfilePreference) {
- onProfileClicked(Profile.valueOf(key));
+ onProfileClicked(mProfileManager.getProfileByName(key));
return true;
} else if (key.equals(KEY_UNPAIR)) {
unpairDevice();
@@ -194,7 +198,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
return true;
}
- return false;
+ return super.onPreferenceTreeClick(screen, preference);
}
public boolean onPreferenceChange(Preference preference, Object newValue) {
@@ -202,10 +206,8 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
mCachedDevice.setName((String) newValue);
} else if (preference instanceof CheckBoxPreference) {
boolean autoConnect = (Boolean) newValue;
- Profile prof = getProfileOf(preference);
- LocalBluetoothProfileManager
- .getProfileManager(mManager, prof)
- .setPreferred(mCachedDevice.getDevice(),
+ LocalBluetoothProfile prof = getProfileOf(preference);
+ prof.setPreferred(mCachedDevice.getDevice(),
autoConnect);
return true;
} else {
@@ -215,20 +217,44 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
return true;
}
- private void onProfileClicked(Profile profile) {
+ private void onProfileClicked(LocalBluetoothProfile profile) {
BluetoothDevice device = mCachedDevice.getDevice();
- LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
- .getProfileManager(mManager, profile);
- int status = profileManager.getConnectionStatus(device);
+ int status = profile.getConnectionStatus(device);
boolean isConnected =
- SettingsBtStatus.isConnectionStatusConnected(status);
+ status == BluetoothProfile.STATE_CONNECTED;
if (isConnected) {
- mCachedDevice.askDisconnect(profile);
+ askDisconnect(getActivity(), profile);
} 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() {
@@ -242,7 +268,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
// transaction.setBreadCrumbTitle(deviceName);
// transaction.commit();
- findPreference(KEY_TITLE).setTitle(getResources().getString(
+ findPreference(KEY_TITLE).setTitle(getString(
R.string.bluetooth_device_advanced_title,
deviceName));
mDeviceNamePref = (EditTextPreference) findPreference(KEY_RENAME_DEVICE);
@@ -253,7 +279,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
}
private void refreshProfiles() {
- for (Profile profile : mCachedDevice.getConnectableProfiles()) {
+ for (LocalBluetoothProfile profile : mCachedDevice.getConnectableProfiles()) {
Preference profilePref = findPreference(profile.toString());
if (profilePref == null) {
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();
- LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
- .getProfileManager(mManager, profile);
-
- int connectionStatus = profileManager.getConnectionStatus(device);
/*
* Gray out checkbox while connecting and disconnecting
*/
profilePref.setEnabled(!mCachedDevice.isBusy());
- profilePref.setSummary(getProfileSummary(profileManager, profile, device,
- connectionStatus, isDeviceOnline()));
- // TODO:
- //profilePref.setChecked(profileManager.isPreferred(device));
+ profilePref.setSummary(profile.getSummaryResourceForDevice(device));
}
- private Profile getProfileOf(Preference pref) {
- if (!(pref instanceof CheckBoxPreference)) return null;
+ private LocalBluetoothProfile getProfileOf(Preference pref) {
+ if (!(pref instanceof CheckBoxPreference)) {
+ return null;
+ }
String key = pref.getKey();
if (TextUtils.isEmpty(key)) return null;
try {
- return Profile.valueOf(pref.getKey());
- } catch (IllegalArgumentException e) {
+ return mProfileManager.getProfileByName(pref.getKey());
+ } catch (IllegalArgumentException ignored) {
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) {
- if (v.getTag() instanceof Profile) {
- Profile prof = (Profile) v.getTag();
- CheckBoxPreference autoConnectPref = mAutoConnectPrefs.get(prof.toString());
+ if (v.getTag() instanceof LocalBluetoothProfile) {
+ LocalBluetoothProfile prof = (LocalBluetoothProfile) v.getTag();
+ CheckBoxPreference autoConnectPref = mAutoConnectPrefs.get(prof);
if (autoConnectPref == null) {
autoConnectPref = new CheckBoxPreference(getActivity());
autoConnectPref.setLayoutResource(com.android.internal.R.layout.preference_child);
autoConnectPref.setKey(prof.toString());
autoConnectPref.setTitle(R.string.bluetooth_auto_connect);
- autoConnectPref.setOrder(getProfilePreferenceIndex(prof) + 1);
+ autoConnectPref.setOrder(getProfilePreferenceIndex(prof.getOrdinal()) + 1);
autoConnectPref.setChecked(getAutoConnect(prof));
autoConnectPref.setOnPreferenceChangeListener(this);
- mAutoConnectPrefs.put(prof.name(), autoConnectPref);
+ mAutoConnectPrefs.put(prof, autoConnectPref);
}
BluetoothProfilePreference profilePref =
(BluetoothProfilePreference) findPreference(prof.toString());
@@ -349,19 +340,14 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
}
}
- private int getProfilePreferenceIndex(Profile prof) {
- return mProfileContainer.getOrder() + prof.ordinal() * 10;
+ private int getProfilePreferenceIndex(int profIndex) {
+ return mProfileContainer.getOrder() + profIndex * 10;
}
private void unpairDevice() {
mCachedDevice.unpair();
}
- private boolean isDeviceOnline() {
- // TODO: Verify
- return mCachedDevice.isConnected() || mCachedDevice.isBusy();
- }
-
private void setIncomingFileTransfersAllowed(boolean allow) {
// TODO: make an IPC call into BluetoothOpp to update
Log.d(TAG, "Set allow incoming = " + allow);
@@ -372,8 +358,7 @@ public class DeviceProfilesSettings extends SettingsPreferenceFragment
return true;
}
- private boolean getAutoConnect(Profile prof) {
- return LocalBluetoothProfileManager.getProfileManager(mManager, prof)
- .isPreferred(mCachedDevice.getDevice());
+ private boolean getAutoConnect(LocalBluetoothProfile prof) {
+ return prof.isPreferred(mCachedDevice.getDevice());
}
}
diff --git a/src/com/android/settings/bluetooth/DockEventReceiver.java b/src/com/android/settings/bluetooth/DockEventReceiver.java
index 041099813..6ecbef63a 100644
--- a/src/com/android/settings/bluetooth/DockEventReceiver.java
+++ b/src/com/android/settings/bluetooth/DockEventReceiver.java
@@ -28,7 +28,7 @@ import android.content.Intent;
import android.os.PowerManager;
import android.util.Log;
-public class DockEventReceiver extends BroadcastReceiver {
+public final class DockEventReceiver extends BroadcastReceiver {
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 Object mStartingServiceSync = new Object();
+ private static final Object sStartingServiceSync = new Object();
- private static final long WAKELOCK_TIMEOUT = 5000;
-
- private static PowerManager.WakeLock mStartingService;
+ private static PowerManager.WakeLock sStartingService;
@Override
public void onReceive(Context context, Intent intent) {
@@ -75,7 +73,7 @@ public class DockEventReceiver extends BroadcastReceiver {
beginStartingService(context, i);
break;
default:
- if (DEBUG) Log.e(TAG, "Unknown state");
+ Log.e(TAG, "Unknown state: " + state);
break;
}
} 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
* the wake lock before returning to ensure that the service will run.
*/
- public static void beginStartingService(Context context, Intent intent) {
- synchronized (mStartingServiceSync) {
- if (mStartingService == null) {
+ private static void beginStartingService(Context context, Intent intent) {
+ synchronized (sStartingServiceSync) {
+ if (sStartingService == null) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
- mStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ sStartingService = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"StartingDockService");
}
- mStartingService.acquire(WAKELOCK_TIMEOUT);
+ sStartingService.acquire();
if (context.startService(intent) == null) {
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.
*/
public static void finishStartingService(Service service, int startId) {
- synchronized (mStartingServiceSync) {
- if (mStartingService != null) {
- if (DEBUG) Log.d(TAG, "stopSelf id = "+ startId);
- service.stopSelfResult(startId);
+ synchronized (sStartingServiceSync) {
+ if (sStartingService != null) {
+ if (DEBUG) Log.d(TAG, "stopSelf id = " + startId);
+ if (service.stopSelfResult(startId)) {
+ Log.d(TAG, "finishStartingService: stopping service");
+ sStartingService.release();
+ }
}
}
}
diff --git a/src/com/android/settings/bluetooth/DockService.java b/src/com/android/settings/bluetooth/DockService.java
index 810465238..b45770658 100644
--- a/src/com/android/settings/bluetooth/DockService.java
+++ b/src/com/android/settings/bluetooth/DockService.java
@@ -17,7 +17,6 @@
package com.android.settings.bluetooth;
import com.android.settings.R;
-import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
import com.android.settings.bluetooth.LocalBluetoothProfileManager.ServiceListener;
import android.app.AlertDialog;
@@ -27,7 +26,7 @@ import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
-import android.content.Context;
+import android.bluetooth.BluetoothProfile;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
@@ -44,12 +43,11 @@ import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
+import java.util.Collection;
import java.util.List;
import java.util.Set;
-public class DockService extends Service implements AlertDialog.OnMultiChoiceClickListener,
- DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
- CompoundButton.OnCheckedChangeListener, ServiceListener {
+public final class DockService extends Service implements ServiceListener {
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_KEY_DISABLE_BT_WHEN_UNDOCKED =
- "disable_bt_when_undock";
+ private static final String KEY_DISABLE_BT_WHEN_UNDOCKED = "disable_bt_when_undock";
- private static final String SHARED_PREFERENCES_KEY_DISABLE_BT =
- "disable_bt";
+ private static final String KEY_DISABLE_BT = "disable_bt";
- private static final String SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT =
- "connect_retry_count";
+ private static final String KEY_CONNECT_RETRY_COUNT = "connect_retry_count";
/*
* 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 ServiceHandler mServiceHandler;
private Runnable mRunnable;
- private DockService mContext;
- private LocalBluetoothManager mBtManager;
+ private LocalBluetoothAdapter mLocalAdapter;
+ private CachedBluetoothDeviceManager mDeviceManager;
+ private LocalBluetoothProfileManager mProfileManager;
// 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
@@ -113,7 +109,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
// Created and used for the duration of the dialog
private AlertDialog mDialog;
- private Profile[] mProfiles;
+ private LocalBluetoothProfile[] mProfiles;
private boolean[] mCheckedItems;
private int mStartIdAssociatedWithDialog;
@@ -127,8 +123,19 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
public void onCreate() {
if (DEBUG) Log.d(TAG, "onCreate");
- mBtManager = LocalBluetoothManager.getInstance(this);
- mContext = this;
+ LocalBluetoothManager manager = LocalBluetoothManager.getInstance(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");
thread.start();
@@ -141,12 +148,22 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
public void onDestroy() {
if (DEBUG) Log.d(TAG, "onDestroy");
mRunnable = null;
- LocalBluetoothProfileManager.removeServiceListener(this);
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
}
- mServiceLooper.quit();
+ if (mProfileManager != null) {
+ mProfileManager.removeServiceListener(this);
+ }
+ if (mServiceLooper != null) {
+ mServiceLooper.quit();
+ }
+
+ mLocalAdapter = null;
+ mDeviceManager = null;
+ mProfileManager = null;
+ mServiceLooper = null;
+ mServiceHandler = null;
}
@Override
@@ -155,9 +172,13 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
return null;
}
+ private SharedPreferences getPrefs() {
+ return getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE);
+ }
+
@Override
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) {
// 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
* and that the intent is for a disconnect
*/
+ final SharedPreferences prefs = getPrefs();
if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
BluetoothDevice disconnectedDevice = intent
.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) {
- setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
- handleUnexpectedDisconnect(disconnectedDevice, Profile.HEADSET, startId);
+ prefs.edit().putInt(KEY_CONNECT_RETRY_COUNT, retryCount + 1).apply();
+ handleUnexpectedDisconnect(disconnectedDevice, mProfileManager.getHeadsetProfile(), startId);
}
return START_NOT_STICKY;
} else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
BluetoothDevice disconnectedDevice = intent
.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) {
- setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
- handleUnexpectedDisconnect(disconnectedDevice, Profile.A2DP, startId);
+ prefs.edit().putInt(KEY_CONNECT_RETRY_COUNT, retryCount + 1).apply();
+ handleUnexpectedDisconnect(disconnectedDevice, mProfileManager.getA2dpProfile(), startId);
}
return START_NOT_STICKY;
}
@@ -209,7 +230,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
}
if (msg.what == MSG_TYPE_DOCKED) {
- removeSetting(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT);
+ prefs.edit().remove(KEY_CONNECT_RETRY_COUNT).apply();
}
msg.arg2 = startId;
@@ -219,7 +240,7 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
}
private final class ServiceHandler extends Handler {
- public ServiceHandler(Looper looper) {
+ private ServiceHandler(Looper looper) {
super(looper);
}
@@ -234,7 +255,6 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
int msgType = msg.what;
final int state = msg.arg1;
final int startId = msg.arg2;
- boolean deferFinishCall = false;
BluetoothDevice device = null;
if (msg.obj != null) {
device = (BluetoothDevice) msg.obj;
@@ -243,102 +263,27 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
if(DEBUG) Log.d(TAG, "processMessage: " + msgType + " state: " + state + " device = "
+ (device == null ? "null" : device.toString()));
+ boolean deferFinishCall = false;
+
switch (msgType) {
case MSG_TYPE_SHOW_UI:
- if (mDialog != null) {
- // Shouldn't normally happen
- mDialog.dismiss();
- mDialog = null;
- }
- mDevice = device;
- createDialog(mContext, mDevice, state, startId);
+ createDialog(device, state, startId);
break;
case MSG_TYPE_DOCKED:
- if (DEBUG) {
- // TODO figure out why hasMsg always returns false if device
- // is supplied
- Log.d(TAG, "1 Has undock perm msg = "
- + mServiceHandler.hasMessages(MSG_TYPE_UNDOCKED_PERMANENT, mDevice));
- Log.d(TAG, "2 Has undock perm msg = "
- + mServiceHandler.hasMessages(MSG_TYPE_UNDOCKED_PERMANENT, device));
- }
-
- mServiceHandler.removeMessages(MSG_TYPE_UNDOCKED_PERMANENT);
- mServiceHandler.removeMessages(MSG_TYPE_DISABLE_BT);
- removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
-
- if (!device.equals(mDevice)) {
- if (mDevice != null) {
- // Not expected. Cleanup/undock existing
- handleUndocked(mContext, mBtManager, mDevice);
- }
-
- mDevice = device;
-
- // Register first in case LocalBluetoothProfileManager
- // becomes ready after isManagerReady is called and it
- // would be too late to register a service listener.
- LocalBluetoothProfileManager.addServiceListener(this);
- if (LocalBluetoothProfileManager.isManagerReady()) {
- handleDocked(device, state, startId);
- // Not needed after all
- LocalBluetoothProfileManager.removeServiceListener(this);
- } else {
- final BluetoothDevice d = device;
- mRunnable = new Runnable() {
- public void run() {
- handleDocked(d, state, startId);
- }
- };
- deferFinishCall = true;
- }
- }
+ deferFinishCall = msgTypeDocked(device, state, startId);
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);
- }
- }
+ deferFinishCall = msgTypeUndockedPermanent(device, startId);
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);
+ msgTypeUndockedTemporary(device, state, startId);
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);
- }
+ deferFinishCall = msgTypeDisableBluetooth(startId);
break;
}
@@ -346,24 +291,127 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
&& !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);
+ DockEventReceiver.finishStartingService(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) {
+ 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;
}
- if(DEBUG) Log.d(TAG, "btDevices = " + btDevices.size());
- if(DEBUG) Log.d(TAG, "cachedDevices = " + cachedDevices.size());
+ }
- for (CachedBluetoothDevice device : cachedDevices) {
- BluetoothDevice btDevice = device.getDevice();
- if (!btDevice.equals(dock) && btDevices.contains(btDevice) && device.isConnected()) {
- if(DEBUG) Log.d(TAG, "connected device = " + device.getName());
+ 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) {
+ // TODO figure out why hasMsg always returns false if device
+ // is supplied
+ Log.d(TAG, "1 Has undock perm msg = "
+ + mServiceHandler.hasMessages(MSG_TYPE_UNDOCKED_PERMANENT, mDevice));
+ Log.d(TAG, "2 Has undock perm msg = "
+ + mServiceHandler.hasMessages(MSG_TYPE_UNDOCKED_PERMANENT, device));
+ }
+
+ mServiceHandler.removeMessages(MSG_TYPE_UNDOCKED_PERMANENT);
+ mServiceHandler.removeMessages(MSG_TYPE_DISABLE_BT);
+ getPrefs().edit().remove(KEY_DISABLE_BT).apply();
+
+ if (device != null && !device.equals(mDevice)) {
+ if (mDevice != null) {
+ // Not expected. Cleanup/undock existing
+ handleUndocked(mDevice);
+ }
+
+ mDevice = device;
+
+ // Register first in case LocalBluetoothProfileManager
+ // becomes ready after isManagerReady is called and it
+ // would be too late to register a service listener.
+ mProfileManager.addServiceListener(this);
+ if (mProfileManager.isManagerReady()) {
+ handleDocked(device, state, startId);
+ // Not needed after all
+ mProfileManager.removeServiceListener(this);
+ } else {
+ final BluetoothDevice d = device;
+ mRunnable = new Runnable() {
+ public void run() {
+ handleDocked(d, state, startId); // FIXME: WTF runnable here?
+ }
+ };
+ return true;
+ }
+ }
+ return false;
+ }
+
+ synchronized boolean hasOtherConnectedDevices(BluetoothDevice dock) {
+ Collection<CachedBluetoothDevice> cachedDevices = mDeviceManager.getCachedDevicesCopy();
+ Set<BluetoothDevice> btDevices = mLocalAdapter.getBondedDevices();
+ 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;
}
}
@@ -404,96 +452,128 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
return mServiceHandler.obtainMessage(msgType, state, 0, device);
}
- private boolean createDialog(DockService service, BluetoothDevice device, int state,
- int startId) {
+ private void createDialog(BluetoothDevice device,
+ int state, int startId) {
+ if (mDialog != null) {
+ // Shouldn't normally happen
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ mDevice = device;
switch (state) {
case Intent.EXTRA_DOCK_STATE_CAR:
case Intent.EXTRA_DOCK_STATE_DESK:
break;
default:
- return false;
+ return;
}
startForeground(0, new Notification());
// 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);
- ab.setTitle(service.getString(R.string.bluetooth_dock_settings_title));
+ final AlertDialog.Builder ab = new AlertDialog.Builder(this);
+ ab.setTitle(getString(R.string.bluetooth_dock_settings_title));
// Profiles
- ab.setMultiChoiceItems(items, mCheckedItems, service);
+ ab.setMultiChoiceItems(items, mCheckedItems, mMultiClickListener);
// Remember this settings
- LayoutInflater inflater = (LayoutInflater) service
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- float pixelScaleFactor = service.getResources().getDisplayMetrics().density;
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(LAYOUT_INFLATER_SERVICE);
+ float pixelScaleFactor = getResources().getDisplayMetrics().density;
View view = inflater.inflate(R.layout.remember_dock_setting, null);
CheckBox rememberCheckbox = (CheckBox) view.findViewById(R.id.remember);
// 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.setOnCheckedChangeListener(this);
+ rememberCheckbox.setOnCheckedChangeListener(mCheckedChangeListener);
int viewSpacingLeft = (int) (14 * pixelScaleFactor);
int viewSpacingRight = (int) (14 * pixelScaleFactor);
ab.setView(view, viewSpacingLeft, 0 /* top */, viewSpacingRight, 0 /* bottom */);
if (DEBUG) {
Log.d(TAG, "Auto connect = "
- + mBtManager.getDockAutoConnectSetting(device.getAddress()));
+ + LocalBluetoothPreferences.getDockAutoConnectSetting(this, device.getAddress()));
}
// Ok Button
- ab.setPositiveButton(service.getString(android.R.string.ok), service);
+ ab.setPositiveButton(getString(android.R.string.ok), mClickListener);
mStartIdAssociatedWithDialog = startId;
mDialog = ab.create();
mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- mDialog.setOnDismissListener(service);
+ mDialog.setOnDismissListener(mDismissListener);
mDialog.show();
- return true;
}
// Called when the individual bt profiles are clicked.
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- if (DEBUG) Log.d(TAG, "Item " + which + " changed to " + isChecked);
- mCheckedItems[which] = isChecked;
- }
+ private final DialogInterface.OnMultiChoiceClickListener mMultiClickListener =
+ new DialogInterface.OnMultiChoiceClickListener() {
+ public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+ if (DEBUG) {
+ Log.d(TAG, "Item " + which + " changed to " + isChecked);
+ }
+ mCheckedItems[which] = isChecked;
+ }
+ };
+
// Called when the "Remember" Checkbox is clicked
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (DEBUG) Log.d(TAG, "onCheckedChanged: Remember Settings = " + isChecked);
- if (mDevice != null) {
- mBtManager.saveDockAutoConnectSetting(mDevice.getAddress(), isChecked);
- }
- }
+ private final CompoundButton.OnCheckedChangeListener mCheckedChangeListener =
+ new CompoundButton.OnCheckedChangeListener() {
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (DEBUG) {
+ Log.d(TAG, "onCheckedChanged: Remember Settings = " + isChecked);
+ }
+ if (mDevice != null) {
+ LocalBluetoothPreferences.saveDockAutoConnectSetting(
+ DockService.this, mDevice.getAddress(), isChecked);
+ }
+ }
+ };
+
// Called when the dialog is dismissed
- public void onDismiss(DialogInterface dialog) {
- // NOTE: We MUST not call stopSelf() directly, since we need to
- // make sure the wake lock acquired by the Receiver is released.
- if (mPendingDevice == null) {
- DockEventReceiver.finishStartingService(mContext, mStartIdAssociatedWithDialog);
- }
- mContext.stopForeground(true);
- }
+ private final DialogInterface.OnDismissListener mDismissListener =
+ new DialogInterface.OnDismissListener() {
+ public void onDismiss(DialogInterface dialog) {
+ // NOTE: We MUST not call stopSelf() directly, since we need to
+ // make sure the wake lock acquired by the Receiver is released.
+ if (mPendingDevice == null) {
+ DockEventReceiver.finishStartingService(
+ DockService.this, mStartIdAssociatedWithDialog);
+ }
+ stopForeground(true);
+ }
+ };
// Called when clicked on the OK button
- public void onClick(DialogInterface dialog, int which) {
- if (which == DialogInterface.BUTTON_POSITIVE && mDevice != null) {
- if (!mBtManager.hasDockAutoConnectSetting(mDevice.getAddress())) {
- mBtManager.saveDockAutoConnectSetting(mDevice.getAddress(), true);
- }
+ private final DialogInterface.OnClickListener mClickListener =
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE
+ && mDevice != null) {
+ 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,
- boolean firstTime) {
+ private CharSequence[] initBtSettings(BluetoothDevice device,
+ int state, boolean firstTime) {
// TODO Avoid hardcoding dock and profiles. Read from system properties
int numOfProfiles;
switch (state) {
@@ -507,96 +587,54 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
return null;
}
- mProfiles = new Profile[numOfProfiles];
+ mProfiles = new LocalBluetoothProfile[numOfProfiles];
mCheckedItems = new boolean[numOfProfiles];
CharSequence[] items = new CharSequence[numOfProfiles];
+ // FIXME: convert switch to something else
switch (state) {
case Intent.EXTRA_DOCK_STATE_CAR:
- items[0] = service.getString(R.string.bluetooth_dock_settings_headset);
- items[1] = service.getString(R.string.bluetooth_dock_settings_a2dp);
- mProfiles[0] = Profile.HEADSET;
- mProfiles[1] = Profile.A2DP;
+ items[0] = getString(R.string.bluetooth_dock_settings_headset);
+ items[1] = getString(R.string.bluetooth_dock_settings_a2dp);
+ mProfiles[0] = mProfileManager.getHeadsetProfile();
+ mProfiles[1] = mProfileManager.getA2dpProfile();
if (firstTime) {
// Enable by default for car dock
mCheckedItems[0] = true;
mCheckedItems[1] = true;
} else {
- mCheckedItems[0] = LocalBluetoothProfileManager.getProfileManager(mBtManager,
- Profile.HEADSET).isPreferred(device);
- mCheckedItems[1] = LocalBluetoothProfileManager.getProfileManager(mBtManager,
- Profile.A2DP).isPreferred(device);
+ mCheckedItems[0] = mProfiles[0].isPreferred(device);
+ mCheckedItems[1] = mProfiles[1].isPreferred(device);
}
break;
case Intent.EXTRA_DOCK_STATE_DESK:
- items[0] = service.getString(R.string.bluetooth_dock_settings_a2dp);
- mProfiles[0] = Profile.A2DP;
+ items[0] = getString(R.string.bluetooth_dock_settings_a2dp);
+ mProfiles[0] = mProfileManager.getA2dpProfile();
if (firstTime) {
// Disable by default for desk dock
mCheckedItems[0] = false;
} else {
- mCheckedItems[0] = LocalBluetoothProfileManager.getProfileManager(mBtManager,
- Profile.A2DP).isPreferred(device);
+ mCheckedItems[0] = mProfiles[0].isPreferred(device);
}
break;
}
return items;
}
+ // TODO: move to background thread to fix strict mode warnings
private void handleBtStateChange(Intent intent, int startId) {
int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
synchronized (this) {
if(DEBUG) Log.d(TAG, "BtState = " + btState + " mPendingDevice = " + mPendingDevice);
if (btState == BluetoothAdapter.STATE_ON) {
- if (mPendingDevice != null) {
- if (mPendingDevice.equals(mDevice)) {
- if(DEBUG) Log.d(TAG, "applying settings");
- applyBtSettings(mPendingDevice, mPendingStartId);
- } else if(DEBUG) {
- Log.d(TAG, "mPendingDevice (" + mPendingDevice + ") != mDevice ("
- + mDevice + ")");
- }
-
- mPendingDevice = null;
- DockEventReceiver.finishStartingService(mContext, mPendingStartId);
- } else {
- if (DEBUG) {
- Log.d(TAG, "A DISABLE_BT_WHEN_UNDOCKED = "
- + getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
- }
- // Reconnect if docked and bluetooth was enabled by user.
- Intent i = 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) {
- connectIfEnabled(device);
- }
- } else if (getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT)
- && mBtManager.getBluetoothAdapter().disable()) {
- mPendingTurnOffStartId = startId;
- removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
- return;
- }
- }
- }
-
- if (mPendingTurnOnStartId != INVALID_STARTID) {
- DockEventReceiver.finishStartingService(this, mPendingTurnOnStartId);
- mPendingTurnOnStartId = INVALID_STARTID;
- }
-
- DockEventReceiver.finishStartingService(this, startId);
+ 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.
- removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED);
+ 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.
@@ -605,12 +643,12 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
if (mPendingTurnOffStartId != INVALID_STARTID) {
DockEventReceiver.finishStartingService(this, mPendingTurnOffStartId);
- removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
+ getPrefs().edit().remove(KEY_DISABLE_BT).apply();
mPendingTurnOffStartId = INVALID_STARTID;
}
if (mPendingDevice != null) {
- mBtManager.getBluetoothAdapter().enable();
+ mLocalAdapter.enable();
mPendingTurnOnStartId = startId;
} else {
DockEventReceiver.finishStartingService(this, startId);
@@ -619,86 +657,124 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
}
}
- private void handleUnexpectedDisconnect(BluetoothDevice disconnectedDevice, Profile profile,
- int startId) {
- synchronized (this) {
- if (DEBUG) Log.d(TAG, "handling failed connect for " + disconnectedDevice);
+ private void handleBluetoothStateOn(int startId) {
+ if (mPendingDevice != null) {
+ if (mPendingDevice.equals(mDevice)) {
+ if(DEBUG) {
+ Log.d(TAG, "applying settings");
+ }
+ applyBtSettings(mPendingDevice, mPendingStartId);
+ } else if(DEBUG) {
+ Log.d(TAG, "mPendingDevice (" + mPendingDevice + ") != mDevice ("
+ + mDevice + ')');
+ }
+
+ mPendingDevice = null;
+ DockEventReceiver.finishStartingService(this, mPendingStartId);
+ } else {
+ final SharedPreferences prefs = getPrefs();
+ if (DEBUG) {
+ Log.d(TAG, "A DISABLE_BT_WHEN_UNDOCKED = "
+ + prefs.getBoolean(KEY_DISABLE_BT_WHEN_UNDOCKED, false));
+ }
+ // Reconnect if docked and bluetooth was enabled by user.
+ Intent i = 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) {
+ connectIfEnabled(device);
+ }
+ } else if (prefs.getBoolean(KEY_DISABLE_BT, false)
+ && mLocalAdapter.disable()) {
+ mPendingTurnOffStartId = startId;
+ prefs.edit().remove(KEY_DISABLE_BT).apply();
+ return;
+ }
+ }
+ }
+
+ if (mPendingTurnOnStartId != INVALID_STARTID) {
+ DockEventReceiver.finishStartingService(this, mPendingTurnOnStartId);
+ mPendingTurnOnStartId = INVALID_STARTID;
+ }
+
+ DockEventReceiver.finishStartingService(this, startId);
+ }
+
+ private synchronized void handleUnexpectedDisconnect(BluetoothDevice disconnectedDevice,
+ LocalBluetoothProfile profile, int startId) {
+ if (DEBUG) {
+ Log.d(TAG, "handling failed connect for " + disconnectedDevice);
+ }
// Reconnect if docked.
if (disconnectedDevice != null) {
// registerReceiver can't be called from a BroadcastReceiver
- Intent i = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
- if (i != null) {
- int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent intent = registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
+ if (intent != null) {
+ int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
Intent.EXTRA_DOCK_STATE_UNDOCKED);
if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- BluetoothDevice dockedDevice = i
+ BluetoothDevice dockedDevice = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (dockedDevice != null && dockedDevice.equals(disconnectedDevice)) {
- CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext,
- mBtManager, dockedDevice);
- cachedDevice.connect(profile);
+ CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(
+ dockedDevice);
+ cachedDevice.connectProfile(profile);
}
}
}
}
DockEventReceiver.finishStartingService(this, startId);
- }
}
private synchronized void connectIfEnabled(BluetoothDevice device) {
- CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext, mBtManager, device);
- List<Profile> profiles = cachedDevice.getConnectableProfiles();
- for (int i = 0; i < profiles.size(); i++) {
- LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
- .getProfileManager(mBtManager, profiles.get(i));
- 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) {
+ CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(
+ device);
+ List<LocalBluetoothProfile> profiles = cachedDevice.getConnectableProfiles();
+ for (LocalBluetoothProfile profile : profiles) {
+ if (profile.getPreferred(device) == BluetoothProfile.PRIORITY_AUTO_CONNECT) {
cachedDevice.connect(false);
- break;
+ return;
}
}
}
- private synchronized void applyBtSettings(final BluetoothDevice device, int startId) {
- if (device == null || mProfiles == null || mCheckedItems == null)
+ private synchronized void applyBtSettings(BluetoothDevice device, int startId) {
+ if (device == null || mProfiles == null || mCheckedItems == null
+ || mLocalAdapter == null) {
return;
+ }
// Turn on BT if something is enabled
- synchronized (this) {
- for (boolean enable : mCheckedItems) {
- if (enable) {
- int btState = mBtManager.getBluetoothState();
- if(DEBUG) Log.d(TAG, "BtState = " + btState);
- // May have race condition as the phone comes in and out and in the dock.
- // Always turn on BT
- mBtManager.getBluetoothAdapter().enable();
-
- switch (btState) {
- case BluetoothAdapter.STATE_OFF:
- case BluetoothAdapter.STATE_TURNING_OFF:
- case BluetoothAdapter.STATE_TURNING_ON:
- if (mPendingDevice != null && mPendingDevice.equals(mDevice)) {
- return;
- }
-
- mPendingDevice = device;
- mPendingStartId = startId;
- if (btState != BluetoothAdapter.STATE_TURNING_ON) {
- setSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED,
- true);
- }
- return;
+ for (boolean enable : mCheckedItems) {
+ if (enable) {
+ int btState = mLocalAdapter.getBluetoothState();
+ if (DEBUG) {
+ Log.d(TAG, "BtState = " + btState);
+ }
+ // May have race condition as the phone comes in and out and in the dock.
+ // Always turn on BT
+ mLocalAdapter.enable();
+
+ // if adapter was previously OFF, TURNING_OFF, or TURNING_ON
+ if (btState != BluetoothAdapter.STATE_ON) {
+ if (mPendingDevice != null && mPendingDevice.equals(mDevice)) {
+ return;
+ }
+
+ mPendingDevice = device;
+ mPendingStartId = startId;
+ if (btState != BluetoothAdapter.STATE_TURNING_ON) {
+ getPrefs().edit().putBoolean(
+ KEY_DISABLE_BT_WHEN_UNDOCKED, true).apply();
}
+ return;
}
}
}
@@ -706,28 +782,26 @@ public class DockService extends Service implements AlertDialog.OnMultiChoiceCli
mPendingDevice = null;
boolean callConnect = false;
- CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext, mBtManager,
+ CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(
device);
for (int i = 0; i < mProfiles.length; i++) {
- LocalBluetoothProfileManager profileManager = LocalBluetoothProfileManager
- .getProfileManager(mBtManager, mProfiles[i]);
-
- if (DEBUG) Log.d(TAG, mProfiles[i].toString() + " = " + mCheckedItems[i]);
+ LocalBluetoothProfile profile = mProfiles[i];
+ if (DEBUG) Log.d(TAG, profile.toString() + " = " + mCheckedItems[i]);
if (mCheckedItems[i]) {
// Checked but not connected
callConnect = true;
} else if (!mCheckedItems[i]) {
// Unchecked, may or may not be connected.
- int status = profileManager.getConnectionStatus(cachedDevice.getDevice());
- if (SettingsBtStatus.isConnectionStatusConnected(status)) {
+ int status = profile.getConnectionStatus(cachedDevice.getDevice());
+ if (status == BluetoothProfile.STATE_CONNECTED) {
if (DEBUG) Log.d(TAG, "applyBtSettings - Disconnecting");
cachedDevice.disconnect(mProfiles[i]);
}
}
- profileManager.setPreferred(device, mCheckedItems[i]);
+ profile.setPreferred(device, mCheckedItems[i]);
if (DEBUG) {
- if (mCheckedItems[i] != profileManager.isPreferred(device)) {
+ if (mCheckedItems[i] != profile.isPreferred(device)) {
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,
- final int startId) {
- if (mBtManager.getDockAutoConnectSetting(device.getAddress())) {
+ private synchronized void handleDocked(BluetoothDevice device, int state,
+ int startId) {
+ if (LocalBluetoothPreferences.getDockAutoConnectSetting(this, device.getAddress())) {
// Setting == auto connect
- initBtSettings(mContext, device, state, false);
+ initBtSettings(device, state, false);
applyBtSettings(mDevice, startId);
} else {
- createDialog(mContext, device, state, startId);
+ createDialog(device, state, startId);
}
}
- private synchronized void handleUndocked(Context context, LocalBluetoothManager localManager,
- BluetoothDevice device) {
+ private synchronized void handleUndocked(BluetoothDevice device) {
mRunnable = null;
- LocalBluetoothProfileManager.removeServiceListener(this);
+ mProfileManager.removeServiceListener(this);
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
}
mDevice = null;
mPendingDevice = null;
- CachedBluetoothDevice cachedBluetoothDevice = getCachedBluetoothDevice(context,
- localManager, device);
- cachedBluetoothDevice.disconnect();
+ CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(device);
+ cachedDevice.disconnect();
}
- private static CachedBluetoothDevice getCachedBluetoothDevice(Context context,
- LocalBluetoothManager localManager, BluetoothDevice device) {
- CachedBluetoothDeviceManager cachedDeviceManager = localManager.getCachedDeviceManager();
- CachedBluetoothDevice cachedBluetoothDevice = cachedDeviceManager.findDevice(device);
- if (cachedBluetoothDevice == null) {
- cachedBluetoothDevice = new CachedBluetoothDevice(context, device);
+ private CachedBluetoothDevice getCachedBluetoothDevice(BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
}
- return cachedBluetoothDevice;
- }
-
- 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();
+ return cachedDevice;
}
public synchronized void onServiceConnected() {
if (mRunnable != null) {
mRunnable.run();
mRunnable = null;
- LocalBluetoothProfileManager.removeServiceListener(this);
+ mProfileManager.removeServiceListener(this);
}
}
public void onServiceDisconnected() {
+ // FIXME: shouldn't I do something on service disconnected too?
}
}
diff --git a/src/com/android/settings/bluetooth/HeadsetProfile.java b/src/com/android/settings/bluetooth/HeadsetProfile.java
new file mode 100644
index 000000000..dac47b767
--- /dev/null
+++ b/src/com/android/settings/bluetooth/HeadsetProfile.java
@@ -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;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/HidProfile.java b/src/com/android/settings/bluetooth/HidProfile.java
new file mode 100644
index 000000000..9185059d6
--- /dev/null
+++ b/src/com/android/settings/bluetooth/HidProfile.java
@@ -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;
+ }
+ }
+}
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java b/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java
new file mode 100644
index 000000000..013171c14
--- /dev/null
+++ b/src/com/android/settings/bluetooth/LocalBluetoothAdapter.java
@@ -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();
+ }
+ }
+}
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothManager.java b/src/com/android/settings/bluetooth/LocalBluetoothManager.java
index f3c5e6f30..0c04e2273 100644
--- a/src/com/android/settings/bluetooth/LocalBluetoothManager.java
+++ b/src/com/android/settings/bluetooth/LocalBluetoothManager.java
@@ -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");
* you may not use this file except in compliance with the License.
@@ -16,376 +16,96 @@
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.SharedPreferences;
-import android.os.ParcelUuid;
-import android.util.Config;
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
- * 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";
- static final boolean V = Config.LOGV;
- static final boolean D = Config.LOGD;
-
- private static final String SHARED_PREFERENCES_NAME = "bluetooth_settings";
/** Singleton instance. */
private static LocalBluetoothManager sInstance;
- private Context mContext;
- /** If a BT-related activity is in the foreground, this will be it. */
- private Activity mForegroundActivity;
- private AlertDialog mErrorDialog = null;
-
- private BluetoothAdapter mAdapter;
-
- private CachedBluetoothDeviceManager mCachedDeviceManager;
- private BluetoothA2dp mBluetoothA2dp;
-
- private int mState = BluetoothAdapter.ERROR;
+ private final Context mContext;
- private final List<Callback> mCallbacks = new ArrayList<Callback>();
-
- private static final int SCAN_EXPIRATION_MS = 5 * 60 * 1000; // 5 mins
-
- // 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";
+ /** If a BT-related activity is in the foreground, this will be it. */
+ private Context mForegroundActivity;
- private static final String SHARED_PREFERENCES_KEY_LAST_SELECTED_DEVICE_TIME =
- "last_selected_device_time";
+ private final LocalBluetoothAdapter mLocalAdapter;
- private static final String SHARED_PREFERENCES_KEY_DOCK_AUTO_CONNECT = "auto_connect_to_dock";
+ private final CachedBluetoothDeviceManager mCachedDeviceManager;
- private long mLastScan;
+ /** The Bluetooth profile manager. */
+ private final LocalBluetoothProfileManager mProfileManager;
- private LocalBluetoothManager() { }
+ /** The broadcast receiver event manager. */
+ private final BluetoothEventManager mEventManager;
- public static LocalBluetoothManager getInstance(Context context) {
- synchronized (LocalBluetoothManager.class) {
- if (sInstance == null) {
- sInstance = new LocalBluetoothManager();
- if (!sInstance.init(context)) {
- return null;
- }
- LocalBluetoothProfileManager.init(sInstance);
+ public static synchronized LocalBluetoothManager getInstance(Context context) {
+ if (sInstance == null) {
+ LocalBluetoothAdapter adapter = LocalBluetoothAdapter.getInstance();
+ if (adapter == null) {
+ return null;
}
-
- return sInstance;
- }
- }
-
- private boolean init(Context context) {
- // This will be around as long as this process is
- mContext = context.getApplicationContext();
-
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- if (mAdapter == null) {
- return false;
+ // This will be around as long as this process is
+ Context appContext = context.getApplicationContext();
+ sInstance = new LocalBluetoothManager(adapter, appContext);
}
- mCachedDeviceManager = new CachedBluetoothDeviceManager(this);
-
- new BluetoothEventRedirector(this).registerReceiver();
+ return sInstance;
+ }
- mAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
+ private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context) {
+ mContext = context;
+ mLocalAdapter = adapter;
- return true;
+ mCachedDeviceManager = new CachedBluetoothDeviceManager();
+ mEventManager = new BluetoothEventManager(mLocalAdapter,
+ mCachedDeviceManager);
+ mProfileManager = new LocalBluetoothProfileManager(context,
+ mLocalAdapter, mCachedDeviceManager, mEventManager);
}
- 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 LocalBluetoothAdapter getBluetoothAdapter() {
+ return mLocalAdapter;
}
public Context getContext() {
return mContext;
}
- public Activity getForegroundActivity() {
- return mForegroundActivity;
- }
-
- public void setForegroundActivity(Activity activity) {
- if (mErrorDialog != null) {
- mErrorDialog.dismiss();
- mErrorDialog = null;
- }
- mForegroundActivity = activity;
+ boolean isForegroundActivity() {
+ return mForegroundActivity != null;
}
- public SharedPreferences getSharedPreferences() {
- return mContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
- }
-
- public CachedBluetoothDeviceManager getCachedDeviceManager() {
- return mCachedDeviceManager;
- }
-
- List<Callback> getCallbacks() {
- return mCallbacks;
- }
-
- public void registerCallback(Callback callback) {
- synchronized (mCallbacks) {
- 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);
+ synchronized void setForegroundActivity(Context context) {
+ if (context != null) {
+ Log.d(TAG, "setting foreground activity to non-null context");
+ mForegroundActivity = context;
+ mEventManager.resume(context);
} 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 (mForegroundActivity != null) {
+ Log.d(TAG, "setting foreground activity to null");
+ mEventManager.pause(mForegroundActivity);
+ mForegroundActivity = null;
}
-
- 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);
+ CachedBluetoothDeviceManager getCachedDeviceManager() {
+ return mCachedDeviceManager;
}
- public void saveDockAutoConnectSetting(String addr, boolean autoConnect) {
- SharedPreferences.Editor editor = getSharedPreferences().edit();
- editor.putBoolean(SHARED_PREFERENCES_KEY_DOCK_AUTO_CONNECT + addr, autoConnect);
- editor.apply();
+ BluetoothEventManager getEventManager() {
+ return mEventManager;
}
- public void removeDockAutoConnectSetting(String addr) {
- SharedPreferences.Editor editor = getSharedPreferences().edit();
- editor.remove(SHARED_PREFERENCES_KEY_DOCK_AUTO_CONNECT + addr);
- editor.apply();
+ LocalBluetoothProfileManager getProfileManager() {
+ return mProfileManager;
}
}
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java
new file mode 100644
index 000000000..7e62b0e1a
--- /dev/null
+++ b/src/com/android/settings/bluetooth/LocalBluetoothPreferences.java
@@ -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();
+ }
+}
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfile.java b/src/com/android/settings/bluetooth/LocalBluetoothProfile.java
new file mode 100644
index 000000000..936231a1c
--- /dev/null
+++ b/src/com/android/settings/bluetooth/LocalBluetoothProfile.java
@@ -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);
+}
diff --git a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
index de15dcc50..0bb6f115b 100644
--- a/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
+++ b/src/com/android/settings/bluetooth/LocalBluetoothProfileManager.java
@@ -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");
* you may not use this file except in compliance with the License.
@@ -16,67 +16,37 @@
package com.android.settings.bluetooth;
-import com.android.settings.R;
-
import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
-import android.os.Handler;
+import android.content.Context;
+import android.content.Intent;
import android.os.ParcelUuid;
import android.util.Log;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
/**
- * LocalBluetoothProfileManager is an abstract class defining the basic
- * functionality related to a profile.
+ * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile
+ * objects for the available Bluetooth profiles.
*/
-abstract class LocalBluetoothProfileManager {
+final class LocalBluetoothProfileManager {
private static final String TAG = "LocalBluetoothProfileManager";
- /* package */ static final ParcelUuid[] HEADSET_PROFILE_UUIDS = new ParcelUuid[] {
- BluetoothUuid.HSP,
- BluetoothUuid.Handsfree,
- };
-
- /* package */ static final ParcelUuid[] A2DP_SINK_PROFILE_UUIDS = new ParcelUuid[] {
- BluetoothUuid.AudioSink,
- BluetoothUuid.AdvAudioDist,
- };
-
- /* package */ static final ParcelUuid[] A2DP_SRC_PROFILE_UUIDS = new ParcelUuid[] {
- BluetoothUuid.AudioSource
- };
-
- /* package */ static final ParcelUuid[] OPP_PROFILE_UUIDS = new ParcelUuid[] {
- BluetoothUuid.ObexObjectPush
- };
-
- /* package */ static final ParcelUuid[] HID_PROFILE_UUIDS = new ParcelUuid[] {
- BluetoothUuid.Hid
- };
-
- /* package */ static final ParcelUuid[] PANU_PROFILE_UUIDS = new ParcelUuid[] {
- BluetoothUuid.PANU
- };
-
- /* package */ static final ParcelUuid[] NAP_PROFILE_UUIDS = new ParcelUuid[] {
- BluetoothUuid.NAP
- };
+ /** Singleton instance. */
+ private static LocalBluetoothProfileManager sInstance;
/**
* An interface for notifying BluetoothHeadset IPC clients when they have
* been connected to the BluetoothHeadset service.
+ * Only used by {@link DockService}.
*/
public interface ServiceListener {
/**
@@ -85,7 +55,7 @@ abstract class LocalBluetoothProfileManager {
* this callback before making IPC calls on the BluetoothHeadset
* service.
*/
- public void onServiceConnected();
+ void onServiceConnected();
/**
* Called to notify the client that this proxy object has been
@@ -94,723 +64,246 @@ abstract class LocalBluetoothProfileManager {
* This callback will currently only occur if the application hosting
* the BluetoothHeadset service, but may be called more often in future.
*/
- public void onServiceDisconnected();
+ void onServiceDisconnected();
}
- // TODO: close profiles when we're shutting down
- private static final Map<Profile, LocalBluetoothProfileManager> sProfileMap =
- new HashMap<Profile, LocalBluetoothProfileManager>();
-
- protected final LocalBluetoothManager mLocalManager;
+ private final Context mContext;
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final BluetoothEventManager mEventManager;
- public static void init(LocalBluetoothManager localManager) {
- synchronized (sProfileMap) {
- if (sProfileMap.size() == 0) {
- LocalBluetoothProfileManager profileManager;
-
- profileManager = new A2dpProfileManager(localManager);
- sProfileMap.put(Profile.A2DP, profileManager);
-
- profileManager = new HeadsetProfileManager(localManager);
- sProfileMap.put(Profile.HEADSET, profileManager);
-
- profileManager = new OppProfileManager(localManager);
- sProfileMap.put(Profile.OPP, profileManager);
-
- profileManager = new HidProfileManager(localManager);
- sProfileMap.put(Profile.HID, profileManager);
-
- profileManager = new PanProfileManager(localManager);
- sProfileMap.put(Profile.PAN, profileManager);
- }
- }
- }
-
- // TODO(): Combine the init and updateLocalProfiles codes.
- // init can get called from various paths, it makes no sense to add and then delete.
- public static void updateLocalProfiles(LocalBluetoothManager localManager, ParcelUuid[] uuids) {
- if (!BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) &&
- !BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
- sProfileMap.remove(Profile.HEADSET);
- }
-
- if (!BluetoothUuid.containsAnyUuid(uuids, A2DP_SRC_PROFILE_UUIDS)) {
- sProfileMap.remove(Profile.A2DP);
- }
-
- if (!BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS)) {
- sProfileMap.remove(Profile.OPP);
- }
-
- // There is no local SDP record for HID and Settings app doesn't control PBAP
- }
-
- private static final LinkedList<ServiceListener> mServiceListeners =
- new LinkedList<ServiceListener>();
-
- public static void addServiceListener(ServiceListener l) {
- mServiceListeners.add(l);
- }
-
- public static void removeServiceListener(ServiceListener l) {
- mServiceListeners.remove(l);
- }
-
- public static boolean isManagerReady() {
- // Getting just the headset profile is fine for now. Will need to deal with A2DP
- // and others if they aren't always in a ready state.
- LocalBluetoothProfileManager profileManager = sProfileMap.get(Profile.HEADSET);
- if (profileManager == null) {
- return sProfileMap.size() > 0;
- }
- return profileManager.isProfileReady();
- }
-
- public static LocalBluetoothProfileManager getProfileManager(LocalBluetoothManager localManager,
- Profile profile) {
- // Note: This code assumes that "localManager" is same as the
- // LocalBluetoothManager that was used to initialize the sProfileMap.
- // If that every changes, we can't just keep one copy of sProfileMap.
- synchronized (sProfileMap) {
- LocalBluetoothProfileManager profileManager = sProfileMap.get(profile);
- if (profileManager == null) {
- Log.e(TAG, "profileManager can't be found for " + profile.toString());
- }
- return profileManager;
- }
- }
+ private A2dpProfile mA2dpProfile;
+ private HeadsetProfile mHeadsetProfile;
+ private final HidProfile mHidProfile;
+ private OppProfile mOppProfile;
+ private final PanProfile mPanProfile;
/**
- * Temporary method to fill profiles based on a device's class.
- *
- * NOTE: This list happens to define the connection order. We should put this logic in a more
- * well known place when this method is no longer temporary.
- * @param uuids of the remote device
- * @param localUuids UUIDs of the local device
- * @param profiles The list of profiles to fill
+ * Mapping from profile name, e.g. "HEADSET" to profile object.
*/
- public static void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
- List<Profile> profiles) {
- profiles.clear();
+ private final Map<String, LocalBluetoothProfile>
+ mProfileNameMap = new HashMap<String, LocalBluetoothProfile>();
- if (uuids == null) {
- return;
- }
+ LocalBluetoothProfileManager(Context context,
+ LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ BluetoothEventManager eventManager) {
+ mContext = context;
- if (sProfileMap.containsKey(Profile.HEADSET)) {
- 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(Profile.HEADSET);
- }
- }
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mEventManager = eventManager;
+ // pass this reference to adapter and event manager (circular dependency)
+ mLocalAdapter.setProfileManager(this);
+ mEventManager.setProfileManager(this);
+ ParcelUuid[] uuids = adapter.getUuids();
- if (BluetoothUuid.containsAnyUuid(uuids, A2DP_SINK_PROFILE_UUIDS) &&
- sProfileMap.containsKey(Profile.A2DP)) {
- profiles.add(Profile.A2DP);
+ // uuids may be null if Bluetooth is turned off
+ if (uuids != null) {
+ updateLocalProfiles(uuids);
}
- if (BluetoothUuid.containsAnyUuid(uuids, OPP_PROFILE_UUIDS) &&
- sProfileMap.containsKey(Profile.OPP)) {
- profiles.add(Profile.OPP);
- }
+ // Always add HID and PAN profiles
+ mHidProfile = new HidProfile(context, mLocalAdapter);
+ addProfile(mHidProfile, HidProfile.NAME,
+ BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
- if (BluetoothUuid.containsAnyUuid(uuids, HID_PROFILE_UUIDS) &&
- sProfileMap.containsKey(Profile.HID)) {
- profiles.add(Profile.HID);
- }
-
- if (BluetoothUuid.containsAnyUuid(uuids, NAP_PROFILE_UUIDS) &&
- sProfileMap.containsKey(Profile.PAN)) {
- profiles.add(Profile.PAN);
- }
- }
-
- protected LocalBluetoothProfileManager(LocalBluetoothManager localManager) {
- mLocalManager = localManager;
- }
-
- public abstract List<BluetoothDevice> getConnectedDevices();
-
- public abstract boolean connect(BluetoothDevice device);
-
- public abstract boolean disconnect(BluetoothDevice device);
-
- public abstract int getConnectionStatus(BluetoothDevice device);
-
- public abstract int getSummary(BluetoothDevice device);
-
- public abstract int convertState(int a2dpState);
-
- public abstract boolean isPreferred(BluetoothDevice device);
-
- public abstract int getPreferred(BluetoothDevice device);
-
- public abstract void setPreferred(BluetoothDevice device, boolean preferred);
-
- public boolean isConnected(BluetoothDevice device) {
- return SettingsBtStatus.isConnectionStatusConnected(getConnectionStatus(device));
- }
-
- public abstract boolean isProfileReady();
-
- public abstract int getDrawableResource();
-
- public static enum Profile {
- HEADSET(R.string.bluetooth_profile_headset, true, true),
- A2DP(R.string.bluetooth_profile_a2dp, true, true),
- OPP(R.string.bluetooth_profile_opp, false, false),
- HID(R.string.bluetooth_profile_hid, true, true),
- PAN(R.string.bluetooth_profile_pan, true, false);
-
- public final int localizedString;
- private final boolean mConnectable;
- private final boolean mAutoConnectable;
-
- private Profile(int localizedString, boolean connectable,
- boolean autoConnectable) {
- this.localizedString = localizedString;
- this.mConnectable = connectable;
- this.mAutoConnectable = autoConnectable;
- }
-
- public boolean isConnectable() {
- return mConnectable;
- }
-
- public boolean isAutoConnectable() {
- return mAutoConnectable;
- }
+ mPanProfile = new PanProfile(context);
+ addProfile(mPanProfile, PanProfile.NAME, BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
+ Log.d(TAG, "LocalBluetoothProfileManager construction complete");
}
/**
- * A2dpProfileManager is an abstraction for the {@link BluetoothA2dp} service.
+ * Initialize or update the local profile objects. If a UUID was previously
+ * present but has been removed, we print a warning but don't remove the
+ * profile object as it might be referenced elsewhere, or the UUID might
+ * come back and we don't want multiple copies of the profile objects.
+ * @param uuids
*/
- private static class A2dpProfileManager extends LocalBluetoothProfileManager
- implements BluetoothProfile.ServiceListener {
- private BluetoothA2dp mService;
-
- // TODO(): The calls must wait for mService. Its not null just
- // because it runs in the system server.
- public A2dpProfileManager(LocalBluetoothManager localManager) {
- super(localManager);
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- adapter.getProfileProxy(localManager.getContext(), this, BluetoothProfile.A2DP);
-
- }
-
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- mService = (BluetoothA2dp) proxy;
- }
-
- public void onServiceDisconnected(int profile) {
- mService = null;
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices() {
- return mService.getDevicesMatchingConnectionStates(
- new int[] {BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_CONNECTING,
- BluetoothProfile.STATE_DISCONNECTING});
- }
-
- @Override
- public boolean connect(BluetoothDevice device) {
- List<BluetoothDevice> sinks = getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- mService.disconnect(sink);
- }
- }
- return mService.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device) {
- // Downgrade priority as user is disconnecting the sink.
- if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
- mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
- }
- return mService.disconnect(device);
- }
-
- @Override
- public int getConnectionStatus(BluetoothDevice device) {
- return convertState(mService.getConnectionState(device));
- }
-
- @Override
- public int getSummary(BluetoothDevice device) {
- int connectionStatus = getConnectionStatus(device);
-
- if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
- return R.string.bluetooth_a2dp_profile_summary_connected;
- } else {
- return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
+ void updateLocalProfiles(ParcelUuid[] uuids) {
+ // A2DP
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
+ if (mA2dpProfile == null) {
+ Log.d(TAG, "Adding local A2DP profile");
+ mA2dpProfile = new A2dpProfile(mContext);
+ addProfile(mA2dpProfile, A2dpProfile.NAME,
+ BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
}
- }
-
- @Override
- public boolean isPreferred(BluetoothDevice device) {
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
- }
-
- @Override
- public int getPreferred(BluetoothDevice device) {
- return mService.getPriority(device);
- }
-
- @Override
- 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);
+ } else if (mA2dpProfile != null) {
+ Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
+ }
+
+ // Headset / Handsfree
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
+ if (mHeadsetProfile == null) {
+ Log.d(TAG, "Adding local HEADSET profile");
+ mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter,
+ mDeviceManager, this);
+ addProfile(mHeadsetProfile, HeadsetProfile.NAME,
+ BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
}
+ } else if (mHeadsetProfile != null) {
+ Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
}
- @Override
- public int convertState(int a2dpState) {
- switch (a2dpState) {
- case BluetoothProfile.STATE_CONNECTED:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
- case BluetoothProfile.STATE_CONNECTING:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
- case BluetoothProfile.STATE_DISCONNECTED:
- return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
- case BluetoothProfile.STATE_DISCONNECTING:
- return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
- case BluetoothA2dp.STATE_PLAYING:
- return SettingsBtStatus.CONNECTION_STATUS_ACTIVE;
- default:
- return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
+ // OPP
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
+ if (mOppProfile == null) {
+ Log.d(TAG, "Adding local OPP profile");
+ mOppProfile = new OppProfile();
+ // Note: no event handler for OPP, only name map.
+ mProfileNameMap.put(OppProfile.NAME, mOppProfile);
}
+ } else if (mOppProfile != null) {
+ Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
}
- @Override
- public boolean isProfileReady() {
- return true;
- }
-
- @Override
- public int getDrawableResource() {
- return R.drawable.ic_bt_headphones_a2dp;
- }
+ // There is no local SDP record for HID and Settings app doesn't control PBAP
}
- /**
- * HeadsetProfileManager is an abstraction for the {@link BluetoothHeadset} service.
- */
- private static class HeadsetProfileManager extends LocalBluetoothProfileManager
- implements BluetoothProfile.ServiceListener {
- private BluetoothHeadset mService;
- private final Handler mUiHandler = new Handler();
- private boolean profileReady = false;
-
- // TODO(): The calls must get queued if mService becomes null.
- // It can happen when phone app crashes for some reason.
- // All callers should have service listeners. Dock Service is the only
- // one right now.
- public HeadsetProfileManager(LocalBluetoothManager localManager) {
- super(localManager);
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- adapter.getProfileProxy(localManager.getContext(), this, BluetoothProfile.HEADSET);
- }
-
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- mService = (BluetoothHeadset) proxy;
- profileReady = true;
- // This could be called on a non-UI thread, funnel to UI thread.
- mUiHandler.post(new Runnable() {
- public void run() {
- /*
- * We just bound to the service, so refresh the UI of the
- * headset device.
- */
- List<BluetoothDevice> deviceList = mService.getConnectedDevices();
- if (deviceList.size() == 0) return;
-
- mLocalManager.getCachedDeviceManager()
- .onProfileStateChanged(deviceList.get(0), Profile.HEADSET,
- BluetoothProfile.STATE_CONNECTED);
- }
- });
-
- if (mServiceListeners.size() > 0) {
- Iterator<ServiceListener> it = mServiceListeners.iterator();
- while(it.hasNext()) {
- it.next().onServiceConnected();
- }
- }
- }
-
- public void onServiceDisconnected(int profile) {
- mService = null;
- profileReady = false;
- if (mServiceListeners.size() > 0) {
- Iterator<ServiceListener> it = mServiceListeners.iterator();
- while(it.hasNext()) {
- it.next().onServiceDisconnected();
- }
- }
- }
+ private final Collection<ServiceListener> mServiceListeners =
+ new ArrayList<ServiceListener>();
- @Override
- public boolean isProfileReady() {
- return profileReady;
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices() {
- if (mService != null) {
- return mService.getConnectedDevices();
- } else {
- return new ArrayList<BluetoothDevice>();
- }
- }
-
- @Override
- public boolean connect(BluetoothDevice device) {
- List<BluetoothDevice> sinks = getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- mService.disconnect(sink);
- }
- }
- return mService.connect(device);
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device) {
- List<BluetoothDevice> deviceList = getConnectedDevices();
- if (deviceList.size() != 0 && 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;
- }
- }
-
- @Override
- public int getConnectionStatus(BluetoothDevice device) {
- List<BluetoothDevice> deviceList = getConnectedDevices();
-
- return deviceList.size() > 0 && deviceList.get(0).equals(device)
- ? convertState(mService.getConnectionState(device))
- : SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
- }
-
- @Override
- public int getSummary(BluetoothDevice device) {
- int connectionStatus = getConnectionStatus(device);
-
- if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
- return R.string.bluetooth_headset_profile_summary_connected;
- } else {
- return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
- }
- }
-
- @Override
- public boolean isPreferred(BluetoothDevice device) {
- return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
- }
-
- @Override
- public int getPreferred(BluetoothDevice device) {
- return mService.getPriority(device);
- }
-
- @Override
- 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);
- }
- }
+ private void addProfile(LocalBluetoothProfile profile,
+ String profileName, String stateChangedAction) {
+ mEventManager.addHandler(stateChangedAction, new StateChangedHandler(profile));
+ mProfileNameMap.put(profileName, profile);
+ }
- @Override
- public int convertState(int headsetState) {
- switch (headsetState) {
- case BluetoothProfile.STATE_CONNECTED:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
- case BluetoothProfile.STATE_CONNECTING:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
- case BluetoothProfile.STATE_DISCONNECTED:
- return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
- default:
- return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
- }
- }
+ LocalBluetoothProfile getProfileByName(String name) {
+ return mProfileNameMap.get(name);
+ }
- @Override
- public int getDrawableResource() {
- return R.drawable.ic_bt_headset_hfp;
+ // Called from LocalBluetoothAdapter when state changes to ON
+ void setBluetoothStateOn() {
+ ParcelUuid[] uuids = mLocalAdapter.getUuids();
+ if (uuids != null) {
+ updateLocalProfiles(uuids);
}
+ mEventManager.readPairedDevices();
}
/**
- * OppProfileManager
+ * Generic handler for connection state change events for the specified profile.
*/
- private static class OppProfileManager extends LocalBluetoothProfileManager {
-
- public OppProfileManager(LocalBluetoothManager localManager) {
- super(localManager);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices() {
- return null;
- }
-
- @Override
- public boolean connect(BluetoothDevice device) {
- return false;
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device) {
- return false;
- }
+ private class StateChangedHandler implements BluetoothEventManager.Handler {
+ private final LocalBluetoothProfile mProfile;
- @Override
- public int getConnectionStatus(BluetoothDevice device) {
- return -1;
+ StateChangedHandler(LocalBluetoothProfile profile) {
+ mProfile = profile;
}
- @Override
- public int getSummary(BluetoothDevice device) {
- int connectionStatus = getConnectionStatus(device);
-
- if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
- return R.string.bluetooth_opp_profile_summary_connected;
- } else {
- return R.string.bluetooth_opp_profile_summary_not_connected;
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.w(TAG, "StateChangedHandler found new device: " + device);
+ cachedDevice = mDeviceManager.addDevice(mLocalAdapter,
+ LocalBluetoothProfileManager.this, device);
}
- }
-
- @Override
- public boolean isPreferred(BluetoothDevice device) {
- return false;
- }
-
- @Override
- public int getPreferred(BluetoothDevice device) {
- return -1;
- }
-
- @Override
- public void setPreferred(BluetoothDevice device, boolean preferred) {
- }
-
- @Override
- public boolean isProfileReady() {
- return true;
- }
-
- @Override
- public int convertState(int oppState) {
- switch (oppState) {
- case 0:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
- case 1:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
- case 2:
- return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
- default:
- return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
+ 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 " + mProfile + " device");
}
- }
- @Override
- public int getDrawableResource() {
- return 0; // no icon for OPP
+ cachedDevice.onProfileStateChanged(mProfile, newState);
+ cachedDevice.refresh();
}
}
- private static class HidProfileManager extends LocalBluetoothProfileManager {
- private final BluetoothInputDevice mService;
-
- public HidProfileManager(LocalBluetoothManager localManager) {
- super(localManager);
- mService = new BluetoothInputDevice(localManager.getContext());
- }
-
- @Override
- public boolean connect(BluetoothDevice device) {
- return mService.connectInputDevice(device);
- }
-
- @Override
- public int convertState(int hidState) {
- switch (hidState) {
- case BluetoothInputDevice.STATE_CONNECTED:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
- case BluetoothInputDevice.STATE_CONNECTING:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
- case BluetoothInputDevice.STATE_DISCONNECTED:
- return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
- case BluetoothInputDevice.STATE_DISCONNECTING:
- return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
- default:
- return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
- }
- }
-
- @Override
- public boolean disconnect(BluetoothDevice device) {
- return mService.disconnectInputDevice(device);
- }
-
- @Override
- public List<BluetoothDevice> getConnectedDevices() {
- return mService.getConnectedInputDevices();
- }
-
- @Override
- public int getConnectionStatus(BluetoothDevice device) {
- return convertState(mService.getInputDeviceState(device));
- }
-
- @Override
- public int getPreferred(BluetoothDevice device) {
- return mService.getInputDevicePriority(device);
- }
-
- @Override
- public int getSummary(BluetoothDevice device) {
- final int connectionStatus = getConnectionStatus(device);
+ // called from DockService
+ void addServiceListener(ServiceListener l) {
+ mServiceListeners.add(l);
+ }
- if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
- return R.string.bluetooth_hid_profile_summary_connected;
- } else {
- return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
- }
- }
+ // called from DockService
+ void removeServiceListener(ServiceListener l) {
+ mServiceListeners.remove(l);
+ }
- @Override
- public boolean isPreferred(BluetoothDevice device) {
- return mService.getInputDevicePriority(device) > BluetoothInputDevice.PRIORITY_OFF;
+ // not synchronized: use only from UI thread! (TODO: verify)
+ void callServiceConnectedListeners() {
+ for (ServiceListener l : mServiceListeners) {
+ l.onServiceConnected();
}
+ }
- @Override
- public boolean isProfileReady() {
- return true;
+ // not synchronized: use only from UI thread! (TODO: verify)
+ void callServiceDisconnectedListeners() {
+ for (ServiceListener listener : mServiceListeners) {
+ listener.onServiceDisconnected();
}
+ }
- @Override
- public void setPreferred(BluetoothDevice device, boolean preferred) {
- if (preferred) {
- if (mService.getInputDevicePriority(device) < BluetoothInputDevice.PRIORITY_ON) {
- mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
- }
- } else {
- mService.setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_OFF);
- }
+ // This is called by DockService, so check Headset and A2DP.
+ public synchronized boolean isManagerReady() {
+ // Getting just the headset profile is fine for now. Will need to deal with A2DP
+ // and others if they aren't always in a ready state.
+ LocalBluetoothProfile profile = mHeadsetProfile;
+ if (profile != null) {
+ return profile.isProfileReady();
}
-
- @Override
- public int getDrawableResource() {
- return R.drawable.ic_bt_keyboard_hid;
+ profile = mA2dpProfile;
+ if (profile != null) {
+ return profile.isProfileReady();
}
+ return false;
}
- private static class PanProfileManager extends LocalBluetoothProfileManager {
- private final BluetoothPan mService;
-
- public PanProfileManager(LocalBluetoothManager localManager) {
- super(localManager);
- mService = new BluetoothPan(localManager.getContext());
- }
+ A2dpProfile getA2dpProfile() {
+ return mA2dpProfile;
+ }
- @Override
- public boolean connect(BluetoothDevice device) {
- List<BluetoothDevice> sinks = getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- mService.disconnect(sink);
- }
- }
- return mService.connect(device);
- }
+ HeadsetProfile getHeadsetProfile() {
+ return mHeadsetProfile;
+ }
- @Override
- public int convertState(int panState) {
- switch (panState) {
- case BluetoothPan.STATE_CONNECTED:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTED;
- case BluetoothPan.STATE_CONNECTING:
- return SettingsBtStatus.CONNECTION_STATUS_CONNECTING;
- case BluetoothPan.STATE_DISCONNECTED:
- return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTED;
- case BluetoothPan.STATE_DISCONNECTING:
- return SettingsBtStatus.CONNECTION_STATUS_DISCONNECTING;
- default:
- return SettingsBtStatus.CONNECTION_STATUS_UNKNOWN;
- }
- }
+ /**
+ * Fill in a list of LocalBluetoothProfile objects that are supported by
+ * the local device and the remote device.
+ *
+ * @param uuids of the remote device
+ * @param localUuids UUIDs of the local device
+ * @param profiles The list of profiles to fill
+ */
+ synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
+ Collection<LocalBluetoothProfile> profiles) {
+ profiles.clear();
- @Override
- public boolean disconnect(BluetoothDevice device) {
- return mService.disconnect(device);
+ if (uuids == null) {
+ return;
}
- @Override
- public int getSummary(BluetoothDevice device) {
- final int connectionStatus = getConnectionStatus(device);
-
- if (SettingsBtStatus.isConnectionStatusConnected(connectionStatus)) {
- return R.string.bluetooth_pan_profile_summary_connected;
- } else {
- return SettingsBtStatus.getConnectionStatusSummary(connectionStatus);
+ 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);
}
}
- @Override
- public boolean isProfileReady() {
- return true;
+ if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
+ mA2dpProfile != null) {
+ profiles.add(mA2dpProfile);
}
- @Override
- public List<BluetoothDevice> getConnectedDevices() {
- return mService.getConnectedDevices();
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
+ mOppProfile != null) {
+ profiles.add(mOppProfile);
}
- @Override
- public int getConnectionStatus(BluetoothDevice device) {
- return convertState(mService.getPanDeviceState(device));
- }
-
- @Override
- public int getPreferred(BluetoothDevice device) {
- return -1;
- }
-
- @Override
- public boolean isPreferred(BluetoothDevice device) {
- return true;
- }
-
- @Override
- public void setPreferred(BluetoothDevice device, boolean preferred) {
- // ignore: isPreferred is always true for PAN
- return;
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) &&
+ mHidProfile != null) {
+ profiles.add(mHidProfile);
}
- @Override
- public int getDrawableResource() {
- return R.drawable.ic_bt_network_pan;
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
+ mPanProfile != null) {
+ profiles.add(mPanProfile);
}
}
}
diff --git a/src/com/android/settings/bluetooth/OppProfile.java b/src/com/android/settings/bluetooth/OppProfile.java
new file mode 100644
index 000000000..3f7df3853
--- /dev/null
+++ b/src/com/android/settings/bluetooth/OppProfile.java
@@ -0,0 +1,93 @@
+/*
+ * 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.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+
+/**
+ * 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 BluetoothProfile.STATE_DISCONNECTED; // Settings app doesn't handle OPP
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ return false;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ return BluetoothProfile.PRIORITY_OFF; // Settings app doesn't handle OPP
+ }
+
+ 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
+ }
+}
diff --git a/src/com/android/settings/bluetooth/PanProfile.java b/src/com/android/settings/bluetooth/PanProfile.java
new file mode 100644
index 000000000..3f456e45b
--- /dev/null
+++ b/src/com/android/settings/bluetooth/PanProfile.java
@@ -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;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/RequestPermissionActivity.java b/src/com/android/settings/bluetooth/RequestPermissionActivity.java
index 97ec01704..07a7316cb 100644
--- a/src/com/android/settings/bluetooth/RequestPermissionActivity.java
+++ b/src/com/android/settings/bluetooth/RequestPermissionActivity.java
@@ -27,7 +27,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
@@ -43,7 +42,7 @@ public class RequestPermissionActivity extends Activity implements
private static final String TAG = "RequestPermissionActivity";
- private static final int MAX_DISCOVERABLE_TIMEOUT = 300;
+ private static final int MAX_DISCOVERABLE_TIMEOUT = 3600; // 1 hr
// Non-error return code: BT is starting or has started successfully. Used
// by this Activity and RequestPermissionHelperActivity
@@ -51,7 +50,7 @@ public class RequestPermissionActivity extends Activity implements
private static final int REQUEST_CODE_START_BT = 1;
- private LocalBluetoothManager mLocalManager;
+ private LocalBluetoothAdapter mLocalAdapter;
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
// 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() {
@Override
public void onReceive(Context context, Intent intent) {
- if (intent == null)
+ if (intent == null) {
return;
+ }
if (mNeededToEnableBluetooth
&& BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothDevice.ERROR);
@@ -94,12 +94,13 @@ public class RequestPermissionActivity extends Activity implements
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ // Note: initializes mLocalAdapter and returns true on error
if (parseIntent()) {
finish();
return;
}
- int btState = mLocalManager.getBluetoothState();
+ int btState = mLocalAdapter.getState();
switch (btState) {
case BluetoothAdapter.STATE_OFF:
@@ -120,28 +121,29 @@ public class RequestPermissionActivity extends Activity implements
*/
registerReceiver(mReceiver,
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
- Intent i = new Intent();
- i.setClass(this, RequestPermissionHelperActivity.class);
+ Intent intent = new Intent();
+ intent.setClass(this, RequestPermissionHelperActivity.class);
if (mEnableOnly) {
- i.setAction(RequestPermissionHelperActivity.ACTION_INTERNAL_REQUEST_BT_ON);
+ intent.setAction(RequestPermissionHelperActivity.ACTION_INTERNAL_REQUEST_BT_ON);
} else {
- i.setAction(RequestPermissionHelperActivity.
+ intent.setAction(RequestPermissionHelperActivity.
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;
break;
case BluetoothAdapter.STATE_ON:
if (mEnableOnly) {
// Nothing to do. Already enabled.
proceedAndFinish();
- return;
} else {
// Ask the user about enabling discovery mode
createDialog();
- break;
}
+ break;
+ default:
+ Log.e(TAG, "Unknown adapter state: " + btState);
}
}
@@ -157,7 +159,14 @@ public class RequestPermissionActivity extends Activity implements
builder.setCancelable(false);
} else {
// Ask the user whether to turn on discovery mode or not
- builder.setMessage(getString(R.string.bluetooth_ask_enablement_and_discovery, mTimeout));
+ // For lasting discoverable mode there is a different message
+ if (mTimeout == BluetoothDiscoverableEnabler.DISCOVERABLE_TIMEOUT_NEVER) {
+ builder.setMessage(
+ getString(R.string.bluetooth_ask_lasting_discovery));
+ } else {
+ builder.setMessage(
+ getString(R.string.bluetooth_ask_discovery, mTimeout));
+ }
builder.setPositiveButton(getString(R.string.yes), this);
builder.setNegativeButton(getString(R.string.no), this);
}
@@ -169,8 +178,8 @@ public class RequestPermissionActivity extends Activity implements
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != REQUEST_CODE_START_BT) {
- Log.e(TAG, "Unexpected onActivityResult " + requestCode + " " + resultCode);
- setResult(Activity.RESULT_CANCELED);
+ Log.e(TAG, "Unexpected onActivityResult " + requestCode + ' ' + resultCode);
+ setResult(RESULT_CANCELED);
finish();
return;
}
@@ -184,7 +193,7 @@ public class RequestPermissionActivity extends Activity implements
// BT and discoverable mode.
mUserConfirmed = true;
- if (mLocalManager.getBluetoothState() == BluetoothAdapter.STATE_ON) {
+ if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
proceedAndFinish();
} else {
// If BT is not up yet, show "Turning on Bluetooth..."
@@ -199,7 +208,7 @@ public class RequestPermissionActivity extends Activity implements
break;
case DialogInterface.BUTTON_NEGATIVE:
- setResult(Activity.RESULT_CANCELED);
+ setResult(RESULT_CANCELED);
finish();
break;
}
@@ -210,18 +219,19 @@ public class RequestPermissionActivity extends Activity implements
if (mEnableOnly) {
// BT enabled. Done
- returnCode = Activity.RESULT_OK;
- } else if (mLocalManager.getBluetoothAdapter().setScanMode(
+ returnCode = RESULT_OK;
+ } else if (mLocalAdapter.setScanMode(
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, mTimeout)) {
// 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;
// Activity.RESULT_FIRST_USER should be 1
- if (returnCode < Activity.RESULT_FIRST_USER) {
- returnCode = Activity.RESULT_FIRST_USER;
+ if (returnCode < RESULT_FIRST_USER) {
+ returnCode = RESULT_FIRST_USER;
}
} else {
- returnCode = Activity.RESULT_CANCELED;
+ returnCode = RESULT_CANCELED;
}
if (mDialog != null) {
@@ -232,6 +242,10 @@ public class RequestPermissionActivity extends Activity implements
finish();
}
+ /**
+ * Parse the received Intent and initialize mLocalBluetoothAdapter.
+ * @return true if an error occurred; false otherwise
+ */
private boolean parseIntent() {
Intent intent = getIntent();
if (intent != null && intent.getAction().equals(BluetoothAdapter.ACTION_REQUEST_ENABLE)) {
@@ -241,27 +255,26 @@ public class RequestPermissionActivity extends Activity implements
mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT);
- Log.e(TAG, "Timeout = " + mTimeout);
+ Log.d(TAG, "Setting Bluetooth Discoverable Timeout = " + mTimeout);
- if (mTimeout > MAX_DISCOVERABLE_TIMEOUT) {
- mTimeout = MAX_DISCOVERABLE_TIMEOUT;
- } else if (mTimeout <= 0) {
+ if (mTimeout < 0 || mTimeout > MAX_DISCOVERABLE_TIMEOUT) {
mTimeout = BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT;
}
} else {
Log.e(TAG, "Error: this activity may be started only with intent "
+ BluetoothAdapter.ACTION_REQUEST_ENABLE + " or "
+ BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
- setResult(Activity.RESULT_CANCELED);
+ setResult(RESULT_CANCELED);
return true;
}
- mLocalManager = LocalBluetoothManager.getInstance(this);
- if (mLocalManager == null) {
- Log.e(TAG, "Error: there's a problem starting bluetooth");
- setResult(Activity.RESULT_CANCELED);
+ LocalBluetoothManager manager = LocalBluetoothManager.getInstance(this);
+ if (manager == null) {
+ Log.e(TAG, "Error: there's a problem starting Bluetooth");
+ setResult(RESULT_CANCELED);
return true;
}
+ mLocalAdapter = manager.getBluetoothAdapter();
return false;
}
@@ -269,20 +282,14 @@ public class RequestPermissionActivity extends Activity implements
@Override
protected void onDestroy() {
super.onDestroy();
- 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();
+ if (mNeededToEnableBluetooth) {
+ unregisterReceiver(mReceiver);
+ }
}
@Override
public void onBackPressed() {
- setResult(Activity.RESULT_CANCELED);
+ setResult(RESULT_CANCELED);
super.onBackPressed();
}
}
diff --git a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java b/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java
index c8698687f..9b5946b59 100644
--- a/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java
+++ b/src/com/android/settings/bluetooth/RequestPermissionHelperActivity.java
@@ -43,7 +43,7 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
public static final String 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;
@@ -55,6 +55,7 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ // Note: initializes mLocalAdapter and returns true on error
if (parseIntent()) {
finish();
return;
@@ -75,7 +76,11 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
if (mEnableOnly) {
tv.setText(getString(R.string.bluetooth_ask_enablement));
} else {
- tv.setText(getString(R.string.bluetooth_ask_enablement_and_discovery, mTimeout));
+ if (mTimeout == BluetoothDiscoverableEnabler.DISCOVERABLE_TIMEOUT_NEVER) {
+ tv.setText(getString(R.string.bluetooth_ask_enablement_and_lasting_discovery));
+ } else {
+ tv.setText(getString(R.string.bluetooth_ask_enablement_and_discovery, mTimeout));
+ }
}
p.mPositiveButtonText = getString(R.string.yes);
@@ -88,32 +93,33 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
public void onClick(DialogInterface dialog, int which) {
int returnCode;
+ // FIXME: fix this ugly switch logic!
switch (which) {
- case DialogInterface.BUTTON_POSITIVE:
+ case BUTTON_POSITIVE:
int btState = 0;
try {
// TODO There's a better way.
int retryCount = 30;
do {
- btState = mLocalManager.getBluetoothState();
+ btState = mLocalAdapter.getBluetoothState();
Thread.sleep(100);
} while (btState == BluetoothAdapter.STATE_TURNING_OFF && --retryCount > 0);
- } catch (InterruptedException e) {
+ } catch (InterruptedException ignored) {
// don't care
}
if (btState == BluetoothAdapter.STATE_TURNING_ON
|| btState == BluetoothAdapter.STATE_ON
- || mLocalManager.getBluetoothAdapter().enable()) {
+ || mLocalAdapter.enable()) {
returnCode = RequestPermissionActivity.RESULT_BT_STARTING_OR_STARTED;
} else {
- returnCode = Activity.RESULT_CANCELED;
+ returnCode = RESULT_CANCELED;
}
break;
- case DialogInterface.BUTTON_NEGATIVE:
- returnCode = Activity.RESULT_CANCELED;
+ case BUTTON_NEGATIVE:
+ returnCode = RESULT_CANCELED;
break;
default:
return;
@@ -121,6 +127,10 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
setResult(returnCode);
}
+ /**
+ * Parse the received Intent and initialize mLocalBluetoothAdapter.
+ * @return true if an error occurred; false otherwise
+ */
private boolean parseIntent() {
Intent intent = getIntent();
if (intent != null && intent.getAction().equals(ACTION_INTERNAL_REQUEST_BT_ON)) {
@@ -132,23 +142,24 @@ public class RequestPermissionHelperActivity extends AlertActivity implements
mTimeout = intent.getIntExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
BluetoothDiscoverableEnabler.DEFAULT_DISCOVERABLE_TIMEOUT);
} else {
- setResult(Activity.RESULT_CANCELED);
+ setResult(RESULT_CANCELED);
return true;
}
- mLocalManager = LocalBluetoothManager.getInstance(this);
- if (mLocalManager == null) {
- Log.e(TAG, "Error: there's a problem starting bluetooth");
- setResult(Activity.RESULT_CANCELED);
+ LocalBluetoothManager manager = LocalBluetoothManager.getInstance(this);
+ if (manager == null) {
+ Log.e(TAG, "Error: there's a problem starting Bluetooth");
+ setResult(RESULT_CANCELED);
return true;
}
+ mLocalAdapter = manager.getBluetoothAdapter();
return false;
}
@Override
public void onBackPressed() {
- setResult(Activity.RESULT_CANCELED);
+ setResult(RESULT_CANCELED);
super.onBackPressed();
}
}
diff --git a/src/com/android/settings/bluetooth/SettingsBtStatus.java b/src/com/android/settings/bluetooth/SettingsBtStatus.java
deleted file mode 100644
index 2407b533d..000000000
--- a/src/com/android/settings/bluetooth/SettingsBtStatus.java
+++ /dev/null
@@ -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;
- }
- }
-}
diff --git a/src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java b/src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java
new file mode 100644
index 000000000..bae6e5682
--- /dev/null
+++ b/src/com/android/settings/bluetooth/Utf8ByteLengthFilter.java
@@ -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;
+ }
+ }
+}
diff --git a/src/com/android/settings/bluetooth/Utils.java b/src/com/android/settings/bluetooth/Utils.java
new file mode 100644
index 000000000..7d38e1700
--- /dev/null
+++ b/src/com/android/settings/bluetooth/Utils.java
@@ -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();
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/Constants.java b/src/com/android/settings/deviceinfo/Constants.java
new file mode 100644
index 000000000..9f494797d
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/Constants.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 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.deviceinfo;
+
+import android.os.Environment;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Some of the constants used in this package
+ */
+class Constants {
+ static final int MEDIA_INDEX = 0;
+ static final int DOWNLOADS_INDEX = 1;
+ static final int PIC_VIDEO_INDEX = 2;
+ static final int MUSIC_INDEX = 3;
+ static final int MEDIA_APPS_DATA_INDEX = 4;
+ static final int MEDIA_MISC_INDEX = 5;
+ static final int NUM_MEDIA_DIRS_TRACKED = MEDIA_MISC_INDEX + 1;
+
+ static class MediaDirectory {
+ final String[] mDirPaths;
+ final String mKey;
+ final String mPreferenceName;
+ MediaDirectory(String pref, String debugInfo, String... paths) {
+ mDirPaths = paths;
+ mKey = debugInfo;
+ mPreferenceName = pref;
+ }
+ }
+ static final ArrayList<MediaDirectory> mMediaDirs = new ArrayList<MediaDirectory>();
+ static final List<String> ExclusionTargetsForMiscFiles = new ArrayList<String>();
+ static {
+ mMediaDirs.add(MEDIA_INDEX,
+ new MediaDirectory(null,
+ "/sdcard",
+ Environment.getExternalStorageDirectory().getAbsolutePath()));
+ mMediaDirs.add(DOWNLOADS_INDEX,
+ new MediaDirectory("memory_internal_downloads",
+ "/sdcard/download",
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()));
+ mMediaDirs.add(PIC_VIDEO_INDEX,
+ new MediaDirectory("memory_internal_dcim",
+ "/sdcard/pic_video",
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DCIM).getAbsolutePath(),
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_MOVIES).getAbsolutePath(),
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES).getAbsolutePath()));
+ mMediaDirs.add(MUSIC_INDEX,
+ new MediaDirectory("memory_internal_music",
+ "/sdcard/audio",
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_MUSIC).getAbsolutePath(),
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_ALARMS).getAbsolutePath(),
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_NOTIFICATIONS).getAbsolutePath(),
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_RINGTONES).getAbsolutePath(),
+ Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PODCASTS).getAbsolutePath()));
+ mMediaDirs.add(MEDIA_APPS_DATA_INDEX,
+ new MediaDirectory(null,
+ "/sdcard/Android",
+ Environment.getExternalStorageAndroidDataDir().getAbsolutePath()));
+ mMediaDirs.add(MEDIA_MISC_INDEX,
+ new MediaDirectory("memory_internal_media_misc",
+ "misc on /sdcard",
+ "not relevant"));
+ // prepare a lit of strings representing dirpaths that should be skipped while looking
+ // for 'other' files
+ for (int j = 0; j < Constants.NUM_MEDIA_DIRS_TRACKED - 1; j++) {
+ String[] dirs = Constants.mMediaDirs.get(j).mDirPaths;
+ int len = dirs.length;
+ if (len > 0) {
+ for (int k = 0; k < len; k++) {
+ ExclusionTargetsForMiscFiles.add(dirs[k]);
+ }
+ }
+ // also add /sdcard/Android
+ ExclusionTargetsForMiscFiles.add(
+ Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android");
+ }
+ }
+}
diff --git a/src/com/android/settings/deviceinfo/FileItemInfoLayout.java b/src/com/android/settings/deviceinfo/FileItemInfoLayout.java
new file mode 100644
index 000000000..990f7f2b8
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/FileItemInfoLayout.java
@@ -0,0 +1,78 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.android.settings.deviceinfo;
+
+import com.android.settings.R;
+
+import android.content.Context;
+import android.os.Environment;
+import android.util.AttributeSet;
+import android.view.ViewDebug;
+import android.widget.CheckBox;
+import android.widget.Checkable;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+/**
+ * Handles display of a single row entry on Settings --> Storage --> Misc Files screen
+ */
+public class FileItemInfoLayout extends RelativeLayout implements Checkable {
+ private TextView mFileNameView;
+ private TextView mFileSizeView;
+ private CheckBox mCheckbox;
+ private static final int mLengthExternalStorageDirPrefix =
+ Environment.getExternalStorageDirectory().getAbsolutePath().length() + 1;
+
+ public FileItemInfoLayout(Context context) {
+ this(context, null);
+ }
+
+ public FileItemInfoLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FileItemInfoLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void toggle() {
+ setChecked(!mCheckbox.isChecked());
+ }
+
+ /* (non-Javadoc)
+ * @see android.view.View#onFinishInflate()
+ */
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mFileNameView = (TextView) findViewById(R.id.misc_filename);
+ mFileSizeView = (TextView) findViewById(R.id.misc_filesize);
+ mCheckbox = (CheckBox) findViewById(R.id.misc_checkbox);
+ }
+
+ public void setFileName(String fileName) {
+ mFileNameView.setText(fileName.substring(mLengthExternalStorageDirPrefix));
+ }
+
+ public void setFileSize(String filesize) {
+ mFileSizeView.setText(filesize);
+ }
+
+ @ViewDebug.ExportedProperty
+ public boolean isChecked() {
+ return mCheckbox.isChecked();
+ }
+
+ public CheckBox getCheckBox() {
+ return mCheckbox;
+ }
+
+ /**
+ * <p>Changes the checked state of this text view.</p>
+ *
+ * @param checked true to check the text, false to uncheck it
+ */
+ public void setChecked(boolean checked) {
+ mCheckbox.setChecked(checked);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java
index db1ff6554..7d2a55e2f 100644
--- a/src/com/android/settings/deviceinfo/Memory.java
+++ b/src/com/android/settings/deviceinfo/Memory.java
@@ -23,6 +23,7 @@ import com.android.settings.deviceinfo.MemoryMeasurement.MeasurementReceiver;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -32,9 +33,7 @@ import android.content.DialogInterface.OnCancelListener;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.graphics.drawable.ShapeDrawable;
-import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.shapes.RoundRectShape;
-import android.hardware.UsbManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -56,8 +55,7 @@ import java.util.List;
public class Memory extends SettingsPreferenceFragment implements OnCancelListener,
MeasurementReceiver {
- private static final String TAG = "Memory";
- static final boolean localLOGV = false;
+ private static final String TAG = "MemorySettings";
private static final String MEMORY_SD_SIZE = "memory_sd_size";
@@ -75,8 +73,6 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen
private static final String MEMORY_INTERNAL_APPS = "memory_internal_apps";
- private static final String MEMORY_INTERNAL_MEDIA = "memory_internal_media";
-
private static final String MEMORY_INTERNAL_CHART = "memory_internal_chart";
private static final int DLG_CONFIRM_UNMOUNT = 1;
@@ -94,13 +90,13 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen
// Internal storage preferences
private Preference mInternalSize;
private Preference mInternalAvail;
- private Preference mInternalMediaUsage;
private Preference mInternalAppsUsage;
+ private final Preference[] mMediaPreferences = new Preference[Constants.NUM_MEDIA_DIRS_TRACKED];
private UsageBarPreference mInternalUsageChart;
// Internal storage chart colors
- private int mInternalMediaColor;
private int mInternalAppsColor;
+ private int mInternalAvailColor;
private int mInternalUsedColor;
boolean mSdMountToggleAdded = true;
@@ -134,9 +130,12 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen
Bundle bundle = msg.getData();
final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE);
final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE);
- final long mediaUsed = bundle.getLong(MemoryMeasurement.MEDIA_USED);
final long appsUsed = bundle.getLong(MemoryMeasurement.APPS_USED);
- updateUiExact(totalSize, availSize, mediaUsed, appsUsed);
+ final long[] mediaSizes = new long[Constants.NUM_MEDIA_DIRS_TRACKED];
+ for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) {
+ mediaSizes[i] = bundle.getLong(Constants.mMediaDirs.get(i).mKey);
+ }
+ updateUiExact(totalSize, availSize, appsUsed, mediaSizes);
break;
}
case MSG_UI_UPDATE_EXTERNAL_APPROXIMATE: {
@@ -175,31 +174,59 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen
}
mInternalSize = findPreference(MEMORY_INTERNAL_SIZE);
- mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL);
- mInternalMediaUsage = findPreference(MEMORY_INTERNAL_MEDIA);
- mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS);
-
- mInternalMediaColor = mRes.getColor(R.color.memory_media_usage);
mInternalAppsColor = mRes.getColor(R.color.memory_apps_usage);
mInternalUsedColor = android.graphics.Color.GRAY;
-
+ mInternalAvailColor = mRes.getColor(R.color.memory_avail);
+ final int buttonSize = (int) mRes.getDimension(R.dimen.device_memory_usage_button_size);
float[] radius = new float[] {
5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f
};
RoundRectShape shape1 = new RoundRectShape(radius, null, null);
- ShapeDrawable mediaShape = new ShapeDrawable(shape1);
- mediaShape.setIntrinsicWidth(32);
- mediaShape.setIntrinsicHeight(32);
- mediaShape.getPaint().setColor(mInternalMediaColor);
- mInternalMediaUsage.setIcon(mediaShape);
+ // total available space
+ mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL);
+ ShapeDrawable availShape = new ShapeDrawable(shape1);
+ availShape.setIntrinsicWidth(buttonSize);
+ availShape.setIntrinsicHeight(buttonSize);
+ availShape.getPaint().setColor(mInternalAvailColor);
+ mInternalAvail.setIcon(availShape);
+ // used by apps
+ mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS);
ShapeDrawable appsShape = new ShapeDrawable(shape1);
- appsShape.setIntrinsicWidth(32);
- appsShape.setIntrinsicHeight(32);
+ appsShape.setIntrinsicWidth(buttonSize);
+ appsShape.setIntrinsicHeight(buttonSize);
appsShape.getPaint().setColor(mInternalAppsColor);
mInternalAppsUsage.setIcon(appsShape);
+ // space used by individual major directories on /sdcard
+ for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) {
+ // nothing to be displayed for certain entries in Constants.mMediaDirs
+ if (Constants.mMediaDirs.get(i).mPreferenceName == null) {
+ continue;
+ }
+ mMediaPreferences[i] = findPreference(Constants.mMediaDirs.get(i).mPreferenceName);
+ ShapeDrawable shape = new ShapeDrawable(shape1);
+ shape.setIntrinsicWidth(buttonSize);
+ shape.setIntrinsicHeight(buttonSize);
+ int color = 0;
+ switch (i) {
+ case Constants.DOWNLOADS_INDEX:
+ color = mRes.getColor(R.color.memory_downloads);
+ break;
+ case Constants.PIC_VIDEO_INDEX:
+ color = mRes.getColor(R.color.memory_video);
+ break;
+ case Constants.MUSIC_INDEX:
+ color = mRes.getColor(R.color.memory_audio);
+ break;
+ case Constants.MEDIA_MISC_INDEX:
+ color = mRes.getColor(R.color.memory_misc);
+ break;
+ }
+ shape.getPaint().setColor(color);
+ mMediaPreferences[i].setIcon(shape);
+ }
mInternalUsageChart = (UsageBarPreference) findPreference(MEMORY_INTERNAL_CHART);
mMeasurement = MemoryMeasurement.getInstance(getActivity());
@@ -209,7 +236,7 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen
@Override
public void onResume() {
super.onResume();
-
+ mMeasurement.setReceiver(this);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
intentFilter.addDataScheme("file");
@@ -282,6 +309,27 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen
com.android.settings.Settings.ManageApplicationsActivity.class);
startActivity(intent);
return true;
+ } else if (preference == mMediaPreferences[Constants.DOWNLOADS_INDEX]) {
+ Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS)
+ .putExtra(DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
+ startActivity(intent);
+ return true;
+ } else if (preference == mMediaPreferences[Constants.MUSIC_INDEX]) {
+ Intent intent = new Intent("android.intent.action.GET_CONTENT");
+ intent.setType("audio/mp3");
+ startActivity(intent);
+ return true;
+ } else if (preference == mMediaPreferences[Constants.PIC_VIDEO_INDEX]) {
+ Intent intent = new Intent("android.intent.action.GET_CONTENT");
+ intent.setType("image/jpeg");
+ startActivity(intent);
+ return true;
+ } else if (preference == mMediaPreferences[Constants.MEDIA_MISC_INDEX]) {
+ Context context = getActivity().getApplicationContext();
+ if (MemoryMeasurement.getInstance(context).isSizeOfMiscCategoryNonZero()) {
+ startActivity(new Intent(context, MiscFilesHandler.class));
+ }
+ return true;
}
return false;
@@ -375,7 +423,6 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen
// Check if external media is in use.
try {
if (hasAppsAccessingStorage()) {
- if (localLOGV) Log.i(TAG, "Do have storage users accessing media");
// Present dialog to user
showDialogInner(DLG_CONFIRM_UNMOUNT);
} else {
@@ -400,19 +447,45 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen
}
}
- private void updateUiExact(long totalSize, long availSize, long mediaSize, long appsSize) {
+ private void updateUiExact(long totalSize, long availSize, long appsSize, long[] mediaSizes) {
// There are other things that can take up storage, but we didn't measure it.
// add that unaccounted-for-usage to Apps Usage
- final long appsPlusRemaining = totalSize - availSize - mediaSize;
-
+ long appsPlusRemaining = totalSize - availSize - mediaSizes[Constants.DOWNLOADS_INDEX] -
+ mediaSizes[Constants.PIC_VIDEO_INDEX] - mediaSizes[Constants.MUSIC_INDEX] -
+ mediaSizes[Constants.MEDIA_MISC_INDEX];
mInternalSize.setSummary(formatSize(totalSize));
mInternalAvail.setSummary(formatSize(availSize));
- mInternalMediaUsage.setSummary(formatSize(mediaSize));
mInternalAppsUsage.setSummary(formatSize(appsPlusRemaining));
mInternalUsageChart.clear();
- mInternalUsageChart.addEntry(mediaSize / (float) totalSize, mInternalMediaColor);
mInternalUsageChart.addEntry(appsPlusRemaining / (float) totalSize, mInternalAppsColor);
+
+ for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) {
+ if (Constants.mMediaDirs.get(i).mPreferenceName == null) {
+ continue;
+ }
+ this.mMediaPreferences[i].setSummary(formatSize(mediaSizes[i]));
+ // don't add entry to color chart for media usage and for zero-sized dirs
+ if (i != Constants.MEDIA_INDEX && mediaSizes[i] > 0) {
+ int color = 0;
+ switch (i) {
+ case Constants.DOWNLOADS_INDEX:
+ color = mRes.getColor(R.color.memory_downloads);
+ break;
+ case Constants.PIC_VIDEO_INDEX:
+ color = mRes.getColor(R.color.memory_video);
+ break;
+ case Constants.MUSIC_INDEX:
+ color = mRes.getColor(R.color.memory_audio);
+ break;
+ case Constants.MEDIA_MISC_INDEX:
+ color = mRes.getColor(R.color.memory_misc);
+ break;
+ }
+ mInternalUsageChart.addEntry(mediaSizes[i] / (float) totalSize, color);
+ }
+ }
+ mInternalUsageChart.addEntry(availSize / (float) totalSize, mInternalAvailColor);
mInternalUsageChart.commit();
}
diff --git a/src/com/android/settings/deviceinfo/MemoryMeasurement.java b/src/com/android/settings/deviceinfo/MemoryMeasurement.java
index 1aef20234..1b42bc10d 100644
--- a/src/com/android/settings/deviceinfo/MemoryMeasurement.java
+++ b/src/com/android/settings/deviceinfo/MemoryMeasurement.java
@@ -23,6 +23,7 @@ import android.util.Log;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -35,14 +36,16 @@ import java.util.List;
*
* Filesystem stats (using StatFs)
* Directory measurements (using DefaultContainerService.measureDir)
- * Applicaiton measurements (using PackageManager)
+ * Application measurements (using PackageManager)
*
* Then the calling application would just specify the type and an argument.
* This class would keep track of it while the calling application would
* decide on how to use it.
*/
public class MemoryMeasurement {
- private static final String TAG = "MemoryMeasurement";
+ private static final String TAG = "MemorySettings";
+ private static final boolean LOCAL_LOGV = true;
+ static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE);
public static final String TOTAL_SIZE = "total_size";
@@ -50,7 +53,7 @@ public class MemoryMeasurement {
public static final String APPS_USED = "apps_used";
- public static final String MEDIA_USED = "media_used";
+ private long[] mMediaSizes = new long[Constants.NUM_MEDIA_DIRS_TRACKED];
private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
@@ -66,13 +69,13 @@ public class MemoryMeasurement {
// Internal memory fields
private long mInternalTotalSize;
private long mInternalAvailSize;
- private long mInternalMediaSize;
private long mInternalAppsSize;
// External memory fields
private long mExternalTotalSize;
private long mExternalAvailSize;
+ List<FileInfo> mFileInfoForMisc;
private MemoryMeasurement(Context context) {
// Start the thread that will measure the disk usage.
@@ -98,7 +101,9 @@ public class MemoryMeasurement {
}
public void setReceiver(MeasurementReceiver receiver) {
- mReceiver = new WeakReference<MeasurementReceiver>(receiver);
+ if (mReceiver == null || mReceiver.get() == null) {
+ mReceiver = new WeakReference<MeasurementReceiver>(receiver);
+ }
}
public void measureExternal() {
@@ -134,6 +139,9 @@ public class MemoryMeasurement {
private void sendInternalExactUpdate() {
MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
if (receiver == null) {
+ if (LOGV) {
+ Log.i(TAG, "measurements dropped because receiver is null! wasted effort");
+ }
return;
}
@@ -141,7 +149,9 @@ public class MemoryMeasurement {
bundle.putLong(TOTAL_SIZE, mInternalTotalSize);
bundle.putLong(AVAIL_SIZE, mInternalAvailSize);
bundle.putLong(APPS_USED, mInternalAppsSize);
- bundle.putLong(MEDIA_USED, mInternalMediaSize);
+ for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) {
+ bundle.putLong(Constants.mMediaDirs.get(i).mKey, mMediaSizes[i]);
+ }
receiver.updateExactInternal(bundle);
}
@@ -252,6 +262,7 @@ public class MemoryMeasurement {
case MSG_CONNECTED: {
IMediaContainerService imcs = (IMediaContainerService) msg.obj;
measureExactInternalStorage(imcs);
+ break;
}
case MSG_DISCONNECT: {
synchronized (mLock) {
@@ -265,6 +276,7 @@ public class MemoryMeasurement {
context.unbindService(mDefContainerConn);
}
}
+ break;
}
case MSG_COMPLETED: {
mMeasured = true;
@@ -356,24 +368,40 @@ public class MemoryMeasurement {
if (context == null) {
return;
}
-
// We have to get installd to measure the package sizes.
PackageManager pm = context.getPackageManager();
if (pm == null) {
return;
}
+ // measure sizes for all except "media_misc" - which is computed
+ for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED - 1; i++) {
+ mMediaSizes[i] = 0;
+ String[] dirs = Constants.mMediaDirs.get(i).mDirPaths;
+ int len = dirs.length;
+ if (len > 0) {
+ for (int k = 0; k < len; k++) {
+ long dirSize = getSize(imcs, dirs[k]);
+ mMediaSizes[i] += dirSize;
+ if (LOGV) {
+ Log.i(TAG, "size of " + dirs[k] + ": " + dirSize);
+ }
+ }
+ }
+ }
- long mediaSize;
- try {
- mediaSize = imcs.calculateDirectorySize(
- Environment.getExternalStorageDirectory().getAbsolutePath());
- } catch (Exception e) {
- Log.i(TAG, "Could not read memory from default container service");
- return;
+ // compute the size of "misc"
+ mMediaSizes[Constants.MEDIA_MISC_INDEX] = mMediaSizes[Constants.MEDIA_INDEX];
+ for (int i = 1; i < Constants.NUM_MEDIA_DIRS_TRACKED - 1; i++) {
+ mMediaSizes[Constants.MEDIA_MISC_INDEX] -= mMediaSizes[i];
+ }
+ if (LOGV) {
+ Log.i(TAG, "media_misc size: " + mMediaSizes[Constants.MEDIA_MISC_INDEX]);
}
- mInternalMediaSize = mediaSize;
+ // compute the sizes of each of the files/directories under 'misc' category
+ measureSizesOfMisc(imcs);
+ // compute apps sizes
final List<ApplicationInfo> apps = pm
.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES
| PackageManager.GET_DISABLED_COMPONENTS);
@@ -393,6 +421,43 @@ public class MemoryMeasurement {
// Sending of the message back to the MeasurementReceiver is
// completed in the PackageObserver
}
+ private void measureSizesOfMisc(IMediaContainerService imcs) {
+ File top = Environment.getExternalStorageDirectory();
+ mFileInfoForMisc = new ArrayList<FileInfo>();
+ File[] files = top.listFiles();
+ int len = files.length;
+ if (len == 0) {
+ return;
+ }
+ // get sizes of all top level nodes in /sdcard dir except the ones already computed...
+ long counter = 0;
+ for (int i = 0; i < len; i++) {
+ String path = files[i].getAbsolutePath();
+ if (Constants.ExclusionTargetsForMiscFiles.contains(path)) {
+ continue;
+ }
+ if (files[i].isFile()) {
+ mFileInfoForMisc.add(new FileInfo(path, files[i].length(), counter++));
+ } else if (files[i].isDirectory()) {
+ long dirSize = getSize(imcs, path);
+ mFileInfoForMisc.add(new FileInfo(path, dirSize, counter++));
+ } else {
+ }
+ }
+ // sort the list of FileInfo objects collected above in descending order of their sizes
+ Collections.sort(mFileInfoForMisc);
+ }
+
+ private long getSize(IMediaContainerService imcs, String dir) {
+ try {
+ long size = imcs.calculateDirectorySize(dir);
+ return size;
+ } catch (Exception e) {
+ Log.w(TAG, "Could not read memory from default container service for " +
+ dir, e);
+ return -1;
+ }
+ }
public void measureApproximateExternalStorage() {
File path = Environment.getExternalStorageDirectory();
@@ -412,4 +477,29 @@ public class MemoryMeasurement {
public void invalidate() {
mHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
}
+
+ boolean isSizeOfMiscCategoryNonZero() {
+ return mFileInfoForMisc != null && mFileInfoForMisc.size() > 0;
+ }
+
+ static class FileInfo implements Comparable<FileInfo> {
+ String mFileName;
+ long mSize;
+ long mId;
+ FileInfo(String fileName, long size, long id) {
+ mFileName = fileName;
+ mSize = size;
+ mId = id;
+ }
+ @Override
+ public int compareTo(FileInfo that) {
+ if (this == that || mSize == that.mSize) return 0;
+ else if (mSize < that.mSize) return 1; // for descending sort
+ else return -1;
+ }
+ @Override
+ public String toString() {
+ return mFileName + " : " + mSize + ", id:" + mId;
+ }
+ }
}
diff --git a/src/com/android/settings/deviceinfo/MiscFilesHandler.java b/src/com/android/settings/deviceinfo/MiscFilesHandler.java
new file mode 100644
index 000000000..15951c959
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/MiscFilesHandler.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2010 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.deviceinfo;
+
+import com.android.settings.R;
+import com.android.settings.deviceinfo.MemoryMeasurement.FileInfo;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ListView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class handles the selection and removal of Misc files.
+ */
+public class MiscFilesHandler extends ListActivity {
+ private static final String TAG = "MemorySettings";
+ private String mNumSelectedFormat;
+ private String mNumBytesSelectedFormat;
+ private MemoryMearurementAdapter mAdapter;
+ private LayoutInflater mInflater;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setFinishOnTouchOutside(true);
+ setTitle(R.string.misc_files);
+ mNumSelectedFormat = getString(R.string.misc_files_selected_count);
+ mNumBytesSelectedFormat = getString(R.string.misc_files_selected_count_bytes);
+ mAdapter = new MemoryMearurementAdapter(this);
+ mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ setContentView(R.layout.settings_storage_miscfiles_list);
+ ListView lv = getListView();
+ lv.setItemsCanFocus(true);
+ lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ lv.setMultiChoiceModeListener(new ModeCallback(this));
+ setListAdapter(mAdapter);
+ }
+
+ private class ModeCallback implements ListView.MultiChoiceModeListener {
+ private int mDataCount;
+ private final Context mContext;
+
+ public ModeCallback(Context context) {
+ mContext = context;
+ mDataCount = mAdapter.getCount();
+ }
+
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ final MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.misc_files_menu, menu);
+ return true;
+ }
+
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return true;
+ }
+
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ ListView lv = getListView();
+ switch (item.getItemId()) {
+ case R.id.action_delete:
+ // delete the files selected
+ SparseBooleanArray checkedItems = lv.getCheckedItemPositions();
+ int checkedCount = getListView().getCheckedItemCount();
+ if (checkedCount > mDataCount) {
+ throw new IllegalStateException("checked item counts do not match. " +
+ "checkedCount: " + checkedCount + ", dataSize: " + mDataCount);
+ }
+ if (mDataCount > 0) {
+ ArrayList<Object> toRemove = new ArrayList<Object>();
+ for (int i = 0; i < mDataCount; i++) {
+ if (!checkedItems.get(i)) {
+ //item not selected
+ continue;
+ }
+ if (MemoryMeasurement.LOGV) {
+ Log.i(TAG, "deleting: " + mAdapter.getItem(i));
+ }
+ // delete the file
+ File file = new File(mAdapter.getItem(i).mFileName);
+ if (file.isDirectory()) {
+ deleteDir(file);
+ } else {
+ file.delete();
+ }
+ toRemove.add(mAdapter.getItem(i));
+ }
+ mAdapter.removeAll(toRemove);
+ mAdapter.notifyDataSetChanged();
+ mDataCount = mAdapter.getCount();
+ }
+ mode.finish();
+ break;
+
+ case R.id.action_select_all:
+ // check ALL items
+ for (int i = 0; i < mDataCount; i++) {
+ lv.setItemChecked(i, true);
+ }
+ // update the title and subtitle with number selected and numberBytes selected
+ onItemCheckedStateChanged(mode, 1, 0, true);
+ break;
+ }
+ return true;
+ }
+
+ // Deletes all files and subdirectories under given dir.
+ // Returns true if all deletions were successful.
+ // If a deletion fails, the method stops attempting to delete and returns false.
+ private boolean deleteDir(File dir) {
+ if (dir.isDirectory()) {
+ String[] children = dir.list();
+ for (int i=0; i < children.length; i++) {
+ boolean success = deleteDir(new File(dir, children[i]));
+ if (!success) {
+ return false;
+ }
+ }
+ }
+ // The directory is now empty so delete it
+ return dir.delete();
+ }
+
+ public void onDestroyActionMode(ActionMode mode) {
+ }
+
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
+ boolean checked) {
+ ListView lv = getListView();
+ int numChecked = lv.getCheckedItemCount();
+ mode.setTitle(String.format(mNumSelectedFormat, numChecked, mAdapter.getCount()));
+
+ // total the sizes of all items selected so far
+ SparseBooleanArray checkedItems = lv.getCheckedItemPositions();
+ long selectedDataSize = 0;
+ if (numChecked > 0) {
+ for (int i = 0; i < mDataCount; i++) {
+ if (checkedItems.get(i)) {
+ // item is checked
+ selectedDataSize += mAdapter.getItem(i).mSize;
+ }
+ }
+ }
+ mode.setSubtitle(String.format(mNumBytesSelectedFormat,
+ Formatter.formatFileSize(mContext, selectedDataSize),
+ Formatter.formatFileSize(mContext, mAdapter.getDataSize())));
+ }
+ }
+
+ public class MemoryMearurementAdapter extends BaseAdapter {
+ private ArrayList<MemoryMeasurement.FileInfo> mData = null;
+ private long mDataSize = 0;
+ private Context mContext;
+
+ public MemoryMearurementAdapter(Context context) {
+ mContext = context;
+ MemoryMeasurement mMeasurement = MemoryMeasurement.getInstance(context);
+ mData = (ArrayList<MemoryMeasurement.FileInfo>)mMeasurement.mFileInfoForMisc;
+ if (mData != null) {
+ for (MemoryMeasurement.FileInfo info : mData) {
+ mDataSize += info.mSize;
+ }
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return (mData == null) ? 0 : mData.size();
+ }
+
+ @Override
+ public MemoryMeasurement.FileInfo getItem(int position) {
+ if (mData == null || mData.size() <= position) {
+ return null;
+ }
+ return mData.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (mData == null || mData.size() <= position) {
+ return 0;
+ }
+ return mData.get(position).mId;
+ }
+ public void removeAll(List<Object> objs) {
+ if (mData == null) {
+ return;
+ }
+ for (Object o : objs) {
+ mData.remove(o);
+ mDataSize -= ((MemoryMeasurement.FileInfo) o).mSize;
+ }
+ }
+
+ public long getDataSize() {
+ return mDataSize;
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ super.notifyDataSetChanged();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final FileItemInfoLayout view = (convertView == null) ?
+ (FileItemInfoLayout) mInflater.inflate(R.layout.settings_storage_miscfiles,
+ parent, false) : (FileItemInfoLayout) convertView;
+ FileInfo item = getItem(position);
+ view.setFileName(item.mFileName);
+ view.setFileSize(Formatter.formatFileSize(mContext, item.mSize));
+ final ListView listView = (ListView) parent;
+ final int listPosition = position;
+ view.getCheckBox().setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ listView.setItemChecked(listPosition, isChecked);
+ }
+
+ });
+ view.setOnLongClickListener(new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (listView.getCheckedItemCount() > 0) {
+ return false;
+ }
+ listView.setItemChecked(listPosition, !view.isChecked());
+ return true;
+ }
+ });
+ view.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (listView.getCheckedItemCount() > 0) {
+ listView.setItemChecked(listPosition, !view.isChecked());
+ }
+ }
+ });
+ return view;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/settings/deviceinfo/Status.java b/src/com/android/settings/deviceinfo/Status.java
index 9e3b47c30..999c765f7 100644
--- a/src/com/android/settings/deviceinfo/Status.java
+++ b/src/com/android/settings/deviceinfo/Status.java
@@ -409,7 +409,7 @@ public class Status extends PreferenceActivity {
if (-1 == signalDbm) signalDbm = 0;
- int signalAsu = mPhoneStateReceiver.getSignalStrength();
+ int signalAsu = mPhoneStateReceiver.getSignalStrengthLevelAsu();
if (-1 == signalAsu) signalAsu = 0;
diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
index fa626faa6..fc903eb33 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java
@@ -42,6 +42,7 @@ import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
+import android.telephony.SignalStrength;
import android.util.Log;
import android.util.SparseArray;
import android.view.Menu;
@@ -572,7 +573,7 @@ public class PowerUsageSummary extends PreferenceFragment implements Runnable {
private void addRadioUsage(long uSecNow) {
double power = 0;
- final int BINS = BatteryStats.NUM_SIGNAL_STRENGTH_BINS;
+ final int BINS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
long signalTimeMs = 0;
for (int i = 0; i < BINS; i++) {
long strengthTimeMs = mStats.getPhoneSignalStrengthTime(i, uSecNow, mStatsType) / 1000;
diff --git a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java
index 80030418a..362fbb5bd 100644
--- a/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java
+++ b/src/com/android/settings/inputmethod/InputMethodAndSubtypeUtil.java
@@ -159,7 +159,7 @@ public class InputMethodAndSubtypeUtil {
HashSet<String> disabledSystemIMEs = getDisabledSystemIMEs(resolver);
final boolean onlyOneIME = inputMethodInfos.size() == 1;
- boolean existsSelectedSubtype = false;
+ boolean needsToResetSelectedSubtype = false;
for (InputMethodInfo imi : inputMethodInfos) {
final String imiId = imi.getId();
Preference pref = context.findPreference(imiId);
@@ -178,7 +178,7 @@ public class InputMethodAndSubtypeUtil {
}
HashSet<String> subtypesSet = enabledIMEAndSubtypesMap.get(imiId);
- boolean subtypeCleared = false;
+ boolean subtypePrefFound = false;
final int subtypeCount = imi.getSubtypeCount();
for (int i = 0; i < subtypeCount; ++i) {
InputMethodSubtype subtype = imi.getSubtypeAt(i);
@@ -187,17 +187,21 @@ public class InputMethodAndSubtypeUtil {
imiId + subtypeHashCodeStr);
// In the Configure input method screen which does not have subtype preferences.
if (subtypePref == null) continue;
- // Once subtype checkbox is found, subtypeSet needs to be cleared.
- // Because of system change, hashCode value could have been changed.
- if (!subtypeCleared) {
+ if (!subtypePrefFound) {
+ // Once subtype checkbox is found, subtypeSet needs to be cleared.
+ // Because of system change, hashCode value could have been changed.
subtypesSet.clear();
- subtypeCleared = true;
+ // If selected subtype preference is disabled, needs to reset.
+ needsToResetSelectedSubtype = true;
+ subtypePrefFound = true;
}
if (subtypePref.isChecked()) {
subtypesSet.add(subtypeHashCodeStr);
if (isCurrentInputMethod) {
if (selectedInputMethodSubtype == subtype.hashCode()) {
- existsSelectedSubtype = true;
+ // Selected subtype is still enabled, there is no need to reset
+ // selected subtype.
+ needsToResetSelectedSubtype = false;
}
}
} else {
@@ -241,12 +245,14 @@ public class InputMethodAndSubtypeUtil {
Log.d(TAG, "--- Save disable system inputmethod settings. :"
+ disabledSysImesBuilder.toString());
Log.d(TAG, "--- Save default inputmethod settings. :" + currentInputMethodId);
+ Log.d(TAG, "--- Needs to reset the selected subtype :" + needsToResetSelectedSubtype);
+ Log.d(TAG, "--- Subtype is selected :" + isInputMethodSubtypeSelected(resolver));
}
// Redefines SelectedSubtype when all subtypes are unchecked or there is no subtype
// selected. And if the selected subtype of the current input method was disabled,
// We should reset the selected input method's subtype.
- if (!existsSelectedSubtype || !isInputMethodSubtypeSelected(resolver)) {
+ if (needsToResetSelectedSubtype || !isInputMethodSubtypeSelected(resolver)) {
if (DEBUG) {
Log.d(TAG, "--- Reset inputmethod subtype because it's not defined.");
}
diff --git a/src/com/android/settings/inputmethod/InputMethodConfig.java b/src/com/android/settings/inputmethod/InputMethodConfig.java
index f2bdafd47..2cfe35d5f 100644
--- a/src/com/android/settings/inputmethod/InputMethodConfig.java
+++ b/src/com/android/settings/inputmethod/InputMethodConfig.java
@@ -104,31 +104,28 @@ public class InputMethodConfig extends SettingsPreferenceFragment {
private void showSecurityWarnDialog(InputMethodInfo imi, final CheckBoxPreference chkPref,
final String imiId) {
- if (mDialog == null) {
- mDialog = (new AlertDialog.Builder(getActivity()))
- .setTitle(android.R.string.dialog_alert_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setCancelable(true)
- .setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- chkPref.setChecked(true);
- for (Preference pref: mInputMethodPrefsMap.get(imiId)) {
- pref.setEnabled(true);
- }
- }
- })
- .setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- }
- })
- .create();
- } else {
- if (mDialog.isShowing()) {
- mDialog.dismiss();
- }
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
}
+ mDialog = (new AlertDialog.Builder(getActivity()))
+ .setTitle(android.R.string.dialog_alert_title)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setCancelable(true)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ chkPref.setChecked(true);
+ for (Preference pref: mInputMethodPrefsMap.get(imiId)) {
+ pref.setEnabled(true);
+ }
+ }
+ })
+ .setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ }
+ })
+ .create();
mDialog.setMessage(getResources().getString(R.string.ime_security_warning,
imi.getServiceInfo().applicationInfo.loadLabel(getPackageManager())));
mDialog.show();
diff --git a/src/com/android/settings/vpn/VpnEditor.java b/src/com/android/settings/vpn/VpnEditor.java
index f4f5828ea..d36279397 100644
--- a/src/com/android/settings/vpn/VpnEditor.java
+++ b/src/com/android/settings/vpn/VpnEditor.java
@@ -22,6 +22,7 @@ import com.android.settings.SettingsPreferenceFragment;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.vpn.L2tpIpsecProfile;
@@ -152,17 +153,15 @@ public class VpnEditor extends SettingsPreferenceFragment {
}*/
private void initViewFor(VpnProfile profile) {
- setTitle(profile);
mProfileEditor.loadPreferencesTo(getPreferenceScreen());
}
- private void setTitle(VpnProfile profile) {
- final Activity activity = getActivity();
- String formatString = mAddingProfile
- ? activity.getString(R.string.vpn_edit_title_add)
- : activity.getString(R.string.vpn_edit_title_edit);
- activity.setTitle(String.format(formatString,
- profile.getType().getDisplayName()));
+ /* package */static String getTitle(Context context, VpnProfile profile, boolean adding) {
+ String formatString = adding
+ ? context.getString(R.string.vpn_edit_title_add)
+ : context.getString(R.string.vpn_edit_title_edit);
+ return String.format(formatString,
+ profile.getType().getDisplayName());
}
/**
diff --git a/src/com/android/settings/vpn/VpnSettings.java b/src/com/android/settings/vpn/VpnSettings.java
index 539a51ec5..e62a5ee09 100644
--- a/src/com/android/settings/vpn/VpnSettings.java
+++ b/src/com/android/settings/vpn/VpnSettings.java
@@ -683,7 +683,7 @@ public class VpnSettings extends SettingsPreferenceFragment
}
private void startVpnTypeSelection() {
- if (getActivity() == null) return;
+ if ((getActivity() == null) || isRemoving()) return;
((PreferenceActivity) getActivity()).startPreferencePanel(
VpnTypeSelection.class.getCanonicalName(), null, R.string.vpn_type_title, null,
@@ -738,14 +738,14 @@ public class VpnSettings extends SettingsPreferenceFragment
}
private void startVpnEditor(final VpnProfile profile, boolean add) {
- if (getActivity() == null) return;
+ if ((getActivity() == null) || isRemoving()) return;
Bundle args = new Bundle();
args.putParcelable(KEY_VPN_PROFILE, profile);
// TODO: Show different titles for add and edit.
((PreferenceActivity)getActivity()).startPreferencePanel(
VpnEditor.class.getCanonicalName(), args,
- add ? R.string.vpn_details_title : R.string.vpn_details_title, null,
+ 0, VpnEditor.getTitle(getActivity(), profile, add),
this, REQUEST_ADD_OR_EDIT_PROFILE);
}
diff --git a/src/com/android/settings/widget/SettingsAppWidgetProvider.java b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
index 8f17e05e6..ba2b61541 100644
--- a/src/com/android/settings/widget/SettingsAppWidgetProvider.java
+++ b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
@@ -38,6 +38,7 @@ import android.provider.Settings;
import android.util.Log;
import android.widget.RemoteViews;
import com.android.settings.R;
+import com.android.settings.bluetooth.LocalBluetoothAdapter;
import com.android.settings.bluetooth.LocalBluetoothManager;
/**
@@ -50,7 +51,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
new ComponentName("com.android.settings",
"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_BRIGHTNESS = 1;
@@ -411,18 +412,19 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
@Override
public int getActualState(Context context) {
- if (sLocalBluetoothManager == null) {
- sLocalBluetoothManager = LocalBluetoothManager.getInstance(context);
- if (sLocalBluetoothManager == null) {
+ if (sLocalBluetoothAdapter == null) {
+ LocalBluetoothManager manager = LocalBluetoothManager.getInstance(context);
+ if (manager == null) {
return STATE_UNKNOWN; // On emulator?
}
+ sLocalBluetoothAdapter = manager.getBluetoothAdapter();
}
- return bluetoothStateToFiveState(sLocalBluetoothManager.getBluetoothState());
+ return bluetoothStateToFiveState(sLocalBluetoothAdapter.getBluetoothState());
}
@Override
protected void requestStateChange(Context context, final boolean desiredState) {
- if (sLocalBluetoothManager == null) {
+ if (sLocalBluetoothAdapter == null) {
Log.d(TAG, "No LocalBluetoothManager");
return;
}
@@ -433,7 +435,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... args) {
- sLocalBluetoothManager.setBluetoothEnabled(desiredState);
+ sLocalBluetoothAdapter.setBluetoothEnabled(desiredState);
return null;
}
}.execute();
@@ -584,7 +586,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// Update each requested appWidgetId
- RemoteViews view = buildUpdate(context, -1);
+ RemoteViews view = buildUpdate(context);
for (int i = 0; i < appWidgetIds.length; i++) {
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.
*/
- static RemoteViews buildUpdate(Context context, int appWidgetId) {
+ static RemoteViews buildUpdate(Context context) {
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.widget);
- views.setOnClickPendingIntent(R.id.btn_wifi, getLaunchPendingIntent(context, appWidgetId,
+ views.setOnClickPendingIntent(R.id.btn_wifi, getLaunchPendingIntent(context,
BUTTON_WIFI));
views.setOnClickPendingIntent(R.id.btn_brightness,
getLaunchPendingIntent(context,
- appWidgetId, BUTTON_BRIGHTNESS));
+ BUTTON_BRIGHTNESS));
views.setOnClickPendingIntent(R.id.btn_sync,
getLaunchPendingIntent(context,
- appWidgetId, BUTTON_SYNC));
+ BUTTON_SYNC));
views.setOnClickPendingIntent(R.id.btn_gps,
- getLaunchPendingIntent(context, appWidgetId, BUTTON_GPS));
+ getLaunchPendingIntent(context, BUTTON_GPS));
views.setOnClickPendingIntent(R.id.btn_bluetooth,
getLaunchPendingIntent(context,
- appWidgetId, BUTTON_BLUETOOTH));
+ BUTTON_BLUETOOTH));
updateButtons(views, context);
return views;
@@ -640,7 +642,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
* @param 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
final AppWidgetManager gm = AppWidgetManager.getInstance(context);
gm.updateAppWidget(THIS_APPWIDGET, views);
@@ -680,10 +682,9 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
* Creates PendingIntent to notify the widget of a button click.
*
* @param context
- * @param appWidgetId
* @return
*/
- private static PendingIntent getLaunchPendingIntent(Context context, int appWidgetId,
+ private static PendingIntent getLaunchPendingIntent(Context context,
int buttonId) {
Intent launchIntent = new Intent();
launchIntent.setClass(context, SettingsAppWidgetProvider.class);
diff --git a/src/com/android/settings/wifi/WifiApDialog.java b/src/com/android/settings/wifi/WifiApDialog.java
index fde6efc69..29c1a5df2 100644
--- a/src/com/android/settings/wifi/WifiApDialog.java
+++ b/src/com/android/settings/wifi/WifiApDialog.java
@@ -46,12 +46,13 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener,
private final DialogInterface.OnClickListener mListener;
- private static final int OPEN_INDEX = 0;
- private static final int WPA_INDEX = 1;
+ static final int OPEN_INDEX = 0;
+ static final int WPA_INDEX = 1;
+ static final int WPA2_INDEX = 2;
private View mView;
private TextView mSsid;
- private int mSecurityType = AccessPoint.SECURITY_NONE;
+ private int mSecurityTypeIndex = OPEN_INDEX;
private EditText mPassword;
WifiConfiguration mWifiConfig;
@@ -61,8 +62,18 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener,
super(context);
mListener = listener;
mWifiConfig = wifiConfig;
- if (wifiConfig != null)
- mSecurityType = AccessPoint.getSecurity(wifiConfig);
+ if (wifiConfig != null) {
+ mSecurityTypeIndex = getSecurityTypeIndex(wifiConfig);
+ }
+ }
+
+ public static int getSecurityTypeIndex(WifiConfiguration wifiConfig) {
+ if (wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
+ return WPA_INDEX;
+ } else if (wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
+ return WPA2_INDEX;
+ }
+ return OPEN_INDEX;
}
public WifiConfiguration getConfig() {
@@ -77,12 +88,12 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener,
*/
config.SSID = mSsid.getText().toString();
- switch (mSecurityType) {
- case AccessPoint.SECURITY_NONE:
+ switch (mSecurityTypeIndex) {
+ case OPEN_INDEX:
config.allowedKeyManagement.set(KeyMgmt.NONE);
return config;
- case AccessPoint.SECURITY_PSK:
+ case WPA_INDEX:
config.allowedKeyManagement.set(KeyMgmt.WPA_PSK);
config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
if (mPassword.length() != 0) {
@@ -90,6 +101,15 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener,
config.preSharedKey = password;
}
return config;
+
+ case WPA2_INDEX:
+ config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK);
+ config.allowedAuthAlgorithms.set(AuthAlgorithm.OPEN);
+ if (mPassword.length() != 0) {
+ String password = mPassword.getText().toString();
+ config.preSharedKey = password;
+ }
+ return config;
}
return null;
}
@@ -116,15 +136,10 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener,
if (mWifiConfig != null) {
mSsid.setText(mWifiConfig.SSID);
- switch (mSecurityType) {
- case AccessPoint.SECURITY_NONE:
- mSecurity.setSelection(OPEN_INDEX);
- break;
- case AccessPoint.SECURITY_PSK:
- String str = mWifiConfig.preSharedKey;
- mPassword.setText(str);
- mSecurity.setSelection(WPA_INDEX);
- break;
+ mSecurity.setSelection(mSecurityTypeIndex);
+ if (mSecurityTypeIndex == WPA_INDEX ||
+ mSecurityTypeIndex == WPA2_INDEX) {
+ mPassword.setText(mWifiConfig.preSharedKey);
}
}
@@ -141,7 +156,8 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener,
private void validate() {
if ((mSsid != null && mSsid.length() == 0) ||
- (mSecurityType == AccessPoint.SECURITY_PSK && mPassword.length() < 8)) {
+ (((mSecurityTypeIndex == WPA_INDEX) || (mSecurityTypeIndex == WPA2_INDEX))&&
+ mPassword.length() < 8)) {
getButton(BUTTON_SUBMIT).setEnabled(false);
} else {
getButton(BUTTON_SUBMIT).setEnabled(true);
@@ -167,10 +183,7 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener,
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- if(position == OPEN_INDEX)
- mSecurityType = AccessPoint.SECURITY_NONE;
- else
- mSecurityType = AccessPoint.SECURITY_PSK;
+ mSecurityTypeIndex = position;
showSecurityFields();
validate();
}
@@ -180,7 +193,7 @@ class WifiApDialog extends AlertDialog implements View.OnClickListener,
}
private void showSecurityFields() {
- if (mSecurityType == AccessPoint.SECURITY_NONE) {
+ if (mSecurityTypeIndex == OPEN_INDEX) {
mView.findViewById(R.id.fields).setVisibility(View.GONE);
return;
}
diff --git a/src/com/android/settings/wifi/WifiApEnabler.java b/src/com/android/settings/wifi/WifiApEnabler.java
index e907cf70e..982e44e3c 100644
--- a/src/com/android/settings/wifi/WifiApEnabler.java
+++ b/src/com/android/settings/wifi/WifiApEnabler.java
@@ -66,6 +66,8 @@ public class WifiApEnabler implements Preference.OnPreferenceChangeListener {
ArrayList<String> errored = intent.getStringArrayListExtra(
ConnectivityManager.EXTRA_ERRORED_TETHER);
updateTetherState(available.toArray(), active.toArray(), errored.toArray());
+ } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
+ enableWifiCheckBox();
}
}
@@ -84,6 +86,7 @@ public class WifiApEnabler implements Preference.OnPreferenceChangeListener {
mIntentFilter = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
mIntentFilter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+ mIntentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
}
public void resume() {
@@ -103,6 +106,7 @@ public class WifiApEnabler implements Preference.OnPreferenceChangeListener {
if(!isAirplaneMode) {
mCheckBox.setEnabled(true);
} else {
+ mCheckBox.setSummary(mOriginalSummary);
mCheckBox.setEnabled(false);
}
}
diff --git a/src/com/android/settings/wifi/WifiApSettings.java b/src/com/android/settings/wifi/WifiApSettings.java
index 7336c6c5e..ddc3c5c14 100644
--- a/src/com/android/settings/wifi/WifiApSettings.java
+++ b/src/com/android/settings/wifi/WifiApSettings.java
@@ -41,9 +41,6 @@ public class WifiApSettings extends SettingsPreferenceFragment
private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
private static final int CONFIG_SUBTEXT = R.string.wifi_tether_configure_subtext;
- private static final int OPEN_INDEX = 0;
- private static final int WPA_INDEX = 1;
-
private static final int DIALOG_AP_SETTINGS = 1;
private String[] mSecurityType;
@@ -81,12 +78,12 @@ public class WifiApSettings extends SettingsPreferenceFragment
final String s = activity.getString(
com.android.internal.R.string.wifi_tether_configure_ssid_default);
mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT),
- s, mSecurityType[OPEN_INDEX]));
+ s, mSecurityType[WifiApDialog.OPEN_INDEX]));
} else {
+ int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT),
mWifiConfig.SSID,
- mWifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
- mSecurityType[WPA_INDEX] : mSecurityType[OPEN_INDEX]));
+ mSecurityType[index]));
}
}
@@ -138,10 +135,10 @@ public class WifiApSettings extends SettingsPreferenceFragment
} else {
mWifiManager.setWifiApConfiguration(mWifiConfig);
}
+ int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
mCreateNetwork.setSummary(String.format(getActivity().getString(CONFIG_SUBTEXT),
mWifiConfig.SSID,
- mWifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
- mSecurityType[WPA_INDEX] : mSecurityType[OPEN_INDEX]));
+ mSecurityType[index]));
}
}
}
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 750e168b2..7ab647e11 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -159,7 +159,7 @@ public class WifiConfigController implements TextWatcher,
mSsidView.addTextChangedListener(this);
mSecuritySpinner = ((Spinner) mView.findViewById(R.id.security));
mSecuritySpinner.setOnItemSelectedListener(this);
- if (context instanceof WifiSettingsForSetupWizardXL) {
+ if (mInXlSetupWizard) {
// We want custom layout. The content must be same as the other cases.
mSecuritySpinner.setAdapter(
new ArrayAdapter<String>(context, R.layout.wifi_setup_custom_list_item_1,
@@ -173,9 +173,6 @@ public class WifiConfigController implements TextWatcher,
mIpSettingsSpinner = (Spinner) mView.findViewById(R.id.ip_settings);
mIpSettingsSpinner.setOnItemSelectedListener(this);
mProxySettingsSpinner = (Spinner) mView.findViewById(R.id.proxy_settings);
- // disable proxy UI until we have better app support
- mProxySettingsSpinner.setVisibility(View.GONE);
- mView.findViewById(R.id.proxy_settings_title).setVisibility(View.GONE);
mProxySettingsSpinner.setOnItemSelectedListener(this);
ViewGroup group = (ViewGroup) mView.findViewById(R.id.info);
@@ -405,46 +402,48 @@ public class WifiConfigController implements TextWatcher,
}
private int validateIpConfigFields(LinkProperties linkProperties) {
+ String ipAddr = mIpAddressView.getText().toString();
+ InetAddress inetAddr = null;
try {
- String ipAddr = mIpAddressView.getText().toString();
- if (!InetAddress.isNumeric(ipAddr)) {
- return R.string.wifi_ip_settings_invalid_ip_address;
- }
- InetAddress inetAddr = InetAddress.getByName(ipAddr);
-
- int networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText()
- .toString());
- if (networkPrefixLength < 0 || networkPrefixLength > 32) {
- return R.string.wifi_ip_settings_invalid_network_prefix_length;
- }
+ inetAddr = NetworkUtils.numericToInetAddress(ipAddr);
+ } catch (IllegalArgumentException e) {
+ return R.string.wifi_ip_settings_invalid_ip_address;
+ }
- linkProperties.addLinkAddress(new LinkAddress(inetAddr, networkPrefixLength));
+ int networkPrefixLength = -1;
+ try {
+ networkPrefixLength = Integer.parseInt(mNetworkPrefixLengthView.getText().toString());
+ } catch (NumberFormatException e) { }
+ if (networkPrefixLength < 0 || networkPrefixLength > 32) {
+ return R.string.wifi_ip_settings_invalid_network_prefix_length;
+ }
+ linkProperties.addLinkAddress(new LinkAddress(inetAddr, networkPrefixLength));
- String gateway = mGatewayView.getText().toString();
- if (!InetAddress.isNumeric(gateway)) {
- return R.string.wifi_ip_settings_invalid_gateway;
- }
- linkProperties.setGateway(InetAddress.getByName(gateway));
+ String gateway = mGatewayView.getText().toString();
+ InetAddress gatewayAddr = null;
+ try {
+ gatewayAddr = NetworkUtils.numericToInetAddress(gateway);
+ } catch (IllegalArgumentException e) {
+ return R.string.wifi_ip_settings_invalid_gateway;
+ }
+ linkProperties.addGateway(gatewayAddr);
- String dns = mDns1View.getText().toString();
- if (!InetAddress.isNumeric(dns)) {
+ String dns = mDns1View.getText().toString();
+ InetAddress dnsAddr = null;
+ try {
+ dnsAddr = NetworkUtils.numericToInetAddress(dns);
+ } catch (IllegalArgumentException e) {
+ return R.string.wifi_ip_settings_invalid_dns;
+ }
+ linkProperties.addDns(dnsAddr);
+ if (mDns2View.length() > 0) {
+ dns = mDns2View.getText().toString();
+ try {
+ dnsAddr = NetworkUtils.numericToInetAddress(dns);
+ } catch (IllegalArgumentException e) {
return R.string.wifi_ip_settings_invalid_dns;
}
- linkProperties.addDns(InetAddress.getByName(dns));
- if (mDns2View.length() > 0) {
- dns = mDns2View.getText().toString();
- if (!InetAddress.isNumeric(dns)) {
- return R.string.wifi_ip_settings_invalid_dns;
- }
- linkProperties.addDns(InetAddress.getByName(dns));
- }
-
- } catch (NumberFormatException ignore) {
- return R.string.wifi_ip_settings_invalid_network_prefix_length;
- } catch (UnknownHostException e) {
- //Should not happen since we have already validated addresses
- Log.e(TAG, "Failure to validate IP configuration " + e);
- return R.string.wifi_ip_settings_invalid_ip_address;
+ linkProperties.addDns(dnsAddr);
}
return 0;
}
@@ -485,16 +484,16 @@ public class WifiConfigController implements TextWatcher,
}
private void showSecurityFields() {
+ if (mInXlSetupWizard) {
+ // Note: XL SetupWizard won't hide "EAP" settings here.
+ if (!((WifiSettingsForSetupWizardXL)mConfigUi.getContext()).initSecurityFields(mView,
+ mAccessPointSecurity)) {
+ return;
+ }
+ }
if (mAccessPointSecurity == AccessPoint.SECURITY_NONE) {
mView.findViewById(R.id.security_fields).setVisibility(View.GONE);
return;
- } else if (mAccessPointSecurity == AccessPoint.SECURITY_EAP && mInXlSetupWizard) {
- // In SetupWizard for XLarge screen, we don't have enough space for showing
- // configurations needed for EAP. We instead disable the whole feature there and let
- // users configure those networks after the setup.
- mView.findViewById(R.id.eap_not_supported).setVisibility(View.VISIBLE);
- mView.findViewById(R.id.security_fields).setVisibility(View.GONE);
- return;
}
mView.findViewById(R.id.security_fields).setVisibility(View.VISIBLE);
@@ -594,9 +593,10 @@ public class WifiConfigController implements TextWatcher,
mNetworkPrefixLengthView.setText(Integer.toString(linkAddress
.getNetworkPrefixLength()));
}
- InetAddress gateway = linkProperties.getGateway();
- if (gateway != null) {
- mGatewayView.setText(linkProperties.getGateway().getHostAddress());
+
+ Iterator<InetAddress>gateways = linkProperties.getGateways().iterator();
+ if (gateways.hasNext()) {
+ mGatewayView.setText(gateways.next().getHostAddress());
}
Iterator<InetAddress> dnsIterator = linkProperties.getDnses().iterator();
if (dnsIterator.hasNext()) {
@@ -621,6 +621,7 @@ public class WifiConfigController implements TextWatcher,
}
if (mProxySettingsSpinner.getSelectedItemPosition() == PROXY_STATIC) {
+ mView.findViewById(R.id.proxy_warning_limited_support).setVisibility(View.VISIBLE);
mView.findViewById(R.id.proxy_fields).setVisibility(View.VISIBLE);
if (mProxyHostView == null) {
mProxyHostView = (TextView) mView.findViewById(R.id.proxy_hostname);
@@ -636,6 +637,7 @@ public class WifiConfigController implements TextWatcher,
}
}
} else {
+ mView.findViewById(R.id.proxy_warning_limited_support).setVisibility(View.GONE);
mView.findViewById(R.id.proxy_fields).setVisibility(View.GONE);
}
}
diff --git a/src/com/android/settings/wifi/WifiSettings.java b/src/com/android/settings/wifi/WifiSettings.java
index 5a2bf45d5..b62142680 100644
--- a/src/com/android/settings/wifi/WifiSettings.java
+++ b/src/com/android/settings/wifi/WifiSettings.java
@@ -18,11 +18,6 @@ package com.android.settings.wifi;
import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
-import com.android.settings.ProgressCategoryBase;
-import com.android.settings.R;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.Utils;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
@@ -53,6 +48,7 @@ import android.provider.Settings.Secure;
import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
+import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -64,6 +60,12 @@ import android.view.ContextMenu.ContextMenuInfo;
import android.widget.Toast;
import android.widget.AdapterView.AdapterContextMenuInfo;
+import com.android.internal.util.AsyncChannel;
+import com.android.settings.ProgressCategoryBase;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.Utils;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -82,6 +84,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public class WifiSettings extends SettingsPreferenceFragment
implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener {
+ private static final String TAG = "WifiSettings";
private static final int MENU_ID_SCAN = Menu.FIRST;
private static final int MENU_ID_ADVANCED = Menu.FIRST + 1;
private static final int MENU_ID_CONNECT = Menu.FIRST + 2;
@@ -168,6 +171,7 @@ public class WifiSettings extends SettingsPreferenceFragment
// this method.
mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ mWifiManager.asyncConnect(getActivity(), new WifiServiceHandler());
final Activity activity = getActivity();
final Intent intent = activity.getIntent();
@@ -238,6 +242,7 @@ public class WifiSettings extends SettingsPreferenceFragment
if (mWifiEnabler != null) {
mWifiEnabler.resume();
}
+
getActivity().registerReceiver(mReceiver, mFilter);
if (mKeyStoreNetworkId != INVALID_NETWORK_ID &&
KeyStore.getInstance().test() == KeyStore.NO_ERROR) {
@@ -594,6 +599,50 @@ public class WifiSettings extends SettingsPreferenceFragment
}
}
+ private class WifiServiceHandler extends Handler {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ //AsyncChannel in msg.obj
+ } else {
+ //AsyncChannel set up failure, ignore
+ Log.e(TAG, "Failed to establish AsyncChannel connection");
+ }
+ break;
+ case WifiManager.CMD_WPS_COMPLETED:
+ WpsResult result = (WpsResult) msg.obj;
+ if (result == null) break;
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity())
+ .setTitle(R.string.wifi_wps_setup_title)
+ .setPositiveButton(android.R.string.ok, null);
+ switch (result.status) {
+ case FAILURE:
+ dialog.setMessage(R.string.wifi_wps_failed);
+ dialog.show();
+ break;
+ case IN_PROGRESS:
+ dialog.setMessage(R.string.wifi_wps_in_progress);
+ dialog.show();
+ break;
+ default:
+ if (result.pin != null) {
+ dialog.setMessage(getResources().getString(
+ R.string.wifi_wps_pin_output, result.pin));
+ dialog.show();
+ }
+ break;
+ }
+ //TODO: more connectivity feedback
+ default:
+ //Ignore
+ break;
+ }
+ }
+ }
+
/**
* Renames/replaces "Next" button when appropriate. "Next" button usually exists in
* Wifi setup screens, not in usual wifi settings screen.
@@ -631,27 +680,7 @@ public class WifiSettings extends SettingsPreferenceFragment
case WifiConfigController.WPS_PBC:
case WifiConfigController.WPS_PIN_FROM_ACCESS_POINT:
case WifiConfigController.WPS_PIN_FROM_DEVICE:
- WpsResult result = mWifiManager.startWps(configController.getWpsConfig());
- AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity())
- .setTitle(R.string.wifi_wps_setup_title)
- .setPositiveButton(android.R.string.ok, null);
- switch (result.status) {
- case FAILURE:
- dialog.setMessage(R.string.wifi_wps_failed);
- dialog.show();
- break;
- case IN_PROGRESS:
- dialog.setMessage(R.string.wifi_wps_in_progress);
- dialog.show();
- break;
- default:
- if (networkSetup == WifiConfigController.WPS_PIN_FROM_DEVICE) {
- dialog.setMessage(getResources().getString(R.string.wifi_wps_pin_output,
- result.pin));
- dialog.show();
- }
- break;
- }
+ mWifiManager.startWps(configController.getWpsConfig());
break;
case WifiConfigController.MANUAL:
final WifiConfiguration config = configController.getConfig();
diff --git a/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java b/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java
index caafabc1f..e8a6a2594 100644
--- a/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java
+++ b/src/com/android/settings/wifi/WifiSettingsForSetupWizardXL.java
@@ -26,6 +26,7 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Message;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.util.Log;
@@ -35,49 +36,54 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
-import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.android.internal.util.AsyncChannel;
+
import java.util.Collection;
import java.util.EnumMap;
+import java.util.List;
/**
* WifiSetings Activity specific for SetupWizard with X-Large screen size.
*/
public class WifiSettingsForSetupWizardXL extends Activity implements OnClickListener {
private static final String TAG = "SetupWizard";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
- private static final EnumMap<DetailedState, DetailedState> stateMap =
+ private static final EnumMap<DetailedState, DetailedState> sNetworkStateMap =
new EnumMap<DetailedState, DetailedState>(DetailedState.class);
static {
- stateMap.put(DetailedState.IDLE, DetailedState.DISCONNECTED);
- stateMap.put(DetailedState.SCANNING, DetailedState.SCANNING);
- stateMap.put(DetailedState.CONNECTING, DetailedState.CONNECTING);
- stateMap.put(DetailedState.AUTHENTICATING, DetailedState.CONNECTING);
- stateMap.put(DetailedState.OBTAINING_IPADDR, DetailedState.CONNECTING);
- stateMap.put(DetailedState.CONNECTED, DetailedState.CONNECTED);
- stateMap.put(DetailedState.SUSPENDED, DetailedState.SUSPENDED); // ?
- stateMap.put(DetailedState.DISCONNECTING, DetailedState.DISCONNECTED);
- stateMap.put(DetailedState.DISCONNECTED, DetailedState.DISCONNECTED);
- stateMap.put(DetailedState.FAILED, DetailedState.FAILED);
+ sNetworkStateMap.put(DetailedState.IDLE, DetailedState.DISCONNECTED);
+ sNetworkStateMap.put(DetailedState.SCANNING, DetailedState.SCANNING);
+ sNetworkStateMap.put(DetailedState.CONNECTING, DetailedState.CONNECTING);
+ sNetworkStateMap.put(DetailedState.AUTHENTICATING, DetailedState.CONNECTING);
+ sNetworkStateMap.put(DetailedState.OBTAINING_IPADDR, DetailedState.CONNECTING);
+ sNetworkStateMap.put(DetailedState.CONNECTED, DetailedState.CONNECTED);
+ sNetworkStateMap.put(DetailedState.SUSPENDED, DetailedState.SUSPENDED); // ?
+ sNetworkStateMap.put(DetailedState.DISCONNECTING, DetailedState.DISCONNECTED);
+ sNetworkStateMap.put(DetailedState.DISCONNECTED, DetailedState.DISCONNECTED);
+ sNetworkStateMap.put(DetailedState.FAILED, DetailedState.FAILED);
}
- private WifiManager mWifiManager;
-
/**
- * Used for resizing a padding above title. Hiden when software keyboard is shown.
+ * Used with {@link Button#setTag(Object)} to remember "Connect" button is pressed in
+ * with "add network" flow.
*/
+ private static final int CONNECT_BUTTON_TAG_ADD_NETWORK = 1;
+
+ private WifiSettings mWifiSettings;
+ private WifiManager mWifiManager;
+
+ /** Used for resizing a padding above title. Hiden when software keyboard is shown. */
private View mTopPadding;
- /**
- * Used for resizing a padding inside Config UI. Hiden when software keyboard is shown.
- */
- private View mWifiConfigPadding;
+ /** Used for resizing a padding of main content. Hiden when software keyboard is shown. */
+ private View mContentPadding;
private TextView mTitleView;
/**
@@ -88,81 +94,91 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
private CharSequence mEditingTitle;
private ProgressBar mProgressBar;
- private WifiSettings mWifiSettings;
+ private View mTopDividerNoProgress;
+ /**
+ * Used for resizing a padding between WifiSettings preference and bottom bar when
+ * ProgressBar is visible as a top divider.
+ */
+ private View mBottomPadding;
private Button mAddNetworkButton;
private Button mRefreshButton;
private Button mSkipOrNextButton;
private Button mBackButton;
- private static int CONNECT_BUTTON_TAG_ADD_NETWORK = 1;
-
private Button mConnectButton;
+ /**
+ * View enclosing {@link WifiSettings}.
+ */
+ private View mWifiSettingsFragmentLayout;
private View mConnectingStatusLayout;
private TextView mConnectingStatusView;
- // true when a user already pressed "Connect" button and waiting for connection.
- // Also true when the device is already connected to a wifi network on launch.
- private boolean mAfterConnectAction;
+ /*
+ * States of current screen, which should be saved and restored when Activity is relaunched
+ * with orientation change, etc.
+ */
+ private static final int SCREEN_STATE_DISCONNECTED = 0;
+ private static final int SCREEN_STATE_EDITING = 1;
+ private static final int SCREEN_STATE_CONNECTING = 2;
+ private static final int SCREEN_STATE_CONNECTED = 3;
+
+ /** Current screen state. */
+ private int mScreenState = SCREEN_STATE_DISCONNECTED;
private WifiConfigUiForSetupWizardXL mWifiConfig;
private InputMethodManager mInputMethodManager;
- private final Handler mHandler = new Handler();
-
- private int mPreviousWpsFieldsVisibility = View.GONE;
- private int mPreviousSecurityFieldsVisibility = View.GONE;
- private int mPreviousTypeVisibility = View.GONE;
-
- private DetailedState mPreviousState = DetailedState.DISCONNECTED;
+ /**
+ * Previous network connection state reported by main Wifi module.
+ *
+ * Note that we don't use original {@link DetailedState} object but simplified one translated
+ * using sNetworkStateMap.
+ */
+ private DetailedState mPreviousNetworkState = DetailedState.DISCONNECTED;
private int mBackgroundId = R.drawable.setups_bg_default;
- // At first, we set "Skip" button disabled so that users won't press it soon after the screen
- // migration. The button is enabled after the wifi module returns some result
- // (a list of available network, etc.) One possible problem is that the notification from the
- // wifi module may be delayed and users may be stuck here, without any other way to exit this
- // screen.
- // To let users exit this Activity, we enable the button after waiting for a moment.
- private final int DELAYED_SKIP_ENABLE_TIME = 10000; // Unit: millis
- private final Runnable mSkipButtonEnabler = new Runnable() {
- @Override
- public void run() {
- if (DEBUG) Log.d(TAG, "Delayed skip enabler starts running.");
- mSkipOrNextButton.setEnabled(true);
- }
- };
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.wifi_settings_for_setup_wizard_xl);
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
-
mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
// There's no button here enabling wifi network, so we need to enable it without
// users' request.
mWifiManager.setWifiEnabled(true);
+ mWifiManager.asyncConnect(this, new WifiServiceHandler());
mWifiSettings =
(WifiSettings)getFragmentManager().findFragmentById(R.id.wifi_setup_fragment);
mInputMethodManager = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
- setup();
+
+ initViews();
+
+ // At first, Wifi module doesn't return SCANNING state (it's too early), so we manually
+ // show it.
+ showScanningProgressBar();
}
- public void setup() {
- final View layoutRoot = findViewById(R.id.layout_root);
- layoutRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_BACK);
+ private void initViews() {
+ if (getIntent().getBooleanExtra("firstRun", false)) {
+ final View layoutRoot = findViewById(R.id.layout_root);
+ layoutRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_BACK);
+ }
mTitleView = (TextView)findViewById(R.id.wifi_setup_title);
mProgressBar = (ProgressBar)findViewById(R.id.scanning_progress_bar);
mProgressBar.setMax(2);
+ mTopDividerNoProgress = findViewById(R.id.top_divider_no_progress);
+ mBottomPadding = findViewById(R.id.bottom_padding);
+ mProgressBar.setVisibility(View.VISIBLE);
mProgressBar.setIndeterminate(true);
+ mTopDividerNoProgress.setVisibility(View.GONE);
mAddNetworkButton = (Button)findViewById(R.id.wifi_setup_add_network);
mAddNetworkButton.setOnClickListener(this);
@@ -176,27 +192,40 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
mBackButton.setOnClickListener(this);
mTopPadding = findViewById(R.id.top_padding);
- mWifiConfigPadding = findViewById(R.id.wifi_config_padding);
+ mContentPadding = findViewById(R.id.content_padding);
+ mWifiSettingsFragmentLayout = findViewById(R.id.wifi_settings_fragment_layout);
mConnectingStatusLayout = findViewById(R.id.connecting_status_layout);
mConnectingStatusView = (TextView) findViewById(R.id.connecting_status);
+ }
- // At first, Wifi module doesn't return SCANNING state (it's too early), so we manually
- // show it.
- showScanningStatus();
- mHandler.postDelayed(mSkipButtonEnabler, DELAYED_SKIP_ENABLE_TIME);
+ private class WifiServiceHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ //AsyncChannel in msg.obj
+ } else {
+ //AsyncChannel set up failure, ignore
+ Log.e(TAG, "Failed to establish AsyncChannel connection");
+ }
+ break;
+ default:
+ //Ignore
+ break;
+ }
+ }
}
- private void restoreFirstButtonVisibilityState() {
+ private void restoreFirstVisibilityState() {
showDefaultTitle();
- // TODO: uncomment this when the layout for it is ready. Note that we also have to remove
- // android:visibility="gone" in xml then.
- // mAddNetworkButton.setVisibility(View.VISIBLE);
+ mAddNetworkButton.setVisibility(View.VISIBLE);
mRefreshButton.setVisibility(View.VISIBLE);
mSkipOrNextButton.setVisibility(View.VISIBLE);
mConnectButton.setVisibility(View.GONE);
mBackButton.setVisibility(View.GONE);
- setPaddingVisibility(View.VISIBLE, View.GONE);
+ setPaddingVisibility(View.VISIBLE);
}
@Override
@@ -215,8 +244,8 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
// any access point.
mWifiManager.setWifiEnabled(false);
}
- setResult(Activity.RESULT_OK);
- finish();
+ setResult(RESULT_OK);
+ finish();
} else if (view == mConnectButton) {
if (DEBUG) Log.d(TAG, "Connect button pressed");
onConnectButtonPressed();
@@ -236,7 +265,7 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
// Called from WifiSettings
/* package */ void updateConnectionState(DetailedState originalState) {
- final DetailedState state = stateMap.get(originalState);
+ final DetailedState state = sNetworkStateMap.get(originalState);
if (originalState == DetailedState.FAILED) {
// We clean up the current connectivity status and let users select another network
@@ -249,70 +278,82 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
// Let users know the device is working correctly though currently there's
// no visible network on the list.
if (mWifiSettings.getAccessPointsCount() == 0) {
- mProgressBar.setIndeterminate(true);
+ showScanningState();
} else {
- // Users already connected to a network, or see available networks.
- mProgressBar.setIndeterminate(false);
+ // Users already see available networks.
+ showDisconnectedProgressBar();
+ if (mScreenState == SCREEN_STATE_DISCONNECTED) {
+ mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
+ mBottomPadding.setVisibility(View.GONE);
+ }
}
break;
}
case CONNECTING: {
- showConnectingStatus();
+ if (mScreenState == SCREEN_STATE_CONNECTING) {
+ showConnectingState();
+ }
break;
}
case CONNECTED: {
- hideSoftwareKeyboard();
- setPaddingVisibility(View.VISIBLE);
-
- // If the device is already connected to a wifi without users' "Connect" request,
- // this can be false here. We want to treat it as "after connect action".
- mAfterConnectAction = true;
-
- trySetBackground(R.drawable.setups_bg_complete);
-
- mProgressBar.setIndeterminate(false);
- mProgressBar.setProgress(2);
-
- showConnectedTitle();
- mConnectingStatusView.setText(R.string.wifi_setup_description_connected);
- mConnectButton.setVisibility(View.GONE);
- mAddNetworkButton.setVisibility(View.GONE);
- mRefreshButton.setVisibility(View.GONE);
- mBackButton.setVisibility(View.VISIBLE);
- mBackButton.setText(R.string.wifi_setup_back);
- mSkipOrNextButton.setVisibility(View.VISIBLE);
- mSkipOrNextButton.setEnabled(true);
- mHandler.removeCallbacks(mSkipButtonEnabler);
+ showConnectedState();
break;
}
default: // DISCONNECTED, FAILED
- showDisconnectedStatus(Summary.get(this, state));
+ if (mScreenState != SCREEN_STATE_CONNECTED) {
+ showDisconnectedState(Summary.get(this, state));
+ }
break;
}
- mPreviousState = state;
+ mPreviousNetworkState = state;
}
- private void showDisconnectedStatus(String stateString) {
- mProgressBar.setIndeterminate(false);
- mProgressBar.setProgress(0);
-
+ private void showDisconnectedState(String stateString) {
+ showDisconnectedProgressBar();
+ if (mScreenState == SCREEN_STATE_DISCONNECTED) {
+ mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
+ mBottomPadding.setVisibility(View.GONE);
+ }
mAddNetworkButton.setEnabled(true);
mRefreshButton.setEnabled(true);
}
- private void showConnectingStatus() {
+ private void showConnectingState() {
+ mScreenState = SCREEN_STATE_CONNECTING;
+
mBackButton.setVisibility(View.VISIBLE);
// We save this title and show it when authentication failed.
mEditingTitle = mTitleView.getText();
showConnectingTitle();
- mProgressBar.setIndeterminate(false);
- mProgressBar.setProgress(1);
+ showConnectingProgressBar();
- // We may enter "Connecting" status during editing password again (if the Wifi module
- // tries to (re)connect a network.)
- if (mAfterConnectAction) {
- setPaddingVisibility(View.VISIBLE);
- }
+ setPaddingVisibility(View.VISIBLE);
+ }
+
+ private void showConnectedState() {
+ // Once we show "connected" screen, we won't change it even when the device becomes
+ // disconnected afterwards. We keep the state unless a user explicitly cancel it
+ // (by pressing "back" button).
+ mScreenState = SCREEN_STATE_CONNECTED;
+
+ hideSoftwareKeyboard();
+ setPaddingVisibility(View.VISIBLE);
+
+ trySetBackground(R.drawable.setups_bg_complete);
+ showConnectedTitle();
+ showConnectedProgressBar();
+
+ mWifiSettingsFragmentLayout.setVisibility(View.GONE);
+ mConnectingStatusLayout.setVisibility(View.VISIBLE);
+
+ mConnectingStatusView.setText(R.string.wifi_setup_description_connected);
+ mConnectButton.setVisibility(View.GONE);
+ mAddNetworkButton.setVisibility(View.GONE);
+ mRefreshButton.setVisibility(View.GONE);
+ mBackButton.setVisibility(View.VISIBLE);
+ mBackButton.setText(R.string.wifi_setup_back);
+ mSkipOrNextButton.setVisibility(View.VISIBLE);
+ mSkipOrNextButton.setEnabled(true);
}
private void showDefaultTitle() {
@@ -363,10 +404,23 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
mTitleView.setText(getString(R.string.wifi_setup_title_connected_network, mNetworkName));
}
- private void showScanningStatus() {
- mProgressBar.setIndeterminate(true);
- mAddNetworkButton.setEnabled(false);
- mRefreshButton.setEnabled(false);
+ /**
+ * Shows top divider with ProgressBar without defining the state of the ProgressBar.
+ *
+ * @see #showScanningProgressBar()
+ * @see #showConnectedProgressBar()
+ * @see #showConnectingProgressBar()
+ */
+ private void showTopDividerWithProgressBar() {
+ mProgressBar.setVisibility(View.VISIBLE);
+ mTopDividerNoProgress.setVisibility(View.GONE);
+ mBottomPadding.setVisibility(View.GONE);
+ }
+
+ private void showScanningState() {
+ setPaddingVisibility(View.VISIBLE);
+ mWifiSettingsFragmentLayout.setVisibility(View.GONE);
+ showScanningProgressBar();
}
private void onAddNetworkButtonPressed() {
@@ -381,6 +435,8 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
* "Add network" button, meaning there's no selected access point.
*/
/* package */ void showConfigUi(AccessPoint selectedAccessPoint, boolean edit) {
+ mScreenState = SCREEN_STATE_EDITING;
+
if (selectedAccessPoint != null &&
(selectedAccessPoint.security == AccessPoint.SECURITY_WEP ||
selectedAccessPoint.security == AccessPoint.SECURITY_PSK)) {
@@ -392,23 +448,20 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
trySetBackground(R.drawable.setups_bg_default);
- // We don't want to keep scanning Wi-Fi networks during users' configuring one network.
+ // We don't want to keep scanning Wifi networks during users' configuring a network.
mWifiSettings.pauseWifiScan();
- findViewById(R.id.wifi_setup).setVisibility(View.GONE);
+ mWifiSettingsFragmentLayout.setVisibility(View.GONE);
mConnectingStatusLayout.setVisibility(View.GONE);
final ViewGroup parent = (ViewGroup)findViewById(R.id.wifi_config_ui);
parent.setVisibility(View.VISIBLE);
parent.removeAllViews();
mWifiConfig = new WifiConfigUiForSetupWizardXL(this, parent, selectedAccessPoint, edit);
- // For safety, we forget the tag once. Tag will be updated in this method when needed.
+ // Tag will be updated in this method when needed.
mConnectButton.setTag(null);
if (selectedAccessPoint == null) { // "Add network" flow
showAddNetworkTitle();
- if (mWifiConfig != null) {
- mWifiConfig.getView().findViewById(R.id.wifi_general_info).setVisibility(View.GONE);
- }
mConnectButton.setVisibility(View.VISIBLE);
mConnectButton.setTag(CONNECT_BUTTON_TAG_ADD_NETWORK);
@@ -423,8 +476,7 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
showEditingTitle();
showEditingButtonState();
if (selectedAccessPoint.security == AccessPoint.SECURITY_EAP) {
- mConnectButton.setVisibility(View.GONE);
- mBackButton.setText(R.string.wifi_setup_back);
+ onEapNetworkSelected();
} else {
mConnectButton.setVisibility(View.VISIBLE);
@@ -437,6 +489,51 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
}
}
+ /**
+ * Called before security fields are correctly set by {@link WifiConfigController}.
+ *
+ * @param view security field view
+ * @param accessPointSecurity type of security. e.g. AccessPoint.SECURITY_NONE
+ * @return true when it is ok for the caller to init security fields. false when
+ * all security fields are managed by this method, and thus the caller shouldn't touch them.
+ */
+ /* package */ boolean initSecurityFields(View view, int accessPointSecurity) {
+ // Reset all states tweaked below.
+ view.findViewById(R.id.eap_not_supported).setVisibility(View.GONE);
+ view.findViewById(R.id.eap_not_supported_for_add_network).setVisibility(View.GONE);
+ view.findViewById(R.id.ssid_text).setVisibility(View.VISIBLE);
+ view.findViewById(R.id.ssid_layout).setVisibility(View.VISIBLE);
+
+ if (accessPointSecurity == AccessPoint.SECURITY_EAP) {
+ hideSoftwareKeyboard();
+
+ // In SetupWizard for XLarge screen, we don't have enough space for showing
+ // configurations needed for EAP. We instead disable the whole feature there and let
+ // users configure those networks after the setup.
+ if (view.findViewById(R.id.type).getVisibility() == View.VISIBLE) {
+ view.findViewById(R.id.eap_not_supported_for_add_network)
+ .setVisibility(View.VISIBLE);
+ } else {
+ view.findViewById(R.id.eap_not_supported).setVisibility(View.VISIBLE);
+ }
+ view.findViewById(R.id.security_fields).setVisibility(View.GONE);
+ view.findViewById(R.id.ssid_text).setVisibility(View.GONE);
+ view.findViewById(R.id.ssid_layout).setVisibility(View.GONE);
+ onEapNetworkSelected();
+
+ // This method did init security fields by itself. The caller must not do it.
+ return false;
+ }
+
+ // Let the caller init security fields.
+ return true;
+ }
+
+ /* package */ void onEapNetworkSelected() {
+ mConnectButton.setVisibility(View.GONE);
+ mBackButton.setText(R.string.wifi_setup_back);
+ }
+
private void showEditingButtonState() {
mSkipOrNextButton.setVisibility(View.GONE);
mAddNetworkButton.setVisibility(View.GONE);
@@ -446,48 +543,29 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
// May be called when user press "connect" button in WifiDialog
/* package */ void onConnectButtonPressed() {
- mAfterConnectAction = true;
+ mScreenState = SCREEN_STATE_CONNECTING;
trySetBackground(R.drawable.setups_bg_wifi);
mWifiSettings.submit(mWifiConfig.getController());
- // updateConnectionState() isn't called soon after the user's "connect" action,
- // and the user still sees "not connected" message for a while, which looks strange.
+ // updateConnectionState() isn't called soon by the main Wifi module after the user's
+ // "connect" request, and the user still sees "not connected" message for a while, which
+ // looks strange for users though legitimate from the view of the module.
+ //
// We instead manually show "connecting" message before the system gets actual
- // "connecting" message from Wi-Fi module.
- showConnectingStatus();
+ // "connecting" message from Wifi module.
+ showConnectingState();
// Might be better to delay showing this button.
mBackButton.setVisibility(View.VISIBLE);
mBackButton.setText(R.string.wifi_setup_back);
- // We need to restore visibility status when the device failed to connect the network.
- final View wpsFieldView = findViewById(R.id.wps_fields);
- if (wpsFieldView != null) {
- mPreviousWpsFieldsVisibility = wpsFieldView.getVisibility();
- wpsFieldView.setVisibility(View.GONE);
- }
- final View securityFieldsView = findViewById(R.id.security_fields);
- if (securityFieldsView != null) {
- mPreviousSecurityFieldsVisibility = securityFieldsView.getVisibility();
- securityFieldsView.setVisibility(View.GONE);
- }
- final View typeView = findViewById(R.id.type);
- if (typeView != null) {
- mPreviousTypeVisibility = typeView.getVisibility();
- typeView.setVisibility(View.GONE);
- }
-
- // TODO: investigate whether visibility handling above is needed. Now that we hide
- // them completely when connecting, so we may not need to do so, though we probably
- // need to show software keyboard conditionaly.
final ViewGroup parent = (ViewGroup)findViewById(R.id.wifi_config_ui);
parent.setVisibility(View.GONE);
mConnectingStatusLayout.setVisibility(View.VISIBLE);
mConnectingStatusView.setText(R.string.wifi_setup_description_connecting);
- mHandler.removeCallbacks(mSkipButtonEnabler);
mSkipOrNextButton.setVisibility(View.VISIBLE);
mSkipOrNextButton.setEnabled(false);
mConnectButton.setVisibility(View.GONE);
@@ -498,29 +576,48 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
private void onBackButtonPressed() {
trySetBackground(R.drawable.setups_bg_default);
- if (mAfterConnectAction) {
+ if (mScreenState == SCREEN_STATE_CONNECTING || mScreenState == SCREEN_STATE_CONNECTED) {
if (DEBUG) Log.d(TAG, "Back button pressed after connect action.");
- mAfterConnectAction = false;
+ mScreenState = SCREEN_STATE_DISCONNECTED;
// When a user press "Back" button after pressing "Connect" button, we want to cancel
- // the "Connect" request and refresh the whole wifi status.
- restoreFirstButtonVisibilityState();
+ // the "Connect" request and refresh the whole Wifi status.
+ restoreFirstVisibilityState();
mSkipOrNextButton.setEnabled(true);
changeNextButtonState(false); // Skip
+ // Wifi list becomes empty for a moment. We show "scanning" effect to a user so that
+ // he/she won't be astonished there. This stops once the scan finishes.
+ showScanningState();
+
+ // Remembered networks may be re-used during SetupWizard, which confuse users.
+ // We force the module to forget them to reduce UX complexity
+ final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ for (WifiConfiguration config : configs) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("forgeting Wi-Fi network \"%s\" (id: %d)",
+ config.SSID, config.networkId));
+ }
+ mWifiManager.forgetNetwork(config.networkId);
+ }
+
+ mWifiSettingsFragmentLayout.setVisibility(View.GONE);
refreshAccessPoints(true);
} else { // During user's Wifi configuration.
+ mScreenState = SCREEN_STATE_DISCONNECTED;
mWifiSettings.resumeWifiScan();
- restoreFirstButtonVisibilityState();
+ restoreFirstVisibilityState();
mAddNetworkButton.setEnabled(true);
mRefreshButton.setEnabled(true);
mSkipOrNextButton.setEnabled(true);
+ mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
+ showDisconnectedProgressBar();
}
- findViewById(R.id.wifi_setup).setVisibility(View.VISIBLE);
+ setPaddingVisibility(View.VISIBLE);
mConnectingStatusLayout.setVisibility(View.GONE);
final ViewGroup parent = (ViewGroup)findViewById(R.id.wifi_config_ui);
parent.removeAllViews();
@@ -548,7 +645,11 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
// If we already show some of access points but the bar still shows "scanning" state, it
// should be stopped.
if (mProgressBar.isIndeterminate() && accessPoints.size() > 0) {
- mProgressBar.setIndeterminate(false);
+ showDisconnectedProgressBar();
+ if (mScreenState == SCREEN_STATE_DISCONNECTED) {
+ mWifiSettingsFragmentLayout.setVisibility(View.VISIBLE);
+ mBottomPadding.setVisibility(View.GONE);
+ }
mAddNetworkButton.setEnabled(true);
mRefreshButton.setEnabled(true);
}
@@ -565,9 +666,9 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
((Integer)tag == CONNECT_BUTTON_TAG_ADD_NETWORK)) {
// In "Add network" flow, we won't get DetaledState available for changing ProgressBar
// state. Instead we manually show previous status here.
- showDisconnectedStatus(Summary.get(this, mPreviousState));
+ showDisconnectedState(Summary.get(this, mPreviousNetworkState));
} else {
- showScanningStatus();
+ showScanningState();
}
if (disconnectNetwork) {
@@ -593,7 +694,8 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
* Called once when Authentication failed.
*/
private void onAuthenticationFailure() {
- mAfterConnectAction = false;
+ mScreenState = SCREEN_STATE_EDITING;
+
mSkipOrNextButton.setVisibility(View.GONE);
mConnectButton.setVisibility(View.VISIBLE);
mConnectButton.setEnabled(true);
@@ -610,56 +712,56 @@ public class WifiSettingsForSetupWizardXL extends Activity implements OnClickLis
final ViewGroup parent = (ViewGroup)findViewById(R.id.wifi_config_ui);
parent.setVisibility(View.VISIBLE);
mConnectingStatusLayout.setVisibility(View.GONE);
+ }
- // Restore View status which was tweaked on connection.
- final View wpsFieldView = findViewById(R.id.wps_fields);
- if (wpsFieldView != null) {
- wpsFieldView.setVisibility(mPreviousWpsFieldsVisibility);
- }
- final View securityFieldsView = findViewById(R.id.security_fields);
- if (securityFieldsView != null) {
- securityFieldsView.setVisibility(mPreviousSecurityFieldsVisibility);
- if (mPreviousSecurityFieldsVisibility == View.VISIBLE && mWifiConfig != null) {
- final View passwordView = findViewById(R.id.password);
- if (passwordView != null) {
- if (passwordView.isFocused()) {
- setPaddingVisibility(View.GONE);
- }
- mWifiConfig.requestFocusAndShowKeyboard(R.id.password);
- }
- }
- }
- final View typeView = findViewById(R.id.type);
- if (typeView != null) {
- typeView.setVisibility(mPreviousTypeVisibility);
- if (mPreviousTypeVisibility == View.VISIBLE && mWifiConfig != null) {
- final View ssidView = findViewById(R.id.ssid);
- if (ssidView != null) {
- if (ssidView.isFocused()) {
- setPaddingVisibility(View.GONE);
- }
- mWifiConfig.requestFocusAndShowKeyboard(R.id.ssid);
- }
- }
+ // Used by WifiConfigUiForSetupWizardXL
+ /* package */ void setPaddingVisibility(int visibility) {
+ mTopPadding.setVisibility(visibility);
+ mContentPadding.setVisibility(visibility);
+ }
+
+ private void showDisconnectedProgressBar() {
+ // The device may report DISCONNECTED during connecting to a network, at which we don't
+ // want to lose bottom padding of top divider implicitly added by ProgressBar.
+ if (mScreenState == SCREEN_STATE_DISCONNECTED) {
+ mProgressBar.setVisibility(View.GONE);
+ mProgressBar.setIndeterminate(false);
+ mTopDividerNoProgress.setVisibility(View.VISIBLE);
+ } else {
+ mProgressBar.setVisibility(View.VISIBLE);
+ mProgressBar.setIndeterminate(false);
+ mTopDividerNoProgress.setVisibility(View.GONE);
}
}
- public void setPaddingVisibility(int visibility) {
- setPaddingVisibility(visibility, visibility);
+ /**
+ * Shows top divider with ProgressBar, whose state is intermediate.
+ */
+ private void showScanningProgressBar() {
+ showTopDividerWithProgressBar();
+ mProgressBar.setIndeterminate(true);
+ }
+
+ /**
+ * Shows top divider with ProgressBar, showing "connecting" state.
+ */
+ private void showConnectingProgressBar() {
+ showTopDividerWithProgressBar();
+ mProgressBar.setIndeterminate(false);
+ mProgressBar.setProgress(1);
}
- private void setPaddingVisibility(int topPaddingVisibility, int configVisibility) {
- mTopPadding.setVisibility(topPaddingVisibility);
- mWifiConfigPadding.setVisibility(configVisibility);
+ private void showConnectedProgressBar() {
+ showTopDividerWithProgressBar();
+ mProgressBar.setIndeterminate(false);
+ mProgressBar.setProgress(2);
}
/**
- * Called when WifiManager is requested to save a network. This method sholud include
- * WifiManager#saveNetwork() call.
- *
- * Currently this method calls {@link WifiManager#connectNetwork(int)}.
+ * Called when WifiManager is requested to save a network.
*/
/* package */ void onSaveNetwork(WifiConfiguration config) {
+ // We want to both save and connect a network. connectNetwork() does both.
mWifiManager.connectNetwork(config);
}