summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
authorJohn Spurlock <jspurlock@google.com>2014-03-10 17:44:07 -0400
committerJohn Spurlock <jspurlock@google.com>2014-06-11 19:38:00 -0400
commit3332ba54ae85df14d761447d86d2aa19d448ce11 (patch)
tree34f932300125edb0e25a745fd1359741eab65800 /packages
parent52769be3ea1fd005ea66b7b08888b3009ac0dadf (diff)
downloadframeworks_base-3332ba54ae85df14d761447d86d2aa19d448ce11.zip
frameworks_base-3332ba54ae85df14d761447d86d2aa19d448ce11.tar.gz
frameworks_base-3332ba54ae85df14d761447d86d2aa19d448ce11.tar.bz2
Heads-up notifications for low battery warnings.
Falls back to dialogs when heads-up are N/A. Add new ongoing notification if battery saver mode is active. Offer to start battery saver on warnings, if not already started. True up BatteryMeterView's levels to the latest threshold levels. 15% for first warning. Bug:13329308 Change-Id: Id8ad11a1997079ee7165ae003a8fa1c744462ab3
Diffstat (limited to 'packages')
-rw-r--r--packages/SystemUI/res/drawable-hdpi/battery_low_battery.pngbin1821 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-mdpi/battery_low_battery.pngbin1311 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xhdpi/battery_low_battery.pngbin2417 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.pngbin3061 -> 0 bytes
-rw-r--r--packages/SystemUI/res/drawable/ic_power_low.xml28
-rw-r--r--packages/SystemUI/res/drawable/ic_power_saver.xml28
-rw-r--r--packages/SystemUI/res/layout/battery_low.xml54
-rw-r--r--packages/SystemUI/res/values/arrays.xml2
-rw-r--r--packages/SystemUI/res/values/strings.xml37
-rwxr-xr-xpackages/SystemUI/src/com/android/systemui/BatteryMeterView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java391
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java75
13 files changed, 587 insertions, 108 deletions
diff --git a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png
deleted file mode 100644
index e6af81e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
deleted file mode 100644
index e865f4c..0000000
--- a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png
deleted file mode 100644
index 83693c1..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png
deleted file mode 100644
index cebbb15..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_power_low.xml b/packages/SystemUI/res/drawable/ic_power_low.xml
new file mode 100644
index 0000000..5bb7aba
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_low.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="24.0dp"
+ android:height="24.0dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M30.0,6.0L30.0,2.0L18.0,2.0l0.0,4.0l-8.0,0.0l0.0,40.0l28.0,0.0L38.0,6.0L30.0,6.0zM26.0,37.0l-4.0,0.0l0.0,-4.0l4.0,0.0L26.0,37.0zM26.0,30.0l-4.0,0.0L22.0,15.0l4.0,0.0L26.0,30.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_power_saver.xml b/packages/SystemUI/res/drawable/ic_power_saver.xml
new file mode 100644
index 0000000..26e7375
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_power_saver.xml
@@ -0,0 +1,28 @@
+<!--
+Copyright (C) 2014 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <size
+ android:width="24.0dp"
+ android:height="24.0dp"/>
+
+ <viewport
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0"/>
+
+ <path
+ android:fill="#FFFFFFFF"
+ android:pathData="M30.0,6.0L30.0,2.0L18.0,2.0l0.0,4.0l-8.0,0.0l0.0,40.0l28.0,0.0L38.0,6.0L30.0,6.0zM32.0,28.0l-6.0,0.0l0.0,6.0l-4.0,0.0l0.0,-6.0l-6.0,0.0l0.0,-4.0l6.0,0.0l0.0,-6.0l4.0,0.0l0.0,6.0l6.0,0.0L32.0,28.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/battery_low.xml b/packages/SystemUI/res/layout/battery_low.xml
deleted file mode 100644
index 2373355..0000000
--- a/packages/SystemUI/res/layout/battery_low.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/res/layout/keyguard.xml
-**
-** Copyright 2007, 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.
-*/
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/padding"
- android:orientation="vertical"
- android:gravity="center"
- android:padding="16dp"
- >
-
- <TextView android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:textColor="#ffffffff"
- android:gravity="start"
- android:text="@string/battery_low_subtitle"
- />
-
- <TextView android:id="@+id/level_percent"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:textColor="#ffffffff"
- android:gravity="start"
- android:paddingBottom="16dp"
- />
-
- <ImageView android:id="@+id/image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/battery_low_battery"
- />
-
-</LinearLayout>
-
-
diff --git a/packages/SystemUI/res/values/arrays.xml b/packages/SystemUI/res/values/arrays.xml
index 1ce4983..6628f3b 100644
--- a/packages/SystemUI/res/values/arrays.xml
+++ b/packages/SystemUI/res/values/arrays.xml
@@ -42,13 +42,11 @@
<!-- BatteryMeterView parameters -->
<array name="batterymeter_color_levels">
- <item>4</item>
<item>15</item>
<item>100</item>
</array>
<array name="batterymeter_color_values">
<item>#FFFF3300</item>
- <item>#FFFF3300</item>
<item>#FFFFFFFF</item>
</array>
<array name="batterymeter_bolt_points">
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index de297e5..260f59c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -59,25 +59,43 @@
<string name="status_bar_latest_events_title">Notifications</string>
<!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. [CHAR LIMIT=NONE]-->
- <string name="battery_low_title">Connect charger</string>
-
- <!-- When the battery is low, this is displayed to the user in a dialog. The subtitle of the low battery alert. [CHAR LIMIT=NONE] -->
- <string name="battery_low_subtitle">The battery is getting low.</string>
+ <string name="battery_low_title">Battery is low</string>
<!-- A message that appears when the battery level is getting low in a dialog. This is
appened to the subtitle of the low battery alert. "number" is the percentage of battery
remaining [CHAR LIMIT=none]-->
<string name="battery_low_percent_format"><xliff:g id="number">%d%%</xliff:g> remaining</string>
+ <!-- Same as battery_low_percent_format, with a notice about battery saver if on. [CHAR LIMIT=none]-->
+ <string name="battery_low_percent_format_saver_started"><xliff:g id="number">%d%%</xliff:g> remaining. Battery saver is on.</string>
+
<!-- A message that appears when a USB charger is plugged in and the device does not
support charging on it. That is, a charger that fits into the USB port and goes into
a wall socket, not into a computer. (This happens because some devices require more
current than the USB spec allows. [CHAR LIMIT=NONE] -->
<string name="invalid_charger">USB charging not supported.\nUse only the supplied charger.</string>
+ <!-- First line of invalid_charger, used in the notification form. [CHAR LIMIT=NONE]-->
+ <string name="invalid_charger_title">USB charging not supported.</string>
+
+ <!-- Second line of invalid_charger, used in the notification form. [CHAR LIMIT=NONE]-->
+ <string name="invalid_charger_text">Use only the supplied charger.</string>
+
<!-- When the battery is low, this is the label of the button to go to the
power usage activity to find out what drained the battery. [CHAR LIMIT=30] -->
- <string name="battery_low_why">Battery use</string>
+ <string name="battery_low_why">Settings</string>
+
+ <!-- Battery saver confirmation dialog title [CHAR LIMIT=NONE]-->
+ <string name="battery_saver_confirmation_title">Start battery saver?</string>
+
+ <!-- Battery saver confirmation dialog ok text [CHAR LIMIT=40]-->
+ <string name="battery_saver_confirmation_ok">Start</string>
+
+ <!-- Battery saver notification action [CHAR LIMIT=NONE]-->
+ <string name="battery_saver_start_action">Start battery saver</string>
+
+ <!-- Battery saver confirmation dialog text [CHAR LIMIT=NONE]-->
+ <string name="battery_saver_confirmation_text">To help improve battery life, Battery saver will reduce your device’s performance.\n\nBattery saver will be disabled when your device is plugged in.</string>
<!-- Name of the button that links to the Settings app. [CHAR LIMIT=NONE] -->
<string name="status_bar_settings_settings_button">Settings</string>
@@ -595,4 +613,13 @@
<item quantity="one">For one hour</item>
<item quantity="other">For %d hours</item>
</plurals>
+
+ <!-- Battery saver notification title. [CHAR LIMIT=60]-->
+ <string name="battery_saver_notification_title">Battery saver is on</string>
+
+ <!-- Battery saver notification text. [CHAR LIMIT=60] -->
+ <string name="battery_saver_notification_text">Device performance is reduced.</string>
+
+ <!-- Battery saver notification action text. [CHAR LIMIT=60] -->
+ <string name="battery_saver_notification_action_text">Open battery saver settings</string>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 19d06be..5e48258 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -25,8 +25,6 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.BatteryManager;
@@ -44,7 +42,6 @@ public class BatteryMeterView extends View implements DemoMode {
private static final boolean SHOW_100_PERCENT = false;
private static final int FULL = 96;
- private static final int EMPTY = 4;
private static final float SUBPIXEL = 0.4f; // inset rects for softer edges
private static final float BOLT_LEVEL_THRESHOLD = 0.3f; // opaque bolt below this fraction
@@ -58,6 +55,7 @@ public class BatteryMeterView extends View implements DemoMode {
private int mHeight;
private int mWidth;
private String mWarningString;
+ private final int mCriticalLevel;
private final int mChargeColor;
private final float[] mBoltPoints;
private final Path mBoltPath = new Path();
@@ -197,6 +195,8 @@ public class BatteryMeterView extends View implements DemoMode {
mShowPercent = ENABLE_PERCENT && 0 != Settings.System.getInt(
context.getContentResolver(), "status_bar_show_battery_percent", 0);
mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
+ mCriticalLevel = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_criticalBatteryWarningLevel);
mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFramePaint.setColor(frameColor);
@@ -303,7 +303,7 @@ public class BatteryMeterView extends View implements DemoMode {
if (level >= FULL) {
drawFrac = 1f;
- } else if (level <= EMPTY) {
+ } else if (level <= mCriticalLevel) {
drawFrac = 0f;
}
@@ -360,7 +360,7 @@ public class BatteryMeterView extends View implements DemoMode {
boolean pctOpaque = false;
float pctX = 0, pctY = 0;
String pctText = null;
- if (!tracker.plugged && level > EMPTY && mShowPercent
+ if (!tracker.plugged && level > mCriticalLevel && mShowPercent
&& !(tracker.level == 100 && !SHOW_100_PERCENT)) {
mTextPaint.setColor(getColorForLevel(level));
mTextPaint.setTextSize(height *
@@ -390,7 +390,7 @@ public class BatteryMeterView extends View implements DemoMode {
c.drawPath(mShapePath, mBatteryPaint);
if (!tracker.plugged) {
- if (level <= EMPTY) {
+ if (level <= mCriticalLevel) {
// draw the warning text
final float x = mWidth * 0.5f;
final float y = (mHeight + mWarningTextHeight) * 0.48f;
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java
index feec87c..2943494 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerDialogWarnings.java
@@ -16,6 +16,7 @@
package com.android.systemui.power;
+import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
@@ -25,13 +26,13 @@ import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
-import android.view.View;
+import android.view.ContextThemeWrapper;
import android.view.WindowManager;
-import android.widget.TextView;
import com.android.systemui.R;
@@ -46,13 +47,14 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI {
private int mBatteryLevel;
private int mBucket;
private long mScreenOffTime;
+ private boolean mSaver;
+ private int mSaverTriggerLevel;
private AlertDialog mInvalidChargerDialog;
private AlertDialog mLowBatteryDialog;
- private TextView mBatteryLevelTextView;
public PowerDialogWarnings(Context context) {
- mContext = context;
+ mContext = new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault_Light);
}
@Override
@@ -77,7 +79,7 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI {
@Override
public void updateLowBatteryWarning() {
- if (mBatteryLevelTextView != null) {
+ if (mLowBatteryDialog != null) {
showLowBatteryWarning(false /*playSound*/);
}
}
@@ -93,27 +95,22 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI {
@Override
public void showLowBatteryWarning(boolean playSound) {
Slog.i(TAG,
- ((mBatteryLevelTextView == null) ? "showing" : "updating")
+ ((mLowBatteryDialog == null) ? "showing" : "updating")
+ " low battery warning: level=" + mBatteryLevel
+ " [" + mBucket + "]");
- CharSequence levelText = mContext.getString(
- R.string.battery_low_percent_format, mBatteryLevel);
+ final int textRes = mSaver ? R.string.battery_low_percent_format_saver_started
+ : R.string.battery_low_percent_format;
+ final CharSequence levelText = mContext.getString(textRes, mBatteryLevel);
- if (mBatteryLevelTextView != null) {
- mBatteryLevelTextView.setText(levelText);
+ if (mLowBatteryDialog != null) {
+ mLowBatteryDialog.setMessage(levelText);
} else {
- View v = View.inflate(mContext, R.layout.battery_low, null);
- mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);
-
- mBatteryLevelTextView.setText(levelText);
-
AlertDialog.Builder b = new AlertDialog.Builder(mContext);
- b.setCancelable(true);
- b.setTitle(R.string.battery_low_title);
- b.setView(v);
- b.setIconAttribute(android.R.attr.alertDialogIcon);
- b.setPositiveButton(android.R.string.ok, null);
+ b.setCancelable(true);
+ b.setTitle(R.string.battery_low_title);
+ b.setMessage(levelText);
+ b.setPositiveButton(android.R.string.ok, null);
final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -125,6 +122,11 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI {
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
+ try {
+ ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+ } catch (RemoteException e) {
+ // we tried
+ }
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
dismissLowBatteryWarning();
}
@@ -136,10 +138,9 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI {
@Override
public void onDismiss(DialogInterface dialog) {
mLowBatteryDialog = null;
- mBatteryLevelTextView = null;
}
});
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
d.getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
d.show();
@@ -198,21 +199,32 @@ public class PowerDialogWarnings implements PowerUI.WarningsUI {
dismissLowBatteryWarning();
AlertDialog.Builder b = new AlertDialog.Builder(mContext);
- b.setCancelable(true);
- b.setMessage(R.string.invalid_charger);
- b.setIconAttribute(android.R.attr.alertDialogIcon);
- b.setPositiveButton(android.R.string.ok, null);
+ b.setCancelable(true);
+ b.setTitle(R.string.invalid_charger_title);
+ b.setMessage(R.string.invalid_charger_text);
+ b.setPositiveButton(android.R.string.ok, null);
AlertDialog d = b.create();
d.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
mInvalidChargerDialog = null;
- mBatteryLevelTextView = null;
}
});
- d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ d.getWindow().getAttributes().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
d.show();
mInvalidChargerDialog = d;
}
+
+ @Override
+ public void showSaverMode(boolean mode) {
+ mSaver = mode;
+ }
+
+ @Override
+ public void setSaverTrigger(int level) {
+ mSaverTriggerLevel = level;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
new file mode 100644
index 0000000..3fa8b99
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2014 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.systemui.power;
+
+import android.app.AlertDialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.Slog;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.android.systemui.R;
+
+import java.io.PrintWriter;
+
+public class PowerNotificationWarnings implements PowerUI.WarningsUI {
+ private static final String TAG = PowerUI.TAG + ".Notification";
+ private static final boolean DEBUG = PowerUI.DEBUG;
+
+ private static final String TAG_NOTIFICATION = "low_battery";
+ private static final int ID_NOTIFICATION = 100;
+ private static final int AUTO_DISMISS_MS = 10000;
+
+ private static final int SHOWING_NOTHING = 0;
+ private static final int SHOWING_WARNING = 1;
+ private static final int SHOWING_SAVER = 2;
+ private static final int SHOWING_INVALID_CHARGER = 3;
+ private static final String[] SHOWING_STRINGS = {
+ "SHOWING_NOTHING",
+ "SHOWING_WARNING",
+ "SHOWING_SAVER",
+ "SHOWING_INVALID_CHARGER",
+ };
+
+ private static final String ACTION_SHOW_FALLBACK_WARNING = "PNW.warningFallback";
+ private static final String ACTION_SHOW_FALLBACK_CHARGER = "PNW.chargerFallback";
+ private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings";
+ private static final String ACTION_START_SAVER = "PNW.startSaver";
+
+ private final Context mContext;
+ private final Context mLightContext;
+ private final NotificationManager mNoMan;
+ private final Handler mHandler = new Handler();
+ private final PowerDialogWarnings mFallbackDialogs;
+ private final Receiver mReceiver = new Receiver();
+ private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY);
+ private final Intent mOpenSaverSettings = settings(Settings.ACTION_BATTERY_SAVER_SETTINGS);
+
+ private int mBatteryLevel;
+ private int mBucket;
+ private long mScreenOffTime;
+ private int mShowing;
+
+ private boolean mSaver;
+ private int mSaverTriggerLevel;
+ private boolean mWarning;
+ private boolean mPlaySound;
+ private boolean mInvalidCharger;
+
+ public PowerNotificationWarnings(Context context) {
+ mContext = context;
+ mLightContext = new ContextThemeWrapper(mContext,
+ android.R.style.Theme_DeviceDefault_Light);
+ mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ mFallbackDialogs = new PowerDialogWarnings(context);
+ mReceiver.init();
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.print("mSaver="); pw.println(mSaver);
+ pw.print("mWarning="); pw.println(mWarning);
+ pw.print("mPlaySound="); pw.println(mPlaySound);
+ pw.print("mInvalidCharger="); pw.println(mInvalidCharger);
+ pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]);
+ }
+
+ @Override
+ public void update(int batteryLevel, int bucket, long screenOffTime) {
+ mBatteryLevel = batteryLevel;
+ mBucket = bucket;
+ mScreenOffTime = screenOffTime;
+ mFallbackDialogs.update(batteryLevel, bucket, screenOffTime);
+ }
+
+ @Override
+ public void showSaverMode(boolean mode) {
+ mSaver = mode;
+ updateNotification();
+ }
+
+ @Override
+ public void setSaverTrigger(int level) {
+ mSaverTriggerLevel = level;
+ updateNotification();
+ }
+
+ private void cancelOther(int showing) {
+ if (mShowing != SHOWING_NOTHING && mShowing != showing) {
+ mNoMan.cancel(TAG_NOTIFICATION, ID_NOTIFICATION); // workaround no HUN on updates
+ }
+ }
+
+ private void updateNotification() {
+ Slog.d(TAG, "updateNotification mWarning=" + mWarning
+ + " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger);
+ if (mInvalidCharger) {
+ cancelOther(SHOWING_INVALID_CHARGER);
+ showInvalidChargerNotification();
+ mShowing = SHOWING_INVALID_CHARGER;
+ } else if (mWarning) {
+ cancelOther(SHOWING_WARNING);
+ showWarningNotification();
+ mShowing = SHOWING_WARNING;
+ } else if (mSaver) {
+ cancelOther(SHOWING_SAVER);
+ showSaverNotification();
+ mShowing = SHOWING_SAVER;
+ } else {
+ mNoMan.cancel(TAG_NOTIFICATION, ID_NOTIFICATION);
+ mShowing = SHOWING_NOTHING;
+ }
+ }
+
+ private void showInvalidChargerNotification() {
+ final Notification.Builder nb = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_power_low)
+ .setShowWhen(false)
+ .setOngoing(true)
+ .setContentTitle(mContext.getString(R.string.invalid_charger_title))
+ .setContentText(mContext.getString(R.string.invalid_charger_text))
+ .setPriority(Notification.PRIORITY_MAX)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_CHARGER), true);
+ final Notification n = nb.build();
+ if (n.headsUpContentView != null) {
+ n.headsUpContentView.setViewVisibility(com.android.internal.R.id.right_icon, View.GONE);
+ }
+ mNoMan.notifyAsUser(TAG_NOTIFICATION, ID_NOTIFICATION, n, UserHandle.CURRENT);
+ }
+
+ private void showWarningNotification() {
+ final int textRes = mSaver ? R.string.battery_low_percent_format_saver_started
+ : R.string.battery_low_percent_format;
+ final Notification.Builder nb = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_power_low)
+ .setShowWhen(false)
+ .setContentTitle(mContext.getString(R.string.battery_low_title))
+ .setContentText(mContext.getString(textRes, mBatteryLevel))
+ .setOngoing(true)
+ .setPriority(Notification.PRIORITY_MAX)
+ .setCategory(Notification.CATEGORY_SYSTEM)
+ .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_WARNING), true);
+ if (hasBatterySettings()) {
+ nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
+ }
+ if (!mSaver && mSaverTriggerLevel <= 0) {
+ nb.addAction(R.drawable.ic_power_saver,
+ mContext.getString(R.string.battery_saver_start_action),
+ pendingBroadcast(ACTION_START_SAVER));
+ }
+ if (mPlaySound) {
+ attachLowBatterySound(nb);
+ }
+ final Notification n = nb.build();
+ if (n.headsUpContentView != null) {
+ n.headsUpContentView.setViewVisibility(com.android.internal.R.id.right_icon, View.GONE);
+ }
+ mNoMan.notifyAsUser(TAG_NOTIFICATION, ID_NOTIFICATION, n, UserHandle.CURRENT);
+ }
+
+ private void showSaverNotification() {
+ final Notification.Builder nb = new Notification.Builder(mContext)
+ .setSmallIcon(R.drawable.ic_power_saver)
+ .setContentTitle(mContext.getString(R.string.battery_saver_notification_title))
+ .setContentText(mContext.getString(R.string.battery_saver_notification_text))
+ .setOngoing(true)
+ .setShowWhen(false)
+ .setCategory(Notification.CATEGORY_SYSTEM);
+ if (hasSaverSettings()) {
+ nb.addAction(0,
+ mContext.getString(R.string.battery_saver_notification_action_text),
+ pendingActivity(mOpenSaverSettings));
+ nb.setContentIntent(pendingActivity(mOpenSaverSettings));
+ }
+ mNoMan.notifyAsUser(TAG_NOTIFICATION, ID_NOTIFICATION, nb.build(), UserHandle.CURRENT);
+ }
+
+ private PendingIntent pendingActivity(Intent intent) {
+ return PendingIntent.getActivityAsUser(mContext,
+ 0, intent, 0, null, UserHandle.CURRENT);
+ }
+
+ private PendingIntent pendingBroadcast(String action) {
+ return PendingIntent.getBroadcastAsUser(mContext,
+ 0, new Intent(action), 0, UserHandle.CURRENT);
+ }
+
+ private static Intent settings(String action) {
+ return new Intent(action).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_NO_HISTORY
+ | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ }
+
+ @Override
+ public boolean isInvalidChargerWarningShowing() {
+ return mInvalidCharger;
+ }
+
+ @Override
+ public void updateLowBatteryWarning() {
+ updateNotification();
+ mFallbackDialogs.updateLowBatteryWarning();
+ }
+
+ @Override
+ public void dismissLowBatteryWarning() {
+ Slog.i(TAG, "dismissing low battery warning: level=" + mBatteryLevel);
+ dismissLowBatteryNotification();
+ mFallbackDialogs.dismissLowBatteryWarning();
+ }
+
+ private void dismissLowBatteryNotification() {
+ Slog.i(TAG, "dismissing low battery notification");
+ mWarning = false;
+ updateNotification();
+ }
+
+ private boolean hasBatterySettings() {
+ return mOpenBatterySettings.resolveActivity(mContext.getPackageManager()) != null;
+ }
+
+ private boolean hasSaverSettings() {
+ return mOpenSaverSettings.resolveActivity(mContext.getPackageManager()) != null;
+ }
+
+ @Override
+ public void showLowBatteryWarning(boolean playSound) {
+ Slog.i(TAG,
+ "show low battery warning: level=" + mBatteryLevel
+ + " [" + mBucket + "]");
+ mPlaySound = playSound;
+ mWarning = true;
+ updateNotification();
+ mHandler.removeCallbacks(mDismissLowBatteryNotification);
+ mHandler.postDelayed(mDismissLowBatteryNotification, AUTO_DISMISS_MS);
+ }
+
+ private void attachLowBatterySound(Notification.Builder b) {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ final int silenceAfter = Settings.Global.getInt(cr,
+ Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0);
+ final long offTime = SystemClock.elapsedRealtime() - mScreenOffTime;
+ if (silenceAfter > 0
+ && mScreenOffTime > 0
+ && offTime > silenceAfter) {
+ Slog.i(TAG, "screen off too long (" + offTime + "ms, limit " + silenceAfter
+ + "ms): not waking up the user with low battery sound");
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "playing low battery sound. pick-a-doop!"); // WOMP-WOMP is deprecated
+ }
+
+ if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {
+ final String soundPath = Settings.Global.getString(cr,
+ Settings.Global.LOW_BATTERY_SOUND);
+ if (soundPath != null) {
+ final Uri soundUri = Uri.parse("file://" + soundPath);
+ if (soundUri != null) {
+ b.setSound(soundUri, AudioManager.STREAM_SYSTEM);
+ Slog.d(TAG, "playing sound " + soundUri);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void dismissInvalidChargerWarning() {
+ mInvalidCharger = false;
+ updateNotification();
+ }
+
+ @Override
+ public void showInvalidChargerWarning() {
+ mInvalidCharger = true;
+ updateNotification();
+ }
+
+ private void showStartSaverConfirmation() {
+ final AlertDialog d = new AlertDialog.Builder(mLightContext)
+ .setTitle(R.string.battery_saver_confirmation_title)
+ .setMessage(R.string.battery_saver_confirmation_text)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(R.string.battery_saver_confirmation_ok, mStartSaverMode)
+ .create();
+
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
+ d.getWindow().getAttributes().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ d.show();
+ }
+
+ private void setSaverSetting(boolean mode) {
+ final int val = mode ? 1 : 0;
+ Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.LOW_POWER_MODE, val);
+ }
+
+ private final class Receiver extends BroadcastReceiver {
+
+ public void init() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_SHOW_FALLBACK_WARNING);
+ filter.addAction(ACTION_SHOW_FALLBACK_CHARGER);
+ filter.addAction(ACTION_SHOW_BATTERY_SETTINGS);
+ filter.addAction(ACTION_START_SAVER);
+ mContext.registerReceiver(this, filter, null, mHandler);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ Log.d(TAG, "got " + action);
+ if (action.equals(ACTION_SHOW_FALLBACK_WARNING)) {
+ mFallbackDialogs.showLowBatteryWarning(false /*playSound*/);
+ } else if (action.equals(ACTION_SHOW_FALLBACK_CHARGER)) {
+ mFallbackDialogs.showInvalidChargerWarning();
+ } else if (action.equals(ACTION_SHOW_BATTERY_SETTINGS)) {
+ dismissLowBatteryNotification();
+ mContext.startActivityAsUser(mOpenBatterySettings, UserHandle.CURRENT);
+ } else if (action.equals(ACTION_START_SAVER)) {
+ dismissLowBatteryNotification();
+ showStartSaverConfirmation();
+ }
+ }
+ }
+
+ private final OnClickListener mStartSaverMode = new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ AsyncTask.execute(new Runnable() {
+ @Override
+ public void run() {
+ setSaverSetting(true);
+ }
+ });
+ }
+ };
+
+ private final Runnable mDismissLowBatteryNotification = new Runnable() {
+ @Override
+ public void run() {
+ dismissLowBatteryNotification();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 192ba57..1bb7edb 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
+import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.PowerManager;
@@ -38,13 +39,15 @@ import java.util.Arrays;
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
-
static final boolean DEBUG = false;
- private WarningsUI mWarnings;
private final Handler mHandler = new Handler();
+ private final SettingsObserver mObserver = new SettingsObserver(mHandler);
+ private final Receiver mReceiver = new Receiver();
+ private PowerManager mPowerManager;
+ private WarningsUI mWarnings;
private int mBatteryLevel = 100;
private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
private int mPlugType = 0;
@@ -56,10 +59,9 @@ public class PowerUI extends SystemUI {
private long mScreenOffTime = -1;
public void start() {
-
- final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mScreenOffTime = pm.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
- mWarnings = new PowerDialogWarnings(mContext);
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
+ mWarnings = new PowerNotificationWarnings(mContext);
ContentObserver obs = new ContentObserver(mHandler) {
@Override
@@ -72,13 +74,16 @@ public class PowerUI extends SystemUI {
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevels();
+ mReceiver.init();
+ mObserver.init();
+ }
- // Register for Intent broadcasts for...
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Intent.ACTION_SCREEN_ON);
- mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+ private void setSaverMode(boolean mode) {
+ mWarnings.showSaverMode(mode);
+ }
+
+ private void setSaverTrigger(int level) {
+ mWarnings.setSaverTrigger(level);
}
void updateBatteryWarningLevels() {
@@ -130,7 +135,23 @@ public class PowerUI extends SystemUI {
throw new RuntimeException("not possible!");
}
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ private final class Receiver extends BroadcastReceiver {
+
+ public void init() {
+ // Register for Intent broadcasts for...
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ mContext.registerReceiver(this, filter, null, mHandler);
+ updateSaverMode();
+ }
+
+ private void updateSaverMode() {
+ setSaverMode(mPowerManager.isPowerSaveMode());
+ }
+
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -191,6 +212,8 @@ public class PowerUI extends SystemUI {
mScreenOffTime = SystemClock.elapsedRealtime();
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOffTime = -1;
+ } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
+ updateSaverMode();
} else {
Slog.w(TAG, "unknown intent: " + intent);
}
@@ -228,6 +251,8 @@ public class PowerUI extends SystemUI {
public interface WarningsUI {
void update(int batteryLevel, int bucket, long screenOffTime);
+ void setSaverTrigger(int level);
+ void showSaverMode(boolean mode);
void dismissLowBatteryWarning();
void showLowBatteryWarning(boolean playSound);
void dismissInvalidChargerWarning();
@@ -236,5 +261,29 @@ public class PowerUI extends SystemUI {
boolean isInvalidChargerWarningShowing();
void dump(PrintWriter pw);
}
+
+ private final class SettingsObserver extends ContentObserver {
+ private final Uri LOW_POWER_MODE_TRIGGER_LEVEL_URI =
+ Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL);
+
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void init() {
+ onChange(true, LOW_POWER_MODE_TRIGGER_LEVEL_URI);
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(LOW_POWER_MODE_TRIGGER_LEVEL_URI, false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (LOW_POWER_MODE_TRIGGER_LEVEL_URI.equals(uri)) {
+ final int level = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
+ setSaverTrigger(level);
+ }
+ }
+ }
}