diff options
14 files changed, 600 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 Binary files differdeleted file mode 100644 index e6af81e..0000000 --- a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png Binary files differdeleted file mode 100644 index e865f4c..0000000 --- a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png Binary files differdeleted file mode 100644 index 83693c1..0000000 --- a/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png b/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png Binary files differdeleted file mode 100644 index cebbb15..0000000 --- a/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png +++ /dev/null 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); + } + } + } } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 50a32c4..b95db9c 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -18,6 +18,7 @@ package com.android.server.notification; import android.app.AlarmManager; import android.app.AppOpsManager; +import android.app.Notification; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -88,6 +89,10 @@ public class ZenModeHelper { private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList( "com.google.android.deskclock" )); + private static final Set<String> SYSTEM_PACKAGES = new HashSet<String>(Arrays.asList( + "android", + "com.android.systemui" + )); public ZenModeHelper(Context context, Handler handler) { mContext = context; @@ -130,6 +135,9 @@ public class ZenModeHelper { // notifications never transition from not intercepted to intercepted return false; } + if (isSystem(record)) { + return false; + } if (isAlarm(record)) { return false; } @@ -239,6 +247,11 @@ public class ZenModeHelper { } } + private boolean isSystem(NotificationRecord record) { + return SYSTEM_PACKAGES.contains(record.sbn.getPackageName()) + && Notification.CATEGORY_SYSTEM.equals(record.getNotification().category); + } + private boolean isAlarm(NotificationRecord record) { return ALARM_PACKAGES.contains(record.sbn.getPackageName()); } |