Merge "Fix for bug #2577984 - Settings app was trying to access the TTS before the TTS was ready." into froyo
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 97fbaec..3499a42 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -466,7 +466,7 @@
<category android:name="android.intent.category.VOICE_LAUNCH" />
</intent-filter>
</activity>
-
+
<activity android:name="VoiceInputOutputSettings"
android:label="@string/voice_input_output_settings">
<intent-filter>
@@ -614,6 +614,8 @@
<intent-filter>
<action android:name="android.intent.action.DOCK_EVENT" />
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
+ <action android:name="android.bluetooth.headset.action.STATE_CHANGED" />
+ <action android:name="android.bluetooth.a2dp.action.SINK_STATE_CHANGED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
diff --git a/src/com/android/settings/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index 2ab7a50..79b081f 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -19,6 +19,7 @@
import com.android.settings.wifi.WifiApEnabler;
import android.app.AlertDialog;
+import android.app.Dialog;
import android.os.Bundle;
import android.os.SystemProperties;
import android.content.BroadcastReceiver;
@@ -50,6 +51,9 @@
private static final String WIFI_HELP_MODIFIER = "wifi_";
private static final String HELP_URL = "file:///android_asset/html/%y_%z/tethering_%xhelp.html";
+ private static final int DIALOG_TETHER_HELP = 1;
+
+ private WebView mView;
private CheckBoxPreference mUsbTether;
private CheckBoxPreference mEnableWifiAp;
@@ -89,8 +93,35 @@
getPreferenceScreen().removePreference(mWifiApSettings);
}
mWifiApEnabler = new WifiApEnabler(this, mEnableWifiAp);
+ mView = new WebView(this);
}
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ if (id == DIALOG_TETHER_HELP) {
+ Locale locale = Locale.getDefault();
+ String url = HELP_URL.replace("%y", locale.getLanguage().toLowerCase());
+ url = url.replace("%z", 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)) {
+ url = url.replace("%x", WIFI_HELP_MODIFIER);
+ } else {
+ // could assert that both wifi and usb have regexs, but the default
+ // is to use this anyway so no check is needed
+ url = url.replace("%x", "");
+ }
+ mView.loadUrl(url);
+
+ return new AlertDialog.Builder(this)
+ .setCancelable(true)
+ .setTitle(R.string.tethering_help_button_text)
+ .setView(mView)
+ .create();
+ }
+ return null;
+ }
private class TetherChangeReceiver extends BroadcastReceiver {
public void onReceive(Context content, Intent intent) {
@@ -244,27 +275,8 @@
mUsbTether.setSummary("");
}
} else if (preference == mTetherHelp) {
- Locale locale = Locale.getDefault();
- String url = HELP_URL.replace("%y", locale.getLanguage().toLowerCase());
- url = url.replace("%z", 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)) {
- url = url.replace("%x", WIFI_HELP_MODIFIER);
- } else {
- // could assert that both wifi and usb have regexs, but the default
- // is to use this anyway so no check is needed
- url = url.replace("%x", "");
- }
- WebView view = new WebView(this);
- view.loadUrl(url);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setCancelable(true);
- builder.setTitle(R.string.tethering_help_button_text);
- builder.setView(view);
- builder.show();
+ showDialog(DIALOG_TETHER_HELP);
}
return false;
}
diff --git a/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java b/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java
index 7906d79..4497480 100644
--- a/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java
+++ b/src/com/android/settings/bluetooth/CachedBluetoothDeviceManager.java
@@ -43,7 +43,6 @@
public CachedBluetoothDeviceManager(LocalBluetoothManager localManager) {
mLocalManager = localManager;
mCallbacks = localManager.getCallbacks();
- readPairedDevices();
}
private synchronized boolean readPairedDevices() {
diff --git a/src/com/android/settings/bluetooth/DockEventReceiver.java b/src/com/android/settings/bluetooth/DockEventReceiver.java
index 2d634b2..6d11972 100644
--- a/src/com/android/settings/bluetooth/DockEventReceiver.java
+++ b/src/com/android/settings/bluetooth/DockEventReceiver.java
@@ -16,12 +16,17 @@
package com.android.settings.bluetooth;
+import com.android.settings.bluetooth.LocalBluetoothProfileManager.Profile;
+
import android.app.Service;
+import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.os.PowerManager;
import android.util.Log;
@@ -75,6 +80,54 @@
if (DEBUG) Log.e(TAG, "Unknown state");
break;
}
+ } else if (BluetoothHeadset.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+ /*
+ * Reconnect to the dock if:
+ * 1) it is a dock
+ * 2) it is disconnected
+ * 3) the disconnect is initiated remotely
+ * 4) the dock is still docked (check can only be done in the Service)
+ */
+ if (device == null) {
+ if (DEBUG) Log.d(TAG, "Device is missing");
+ return;
+ }
+
+ int newState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+ BluetoothHeadset.STATE_CONNECTED);
+ if (newState != BluetoothHeadset.STATE_DISCONNECTED) return;
+
+ int source = intent.getIntExtra(BluetoothHeadset.EXTRA_DISCONNECT_INITIATOR,
+ BluetoothHeadset.LOCAL_DISCONNECT);
+ if (source != BluetoothHeadset.REMOTE_DISCONNECT) return;
+
+ // Too bad, the dock state can't be checked from a BroadcastReceiver.
+ Intent i = new Intent(intent);
+ i.setClass(context, DockService.class);
+ beginStartingService(context, i);
+
+ } else if (BluetoothA2dp.ACTION_SINK_STATE_CHANGED.equals(intent.getAction())) {
+ /*
+ * Reconnect to the dock if:
+ * 1) it is a dock
+ * 2) it is an unexpected disconnect i.e. didn't go through disconnecting state
+ * 3) the dock is still docked (check can only be done in the Service)
+ */
+ if (device == null) {
+ if (DEBUG) Log.d(TAG, "Device is missing");
+ return;
+ }
+
+ int newState = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 0);
+ int oldState = intent.getIntExtra(BluetoothA2dp.EXTRA_PREVIOUS_SINK_STATE, 0);
+ if (newState == BluetoothA2dp.STATE_DISCONNECTED &&
+ oldState != BluetoothA2dp.STATE_DISCONNECTING) {
+ // Too bad, the dock state can't be checked from a BroadcastReceiver.
+ Intent i = new Intent(intent);
+ i.setClass(context, DockService.class);
+ beginStartingService(context, i);
+ }
+
} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
int btState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
if (btState != BluetoothAdapter.STATE_TURNING_ON) {
diff --git a/src/com/android/settings/bluetooth/DockService.java b/src/com/android/settings/bluetooth/DockService.java
index 1425e23..f318987 100644
--- a/src/com/android/settings/bluetooth/DockService.java
+++ b/src/com/android/settings/bluetooth/DockService.java
@@ -87,6 +87,15 @@
private static final String SHARED_PREFERENCES_KEY_DISABLE_BT =
"disable_bt";
+ private static final String SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT =
+ "connect_retry_count";
+
+ /*
+ * If disconnected unexpectedly, reconnect up to 6 times. Each profile counts
+ * as one time so it's only 3 times for both profiles on the car dock.
+ */
+ private static final int MAX_CONNECT_RETRY = 6;
+
private static final int INVALID_STARTID = -100;
// Created in OnCreate()
@@ -161,6 +170,32 @@
return START_NOT_STICKY;
}
+ /*
+ * This assumes that the intent sender has checked that this is a dock
+ * and that the intent is for a disconnect
+ */
+ if (BluetoothHeadset.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+ BluetoothDevice disconnectedDevice = intent
+ .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+ int retryCount = getSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, 0);
+ if (retryCount < MAX_CONNECT_RETRY) {
+ setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
+ handleUnexpectedDisconnect(disconnectedDevice, Profile.HEADSET, startId);
+ }
+ return START_NOT_STICKY;
+ } else if (BluetoothA2dp.ACTION_SINK_STATE_CHANGED.equals(intent.getAction())) {
+ BluetoothDevice disconnectedDevice = intent
+ .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+ int retryCount = getSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, 0);
+ if (retryCount < MAX_CONNECT_RETRY) {
+ setSettingInt(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT, retryCount + 1);
+ handleUnexpectedDisconnect(disconnectedDevice, Profile.A2DP, startId);
+ }
+ return START_NOT_STICKY;
+ }
+
Message msg = parseIntent(intent);
if (msg == null) {
// Bad intent
@@ -169,6 +204,10 @@
return START_NOT_STICKY;
}
+ if (msg.what == MSG_TYPE_DOCKED) {
+ removeSetting(SHARED_PREFERENCES_KEY_CONNECT_RETRY_COUNT);
+ }
+
msg.arg2 = startId;
processMessage(msg);
@@ -248,10 +287,10 @@
if (DEBUG) {
Log.d(TAG, "DISABLE_BT_WHEN_UNDOCKED = "
- + getSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
+ + getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED));
}
- if (getSetting(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");
@@ -280,7 +319,7 @@
removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED);
} else {
// disable() returned an error. Persist a flag to disable BT later
- setSetting(SHARED_PREFERENCES_KEY_DISABLE_BT, true);
+ setSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT, true);
mPendingTurnOffStartId = startId;
deferFinishCall = true;
if(DEBUG) Log.d(TAG, "disable failed. try again later " + startId);
@@ -509,7 +548,7 @@
} else {
if (DEBUG) {
Log.d(TAG, "A DISABLE_BT_WHEN_UNDOCKED = "
- + getSetting(SHARED_PREFERENCES_KEY_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));
@@ -522,7 +561,7 @@
if (device != null) {
connectIfEnabled(device);
}
- } else if (getSetting(SHARED_PREFERENCES_KEY_DISABLE_BT)
+ } else if (getSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT)
&& mBtManager.getBluetoothAdapter().disable()) {
mPendingTurnOffStartId = startId;
removeSetting(SHARED_PREFERENCES_KEY_DISABLE_BT);
@@ -565,6 +604,34 @@
}
}
+ private void handleUnexpectedDisconnect(BluetoothDevice disconnectedDevice, Profile profile,
+ int startId) {
+ synchronized (this) {
+ 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.EXTRA_DOCK_STATE_UNDOCKED);
+ if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+ BluetoothDevice dockedDevice = i
+ .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+ if (dockedDevice != null && dockedDevice.equals(disconnectedDevice)) {
+ CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext,
+ mBtManager, dockedDevice);
+ cachedDevice.connect(profile);
+ }
+ }
+ }
+ }
+
+ DockEventReceiver.finishStartingService(this, startId);
+ }
+ }
+
private synchronized void connectIfEnabled(BluetoothDevice device) {
CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(mContext, mBtManager, device);
List<Profile> profiles = cachedDevice.getConnectableProfiles();
@@ -612,7 +679,8 @@
mPendingDevice = device;
mPendingStartId = startId;
if (btState != BluetoothAdapter.STATE_TURNING_ON) {
- setSetting(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED, true);
+ setSettingBool(SHARED_PREFERENCES_KEY_DISABLE_BT_WHEN_UNDOCKED,
+ true);
}
return;
}
@@ -676,16 +744,29 @@
return cachedBluetoothDevice;
}
- private boolean getSetting(String key) {
+ private boolean getSettingBool(String key) {
SharedPreferences sharedPref = getSharedPreferences(SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);
return sharedPref.getBoolean(key, false);
}
- private void setSetting(String key, boolean disableBt) {
+ 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, disableBt);
+ editor.putBoolean(key, bool);
+ editor.commit();
+ }
+
+ private void setSettingInt(String key, int value) {
+ SharedPreferences.Editor editor = getSharedPreferences(SHARED_PREFERENCES_NAME,
+ Context.MODE_PRIVATE).edit();
+ editor.putInt(key, value);
editor.commit();
}
diff --git a/src/com/android/settings/widget/SettingsAppWidgetProvider.java b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
index 872d303..939e8e3 100644
--- a/src/com/android/settings/widget/SettingsAppWidgetProvider.java
+++ b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
@@ -30,13 +30,13 @@
import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.wifi.WifiManager;
+import android.os.AsyncTask;
import android.os.IPowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
import android.widget.RemoteViews;
-import android.widget.Toast;
import com.android.settings.R;
import com.android.settings.bluetooth.LocalBluetoothManager;
@@ -129,8 +129,7 @@
mDeferredStateChangeRequestNeeded = true;
} else {
mInTransition = true;
- boolean showToast = newState; // only show Toast on the up transition
- requestStateChange(context, newState, showToast);
+ requestStateChange(context, newState);
}
}
@@ -146,7 +145,7 @@
* STATE_TURNING_OFF, STATE_UNKNOWN
*/
protected final void setCurrentState(Context context, int newState) {
- boolean wasInTransition = mInTransition;
+ final boolean wasInTransition = mInTransition;
switch (newState) {
case STATE_DISABLED:
mInTransition = false;
@@ -174,7 +173,7 @@
Log.v(TAG, "... but intended state matches, so no changes.");
} else if (mIntendedState != null) {
mInTransition = true;
- requestStateChange(context, mIntendedState, false /* no toast */);
+ requestStateChange(context, mIntendedState);
}
mDeferredStateChangeRequestNeeded = false;
}
@@ -197,6 +196,16 @@
* @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
*/
public final int getTriState(Context context) {
+ if (mInTransition) {
+ // If we know we just got a toggle request recently
+ // (which set mInTransition), don't even ask the
+ // underlying interface for its state. We know we're
+ // changing. This avoids blocking the UI thread
+ // during UI refresh post-toggle if the underlying
+ // service state accessor has coarse locking on its
+ // state (to be fixed separately).
+ return STATE_INTERMEDIATE;
+ }
switch (getActualState(context)) {
case STATE_DISABLED:
return STATE_DISABLED;
@@ -220,8 +229,7 @@
* Actually make the desired change to the underlying radio
* API.
*/
- protected abstract void requestStateChange(Context context,
- boolean desiredState, boolean withToast);
+ protected abstract void requestStateChange(Context context, boolean desiredState);
}
/**
@@ -238,26 +246,33 @@
}
@Override
- protected void requestStateChange(Context context,
- boolean desiredState, boolean withToast) {
- WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ protected void requestStateChange(Context context, final boolean desiredState) {
+ final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
if (wifiManager == null) {
Log.d(TAG, "No wifiManager.");
return;
}
- if (withToast) {
- Toast.makeText(context, R.string.gadget_toggle_wifi, Toast.LENGTH_SHORT).show();
- }
- /**
- * Disable tethering if enabling Wifi
- */
- int wifiApState = wifiManager.getWifiApState();
- if (desiredState && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
- (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
- wifiManager.setWifiApEnabled(null, false);
- }
- wifiManager.setWifiEnabled(desiredState);
+ // Actually request the wifi change and persistent
+ // settings write off the UI thread, as it can take a
+ // user-noticeable amount of time, especially if there's
+ // disk contention.
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... args) {
+ /**
+ * Disable tethering if enabling Wifi
+ */
+ int wifiApState = wifiManager.getWifiApState();
+ if (desiredState && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
+ (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
+ wifiManager.setWifiApEnabled(null, false);
+ }
+
+ wifiManager.setWifiEnabled(desiredState);
+ return null;
+ }
+ }.execute();
}
@Override
@@ -306,17 +321,22 @@
}
@Override
- protected void requestStateChange(Context context,
- boolean desiredState, boolean withToast) {
+ protected void requestStateChange(Context context, final boolean desiredState) {
if (sLocalBluetoothManager == null) {
Log.d(TAG, "No LocalBluetoothManager");
return;
}
- if (withToast) {
- Toast.makeText(context,
- R.string.gadget_toggle_bluetooth, Toast.LENGTH_SHORT).show();
- }
- sLocalBluetoothManager.setBluetoothEnabled(desiredState);
+ // Actually request the Bluetooth change and persistent
+ // settings write off the UI thread, as it can take a
+ // user-noticeable amount of time, especially if there's
+ // disk contention.
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... args) {
+ sLocalBluetoothManager.setBluetoothEnabled(desiredState);
+ return null;
+ }
+ }.execute();
}
@Override
@@ -563,6 +583,11 @@
} else if (buttonId == BUTTON_BLUETOOTH) {
sBluetoothState.toggleState(context);
}
+ } else {
+ // Don't fall-through to updating the widget. The Intent
+ // was something unrelated or that our super class took
+ // care of.
+ return;
}
// State changes fall through
diff --git a/src/com/android/settings/wifi/WifiApSettings.java b/src/com/android/settings/wifi/WifiApSettings.java
index bca4835..0815238 100644
--- a/src/com/android/settings/wifi/WifiApSettings.java
+++ b/src/com/android/settings/wifi/WifiApSettings.java
@@ -17,6 +17,7 @@
package com.android.settings.wifi;
import com.android.settings.R;
+import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -48,6 +49,8 @@
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;
private Preference mCreateNetwork;
private CheckBoxPreference mEnableWifiAp;
@@ -85,6 +88,15 @@
}
@Override
+ protected Dialog onCreateDialog(int id) {
+ if (id == DIALOG_AP_SETTINGS) {
+ mDialog = new WifiApDialog(this, this, mWifiConfig);
+ return mDialog;
+ }
+ return null;
+ }
+
+ @Override
protected void onResume() {
super.onResume();
mWifiApEnabler.resume();
@@ -99,19 +111,11 @@
@Override
public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
if (preference == mCreateNetwork) {
- showDialog();
+ showDialog(DIALOG_AP_SETTINGS);
}
return true;
}
- private void showDialog() {
- if (mDialog != null) {
- mDialog.dismiss();
- }
- mDialog = new WifiApDialog(this, this, mWifiConfig);
- mDialog.show();
- }
-
public void onClick(DialogInterface dialogInterface, int button) {
if (button == DialogInterface.BUTTON_POSITIVE) {