diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 204da691522..150e27deda4 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -520,9 +520,16 @@ + + + + + + + + + + + - - - - diff --git a/res/drawable/icon.png b/res/drawable/icon.png new file mode 100755 index 00000000000..16db056f788 Binary files /dev/null and b/res/drawable/icon.png differ diff --git a/res/drawable/widget_bg.9.png b/res/drawable/widget_bg.9.png new file mode 100644 index 00000000000..a2007ad92e0 Binary files /dev/null and b/res/drawable/widget_bg.9.png differ diff --git a/res/drawable/widget_btn.xml b/res/drawable/widget_btn.xml new file mode 100644 index 00000000000..720bc4999af --- /dev/null +++ b/res/drawable/widget_btn.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + diff --git a/res/drawable/widget_btn_bluetooth.png b/res/drawable/widget_btn_bluetooth.png new file mode 100644 index 00000000000..5de9d78f856 Binary files /dev/null and b/res/drawable/widget_btn_bluetooth.png differ diff --git a/res/drawable/widget_btn_bluetooth_gray.png b/res/drawable/widget_btn_bluetooth_gray.png new file mode 100644 index 00000000000..843dccef75b Binary files /dev/null and b/res/drawable/widget_btn_bluetooth_gray.png differ diff --git a/res/drawable/widget_btn_bluetooth_off.png b/res/drawable/widget_btn_bluetooth_off.png new file mode 100644 index 00000000000..b831a46bca0 Binary files /dev/null and b/res/drawable/widget_btn_bluetooth_off.png differ diff --git a/res/drawable/widget_btn_brightness.png b/res/drawable/widget_btn_brightness.png new file mode 100644 index 00000000000..a162899e218 Binary files /dev/null and b/res/drawable/widget_btn_brightness.png differ diff --git a/res/drawable/widget_btn_brightness_off.png b/res/drawable/widget_btn_brightness_off.png new file mode 100644 index 00000000000..0f42f2dae97 Binary files /dev/null and b/res/drawable/widget_btn_brightness_off.png differ diff --git a/res/drawable/widget_btn_default.9.png b/res/drawable/widget_btn_default.9.png new file mode 100644 index 00000000000..febf2223eb5 Binary files /dev/null and b/res/drawable/widget_btn_default.9.png differ diff --git a/res/drawable/widget_btn_gps.png b/res/drawable/widget_btn_gps.png new file mode 100644 index 00000000000..3394cb40b86 Binary files /dev/null and b/res/drawable/widget_btn_gps.png differ diff --git a/res/drawable/widget_btn_gps_off.png b/res/drawable/widget_btn_gps_off.png new file mode 100644 index 00000000000..7f481d8be3b Binary files /dev/null and b/res/drawable/widget_btn_gps_off.png differ diff --git a/res/drawable/widget_btn_pressed.9.png b/res/drawable/widget_btn_pressed.9.png new file mode 100644 index 00000000000..70a200b9605 Binary files /dev/null and b/res/drawable/widget_btn_pressed.9.png differ diff --git a/res/drawable/widget_btn_selected.9.png b/res/drawable/widget_btn_selected.9.png new file mode 100644 index 00000000000..6f2989fa1f9 Binary files /dev/null and b/res/drawable/widget_btn_selected.9.png differ diff --git a/res/drawable/widget_btn_sync.png b/res/drawable/widget_btn_sync.png new file mode 100644 index 00000000000..9682879e373 Binary files /dev/null and b/res/drawable/widget_btn_sync.png differ diff --git a/res/drawable/widget_btn_sync_off.png b/res/drawable/widget_btn_sync_off.png new file mode 100644 index 00000000000..0f48a1d2e87 Binary files /dev/null and b/res/drawable/widget_btn_sync_off.png differ diff --git a/res/drawable/widget_btn_wifi.png b/res/drawable/widget_btn_wifi.png new file mode 100644 index 00000000000..ff7d2ef885c Binary files /dev/null and b/res/drawable/widget_btn_wifi.png differ diff --git a/res/drawable/widget_btn_wifi_gray.png b/res/drawable/widget_btn_wifi_gray.png new file mode 100644 index 00000000000..8a3cc49c229 Binary files /dev/null and b/res/drawable/widget_btn_wifi_gray.png differ diff --git a/res/drawable/widget_btn_wifi_off.png b/res/drawable/widget_btn_wifi_off.png new file mode 100644 index 00000000000..8f15b4f9d48 Binary files /dev/null and b/res/drawable/widget_btn_wifi_off.png differ diff --git a/res/layout/widget.xml b/res/layout/widget.xml new file mode 100644 index 00000000000..63ce5a50895 --- /dev/null +++ b/res/layout/widget.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 9ea852b6fef..62120fcaee6 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1649,6 +1649,6 @@ found in the list of installed applications. Refresh + + Power Control - - diff --git a/res/xml/appwidget_info.xml b/res/xml/appwidget_info.xml new file mode 100644 index 00000000000..2186dab6592 --- /dev/null +++ b/res/xml/appwidget_info.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/src/com/android/settings/widget/SettingsAppWidgetProvider.java b/src/com/android/settings/widget/SettingsAppWidgetProvider.java new file mode 100644 index 00000000000..002816f8b81 --- /dev/null +++ b/src/com/android/settings/widget/SettingsAppWidgetProvider.java @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.widget; + +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.bluetooth.BluetoothDevice; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.IContentService; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.LocationManager; +import android.net.ConnectivityManager; +import android.net.Uri; +import android.net.wifi.WifiManager; +import android.os.IHardwareService; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; +import android.util.Log; +import android.widget.RemoteViews; +import com.android.settings.R; +import com.android.settings.bluetooth.LocalBluetoothManager; + +/** + * Provides control of power-related settings from a widget. + */ +public class SettingsAppWidgetProvider extends AppWidgetProvider { + static final String TAG = "SettingsAppWidgetProvider"; + + static final ComponentName THIS_APPWIDGET = + new ComponentName("com.android.settings", + "com.android.settings.widget.SettingsAppWidgetProvider"); + + private static LocalBluetoothManager mLocalBluetoothManager = null; + + private static final int BUTTON_WIFI = 0; + private static final int BUTTON_BRIGHTNESS = 1; + private static final int BUTTON_SYNC = 2; + private static final int BUTTON_GPS = 3; + private static final int BUTTON_BLUETOOTH = 4; + + private static final int STATE_DISABLED = 0; + private static final int STATE_ENABLED = 1; + private static final int STATE_INTERMEDIATE = 2; + + /** + * Minimum and maximum brightnesses. Don't go to 0 since that makes the display unusable + */ + private static final int MINIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_DIM + 10; + private static final int MAXIMUM_BACKLIGHT = android.os.Power.BRIGHTNESS_ON; + private static final int DEFAULT_BACKLIGHT = (int) (android.os.Power.BRIGHTNESS_ON * 0.4f); + + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, + int[] appWidgetIds) { + // Update each requested appWidgetId + RemoteViews view = buildUpdate(context, -1); + + for (int i = 0; i < appWidgetIds.length; i++) { + appWidgetManager.updateAppWidget(appWidgetIds[i], view); + } + } + + @Override + public void onEnabled(Context context) { + PackageManager pm = context.getPackageManager(); + pm.setComponentEnabledSetting( + new ComponentName("com.android.settings", ".widget.SettingsAppWidgetProvider"), + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP); + } + + @Override + public void onDisabled(Context context) { + Class clazz = com.android.settings.widget.SettingsAppWidgetProvider.class; + PackageManager pm = context.getPackageManager(); + pm.setComponentEnabledSetting( + new ComponentName("com.android.settings", ".widget.SettingsAppWidgetProvider"), + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + } + + /** + * Load image for given widget and build {@link RemoteViews} for it. + */ + static RemoteViews buildUpdate(Context context, int appWidgetId) { + RemoteViews views = new RemoteViews(context.getPackageName(), + R.layout.widget); + views.setOnClickPendingIntent(R.id.btn_wifi, getLaunchPendingIntent(context, appWidgetId, + BUTTON_WIFI)); + views.setOnClickPendingIntent(R.id.btn_brightness, + getLaunchPendingIntent(context, + appWidgetId, BUTTON_BRIGHTNESS)); + views.setOnClickPendingIntent(R.id.btn_sync, + getLaunchPendingIntent(context, + appWidgetId, BUTTON_SYNC)); + views.setOnClickPendingIntent(R.id.btn_gps, + getLaunchPendingIntent(context, appWidgetId, BUTTON_GPS)); + views.setOnClickPendingIntent(R.id.btn_bluetooth, + getLaunchPendingIntent(context, + appWidgetId, BUTTON_BLUETOOTH)); + + updateButtons(views, context); + return views; + } + + /** + * Updates the widget when something changes, or when a button is pushed. + * + * @param context + */ + public static void updateWidget(Context context) { + RemoteViews views = buildUpdate(context, -1); + // Update specific list of appWidgetIds if given, otherwise default to all + final AppWidgetManager gm = AppWidgetManager.getInstance(context); + gm.updateAppWidget(THIS_APPWIDGET, views); + } + + /** + * Updates the buttons based on the underlying states of wifi, etc. + * + * @param views The RemoteViews to update. + * @param context + */ + private static void updateButtons(RemoteViews views, Context context) { + switch (getWifiState(context)) { + case STATE_DISABLED: + views.setImageViewResource(R.id.btn_wifi, R.drawable.widget_btn_wifi_off); + break; + case STATE_ENABLED: + views.setImageViewResource(R.id.btn_wifi, R.drawable.widget_btn_wifi); + break; + case STATE_INTERMEDIATE: + views.setImageViewResource(R.id.btn_wifi, R.drawable.widget_btn_wifi_gray); + break; + } + if (getBrightness(context)) { + views.setImageViewResource(R.id.btn_brightness, R.drawable.widget_btn_brightness); + } else { + views.setImageViewResource(R.id.btn_brightness, R.drawable.widget_btn_brightness_off); + } + if (getBackgroundDataState(context)) { + views.setImageViewResource(R.id.btn_sync, R.drawable.widget_btn_sync); + } else { + views.setImageViewResource(R.id.btn_sync, R.drawable.widget_btn_sync_off); + } + if (getGpsState(context)) { + views.setImageViewResource(R.id.btn_gps, R.drawable.widget_btn_gps); + } else { + views.setImageViewResource(R.id.btn_gps, R.drawable.widget_btn_gps_off); + } + switch (getBluetoothState(context)) { + case STATE_DISABLED: + views.setImageViewResource(R.id.btn_bluetooth, R.drawable.widget_btn_bluetooth_off); + break; + case STATE_ENABLED: + views.setImageViewResource(R.id.btn_bluetooth, R.drawable.widget_btn_bluetooth); + break; + case STATE_INTERMEDIATE: + views.setImageViewResource(R.id.btn_bluetooth, R.drawable.widget_btn_bluetooth_gray); + break; + } + } + + /** + * Creates PendingIntent to notify the widget of a button click. + * + * @param context + * @param appWidgetId + * @return + */ + private static PendingIntent getLaunchPendingIntent(Context context, int appWidgetId, int buttonId) { + Intent launchIntent = new Intent(); + launchIntent.setClass(context, SettingsAppWidgetProvider.class); + launchIntent.addCategory(Intent.CATEGORY_ALTERNATIVE); + launchIntent.setData(Uri.parse("custom:" + buttonId)); + PendingIntent pi = PendingIntent.getBroadcast(context, 0 /* no requestCode */, + launchIntent, 0 /* no flags */); + return pi; + } + + /** + * Receives and processes a button pressed intent or state change. + * + * @param context + * @param intent Indicates the pressed button. + */ + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) { + Uri data = intent.getData(); + int buttonId = Integer.parseInt(data.getSchemeSpecificPart()); + if (buttonId == BUTTON_WIFI) { + toggleWifi(context); + } else if (buttonId == BUTTON_BRIGHTNESS) { + toggleBrightness(context); + } else if (buttonId == BUTTON_SYNC) { + toggleBackgroundData(context); + } else if (buttonId == BUTTON_GPS) { + toggleGps(context); + } else if (buttonId == BUTTON_BLUETOOTH) { + toggleBluetooth(context); + } + } + // State changes fall through + updateWidget(context); + } + + /** + * Gets the state of Wi-Fi + * + * @param context + * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE + */ + private static int getWifiState(Context context) { + WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + int wifiState = wifiManager.getWifiState(); + if (wifiState == WifiManager.WIFI_STATE_DISABLED) { + return STATE_DISABLED; + } else if (wifiState == WifiManager.WIFI_STATE_ENABLED) { + return STATE_ENABLED; + } else { + return STATE_INTERMEDIATE; + } + } + + /** + * Toggles the state of Wi-Fi + * + * @param context + */ + private void toggleWifi(Context context) { + WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); + int wifiState = getWifiState(context); + if (wifiState == STATE_ENABLED) { + wifiManager.setWifiEnabled(false); + } else if (wifiState == STATE_DISABLED) { + wifiManager.setWifiEnabled(true); + mLocalBluetoothManager.setBluetoothEnabled(true); + } + } + + /** + * Gets the state of background data. + * + * @param context + * @return true if enabled + */ + private static boolean getBackgroundDataState(Context context) { + ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + return connManager.getBackgroundDataSetting(); + } + + /** + * Toggle background data and sync tickles. + * + * @param context + */ + private void toggleBackgroundData(Context context) { + ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + boolean sync = getBackgroundDataState(context); + connManager.setBackgroundDataSetting(!sync); + + IContentService contentService = ContentResolver.getContentService(); + try { + contentService.setListenForNetworkTickles(!sync); + } catch (RemoteException e) { + Log.d(TAG, "toggleBackgroundData: " + e); + } + } + + /** + * Gets the state of GPS location. + * + * @param context + * @return true if enabled. + */ + private static boolean getGpsState(Context context) { + ContentResolver resolver = context.getContentResolver(); + return Settings.Secure.isLocationProviderEnabled(resolver, LocationManager.GPS_PROVIDER); + } + + /** + * Toggles the state of GPS. + * + * @param context + */ + private void toggleGps(Context context) { + ContentResolver resolver = context.getContentResolver(); + boolean enabled = getGpsState(context); + Settings.Secure.setLocationProviderEnabled(resolver, LocationManager.GPS_PROVIDER, !enabled); + } + + /** + * Gets state of brightness. + * + * @param context + * @return true if more than moderately bright. + */ + private static boolean getBrightness(Context context) { + try { + IHardwareService hardware = IHardwareService.Stub.asInterface( + ServiceManager.getService("hardware")); + if (hardware != null) { + int brightness = Settings.System.getInt(context.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS); + return brightness > 100; + } + } catch (Exception e) { + Log.d(TAG, "getBrightness: " + e); + } + return false; + } + + /** + * Increases or decreases the brightness. + * + * @param context + */ + private void toggleBrightness(Context context) { + try { + IHardwareService hardware = IHardwareService.Stub.asInterface( + ServiceManager.getService("hardware")); + if (hardware != null) { + ContentResolver cr = context.getContentResolver(); + int brightness = Settings.System.getInt(cr, + Settings.System.SCREEN_BRIGHTNESS); + // Rotate MINIMUM -> DEFAULT -> MAXIMUM + // Technically, not a toggle... + if (brightness < DEFAULT_BACKLIGHT) { + brightness = DEFAULT_BACKLIGHT; + } else if (brightness < MAXIMUM_BACKLIGHT) { + brightness = MAXIMUM_BACKLIGHT; + } else { + brightness = MINIMUM_BACKLIGHT; + } + hardware.setBacklights(brightness); + Settings.System.putInt(cr, Settings.System.SCREEN_BRIGHTNESS, brightness); + brightness = Settings.System.getInt(cr, + Settings.System.SCREEN_BRIGHTNESS); + } + } catch (RemoteException e) { + Log.d(TAG, "toggleBrightness: " + e); + } catch (Settings.SettingNotFoundException e) { + Log.d(TAG, "toggleBrightness: " + e); + } + } + + /** + * Gets state of bluetooth + * + * @param context + * @return STATE_ENABLED, STATE_DISABLED, or STATE_INTERMEDIATE + */ + private static int getBluetoothState(Context context) { + if (mLocalBluetoothManager == null) { + mLocalBluetoothManager = LocalBluetoothManager.getInstance(context); + if (mLocalBluetoothManager == null) { + return STATE_INTERMEDIATE; // On emulator? + } + } + int state = mLocalBluetoothManager.getBluetoothState(); + if (state == BluetoothDevice.BLUETOOTH_STATE_OFF) { + return STATE_DISABLED; + } else if (state == BluetoothDevice.BLUETOOTH_STATE_ON) { + return STATE_ENABLED; + } else { + return STATE_INTERMEDIATE; + } + } + + /** + * Toggles the state of bluetooth + * + * @param context + */ + private void toggleBluetooth(Context context) { + int state = getBluetoothState(context); + if (state == STATE_ENABLED) { + mLocalBluetoothManager.setBluetoothEnabled(false); + } else if (state == STATE_DISABLED) { + mLocalBluetoothManager.setBluetoothEnabled(true); + } + } +}