summaryrefslogtreecommitdiffstats
path: root/packages
diff options
context:
space:
mode:
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);
+ }
+ }
+ }
}