From ae0ff95272fe2a7e32114ef4565e8c6b52c269b4 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Fri, 27 Aug 2010 16:59:41 -0700 Subject: Make power control widget be more responsive. Fix StrictMode violations. Also use new location providers changed broadcast intent, and fix a bug introduced in Froyo where we stopped observing the SYSTEM_CLOSE_DIALOGS intent so we fell out of sync (more often) with the sync settings. Now even responsive during the boot I/O storm! (except for toggling brightness, which still does disk writes from the UI thread...) BUG=2956025 Change-Id: I77876ff6e0be2325c15d5f08bb2970edf233f839 --- .../settings/widget/SettingsAppWidgetProvider.java | 360 ++++++++++++--------- 1 file changed, 204 insertions(+), 156 deletions(-) (limited to 'src') diff --git a/src/com/android/settings/widget/SettingsAppWidgetProvider.java b/src/com/android/settings/widget/SettingsAppWidgetProvider.java index 939e8e3..444b8d4 100644 --- a/src/com/android/settings/widget/SettingsAppWidgetProvider.java +++ b/src/com/android/settings/widget/SettingsAppWidgetProvider.java @@ -78,10 +78,12 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { private static final StateTracker sWifiState = new WifiStateTracker(); private static final StateTracker sBluetoothState = new BluetoothStateTracker(); + private static final StateTracker sGpsState = new GpsStateTracker(); + private static final StateTracker sSyncState = new SyncStateTracker(); /** - * The state machine for Wifi and Bluetooth toggling, tracking - * reality versus the user's intent. + * The state machine for a setting's 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 @@ -134,6 +136,59 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { } /** + * Return the ID of the main large image button for the setting. + */ + public abstract int getButtonId(); + + /** + * Returns the small indicator image ID underneath the setting. + */ + public abstract int getIndicatorId(); + + /** + * Returns the resource ID of the image to show as a function of + * the on-vs-off state. + */ + public abstract int getButtonImageId(boolean on); + + /** + * Updates the remote views depending on the state (off, on, + * turning off, turning on) of the setting. + */ + public final void setImageViewResources(Context context, RemoteViews views) { + int buttonId = getButtonId(); + int indicatorId = getIndicatorId(); + switch (getTriState(context)) { + case STATE_DISABLED: + views.setImageViewResource(buttonId, getButtonImageId(false)); + views.setImageViewResource( + indicatorId, R.drawable.appwidget_settings_ind_off_l); + break; + case STATE_ENABLED: + views.setImageViewResource(buttonId, getButtonImageId(true)); + views.setImageViewResource( + indicatorId, R.drawable.appwidget_settings_ind_on_l); + break; + case STATE_INTERMEDIATE: + // 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 (isTurningOn()) { + views.setImageViewResource(buttonId, getButtonImageId(true)); + views.setImageViewResource( + indicatorId, R.drawable.appwidget_settings_ind_mid_l); + } else { + views.setImageViewResource(buttonId, getButtonImageId(false)); + views.setImageViewResource( + indicatorId, R.drawable.appwidget_settings_ind_off_l); + } + break; + } + } + + /** * Update internal state from a broadcast state change. */ public abstract void onActualStateChange(Context context, Intent intent); @@ -236,6 +291,13 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { * Subclass of StateTracker to get/set Wifi state. */ private static final class WifiStateTracker extends StateTracker { + public int getButtonId() { return R.id.img_wifi; } + public int getIndicatorId() { return R.id.ind_wifi; } + public int getButtonImageId(boolean on) { + return on ? R.drawable.ic_appwidget_settings_wifi_on + : R.drawable.ic_appwidget_settings_wifi_off; + } + @Override public int getActualState(Context context) { WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); @@ -247,7 +309,8 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { @Override protected void requestStateChange(Context context, final boolean desiredState) { - final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + final WifiManager wifiManager = + (WifiManager) context.getSystemService(Context.WIFI_SERVICE); if (wifiManager == null) { Log.d(TAG, "No wifiManager."); return; @@ -308,6 +371,12 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { * Subclass of StateTracker to get/set Bluetooth state. */ private static final class BluetoothStateTracker extends StateTracker { + public int getButtonId() { return R.id.img_bluetooth; } + public int getIndicatorId() { return R.id.ind_bluetooth; } + public int getButtonImageId(boolean on) { + return on ? R.drawable.ic_appwidget_settings_bluetooth_on + : R.drawable.ic_appwidget_settings_bluetooth_off; + } @Override public int getActualState(Context context) { @@ -368,6 +437,120 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { } } + /** + * Subclass of StateTracker for GPS state. + */ + private static final class GpsStateTracker extends StateTracker { + public int getButtonId() { return R.id.img_gps; } + public int getIndicatorId() { return R.id.ind_gps; } + public int getButtonImageId(boolean on) { + return on ? R.drawable.ic_appwidget_settings_gps_on + : R.drawable.ic_appwidget_settings_gps_off; + } + + @Override + public int getActualState(Context context) { + ContentResolver resolver = context.getContentResolver(); + boolean on = Settings.Secure.isLocationProviderEnabled( + resolver, LocationManager.GPS_PROVIDER); + return on ? STATE_ENABLED : STATE_DISABLED; + } + + @Override + public void onActualStateChange(Context context, Intent unused) { + // Note: the broadcast location providers changed intent + // doesn't include an extras bundles saying what the new value is. + setCurrentState(context, getActualState(context)); + } + + @Override + public void requestStateChange(final Context context, final boolean desiredState) { + final ContentResolver resolver = context.getContentResolver(); + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... args) { + Settings.Secure.setLocationProviderEnabled( + resolver, + LocationManager.GPS_PROVIDER, + desiredState); + return desiredState; + } + + @Override + protected void onPostExecute(Boolean result) { + setCurrentState( + context, + result ? STATE_ENABLED : STATE_DISABLED); + updateWidget(context); + } + }.execute(); + } + } + + /** + * Subclass of StateTracker for sync state. + */ + private static final class SyncStateTracker extends StateTracker { + public int getButtonId() { return R.id.img_sync; } + public int getIndicatorId() { return R.id.ind_sync; } + public int getButtonImageId(boolean on) { + return on ? R.drawable.ic_appwidget_settings_sync_on + : R.drawable.ic_appwidget_settings_sync_off; + } + + @Override + public int getActualState(Context context) { + boolean on = getBackgroundDataState(context) && + ContentResolver.getMasterSyncAutomatically(); + return on ? STATE_ENABLED : STATE_DISABLED; + } + + @Override + public void onActualStateChange(Context context, Intent unused) { + // Well, ACTION_CLOSE_SYSTEM_DIALOGS fired. So _maybe_ + // the Sync settings changed. + // TODO: find something more reliable. + setCurrentState(context, getActualState(context)); + } + + @Override + public void requestStateChange(final Context context, final boolean desiredState) { + final ConnectivityManager connManager = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + final boolean backgroundData = getBackgroundDataState(context); + final boolean sync = ContentResolver.getMasterSyncAutomatically(); + + new AsyncTask() { + @Override + protected Boolean doInBackground(Void... args) { + // Turning sync on. + if (desiredState) { + if (!backgroundData) { + connManager.setBackgroundDataSetting(true); + } + if (!sync) { + ContentResolver.setMasterSyncAutomatically(true); + } + return true; + } + + // Turning sync off + if (sync) { + ContentResolver.setMasterSyncAutomatically(false); + } + return false; + } + + @Override + protected void onPostExecute(Boolean result) { + setCurrentState( + context, + result ? STATE_ENABLED : STATE_DISABLED); + updateWidget(context); + } + }.execute(); + } + } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, @@ -442,38 +625,11 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { * @param context */ private static void updateButtons(RemoteViews views, Context 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); - 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); - break; - case STATE_INTERMEDIATE: - // 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; - } + sWifiState.setImageViewResources(context, views); + sBluetoothState.setImageViewResources(context, views); + sGpsState.setImageViewResources(context, views); + sSyncState.setImageViewResources(context, views); + if (getBrightnessMode(context)) { views.setImageViewResource(R.id.img_brightness, R.drawable.ic_appwidget_settings_brightness_auto); @@ -490,52 +646,6 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { 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); - views.setImageViewResource(R.id.ind_sync, R.drawable.appwidget_settings_ind_on_c); - } else { - views.setImageViewResource(R.id.img_sync, R.drawable.ic_appwidget_settings_sync_off); - views.setImageViewResource(R.id.ind_sync, R.drawable.appwidget_settings_ind_off_c); - } - if (getGpsState(context)) { - views.setImageViewResource(R.id.img_gps, R.drawable.ic_appwidget_settings_gps_on); - views.setImageViewResource(R.id.ind_gps, R.drawable.appwidget_settings_ind_on_c); - } else { - 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 (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); - 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); - break; - case STATE_INTERMEDIATE: - // 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; - } } /** @@ -565,10 +675,19 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); - if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) { + String action = intent.getAction(); + if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { sWifiState.onActualStateChange(context, intent); - } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { + } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { sBluetoothState.onActualStateChange(context, intent); + } else if (LocationManager.PROVIDERS_CHANGED_ACTION.equals(action)) { + sGpsState.onActualStateChange(context, intent); + } else if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { + // Sadly, for lack of a "sync settings changed" intent, + // this is where we check where sync is enabled or not. + // It's not 100% reliable though as there are paths where + // this doesn't fire. + sSyncState.onActualStateChange(context, intent); } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) { Uri data = intent.getData(); int buttonId = Integer.parseInt(data.getSchemeSpecificPart()); @@ -577,9 +696,9 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { } else if (buttonId == BUTTON_BRIGHTNESS) { toggleBrightness(context); } else if (buttonId == BUTTON_SYNC) { - toggleSync(context); + sSyncState.toggleState(context); } else if (buttonId == BUTTON_GPS) { - toggleGps(context); + sGpsState.toggleState(context); } else if (buttonId == BUTTON_BLUETOOTH) { sBluetoothState.toggleState(context); } @@ -607,77 +726,6 @@ public class SettingsAppWidgetProvider extends AppWidgetProvider { } /** - * Gets the state of auto-sync. - * - * @param context - * @return true if enabled - */ - private static boolean getSync(Context context) { - boolean backgroundData = getBackgroundDataState(context); - boolean sync = ContentResolver.getMasterSyncAutomatically(); - return backgroundData && sync; - } - - /** - * Toggle auto-sync - * - * @param context - */ - private void toggleSync(Context context) { - ConnectivityManager connManager = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - boolean backgroundData = getBackgroundDataState(context); - boolean sync = ContentResolver.getMasterSyncAutomatically(); - - // four cases to handle: - // setting toggled from off to on: - // 1. background data was off, sync was off: turn on both - if (!backgroundData && !sync) { - connManager.setBackgroundDataSetting(true); - ContentResolver.setMasterSyncAutomatically(true); - } - - // 2. background data was off, sync was on: turn on background data - if (!backgroundData && sync) { - connManager.setBackgroundDataSetting(true); - } - - // 3. background data was on, sync was off: turn on sync - if (backgroundData && !sync) { - ContentResolver.setMasterSyncAutomatically(true); - } - - // setting toggled from on to off: - // 4. background data was on, sync was on: turn off sync - if (backgroundData && sync) { - ContentResolver.setMasterSyncAutomatically(false); - } - } - - /** - * Gets the state of GPS location. - * - * @param context - * @return true if enabled. - */ - private static boolean getGpsState(Context context) { - ContentResolver resolver = context.getContentResolver(); - return Settings.Secure.isLocationProviderEnabled(resolver, LocationManager.GPS_PROVIDER); - } - - /** - * Toggles the state of GPS. - * - * @param context - */ - private void toggleGps(Context context) { - ContentResolver resolver = context.getContentResolver(); - boolean enabled = getGpsState(context); - Settings.Secure.setLocationProviderEnabled(resolver, LocationManager.GPS_PROVIDER, - !enabled); - } - - /** * Gets state of brightness. * * @param context -- cgit v1.1