summaryrefslogtreecommitdiffstats
path: root/src/com/android/settings/widget
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@android.com>2010-03-22 15:58:01 -0700
committerBrad Fitzpatrick <bradfitz@android.com>2010-03-22 18:07:17 -0700
commit94ea6e21c337225f01856b65318d34fbaa044ac4 (patch)
tree5be3fe58a9346b38c8a9140073ac78b917ff53d5 /src/com/android/settings/widget
parent38925c0b964b7aef23c8a03af4d1a9499474f1b2 (diff)
downloadpackages_apps_Settings-94ea6e21c337225f01856b65318d34fbaa044ac4.zip
packages_apps_Settings-94ea6e21c337225f01856b65318d34fbaa044ac4.tar.gz
packages_apps_Settings-94ea6e21c337225f01856b65318d34fbaa044ac4.tar.bz2
Make the Power Control widget more responsive.
BUG=2535155 Change-Id: Id3049fa9ba850c05140d7374065717b854a5d721
Diffstat (limited to 'src/com/android/settings/widget')
-rw-r--r--src/com/android/settings/widget/SettingsAppWidgetProvider.java444
1 files changed, 343 insertions, 101 deletions
diff --git a/src/com/android/settings/widget/SettingsAppWidgetProvider.java b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
index 005fc13..2fd618c 100644
--- a/src/com/android/settings/widget/SettingsAppWidgetProvider.java
+++ b/src/com/android/settings/widget/SettingsAppWidgetProvider.java
@@ -50,7 +50,7 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
new ComponentName("com.android.settings",
"com.android.settings.widget.SettingsAppWidgetProvider");
- private static LocalBluetoothManager mLocalBluetoothManager = null;
+ private static LocalBluetoothManager sLocalBluetoothManager = null;
private static final int BUTTON_WIFI = 0;
private static final int BUTTON_BRIGHTNESS = 1;
@@ -58,9 +58,16 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
private static final int BUTTON_GPS = 3;
private static final int BUTTON_BLUETOOTH = 4;
+ // This widget keeps track of two sets of states:
+ // "3-state": STATE_DISABLED, STATE_ENABLED, STATE_INTERMEDIATE
+ // "5-state": STATE_DISABLED, STATE_ENABLED, STATE_TURNING_ON, STATE_TURNING_OFF, STATE_UNKNOWN
private static final int STATE_DISABLED = 0;
private static final int STATE_ENABLED = 1;
- private static final int STATE_INTERMEDIATE = 2;
+ private static final int STATE_TURNING_ON = 2;
+ private static final int STATE_TURNING_OFF = 3;
+ private static final int STATE_UNKNOWN = 4;
+ private static final int STATE_INTERMEDIATE = 5;
+
/**
* Minimum and maximum brightnesses. Don't go to 0 since that makes the display unusable
@@ -69,6 +76,270 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f);
+ private static final StateTracker sWifiState = new WifiStateTracker();
+ private static final StateTracker sBluetoothState = new BluetoothStateTracker();
+
+ /**
+ * The state machine for Wifi and Bluetooth toggling, tracking
+ * reality versus the user's intent.
+ *
+ * This is necessary because reality moves relatively slowly
+ * (turning on &amp; off radio drivers), compared to user's
+ * expectations.
+ */
+ private abstract static class StateTracker {
+ // Is the state in the process of changing?
+ private boolean mInTransition = false;
+ private Boolean mActualState = null; // initially not set
+ private Boolean mIntendedState = null; // initially not set
+
+ // Did a toggle request arrive while a state update was
+ // already in-flight? If so, the mIntendedState needs to be
+ // requested when the other one is done, unless we happened to
+ // arrive at that state already.
+ private boolean mDeferredStateChangeRequestNeeded = false;
+
+ /**
+ * User pressed a button to change the state. Something
+ * should immediately appear to the user afterwards, even if
+ * we effectively do nothing. Their press must be heard.
+ */
+ public final void toggleState(Context context) {
+ int currentState = getTriState(context);
+ boolean newState = false;
+ switch (currentState) {
+ case STATE_ENABLED:
+ newState = false;
+ break;
+ case STATE_DISABLED:
+ newState = true;
+ break;
+ case STATE_INTERMEDIATE:
+ if (mIntendedState != null) {
+ newState = !mIntendedState;
+ }
+ break;
+ }
+ mIntendedState = newState;
+ if (mInTransition) {
+ // We don't send off a transition request if we're
+ // already transitioning. Makes our state tracking
+ // easier, and is probably nicer on lower levels.
+ // (even though they should be able to take it...)
+ mDeferredStateChangeRequestNeeded = true;
+ } else {
+ mInTransition = true;
+ boolean showToast = newState; // only show Toast on the up transition
+ requestStateChange(context, newState, showToast);
+ }
+ }
+
+ /**
+ * Update internal state from a broadcast state change.
+ */
+ public abstract void onActualStateChange(Context context, Intent intent);
+
+ /**
+ * Sets the value that we're now in. To be called from onActualStateChange.
+ *
+ * @param newState one of STATE_DISABLED, STATE_ENABLED, STATE_TURNING_ON,
+ * STATE_TURNING_OFF, STATE_UNKNOWN
+ */
+ protected final void setCurrentState(Context context, int newState) {
+ boolean wasInTransition = mInTransition;
+ switch (newState) {
+ case STATE_DISABLED:
+ mInTransition = false;
+ mActualState = false;
+ break;
+ case STATE_ENABLED:
+ mInTransition = false;
+ mActualState = true;
+ break;
+ case STATE_TURNING_ON:
+ mInTransition = true;
+ mActualState = false;
+ break;
+ case STATE_TURNING_OFF:
+ mInTransition = true;
+ mActualState = true;
+ break;
+ }
+
+ if (wasInTransition && !mInTransition) {
+ if (mDeferredStateChangeRequestNeeded) {
+ Log.v(TAG, "processing deferred state change");
+ if (mActualState != null && mIntendedState != null &&
+ mIntendedState.equals(mActualState)) {
+ Log.v(TAG, "... but intended state matches, so no changes.");
+ } else if (mIntendedState != null) {
+ mInTransition = true;
+ requestStateChange(context, mIntendedState, false /* no toast */);
+ }
+ mDeferredStateChangeRequestNeeded = false;
+ }
+ }
+ }
+
+
+ /**
+ * If we're in a transition mode, this returns true if we're
+ * transitioning towards being enabled.
+ */
+ public final boolean isTurningOn() {
+ return mIntendedState != null && mIntendedState;
+ }
+
+ /**
+ * Returns simplified 3-state value from underlying 5-state.
+ *
+ * @param context
+ * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
+ */
+ public final int getTriState(Context context) {
+ switch (getActualState(context)) {
+ case STATE_DISABLED:
+ return STATE_DISABLED;
+ case STATE_ENABLED:
+ return STATE_ENABLED;
+ default:
+ return STATE_INTERMEDIATE;
+ }
+ }
+
+ /**
+ * Gets underlying actual state.
+ *
+ * @param context
+ * @return STATE_ENABLED, STATE_DISABLED, STATE_ENABLING, STATE_DISABLING,
+ * or or STATE_UNKNOWN.
+ */
+ public abstract int getActualState(Context context);
+
+ /**
+ * Actually make the desired change to the underlying radio
+ * API.
+ */
+ protected abstract void requestStateChange(Context context,
+ boolean desiredState, boolean withToast);
+ }
+
+ /**
+ * Subclass of StateTracker to get/set Wifi state.
+ */
+ private static final class WifiStateTracker extends StateTracker {
+ @Override
+ public int getActualState(Context context) {
+ WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ if (wifiManager != null) {
+ return wifiStateToFiveState(wifiManager.getWifiState());
+ }
+ return STATE_UNKNOWN;
+ }
+
+ @Override
+ protected void requestStateChange(Context context,
+ boolean desiredState, boolean withToast) {
+ 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();
+ }
+ wifiManager.setWifiEnabled(desiredState);
+ }
+
+ @Override
+ public void onActualStateChange(Context context, Intent intent) {
+ if (!WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+ return;
+ }
+ int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1);
+ setCurrentState(context, wifiStateToFiveState(wifiState));
+ }
+
+ /**
+ * Converts WifiManager's state values into our
+ * Wifi/Bluetooth-common state values.
+ */
+ private static int wifiStateToFiveState(int wifiState) {
+ switch (wifiState) {
+ case WifiManager.WIFI_STATE_DISABLED:
+ return STATE_DISABLED;
+ case WifiManager.WIFI_STATE_ENABLED:
+ return STATE_ENABLED;
+ case WifiManager.WIFI_STATE_DISABLING:
+ return STATE_TURNING_OFF;
+ case WifiManager.WIFI_STATE_ENABLING:
+ return STATE_TURNING_ON;
+ default:
+ return STATE_UNKNOWN;
+ }
+ }
+ }
+
+ /**
+ * Subclass of StateTracker to get/set Bluetooth state.
+ */
+ private static final class BluetoothStateTracker extends StateTracker {
+
+ @Override
+ public int getActualState(Context context) {
+ if (sLocalBluetoothManager == null) {
+ sLocalBluetoothManager = LocalBluetoothManager.getInstance(context);
+ if (sLocalBluetoothManager == null) {
+ return STATE_UNKNOWN; // On emulator?
+ }
+ }
+ return bluetoothStateToFiveState(sLocalBluetoothManager.getBluetoothState());
+ }
+
+ @Override
+ protected void requestStateChange(Context context,
+ boolean desiredState, boolean withToast) {
+ 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);
+ }
+
+ @Override
+ public void onActualStateChange(Context context, Intent intent) {
+ if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+ return;
+ }
+ int bluetoothState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ setCurrentState(context, bluetoothStateToFiveState(bluetoothState));
+ }
+
+ /**
+ * Converts BluetoothAdapter's state values into our
+ * Wifi/Bluetooth-common state values.
+ */
+ private static int bluetoothStateToFiveState(int bluetoothState) {
+ switch (bluetoothState) {
+ case BluetoothAdapter.STATE_OFF:
+ return STATE_DISABLED;
+ case BluetoothAdapter.STATE_ON:
+ return STATE_ENABLED;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ return STATE_TURNING_ON;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ return STATE_TURNING_OFF;
+ default:
+ return STATE_UNKNOWN;
+ }
+ }
+ }
+
+
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
@@ -142,29 +413,53 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
* @param context
*/
private static void updateButtons(RemoteViews views, Context context) {
- switch (getWifiState(context)) {
+ switch (sWifiState.getTriState(context)) {
case STATE_DISABLED:
- views.setImageViewResource(R.id.img_wifi, R.drawable.ic_appwidget_settings_wifi_off);
- views.setImageViewResource(R.id.ind_wifi, R.drawable.appwidget_settings_ind_off_l);
+ views.setImageViewResource(R.id.img_wifi,
+ R.drawable.ic_appwidget_settings_wifi_off);
+ views.setImageViewResource(R.id.ind_wifi,
+ R.drawable.appwidget_settings_ind_off_l);
break;
case STATE_ENABLED:
- views.setImageViewResource(R.id.img_wifi, R.drawable.ic_appwidget_settings_wifi_on);
- views.setImageViewResource(R.id.ind_wifi, R.drawable.appwidget_settings_ind_on_l);
+ views.setImageViewResource(R.id.img_wifi,
+ R.drawable.ic_appwidget_settings_wifi_on);
+ views.setImageViewResource(R.id.ind_wifi,
+ R.drawable.appwidget_settings_ind_on_l);
break;
case STATE_INTERMEDIATE:
- views.setImageViewResource(R.id.img_wifi, R.drawable.ic_appwidget_settings_wifi_off);
- views.setImageViewResource(R.id.ind_wifi, R.drawable.appwidget_settings_ind_mid_l);
+ // In the transitional state, the bottom green bar
+ // shows the tri-state (on, off, transitioning), but
+ // the top dark-gray-or-bright-white logo shows the
+ // user's intent. This is much easier to see in
+ // sunlight.
+ if (sWifiState.isTurningOn()) {
+ views.setImageViewResource(R.id.img_wifi,
+ R.drawable.ic_appwidget_settings_wifi_on);
+ views.setImageViewResource(R.id.ind_wifi,
+ R.drawable.appwidget_settings_ind_mid_l);
+ } else {
+ views.setImageViewResource(R.id.img_wifi,
+ R.drawable.ic_appwidget_settings_wifi_off);
+ views.setImageViewResource(R.id.ind_wifi,
+ R.drawable.appwidget_settings_ind_off_l);
+ }
break;
}
if (getBrightnessMode(context)) {
- views.setImageViewResource(R.id.img_brightness, R.drawable.ic_appwidget_settings_brightness_auto);
- views.setImageViewResource(R.id.ind_brightness, R.drawable.appwidget_settings_ind_on_r);
+ views.setImageViewResource(R.id.img_brightness,
+ R.drawable.ic_appwidget_settings_brightness_auto);
+ views.setImageViewResource(R.id.ind_brightness,
+ R.drawable.appwidget_settings_ind_on_r);
} else if (getBrightness(context)) {
- views.setImageViewResource(R.id.img_brightness, R.drawable.ic_appwidget_settings_brightness_on);
- views.setImageViewResource(R.id.ind_brightness, R.drawable.appwidget_settings_ind_on_r);
+ views.setImageViewResource(R.id.img_brightness,
+ R.drawable.ic_appwidget_settings_brightness_on);
+ views.setImageViewResource(R.id.ind_brightness,
+ R.drawable.appwidget_settings_ind_on_r);
} else {
- views.setImageViewResource(R.id.img_brightness, R.drawable.ic_appwidget_settings_brightness_off);
- views.setImageViewResource(R.id.ind_brightness, R.drawable.appwidget_settings_ind_off_r);
+ views.setImageViewResource(R.id.img_brightness,
+ R.drawable.ic_appwidget_settings_brightness_off);
+ views.setImageViewResource(R.id.ind_brightness,
+ R.drawable.appwidget_settings_ind_off_r);
}
if (getSync(context)) {
views.setImageViewResource(R.id.img_sync, R.drawable.ic_appwidget_settings_sync_on);
@@ -180,18 +475,36 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
views.setImageViewResource(R.id.img_gps, R.drawable.ic_appwidget_settings_gps_off);
views.setImageViewResource(R.id.ind_gps, R.drawable.appwidget_settings_ind_off_c);
}
- switch (getBluetoothState(context)) {
+ switch (sBluetoothState.getTriState(context)) {
case STATE_DISABLED:
- views.setImageViewResource(R.id.img_bluetooth, R.drawable.ic_appwidget_settings_bluetooth_off);
- views.setImageViewResource(R.id.ind_bluetooth, R.drawable.appwidget_settings_ind_off_c);
+ views.setImageViewResource(R.id.img_bluetooth,
+ R.drawable.ic_appwidget_settings_bluetooth_off);
+ views.setImageViewResource(R.id.ind_bluetooth,
+ R.drawable.appwidget_settings_ind_off_c);
break;
case STATE_ENABLED:
- views.setImageViewResource(R.id.img_bluetooth, R.drawable.ic_appwidget_settings_bluetooth_on);
- views.setImageViewResource(R.id.ind_bluetooth, R.drawable.appwidget_settings_ind_on_c);
+ views.setImageViewResource(R.id.img_bluetooth,
+ R.drawable.ic_appwidget_settings_bluetooth_on);
+ views.setImageViewResource(R.id.ind_bluetooth,
+ R.drawable.appwidget_settings_ind_on_c);
break;
case STATE_INTERMEDIATE:
- views.setImageViewResource(R.id.img_bluetooth, R.drawable.ic_appwidget_settings_bluetooth_off);
- views.setImageViewResource(R.id.ind_bluetooth, R.drawable.appwidget_settings_ind_mid_c);
+ // In the transitional state, the bottom green bar
+ // shows the tri-state (on, off, transitioning), but
+ // the top dark-gray-or-bright-white logo shows the
+ // user's intent. This is much easier to see in
+ // sunlight.
+ if (sBluetoothState.isTurningOn()) {
+ views.setImageViewResource(R.id.img_bluetooth,
+ R.drawable.ic_appwidget_settings_bluetooth_on);
+ views.setImageViewResource(R.id.ind_bluetooth,
+ R.drawable.appwidget_settings_ind_mid_c);
+ } else {
+ views.setImageViewResource(R.id.img_bluetooth,
+ R.drawable.ic_appwidget_settings_bluetooth_off);
+ views.setImageViewResource(R.id.ind_bluetooth,
+ R.drawable.appwidget_settings_ind_off_c);
+ }
break;
}
}
@@ -223,11 +536,15 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
- if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
+ sWifiState.onActualStateChange(context, intent);
+ } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+ sBluetoothState.onActualStateChange(context, intent);
+ } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
Uri data = intent.getData();
int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
if (buttonId == BUTTON_WIFI) {
- toggleWifi(context);
+ sWifiState.toggleState(context);
} else if (buttonId == BUTTON_BRIGHTNESS) {
toggleBrightness(context);
} else if (buttonId == BUTTON_SYNC) {
@@ -235,50 +552,15 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
} else if (buttonId == BUTTON_GPS) {
toggleGps(context);
} else if (buttonId == BUTTON_BLUETOOTH) {
- toggleBluetooth(context);
+ sBluetoothState.toggleState(context);
}
}
+
// State changes fall through
updateWidget(context);
}
/**
- * Gets the state of Wi-Fi
- *
- * @param context
- * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
- */
- private static int getWifiState(Context context) {
- WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- int wifiState = wifiManager.getWifiState();
- if (wifiState == WifiManager.WIFI_STATE_DISABLED) {
- return STATE_DISABLED;
- } else if (wifiState == WifiManager.WIFI_STATE_ENABLED) {
- return STATE_ENABLED;
- } else {
- return STATE_INTERMEDIATE;
- }
- }
-
- /**
- * Toggles the state of Wi-Fi
- *
- * @param context
- */
- private void toggleWifi(Context context) {
- WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- int wifiState = getWifiState(context);
- if (wifiState == STATE_ENABLED) {
- wifiManager.setWifiEnabled(false);
- Toast.makeText(context, R.string.gadget_toggle_wifi, Toast.LENGTH_SHORT).show();
- } else if (wifiState == STATE_DISABLED) {
- wifiManager.setWifiEnabled(true);
- Toast.makeText(context, R.string.gadget_toggle_wifi, Toast.LENGTH_SHORT).show();
- }
- // If wifi is in intermediate state, don't do anything.
- }
-
- /**
* Gets the state of background data.
*
* @param context
@@ -459,44 +741,4 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider {
Log.d(TAG, "toggleBrightness: " + e);
}
}
-
- /**
- * Gets state of bluetooth
- *
- * @param context
- * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
- */
- private static int getBluetoothState(Context context) {
- if (mLocalBluetoothManager == null) {
- mLocalBluetoothManager = LocalBluetoothManager.getInstance(context);
- if (mLocalBluetoothManager == null) {
- return STATE_INTERMEDIATE; // On emulator?
- }
- }
- int state = mLocalBluetoothManager.getBluetoothState();
- if (state == BluetoothAdapter.STATE_OFF) {
- return STATE_DISABLED;
- } else if (state == BluetoothAdapter.STATE_ON) {
- return STATE_ENABLED;
- } else {
- return STATE_INTERMEDIATE;
- }
- }
-
- /**
- * Toggles the state of bluetooth
- *
- * @param context
- */
- private void toggleBluetooth(Context context) {
- int state = getBluetoothState(context);
- if (state == STATE_ENABLED) {
- mLocalBluetoothManager.setBluetoothEnabled(false);
- Toast.makeText(context, R.string.gadget_toggle_bluetooth, Toast.LENGTH_SHORT).show();
- } else if (state == STATE_DISABLED) {
- mLocalBluetoothManager.setBluetoothEnabled(true);
- Toast.makeText(context, R.string.gadget_toggle_bluetooth, Toast.LENGTH_SHORT).show();
- }
- // If bluetooth is in intermediate state, don't do anything.
- }
}