2 * Copyright (C) 2009 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings.widget;
19 import android.app.PendingIntent;
20 import android.appwidget.AppWidgetManager;
21 import android.appwidget.AppWidgetProvider;
22 import android.bluetooth.BluetoothAdapter;
23 import android.content.ComponentName;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.database.ContentObserver;
28 import android.location.LocationManager;
29 import android.net.ConnectivityManager;
30 import android.net.Uri;
31 import android.net.wifi.WifiManager;
32 import android.os.AsyncTask;
33 import android.os.Handler;
34 import android.os.IPowerManager;
35 import android.os.PowerManager;
36 import android.os.RemoteException;
37 import android.os.ServiceManager;
38 import android.os.UserManager;
39 import android.provider.Settings;
40 import android.util.Log;
41 import android.widget.RemoteViews;
43 import com.android.settings.R;
44 import com.android.settings.bluetooth.Utils;
45 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
46 import com.android.settingslib.bluetooth.LocalBluetoothManager;
49 * Provides control of power-related settings from a widget.
51 public class SettingsAppWidgetProvider extends AppWidgetProvider {
52 static final String TAG = "SettingsAppWidgetProvider";
54 static final ComponentName THIS_APPWIDGET =
55 new ComponentName("com.android.settings",
56 "com.android.settings.widget.SettingsAppWidgetProvider");
58 private static LocalBluetoothAdapter sLocalBluetoothAdapter = null;
60 private static final int BUTTON_WIFI = 0;
61 private static final int BUTTON_BRIGHTNESS = 1;
62 private static final int BUTTON_SYNC = 2;
63 private static final int BUTTON_LOCATION = 3;
64 private static final int BUTTON_BLUETOOTH = 4;
66 // This widget keeps track of two sets of states:
67 // "3-state": STATE_DISABLED, STATE_ENABLED, STATE_INTERMEDIATE
68 // "5-state": STATE_DISABLED, STATE_ENABLED, STATE_TURNING_ON, STATE_TURNING_OFF, STATE_UNKNOWN
69 private static final int STATE_DISABLED = 0;
70 private static final int STATE_ENABLED = 1;
71 private static final int STATE_TURNING_ON = 2;
72 private static final int STATE_TURNING_OFF = 3;
73 private static final int STATE_UNKNOWN = 4;
74 private static final int STATE_INTERMEDIATE = 5;
76 // Position in the widget bar, to enable different graphics for left, center and right buttons
77 private static final int POS_LEFT = 0;
78 private static final int POS_CENTER = 1;
79 private static final int POS_RIGHT = 2;
81 private static final int[] IND_DRAWABLE_OFF = {
82 R.drawable.appwidget_settings_ind_off_l_holo,
83 R.drawable.appwidget_settings_ind_off_c_holo,
84 R.drawable.appwidget_settings_ind_off_r_holo
87 private static final int[] IND_DRAWABLE_MID = {
88 R.drawable.appwidget_settings_ind_mid_l_holo,
89 R.drawable.appwidget_settings_ind_mid_c_holo,
90 R.drawable.appwidget_settings_ind_mid_r_holo
93 private static final int[] IND_DRAWABLE_ON = {
94 R.drawable.appwidget_settings_ind_on_l_holo,
95 R.drawable.appwidget_settings_ind_on_c_holo,
96 R.drawable.appwidget_settings_ind_on_r_holo
99 /** Minimum brightness at which the indicator is shown at half-full and ON */
100 private static final float HALF_BRIGHTNESS_THRESHOLD = 0.3f;
101 /** Minimum brightness at which the indicator is shown at full */
102 private static final float FULL_BRIGHTNESS_THRESHOLD = 0.8f;
104 private static final StateTracker sWifiState = new WifiStateTracker();
105 private static final StateTracker sBluetoothState = new BluetoothStateTracker();
106 private static final StateTracker sLocationState = new LocationStateTracker();
107 private static final StateTracker sSyncState = new SyncStateTracker();
108 private static SettingsObserver sSettingsObserver;
111 * The state machine for a setting's toggling, tracking reality
112 * versus the user's intent.
114 * This is necessary because reality moves relatively slowly
115 * (turning on & off radio drivers), compared to user's
118 private abstract static class StateTracker {
119 // Is the state in the process of changing?
120 private boolean mInTransition = false;
121 private Boolean mActualState = null; // initially not set
122 private Boolean mIntendedState = null; // initially not set
124 // Did a toggle request arrive while a state update was
125 // already in-flight? If so, the mIntendedState needs to be
126 // requested when the other one is done, unless we happened to
127 // arrive at that state already.
128 private boolean mDeferredStateChangeRequestNeeded = false;
131 * User pressed a button to change the state. Something
132 * should immediately appear to the user afterwards, even if
133 * we effectively do nothing. Their press must be heard.
135 public final void toggleState(Context context) {
136 int currentState = getTriState(context);
137 boolean newState = false;
138 switch (currentState) {
145 case STATE_INTERMEDIATE:
146 if (mIntendedState != null) {
147 newState = !mIntendedState;
151 mIntendedState = newState;
153 // We don't send off a transition request if we're
154 // already transitioning. Makes our state tracking
155 // easier, and is probably nicer on lower levels.
156 // (even though they should be able to take it...)
157 mDeferredStateChangeRequestNeeded = true;
159 mInTransition = true;
160 requestStateChange(context, newState);
165 * Return the ID of the clickable container for the setting.
167 public abstract int getContainerId();
170 * Return the ID of the main large image button for the setting.
172 public abstract int getButtonId();
175 * Returns the small indicator image ID underneath the setting.
177 public abstract int getIndicatorId();
180 * Returns the resource ID of the setting's content description.
182 public abstract int getButtonDescription();
185 * Returns the resource ID of the image to show as a function of
186 * the on-vs-off state.
188 public abstract int getButtonImageId(boolean on);
191 * Returns the position in the button bar - either POS_LEFT, POS_RIGHT or POS_CENTER.
193 public int getPosition() { return POS_CENTER; }
196 * Updates the remote views depending on the state (off, on,
197 * turning off, turning on) of the setting.
199 public final void setImageViewResources(Context context, RemoteViews views) {
200 int containerId = getContainerId();
201 int buttonId = getButtonId();
202 int indicatorId = getIndicatorId();
203 int pos = getPosition();
204 switch (getTriState(context)) {
206 views.setContentDescription(containerId,
207 getContentDescription(context, R.string.gadget_state_off));
208 views.setImageViewResource(buttonId, getButtonImageId(false));
209 views.setImageViewResource(
210 indicatorId, IND_DRAWABLE_OFF[pos]);
213 views.setContentDescription(containerId,
214 getContentDescription(context, R.string.gadget_state_on));
215 views.setImageViewResource(buttonId, getButtonImageId(true));
216 views.setImageViewResource(
217 indicatorId, IND_DRAWABLE_ON[pos]);
219 case STATE_INTERMEDIATE:
220 // In the transitional state, the bottom green bar
221 // shows the tri-state (on, off, transitioning), but
222 // the top dark-gray-or-bright-white logo shows the
223 // user's intent. This is much easier to see in
226 views.setContentDescription(containerId,
227 getContentDescription(context, R.string.gadget_state_turning_on));
228 views.setImageViewResource(buttonId, getButtonImageId(true));
229 views.setImageViewResource(
230 indicatorId, IND_DRAWABLE_MID[pos]);
232 views.setContentDescription(containerId,
233 getContentDescription(context, R.string.gadget_state_turning_off));
234 views.setImageViewResource(buttonId, getButtonImageId(false));
235 views.setImageViewResource(
236 indicatorId, IND_DRAWABLE_OFF[pos]);
243 * Returns the gadget state template populated with the gadget
244 * description and state.
246 private final String getContentDescription(Context context, int stateResId) {
247 final String gadget = context.getString(getButtonDescription());
248 final String state = context.getString(stateResId);
249 return context.getString(R.string.gadget_state_template, gadget, state);
253 * Update internal state from a broadcast state change.
255 public abstract void onActualStateChange(Context context, Intent intent);
258 * Sets the value that we're now in. To be called from onActualStateChange.
260 * @param newState one of STATE_DISABLED, STATE_ENABLED, STATE_TURNING_ON,
261 * STATE_TURNING_OFF, STATE_UNKNOWN
263 protected final void setCurrentState(Context context, int newState) {
264 final boolean wasInTransition = mInTransition;
267 mInTransition = false;
268 mActualState = false;
271 mInTransition = false;
274 case STATE_TURNING_ON:
275 mInTransition = true;
276 mActualState = false;
278 case STATE_TURNING_OFF:
279 mInTransition = true;
284 if (wasInTransition && !mInTransition) {
285 if (mDeferredStateChangeRequestNeeded) {
286 Log.v(TAG, "processing deferred state change");
287 if (mActualState != null && mIntendedState != null &&
288 mIntendedState.equals(mActualState)) {
289 Log.v(TAG, "... but intended state matches, so no changes.");
290 } else if (mIntendedState != null) {
291 mInTransition = true;
292 requestStateChange(context, mIntendedState);
294 mDeferredStateChangeRequestNeeded = false;
301 * If we're in a transition mode, this returns true if we're
302 * transitioning towards being enabled.
304 public final boolean isTurningOn() {
305 return mIntendedState != null && mIntendedState;
309 * Returns simplified 3-state value from underlying 5-state.
312 * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
314 public final int getTriState(Context context) {
316 // If we know we just got a toggle request recently
317 // (which set mInTransition), don't even ask the
318 // underlying interface for its state. We know we're
319 // changing. This avoids blocking the UI thread
320 // during UI refresh post-toggle if the underlying
321 // service state accessor has coarse locking on its
322 // state (to be fixed separately).
323 return STATE_INTERMEDIATE;
325 switch (getActualState(context)) {
327 return STATE_DISABLED;
329 return STATE_ENABLED;
331 return STATE_INTERMEDIATE;
336 * Gets underlying actual state.
339 * @return STATE_ENABLED, STATE_DISABLED, STATE_ENABLING, STATE_DISABLING,
340 * or or STATE_UNKNOWN.
342 public abstract int getActualState(Context context);
345 * Actually make the desired change to the underlying radio
348 protected abstract void requestStateChange(Context context, boolean desiredState);
352 * Subclass of StateTracker to get/set Wifi state.
354 private static final class WifiStateTracker extends StateTracker {
355 public int getContainerId() { return R.id.btn_wifi; }
356 public int getButtonId() { return R.id.img_wifi; }
357 public int getIndicatorId() { return R.id.ind_wifi; }
358 public int getButtonDescription() { return R.string.gadget_wifi; }
359 public int getButtonImageId(boolean on) {
360 return on ? R.drawable.ic_appwidget_settings_wifi_on_holo
361 : R.drawable.ic_appwidget_settings_wifi_off_holo;
365 public int getPosition() { return POS_LEFT; }
368 public int getActualState(Context context) {
369 WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
370 if (wifiManager != null) {
371 return wifiStateToFiveState(wifiManager.getWifiState());
373 return STATE_UNKNOWN;
377 protected void requestStateChange(Context context, final boolean desiredState) {
378 final WifiManager wifiManager =
379 (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
380 if (wifiManager == null) {
381 Log.d(TAG, "No wifiManager.");
385 // Actually request the wifi change and persistent
386 // settings write off the UI thread, as it can take a
387 // user-noticeable amount of time, especially if there's
389 new AsyncTask<Void, Void, Void>() {
391 protected Void doInBackground(Void... args) {
393 * Disable tethering if enabling Wifi
395 int wifiApState = wifiManager.getWifiApState();
396 if (desiredState && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
397 (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
398 final ConnectivityManager connectivityManager =
399 (ConnectivityManager) context.getSystemService(
400 Context.CONNECTIVITY_SERVICE);
401 connectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
404 wifiManager.setWifiEnabled(desiredState);
411 public void onActualStateChange(Context context, Intent intent) {
412 if (!WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
415 int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1);
416 setCurrentState(context, wifiStateToFiveState(wifiState));
420 * Converts WifiManager's state values into our
421 * Wifi/Bluetooth-common state values.
423 private static int wifiStateToFiveState(int wifiState) {
425 case WifiManager.WIFI_STATE_DISABLED:
426 return STATE_DISABLED;
427 case WifiManager.WIFI_STATE_ENABLED:
428 return STATE_ENABLED;
429 case WifiManager.WIFI_STATE_DISABLING:
430 return STATE_TURNING_OFF;
431 case WifiManager.WIFI_STATE_ENABLING:
432 return STATE_TURNING_ON;
434 return STATE_UNKNOWN;
440 * Subclass of StateTracker to get/set Bluetooth state.
442 private static final class BluetoothStateTracker extends StateTracker {
443 public int getContainerId() { return R.id.btn_bluetooth; }
444 public int getButtonId() { return R.id.img_bluetooth; }
445 public int getIndicatorId() { return R.id.ind_bluetooth; }
446 public int getButtonDescription() { return R.string.gadget_bluetooth; }
447 public int getButtonImageId(boolean on) {
448 return on ? R.drawable.ic_appwidget_settings_bluetooth_on_holo
449 : R.drawable.ic_appwidget_settings_bluetooth_off_holo;
453 public int getActualState(Context context) {
454 if (sLocalBluetoothAdapter == null) {
455 LocalBluetoothManager manager = Utils.getLocalBtManager(context);
456 if (manager == null) {
457 return STATE_UNKNOWN; // On emulator?
459 sLocalBluetoothAdapter = manager.getBluetoothAdapter();
460 if (sLocalBluetoothAdapter == null) {
461 return STATE_UNKNOWN; // On emulator?
464 return bluetoothStateToFiveState(sLocalBluetoothAdapter.getBluetoothState());
468 protected void requestStateChange(Context context, final boolean desiredState) {
469 if (sLocalBluetoothAdapter == null) {
470 Log.d(TAG, "No LocalBluetoothManager");
473 // Actually request the Bluetooth change and persistent
474 // settings write off the UI thread, as it can take a
475 // user-noticeable amount of time, especially if there's
477 new AsyncTask<Void, Void, Void>() {
479 protected Void doInBackground(Void... args) {
480 sLocalBluetoothAdapter.setBluetoothEnabled(desiredState);
487 public void onActualStateChange(Context context, Intent intent) {
488 if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
491 int bluetoothState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
492 setCurrentState(context, bluetoothStateToFiveState(bluetoothState));
496 * Converts BluetoothAdapter's state values into our
497 * Wifi/Bluetooth-common state values.
499 private static int bluetoothStateToFiveState(int bluetoothState) {
500 switch (bluetoothState) {
501 case BluetoothAdapter.STATE_OFF:
502 return STATE_DISABLED;
503 case BluetoothAdapter.STATE_ON:
504 return STATE_ENABLED;
505 case BluetoothAdapter.STATE_TURNING_ON:
506 return STATE_TURNING_ON;
507 case BluetoothAdapter.STATE_TURNING_OFF:
508 return STATE_TURNING_OFF;
510 return STATE_UNKNOWN;
516 * Subclass of StateTracker for location state.
518 private static final class LocationStateTracker extends StateTracker {
519 private int mCurrentLocationMode = Settings.Secure.LOCATION_MODE_OFF;
521 public int getContainerId() { return R.id.btn_location; }
522 public int getButtonId() { return R.id.img_location; }
523 public int getIndicatorId() { return R.id.ind_location; }
524 public int getButtonDescription() { return R.string.gadget_location; }
525 public int getButtonImageId(boolean on) {
527 switch (mCurrentLocationMode) {
528 case Settings.Secure.LOCATION_MODE_HIGH_ACCURACY:
529 case Settings.Secure.LOCATION_MODE_SENSORS_ONLY:
530 return R.drawable.ic_appwidget_settings_location_on_holo;
532 return R.drawable.ic_appwidget_settings_location_saving_holo;
536 return R.drawable.ic_appwidget_settings_location_off_holo;
540 public int getActualState(Context context) {
541 ContentResolver resolver = context.getContentResolver();
542 mCurrentLocationMode = Settings.Secure.getInt(resolver,
543 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
544 return (mCurrentLocationMode == Settings.Secure.LOCATION_MODE_OFF)
545 ? STATE_DISABLED : STATE_ENABLED;
549 public void onActualStateChange(Context context, Intent unused) {
550 // Note: the broadcast location providers changed intent
551 // doesn't include an extras bundles saying what the new value is.
552 setCurrentState(context, getActualState(context));
556 public void requestStateChange(final Context context, final boolean desiredState) {
557 final ContentResolver resolver = context.getContentResolver();
558 new AsyncTask<Void, Void, Boolean>() {
560 protected Boolean doInBackground(Void... args) {
561 final UserManager um =
562 (UserManager) context.getSystemService(Context.USER_SERVICE);
563 if (!um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION)) {
564 int currentMode = Settings.Secure.getInt(resolver,
565 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
566 int mode = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
567 switch (currentMode) {
568 case Settings.Secure.LOCATION_MODE_HIGH_ACCURACY:
569 mode = Settings.Secure.LOCATION_MODE_BATTERY_SAVING;
571 case Settings.Secure.LOCATION_MODE_BATTERY_SAVING:
572 mode = Settings.Secure.LOCATION_MODE_HIGH_ACCURACY;
574 case Settings.Secure.LOCATION_MODE_SENSORS_ONLY:
575 mode = Settings.Secure.LOCATION_MODE_OFF;
577 case Settings.Secure.LOCATION_MODE_OFF:
578 mode = Settings.Secure.LOCATION_MODE_PREVIOUS;
581 Settings.Secure.putInt(resolver, Settings.Secure.LOCATION_MODE, mode);
582 return mode != Settings.Secure.LOCATION_MODE_OFF;
585 return getActualState(context) == STATE_ENABLED;
589 protected void onPostExecute(Boolean result) {
592 result ? STATE_ENABLED : STATE_DISABLED);
593 updateWidget(context);
600 * Subclass of StateTracker for sync state.
602 private static final class SyncStateTracker extends StateTracker {
603 public int getContainerId() { return R.id.btn_sync; }
604 public int getButtonId() { return R.id.img_sync; }
605 public int getIndicatorId() { return R.id.ind_sync; }
606 public int getButtonDescription() { return R.string.gadget_sync; }
607 public int getButtonImageId(boolean on) {
608 return on ? R.drawable.ic_appwidget_settings_sync_on_holo
609 : R.drawable.ic_appwidget_settings_sync_off_holo;
613 public int getActualState(Context context) {
614 boolean on = ContentResolver.getMasterSyncAutomatically();
615 return on ? STATE_ENABLED : STATE_DISABLED;
619 public void onActualStateChange(Context context, Intent unused) {
620 setCurrentState(context, getActualState(context));
624 public void requestStateChange(final Context context, final boolean desiredState) {
625 final ConnectivityManager connManager =
626 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
627 final boolean sync = ContentResolver.getMasterSyncAutomatically();
629 new AsyncTask<Void, Void, Boolean>() {
631 protected Boolean doInBackground(Void... args) {
635 ContentResolver.setMasterSyncAutomatically(true);
642 ContentResolver.setMasterSyncAutomatically(false);
648 protected void onPostExecute(Boolean result) {
651 result ? STATE_ENABLED : STATE_DISABLED);
652 updateWidget(context);
658 private static void checkObserver(Context context) {
659 if (sSettingsObserver == null) {
660 sSettingsObserver = new SettingsObserver(new Handler(),
661 context.getApplicationContext());
662 sSettingsObserver.startObserving();
667 public void onUpdate(Context context, AppWidgetManager appWidgetManager,
668 int[] appWidgetIds) {
669 // Update each requested appWidgetId
670 RemoteViews view = buildUpdate(context);
672 for (int i = 0; i < appWidgetIds.length; i++) {
673 appWidgetManager.updateAppWidget(appWidgetIds[i], view);
678 public void onEnabled(Context context) {
679 checkObserver(context);
683 public void onDisabled(Context context) {
684 if (sSettingsObserver != null) {
685 sSettingsObserver.stopObserving();
686 sSettingsObserver = null;
691 * Load image for given widget and build {@link RemoteViews} for it.
693 static RemoteViews buildUpdate(Context context) {
694 RemoteViews views = new RemoteViews(context.getPackageName(),
696 views.setOnClickPendingIntent(R.id.btn_wifi, getLaunchPendingIntent(context,
698 views.setOnClickPendingIntent(R.id.btn_brightness,
699 getLaunchPendingIntent(context,
701 views.setOnClickPendingIntent(R.id.btn_sync,
702 getLaunchPendingIntent(context,
704 views.setOnClickPendingIntent(R.id.btn_location,
705 getLaunchPendingIntent(context, BUTTON_LOCATION));
706 views.setOnClickPendingIntent(R.id.btn_bluetooth,
707 getLaunchPendingIntent(context,
710 updateButtons(views, context);
715 * Updates the widget when something changes, or when a button is pushed.
719 public static void updateWidget(Context context) {
720 RemoteViews views = buildUpdate(context);
721 // Update specific list of appWidgetIds if given, otherwise default to all
722 final AppWidgetManager gm = AppWidgetManager.getInstance(context);
723 gm.updateAppWidget(THIS_APPWIDGET, views);
724 checkObserver(context);
728 * Updates the buttons based on the underlying states of wifi, etc.
730 * @param views The RemoteViews to update.
733 private static void updateButtons(RemoteViews views, Context context) {
734 sWifiState.setImageViewResources(context, views);
735 sBluetoothState.setImageViewResources(context, views);
736 sLocationState.setImageViewResources(context, views);
737 sSyncState.setImageViewResources(context, views);
739 if (getBrightnessMode(context)) {
740 views.setContentDescription(R.id.btn_brightness,
741 context.getString(R.string.gadget_brightness_template,
742 context.getString(R.string.gadget_brightness_state_auto)));
743 views.setImageViewResource(R.id.img_brightness,
744 R.drawable.ic_appwidget_settings_brightness_auto_holo);
745 views.setImageViewResource(R.id.ind_brightness,
746 R.drawable.appwidget_settings_ind_on_r_holo);
748 final int brightness = getBrightness(context);
749 final PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
751 final int full = (int)(pm.getMaximumScreenBrightnessSetting()
752 * FULL_BRIGHTNESS_THRESHOLD);
753 final int half = (int)(pm.getMaximumScreenBrightnessSetting()
754 * HALF_BRIGHTNESS_THRESHOLD);
755 if (brightness > full) {
756 views.setContentDescription(R.id.btn_brightness,
757 context.getString(R.string.gadget_brightness_template,
758 context.getString(R.string.gadget_brightness_state_full)));
759 views.setImageViewResource(R.id.img_brightness,
760 R.drawable.ic_appwidget_settings_brightness_full_holo);
761 } else if (brightness > half) {
762 views.setContentDescription(R.id.btn_brightness,
763 context.getString(R.string.gadget_brightness_template,
764 context.getString(R.string.gadget_brightness_state_half)));
765 views.setImageViewResource(R.id.img_brightness,
766 R.drawable.ic_appwidget_settings_brightness_half_holo);
768 views.setContentDescription(R.id.btn_brightness,
769 context.getString(R.string.gadget_brightness_template,
770 context.getString(R.string.gadget_brightness_state_off)));
771 views.setImageViewResource(R.id.img_brightness,
772 R.drawable.ic_appwidget_settings_brightness_off_holo);
775 if (brightness > half) {
776 views.setImageViewResource(R.id.ind_brightness,
777 R.drawable.appwidget_settings_ind_on_r_holo);
779 views.setImageViewResource(R.id.ind_brightness,
780 R.drawable.appwidget_settings_ind_off_r_holo);
786 * Creates PendingIntent to notify the widget of a button click.
791 private static PendingIntent getLaunchPendingIntent(Context context,
793 Intent launchIntent = new Intent();
794 launchIntent.setClass(context, SettingsAppWidgetProvider.class);
795 launchIntent.addCategory(Intent.CATEGORY_ALTERNATIVE);
796 launchIntent.setData(Uri.parse("custom:" + buttonId));
797 PendingIntent pi = PendingIntent.getBroadcast(context, 0 /* no requestCode */,
798 launchIntent, 0 /* no flags */);
803 * Receives and processes a button pressed intent or state change.
806 * @param intent Indicates the pressed button.
809 public void onReceive(Context context, Intent intent) {
810 super.onReceive(context, intent);
811 String action = intent.getAction();
812 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
813 sWifiState.onActualStateChange(context, intent);
814 } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
815 sBluetoothState.onActualStateChange(context, intent);
816 } else if (LocationManager.MODE_CHANGED_ACTION.equals(action)) {
817 sLocationState.onActualStateChange(context, intent);
818 } else if (ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED.equals(action)) {
819 sSyncState.onActualStateChange(context, intent);
820 } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
821 Uri data = intent.getData();
822 int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
823 if (buttonId == BUTTON_WIFI) {
824 sWifiState.toggleState(context);
825 } else if (buttonId == BUTTON_BRIGHTNESS) {
826 toggleBrightness(context);
827 } else if (buttonId == BUTTON_SYNC) {
828 sSyncState.toggleState(context);
829 } else if (buttonId == BUTTON_LOCATION) {
830 sLocationState.toggleState(context);
831 } else if (buttonId == BUTTON_BLUETOOTH) {
832 sBluetoothState.toggleState(context);
835 // Don't fall-through to updating the widget. The Intent
836 // was something unrelated or that our super class took
841 // State changes fall through
842 updateWidget(context);
846 * Gets brightness level.
849 * @return brightness level between 0 and 255.
851 private static int getBrightness(Context context) {
853 int brightness = Settings.System.getInt(context.getContentResolver(),
854 Settings.System.SCREEN_BRIGHTNESS);
856 } catch (Exception e) {
862 * Gets state of brightness mode.
865 * @return true if auto brightness is on.
867 private static boolean getBrightnessMode(Context context) {
869 int brightnessMode = Settings.System.getInt(context.getContentResolver(),
870 Settings.System.SCREEN_BRIGHTNESS_MODE);
871 return brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
872 } catch (Exception e) {
873 Log.d(TAG, "getBrightnessMode: " + e);
879 * Increases or decreases the brightness.
883 private void toggleBrightness(Context context) {
885 IPowerManager power = IPowerManager.Stub.asInterface(
886 ServiceManager.getService("power"));
888 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
890 ContentResolver cr = context.getContentResolver();
891 int brightness = Settings.System.getInt(cr,
892 Settings.System.SCREEN_BRIGHTNESS);
893 int brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
894 //Only get brightness setting if available
895 if (context.getResources().getBoolean(
896 com.android.internal.R.bool.config_automatic_brightness_available)) {
897 brightnessMode = Settings.System.getInt(cr,
898 Settings.System.SCREEN_BRIGHTNESS_MODE);
901 // Rotate AUTO -> MINIMUM -> DEFAULT -> MAXIMUM
902 // Technically, not a toggle...
903 if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
904 brightness = pm.getMinimumScreenBrightnessSetting();
905 brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
906 } else if (brightness < pm.getDefaultScreenBrightnessSetting()) {
907 brightness = pm.getDefaultScreenBrightnessSetting();
908 } else if (brightness < pm.getMaximumScreenBrightnessSetting()) {
909 brightness = pm.getMaximumScreenBrightnessSetting();
911 brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
912 brightness = pm.getMinimumScreenBrightnessSetting();
915 if (context.getResources().getBoolean(
916 com.android.internal.R.bool.config_automatic_brightness_available)) {
917 // Set screen brightness mode (automatic or manual)
918 Settings.System.putInt(context.getContentResolver(),
919 Settings.System.SCREEN_BRIGHTNESS_MODE,
922 // Make sure we set the brightness if automatic mode isn't available
923 brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
925 if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL) {
926 power.setTemporaryScreenBrightnessSettingOverride(brightness);
927 Settings.System.putInt(cr, Settings.System.SCREEN_BRIGHTNESS, brightness);
930 } catch (RemoteException e) {
931 Log.d(TAG, "toggleBrightness: " + e);
932 } catch (Settings.SettingNotFoundException e) {
933 Log.d(TAG, "toggleBrightness: " + e);
937 /** Observer to watch for changes to the BRIGHTNESS setting */
938 private static class SettingsObserver extends ContentObserver {
940 private Context mContext;
942 SettingsObserver(Handler handler, Context context) {
947 void startObserving() {
948 ContentResolver resolver = mContext.getContentResolver();
949 // Listen to brightness and brightness mode
950 resolver.registerContentObserver(Settings.System
951 .getUriFor(Settings.System.SCREEN_BRIGHTNESS), false, this);
952 resolver.registerContentObserver(Settings.System
953 .getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE), false, this);
954 resolver.registerContentObserver(Settings.System
955 .getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ), false, this);
958 void stopObserving() {
959 mContext.getContentResolver().unregisterContentObserver(this);
963 public void onChange(boolean selfChange) {
964 updateWidget(mContext);