• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 package com.android.settings.widget;
18 
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.content.SyncStorageEngine;
28 import android.content.pm.PackageManager;
29 import android.location.LocationManager;
30 import android.net.ConnectivityManager;
31 import android.net.Uri;
32 import android.net.wifi.WifiManager;
33 import android.os.AsyncTask;
34 import android.os.IPowerManager;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 import android.provider.Settings;
38 import android.util.Log;
39 import android.widget.RemoteViews;
40 import com.android.settings.R;
41 import com.android.settings.bluetooth.LocalBluetoothManager;
42 
43 /**
44  * Provides control of power-related settings from a widget.
45  */
46 public class SettingsAppWidgetProvider extends AppWidgetProvider {
47     static final String TAG = "SettingsAppWidgetProvider";
48 
49     static final ComponentName THIS_APPWIDGET =
50             new ComponentName("com.android.settings",
51                     "com.android.settings.widget.SettingsAppWidgetProvider");
52 
53     private static LocalBluetoothManager sLocalBluetoothManager = null;
54 
55     private static final int BUTTON_WIFI = 0;
56     private static final int BUTTON_BRIGHTNESS = 1;
57     private static final int BUTTON_SYNC = 2;
58     private static final int BUTTON_GPS = 3;
59     private static final int BUTTON_BLUETOOTH = 4;
60 
61     // This widget keeps track of two sets of states:
62     // "3-state": STATE_DISABLED, STATE_ENABLED, STATE_INTERMEDIATE
63     // "5-state": STATE_DISABLED, STATE_ENABLED, STATE_TURNING_ON, STATE_TURNING_OFF, STATE_UNKNOWN
64     private static final int STATE_DISABLED = 0;
65     private static final int STATE_ENABLED = 1;
66     private static final int STATE_TURNING_ON = 2;
67     private static final int STATE_TURNING_OFF = 3;
68     private static final int STATE_UNKNOWN = 4;
69     private static final int STATE_INTERMEDIATE = 5;
70 
71     // Position in the widget bar, to enable different graphics for left, center and right buttons
72     private static final int POS_LEFT = 0;
73     private static final int POS_CENTER = 1;
74     private static final int POS_RIGHT = 2;
75 
76     private static final int[] IND_DRAWABLE_OFF = {
77         R.drawable.appwidget_settings_ind_off_l,
78         R.drawable.appwidget_settings_ind_off_c,
79         R.drawable.appwidget_settings_ind_off_r
80     };
81 
82     private static final int[] IND_DRAWABLE_MID = {
83         R.drawable.appwidget_settings_ind_mid_l,
84         R.drawable.appwidget_settings_ind_mid_c,
85         R.drawable.appwidget_settings_ind_mid_r
86     };
87 
88     private static final int[] IND_DRAWABLE_ON = {
89         R.drawable.appwidget_settings_ind_on_l,
90         R.drawable.appwidget_settings_ind_on_c,
91         R.drawable.appwidget_settings_ind_on_r
92     };
93 
94     /**
95      * Minimum and maximum brightnesses.  Don't go to 0 since that makes the display unusable
96      */
97     private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10;
98     private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON;
99     private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f);
100 
101     private static final StateTracker sWifiState = new WifiStateTracker();
102     private static final StateTracker sBluetoothState = new BluetoothStateTracker();
103     private static final StateTracker sGpsState = new GpsStateTracker();
104     private static final StateTracker sSyncState = new SyncStateTracker();
105 
106     /**
107      * The state machine for a setting's toggling, tracking reality
108      * versus the user's intent.
109      *
110      * This is necessary because reality moves relatively slowly
111      * (turning on & off radio drivers), compared to user's
112      * expectations.
113      */
114     private abstract static class StateTracker {
115         // Is the state in the process of changing?
116         private boolean mInTransition = false;
117         private Boolean mActualState = null;  // initially not set
118         private Boolean mIntendedState = null;  // initially not set
119 
120         // Did a toggle request arrive while a state update was
121         // already in-flight?  If so, the mIntendedState needs to be
122         // requested when the other one is done, unless we happened to
123         // arrive at that state already.
124         private boolean mDeferredStateChangeRequestNeeded = false;
125 
126         /**
127          * User pressed a button to change the state.  Something
128          * should immediately appear to the user afterwards, even if
129          * we effectively do nothing.  Their press must be heard.
130          */
toggleState(Context context)131         public final void toggleState(Context context) {
132             int currentState = getTriState(context);
133             boolean newState = false;
134             switch (currentState) {
135                 case STATE_ENABLED:
136                     newState = false;
137                     break;
138                 case STATE_DISABLED:
139                     newState = true;
140                     break;
141                 case STATE_INTERMEDIATE:
142                     if (mIntendedState != null) {
143                         newState = !mIntendedState;
144                     }
145                     break;
146             }
147             mIntendedState = newState;
148             if (mInTransition) {
149                 // We don't send off a transition request if we're
150                 // already transitioning.  Makes our state tracking
151                 // easier, and is probably nicer on lower levels.
152                 // (even though they should be able to take it...)
153                 mDeferredStateChangeRequestNeeded = true;
154             } else {
155                 mInTransition = true;
156                 requestStateChange(context, newState);
157             }
158         }
159 
160         /**
161          * Return the ID of the main large image button for the setting.
162          */
getButtonId()163         public abstract int getButtonId();
164 
165         /**
166          * Returns the small indicator image ID underneath the setting.
167          */
getIndicatorId()168         public abstract int getIndicatorId();
169 
170         /**
171          * Returns the resource ID of the image to show as a function of
172          * the on-vs-off state.
173          */
getButtonImageId(boolean on)174         public abstract int getButtonImageId(boolean on);
175 
176         /**
177          * Returns the position in the button bar - either POS_LEFT, POS_RIGHT or POS_CENTER.
178          */
getPosition()179         public int getPosition() { return POS_CENTER; }
180 
181         /**
182          * Updates the remote views depending on the state (off, on,
183          * turning off, turning on) of the setting.
184          */
setImageViewResources(Context context, RemoteViews views)185         public final void setImageViewResources(Context context, RemoteViews views) {
186             int buttonId = getButtonId();
187             int indicatorId = getIndicatorId();
188             int pos = getPosition();
189             switch (getTriState(context)) {
190                 case STATE_DISABLED:
191                     views.setImageViewResource(buttonId, getButtonImageId(false));
192                     views.setImageViewResource(
193                         indicatorId, IND_DRAWABLE_OFF[pos]);
194                     break;
195                 case STATE_ENABLED:
196                     views.setImageViewResource(buttonId, getButtonImageId(true));
197                     views.setImageViewResource(
198                         indicatorId, IND_DRAWABLE_ON[pos]);
199                     break;
200                 case STATE_INTERMEDIATE:
201                     // In the transitional state, the bottom green bar
202                     // shows the tri-state (on, off, transitioning), but
203                     // the top dark-gray-or-bright-white logo shows the
204                     // user's intent.  This is much easier to see in
205                     // sunlight.
206                     if (isTurningOn()) {
207                         views.setImageViewResource(buttonId, getButtonImageId(true));
208                         views.setImageViewResource(
209                             indicatorId, IND_DRAWABLE_MID[pos]);
210                     } else {
211                         views.setImageViewResource(buttonId, getButtonImageId(false));
212                         views.setImageViewResource(
213                             indicatorId, IND_DRAWABLE_OFF[pos]);
214                     }
215                     break;
216             }
217         }
218 
219         /**
220          * Update internal state from a broadcast state change.
221          */
onActualStateChange(Context context, Intent intent)222         public abstract void onActualStateChange(Context context, Intent intent);
223 
224         /**
225          * Sets the value that we're now in.  To be called from onActualStateChange.
226          *
227          * @param newState one of STATE_DISABLED, STATE_ENABLED, STATE_TURNING_ON,
228          *                 STATE_TURNING_OFF, STATE_UNKNOWN
229          */
setCurrentState(Context context, int newState)230         protected final void setCurrentState(Context context, int newState) {
231             final boolean wasInTransition = mInTransition;
232             switch (newState) {
233                 case STATE_DISABLED:
234                     mInTransition = false;
235                     mActualState = false;
236                     break;
237                 case STATE_ENABLED:
238                     mInTransition = false;
239                     mActualState = true;
240                     break;
241                 case STATE_TURNING_ON:
242                     mInTransition = true;
243                     mActualState = false;
244                     break;
245                 case STATE_TURNING_OFF:
246                     mInTransition = true;
247                     mActualState = true;
248                     break;
249             }
250 
251             if (wasInTransition && !mInTransition) {
252                 if (mDeferredStateChangeRequestNeeded) {
253                     Log.v(TAG, "processing deferred state change");
254                     if (mActualState != null && mIntendedState != null &&
255                         mIntendedState.equals(mActualState)) {
256                         Log.v(TAG, "... but intended state matches, so no changes.");
257                     } else if (mIntendedState != null) {
258                         mInTransition = true;
259                         requestStateChange(context, mIntendedState);
260                     }
261                     mDeferredStateChangeRequestNeeded = false;
262                 }
263             }
264         }
265 
266 
267         /**
268          * If we're in a transition mode, this returns true if we're
269          * transitioning towards being enabled.
270          */
isTurningOn()271         public final boolean isTurningOn() {
272             return mIntendedState != null && mIntendedState;
273         }
274 
275         /**
276          * Returns simplified 3-state value from underlying 5-state.
277          *
278          * @param context
279          * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE
280          */
getTriState(Context context)281         public final int getTriState(Context context) {
282             if (mInTransition) {
283                 // If we know we just got a toggle request recently
284                 // (which set mInTransition), don't even ask the
285                 // underlying interface for its state.  We know we're
286                 // changing.  This avoids blocking the UI thread
287                 // during UI refresh post-toggle if the underlying
288                 // service state accessor has coarse locking on its
289                 // state (to be fixed separately).
290                 return STATE_INTERMEDIATE;
291             }
292             switch (getActualState(context)) {
293                 case STATE_DISABLED:
294                     return STATE_DISABLED;
295                 case STATE_ENABLED:
296                     return STATE_ENABLED;
297                 default:
298                     return STATE_INTERMEDIATE;
299             }
300         }
301 
302         /**
303          * Gets underlying actual state.
304          *
305          * @param context
306          * @return STATE_ENABLED, STATE_DISABLED, STATE_ENABLING, STATE_DISABLING,
307          *         or or STATE_UNKNOWN.
308          */
getActualState(Context context)309         public abstract int getActualState(Context context);
310 
311         /**
312          * Actually make the desired change to the underlying radio
313          * API.
314          */
requestStateChange(Context context, boolean desiredState)315         protected abstract void requestStateChange(Context context, boolean desiredState);
316     }
317 
318     /**
319      * Subclass of StateTracker to get/set Wifi state.
320      */
321     private static final class WifiStateTracker extends StateTracker {
getButtonId()322         public int getButtonId() { return R.id.img_wifi; }
getIndicatorId()323         public int getIndicatorId() { return R.id.ind_wifi; }
getButtonImageId(boolean on)324         public int getButtonImageId(boolean on) {
325             return on ? R.drawable.ic_appwidget_settings_wifi_on
326                     : R.drawable.ic_appwidget_settings_wifi_off;
327         }
328 
329         @Override
getPosition()330         public int getPosition() { return POS_LEFT; }
331 
332         @Override
getActualState(Context context)333         public int getActualState(Context context) {
334             WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
335             if (wifiManager != null) {
336                 return wifiStateToFiveState(wifiManager.getWifiState());
337             }
338             return STATE_UNKNOWN;
339         }
340 
341         @Override
requestStateChange(Context context, final boolean desiredState)342         protected void requestStateChange(Context context, final boolean desiredState) {
343             final WifiManager wifiManager =
344                     (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
345             if (wifiManager == null) {
346                 Log.d(TAG, "No wifiManager.");
347                 return;
348             }
349 
350             // Actually request the wifi change and persistent
351             // settings write off the UI thread, as it can take a
352             // user-noticeable amount of time, especially if there's
353             // disk contention.
354             new AsyncTask<Void, Void, Void>() {
355                 @Override
356                 protected Void doInBackground(Void... args) {
357                     /**
358                      * Disable tethering if enabling Wifi
359                      */
360                     int wifiApState = wifiManager.getWifiApState();
361                     if (desiredState && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
362                                          (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
363                         wifiManager.setWifiApEnabled(null, false);
364                     }
365 
366                     wifiManager.setWifiEnabled(desiredState);
367                     return null;
368                 }
369             }.execute();
370         }
371 
372         @Override
onActualStateChange(Context context, Intent intent)373         public void onActualStateChange(Context context, Intent intent) {
374             if (!WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
375                 return;
376             }
377             int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, -1);
378             setCurrentState(context, wifiStateToFiveState(wifiState));
379         }
380 
381         /**
382          * Converts WifiManager's state values into our
383          * Wifi/Bluetooth-common state values.
384          */
wifiStateToFiveState(int wifiState)385         private static int wifiStateToFiveState(int wifiState) {
386             switch (wifiState) {
387                 case WifiManager.WIFI_STATE_DISABLED:
388                     return STATE_DISABLED;
389                 case WifiManager.WIFI_STATE_ENABLED:
390                     return STATE_ENABLED;
391                 case WifiManager.WIFI_STATE_DISABLING:
392                     return STATE_TURNING_OFF;
393                 case WifiManager.WIFI_STATE_ENABLING:
394                     return STATE_TURNING_ON;
395                 default:
396                     return STATE_UNKNOWN;
397             }
398         }
399     }
400 
401     /**
402      * Subclass of StateTracker to get/set Bluetooth state.
403      */
404     private static final class BluetoothStateTracker extends StateTracker {
getButtonId()405         public int getButtonId() { return R.id.img_bluetooth; }
getIndicatorId()406         public int getIndicatorId() { return R.id.ind_bluetooth; }
getButtonImageId(boolean on)407         public int getButtonImageId(boolean on) {
408             return on ? R.drawable.ic_appwidget_settings_bluetooth_on
409                     : R.drawable.ic_appwidget_settings_bluetooth_off;
410         }
411 
412         @Override
getActualState(Context context)413         public int getActualState(Context context) {
414             if (sLocalBluetoothManager == null) {
415                 sLocalBluetoothManager = LocalBluetoothManager.getInstance(context);
416                 if (sLocalBluetoothManager == null) {
417                     return STATE_UNKNOWN;  // On emulator?
418                 }
419             }
420             return bluetoothStateToFiveState(sLocalBluetoothManager.getBluetoothState());
421         }
422 
423         @Override
requestStateChange(Context context, final boolean desiredState)424         protected void requestStateChange(Context context, final boolean desiredState) {
425             if (sLocalBluetoothManager == null) {
426                 Log.d(TAG, "No LocalBluetoothManager");
427                 return;
428             }
429             // Actually request the Bluetooth change and persistent
430             // settings write off the UI thread, as it can take a
431             // user-noticeable amount of time, especially if there's
432             // disk contention.
433             new AsyncTask<Void, Void, Void>() {
434                 @Override
435                 protected Void doInBackground(Void... args) {
436                     sLocalBluetoothManager.setBluetoothEnabled(desiredState);
437                     return null;
438                 }
439             }.execute();
440         }
441 
442         @Override
onActualStateChange(Context context, Intent intent)443         public void onActualStateChange(Context context, Intent intent) {
444             if (!BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
445                 return;
446             }
447             int bluetoothState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
448             setCurrentState(context, bluetoothStateToFiveState(bluetoothState));
449         }
450 
451         /**
452          * Converts BluetoothAdapter's state values into our
453          * Wifi/Bluetooth-common state values.
454          */
bluetoothStateToFiveState(int bluetoothState)455         private static int bluetoothStateToFiveState(int bluetoothState) {
456             switch (bluetoothState) {
457                 case BluetoothAdapter.STATE_OFF:
458                     return STATE_DISABLED;
459                 case BluetoothAdapter.STATE_ON:
460                     return STATE_ENABLED;
461                 case BluetoothAdapter.STATE_TURNING_ON:
462                     return STATE_TURNING_ON;
463                 case BluetoothAdapter.STATE_TURNING_OFF:
464                     return STATE_TURNING_OFF;
465                 default:
466                     return STATE_UNKNOWN;
467             }
468         }
469     }
470 
471     /**
472      * Subclass of StateTracker for GPS state.
473      */
474     private static final class GpsStateTracker extends StateTracker {
getButtonId()475         public int getButtonId() { return R.id.img_gps; }
getIndicatorId()476         public int getIndicatorId() { return R.id.ind_gps; }
getButtonImageId(boolean on)477         public int getButtonImageId(boolean on) {
478             return on ? R.drawable.ic_appwidget_settings_gps_on
479                     : R.drawable.ic_appwidget_settings_gps_off;
480         }
481 
482         @Override
getActualState(Context context)483         public int getActualState(Context context) {
484             ContentResolver resolver = context.getContentResolver();
485             boolean on = Settings.Secure.isLocationProviderEnabled(
486                 resolver, LocationManager.GPS_PROVIDER);
487             return on ? STATE_ENABLED : STATE_DISABLED;
488         }
489 
490         @Override
onActualStateChange(Context context, Intent unused)491         public void onActualStateChange(Context context, Intent unused) {
492             // Note: the broadcast location providers changed intent
493             // doesn't include an extras bundles saying what the new value is.
494             setCurrentState(context, getActualState(context));
495         }
496 
497         @Override
requestStateChange(final Context context, final boolean desiredState)498         public void requestStateChange(final Context context, final boolean desiredState) {
499             final ContentResolver resolver = context.getContentResolver();
500             new AsyncTask<Void, Void, Boolean>() {
501                 @Override
502                 protected Boolean doInBackground(Void... args) {
503                     Settings.Secure.setLocationProviderEnabled(
504                         resolver,
505                         LocationManager.GPS_PROVIDER,
506                         desiredState);
507                     return desiredState;
508                 }
509 
510                 @Override
511                 protected void onPostExecute(Boolean result) {
512                     setCurrentState(
513                         context,
514                         result ? STATE_ENABLED : STATE_DISABLED);
515                     updateWidget(context);
516                 }
517             }.execute();
518         }
519     }
520 
521     /**
522      * Subclass of StateTracker for sync state.
523      */
524     private static final class SyncStateTracker extends StateTracker {
getButtonId()525         public int getButtonId() { return R.id.img_sync; }
getIndicatorId()526         public int getIndicatorId() { return R.id.ind_sync; }
getButtonImageId(boolean on)527         public int getButtonImageId(boolean on) {
528             return on ? R.drawable.ic_appwidget_settings_sync_on
529                     : R.drawable.ic_appwidget_settings_sync_off;
530         }
531 
532         @Override
getActualState(Context context)533         public int getActualState(Context context) {
534             boolean on = getBackgroundDataState(context) &&
535                     ContentResolver.getMasterSyncAutomatically();
536             return on ? STATE_ENABLED : STATE_DISABLED;
537         }
538 
539         @Override
onActualStateChange(Context context, Intent unused)540         public void onActualStateChange(Context context, Intent unused) {
541             setCurrentState(context, getActualState(context));
542         }
543 
544         @Override
requestStateChange(final Context context, final boolean desiredState)545         public void requestStateChange(final Context context, final boolean desiredState) {
546             final ConnectivityManager connManager =
547                     (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
548             final boolean backgroundData = getBackgroundDataState(context);
549             final boolean sync = ContentResolver.getMasterSyncAutomatically();
550 
551             new AsyncTask<Void, Void, Boolean>() {
552                 @Override
553                 protected Boolean doInBackground(Void... args) {
554                     // Turning sync on.
555                     if (desiredState) {
556                         if (!backgroundData) {
557                             connManager.setBackgroundDataSetting(true);
558                         }
559                         if (!sync) {
560                             ContentResolver.setMasterSyncAutomatically(true);
561                         }
562                         return true;
563                     }
564 
565                     // Turning sync off
566                     if (sync) {
567                         ContentResolver.setMasterSyncAutomatically(false);
568                     }
569                     return false;
570                 }
571 
572                 @Override
573                 protected void onPostExecute(Boolean result) {
574                     setCurrentState(
575                         context,
576                         result ? STATE_ENABLED : STATE_DISABLED);
577                     updateWidget(context);
578                 }
579             }.execute();
580         }
581     }
582 
583     @Override
onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)584     public void onUpdate(Context context, AppWidgetManager appWidgetManager,
585             int[] appWidgetIds) {
586         // Update each requested appWidgetId
587         RemoteViews view = buildUpdate(context, -1);
588 
589         for (int i = 0; i < appWidgetIds.length; i++) {
590             appWidgetManager.updateAppWidget(appWidgetIds[i], view);
591         }
592     }
593 
594     @Override
onEnabled(Context context)595     public void onEnabled(Context context) {
596         PackageManager pm = context.getPackageManager();
597         pm.setComponentEnabledSetting(
598                 new ComponentName("com.android.settings", ".widget.SettingsAppWidgetProvider"),
599                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
600                 PackageManager.DONT_KILL_APP);
601     }
602 
603     @Override
onDisabled(Context context)604     public void onDisabled(Context context) {
605         Class clazz = com.android.settings.widget.SettingsAppWidgetProvider.class;
606         PackageManager pm = context.getPackageManager();
607         pm.setComponentEnabledSetting(
608                 new ComponentName("com.android.settings", ".widget.SettingsAppWidgetProvider"),
609                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
610                 PackageManager.DONT_KILL_APP);
611     }
612 
613     /**
614      * Load image for given widget and build {@link RemoteViews} for it.
615      */
buildUpdate(Context context, int appWidgetId)616     static RemoteViews buildUpdate(Context context, int appWidgetId) {
617         RemoteViews views = new RemoteViews(context.getPackageName(),
618                 R.layout.widget);
619         views.setOnClickPendingIntent(R.id.btn_wifi, getLaunchPendingIntent(context, appWidgetId,
620                 BUTTON_WIFI));
621         views.setOnClickPendingIntent(R.id.btn_brightness,
622                 getLaunchPendingIntent(context,
623                         appWidgetId, BUTTON_BRIGHTNESS));
624         views.setOnClickPendingIntent(R.id.btn_sync,
625                 getLaunchPendingIntent(context,
626                         appWidgetId, BUTTON_SYNC));
627         views.setOnClickPendingIntent(R.id.btn_gps,
628                 getLaunchPendingIntent(context, appWidgetId, BUTTON_GPS));
629         views.setOnClickPendingIntent(R.id.btn_bluetooth,
630                 getLaunchPendingIntent(context,
631                         appWidgetId, BUTTON_BLUETOOTH));
632 
633         updateButtons(views, context);
634         return views;
635     }
636 
637     /**
638      * Updates the widget when something changes, or when a button is pushed.
639      *
640      * @param context
641      */
updateWidget(Context context)642     public static void updateWidget(Context context) {
643         RemoteViews views = buildUpdate(context, -1);
644         // Update specific list of appWidgetIds if given, otherwise default to all
645         final AppWidgetManager gm = AppWidgetManager.getInstance(context);
646         gm.updateAppWidget(THIS_APPWIDGET, views);
647     }
648 
649     /**
650      * Updates the buttons based on the underlying states of wifi, etc.
651      *
652      * @param views   The RemoteViews to update.
653      * @param context
654      */
updateButtons(RemoteViews views, Context context)655     private static void updateButtons(RemoteViews views, Context context) {
656         sWifiState.setImageViewResources(context, views);
657         sBluetoothState.setImageViewResources(context, views);
658         sGpsState.setImageViewResources(context, views);
659         sSyncState.setImageViewResources(context, views);
660 
661         if (getBrightnessMode(context)) {
662             views.setImageViewResource(R.id.img_brightness,
663                                        R.drawable.ic_appwidget_settings_brightness_auto);
664             views.setImageViewResource(R.id.ind_brightness,
665                                        R.drawable.appwidget_settings_ind_on_r);
666         } else if (getBrightness(context)) {
667             views.setImageViewResource(R.id.img_brightness,
668                                        R.drawable.ic_appwidget_settings_brightness_on);
669             views.setImageViewResource(R.id.ind_brightness,
670                                        R.drawable.appwidget_settings_ind_on_r);
671         } else {
672             views.setImageViewResource(R.id.img_brightness,
673                                        R.drawable.ic_appwidget_settings_brightness_off);
674             views.setImageViewResource(R.id.ind_brightness,
675                                        R.drawable.appwidget_settings_ind_off_r);
676         }
677     }
678 
679     /**
680      * Creates PendingIntent to notify the widget of a button click.
681      *
682      * @param context
683      * @param appWidgetId
684      * @return
685      */
getLaunchPendingIntent(Context context, int appWidgetId, int buttonId)686     private static PendingIntent getLaunchPendingIntent(Context context, int appWidgetId,
687             int buttonId) {
688         Intent launchIntent = new Intent();
689         launchIntent.setClass(context, SettingsAppWidgetProvider.class);
690         launchIntent.addCategory(Intent.CATEGORY_ALTERNATIVE);
691         launchIntent.setData(Uri.parse("custom:" + buttonId));
692         PendingIntent pi = PendingIntent.getBroadcast(context, 0 /* no requestCode */,
693                 launchIntent, 0 /* no flags */);
694         return pi;
695     }
696 
697     /**
698      * Receives and processes a button pressed intent or state change.
699      *
700      * @param context
701      * @param intent  Indicates the pressed button.
702      */
703     @Override
onReceive(Context context, Intent intent)704     public void onReceive(Context context, Intent intent) {
705         super.onReceive(context, intent);
706         String action = intent.getAction();
707         if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
708             sWifiState.onActualStateChange(context, intent);
709         } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
710             sBluetoothState.onActualStateChange(context, intent);
711         } else if (LocationManager.PROVIDERS_CHANGED_ACTION.equals(action)) {
712             sGpsState.onActualStateChange(context, intent);
713         } else if (SyncStorageEngine.SYNC_CONNECTION_SETTING_CHANGED_INTENT.getAction()
714                 .equals(action)) {
715             sSyncState.onActualStateChange(context, intent);
716         } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
717             Uri data = intent.getData();
718             int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
719             if (buttonId == BUTTON_WIFI) {
720                 sWifiState.toggleState(context);
721             } else if (buttonId == BUTTON_BRIGHTNESS) {
722                 toggleBrightness(context);
723             } else if (buttonId == BUTTON_SYNC) {
724                 sSyncState.toggleState(context);
725             } else if (buttonId == BUTTON_GPS) {
726                 sGpsState.toggleState(context);
727             } else if (buttonId == BUTTON_BLUETOOTH) {
728                 sBluetoothState.toggleState(context);
729             }
730         } else {
731             // Don't fall-through to updating the widget.  The Intent
732             // was something unrelated or that our super class took
733             // care of.
734             return;
735         }
736 
737         // State changes fall through
738         updateWidget(context);
739     }
740 
741     /**
742      * Gets the state of background data.
743      *
744      * @param context
745      * @return true if enabled
746      */
getBackgroundDataState(Context context)747     private static boolean getBackgroundDataState(Context context) {
748         ConnectivityManager connManager =
749                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
750         return connManager.getBackgroundDataSetting();
751     }
752 
753     /**
754      * Gets state of brightness.
755      *
756      * @param context
757      * @return true if more than moderately bright.
758      */
getBrightness(Context context)759     private static boolean getBrightness(Context context) {
760         try {
761             IPowerManager power = IPowerManager.Stub.asInterface(
762                     ServiceManager.getService("power"));
763             if (power != null) {
764                 int brightness = Settings.System.getInt(context.getContentResolver(),
765                         Settings.System.SCREEN_BRIGHTNESS);
766                 return brightness > 100;
767             }
768         } catch (Exception e) {
769             Log.d(TAG, "getBrightness: " + e);
770         }
771         return false;
772     }
773 
774     /**
775      * Gets state of brightness mode.
776      *
777      * @param context
778      * @return true if auto brightness is on.
779      */
getBrightnessMode(Context context)780     private static boolean getBrightnessMode(Context context) {
781         try {
782             IPowerManager power = IPowerManager.Stub.asInterface(
783                     ServiceManager.getService("power"));
784             if (power != null) {
785                 int brightnessMode = Settings.System.getInt(context.getContentResolver(),
786                         Settings.System.SCREEN_BRIGHTNESS_MODE);
787                 return brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
788             }
789         } catch (Exception e) {
790             Log.d(TAG, "getBrightnessMode: " + e);
791         }
792         return false;
793     }
794 
795     /**
796      * Increases or decreases the brightness.
797      *
798      * @param context
799      */
toggleBrightness(Context context)800     private void toggleBrightness(Context context) {
801         try {
802             IPowerManager power = IPowerManager.Stub.asInterface(
803                     ServiceManager.getService("power"));
804             if (power != null) {
805                 ContentResolver cr = context.getContentResolver();
806                 int brightness = Settings.System.getInt(cr,
807                         Settings.System.SCREEN_BRIGHTNESS);
808                 int brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
809                 //Only get brightness setting if available
810                 if (context.getResources().getBoolean(
811                         com.android.internal.R.bool.config_automatic_brightness_available)) {
812                     brightnessMode = Settings.System.getInt(cr,
813                             Settings.System.SCREEN_BRIGHTNESS_MODE);
814                 }
815 
816                 // Rotate AUTO -> MINIMUM -> DEFAULT -> MAXIMUM
817                 // Technically, not a toggle...
818                 if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC) {
819                     brightness = MINIMUM_BACKLIGHT;
820                     brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
821                 } else if (brightness < DEFAULT_BACKLIGHT) {
822                     brightness = DEFAULT_BACKLIGHT;
823                 } else if (brightness < MAXIMUM_BACKLIGHT) {
824                     brightness = MAXIMUM_BACKLIGHT;
825                 } else {
826                     brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
827                     brightness = MINIMUM_BACKLIGHT;
828                 }
829 
830                 if (context.getResources().getBoolean(
831                         com.android.internal.R.bool.config_automatic_brightness_available)) {
832                     // Set screen brightness mode (automatic or manual)
833                     Settings.System.putInt(context.getContentResolver(),
834                             Settings.System.SCREEN_BRIGHTNESS_MODE,
835                             brightnessMode);
836                 } else {
837                     // Make sure we set the brightness if automatic mode isn't available
838                     brightnessMode = Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
839                 }
840                 if (brightnessMode == Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL) {
841                     power.setBacklightBrightness(brightness);
842                     Settings.System.putInt(cr, Settings.System.SCREEN_BRIGHTNESS, brightness);
843                 }
844             }
845         } catch (RemoteException e) {
846             Log.d(TAG, "toggleBrightness: " + e);
847         } catch (Settings.SettingNotFoundException e) {
848             Log.d(TAG, "toggleBrightness: " + e);
849         }
850     }
851 }
852