diff options
author | Brad Fitzpatrick <bradfitz@android.com> | 2010-03-22 15:58:01 -0700 |
---|---|---|
committer | Brad Fitzpatrick <bradfitz@android.com> | 2010-03-22 18:07:17 -0700 |
commit | 94ea6e21c337225f01856b65318d34fbaa044ac4 (patch) | |
tree | 5be3fe58a9346b38c8a9140073ac78b917ff53d5 /src/com/android/settings/widget | |
parent | 38925c0b964b7aef23c8a03af4d1a9499474f1b2 (diff) | |
download | packages_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.java | 444 |
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 & 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. - } } |