Make the Power Control widget more responsive.

BUG=2535155

Change-Id: Id3049fa9ba850c05140d7374065717b854a5d721
This commit is contained in:
Brad Fitzpatrick
2010-03-22 15:58:01 -07:00
parent 38925c0b96
commit 94ea6e21c3

View File

@@ -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,49 +552,14 @@ 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.
*
@@ -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.
}
}