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