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