From 3332ba54ae85df14d761447d86d2aa19d448ce11 Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Mon, 10 Mar 2014 17:44:07 -0400 Subject: 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 --- .../res/drawable-hdpi/battery_low_battery.png | Bin 1821 -> 0 bytes .../res/drawable-mdpi/battery_low_battery.png | Bin 1311 -> 0 bytes .../res/drawable-xhdpi/battery_low_battery.png | Bin 2417 -> 0 bytes .../res/drawable-xxhdpi/battery_low_battery.png | Bin 3061 -> 0 bytes packages/SystemUI/res/drawable/ic_power_low.xml | 28 ++ packages/SystemUI/res/drawable/ic_power_saver.xml | 28 ++ packages/SystemUI/res/layout/battery_low.xml | 54 --- packages/SystemUI/res/values/arrays.xml | 2 - packages/SystemUI/res/values/strings.xml | 37 +- .../src/com/android/systemui/BatteryMeterView.java | 12 +- .../systemui/power/PowerDialogWarnings.java | 68 ++-- .../systemui/power/PowerNotificationWarnings.java | 391 +++++++++++++++++++++ .../src/com/android/systemui/power/PowerUI.java | 75 +++- 13 files changed, 587 insertions(+), 108 deletions(-) delete mode 100644 packages/SystemUI/res/drawable-hdpi/battery_low_battery.png delete mode 100644 packages/SystemUI/res/drawable-mdpi/battery_low_battery.png delete mode 100644 packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png delete mode 100644 packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png create mode 100644 packages/SystemUI/res/drawable/ic_power_low.xml create mode 100644 packages/SystemUI/res/drawable/ic_power_saver.xml delete mode 100644 packages/SystemUI/res/layout/battery_low.xml create mode 100644 packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java (limited to 'packages') 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 Binary files a/packages/SystemUI/res/drawable-hdpi/battery_low_battery.png and /dev/null 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 Binary files a/packages/SystemUI/res/drawable-mdpi/battery_low_battery.png and /dev/null 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 Binary files a/packages/SystemUI/res/drawable-xhdpi/battery_low_battery.png and /dev/null 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 Binary files a/packages/SystemUI/res/drawable-xxhdpi/battery_low_battery.png and /dev/null 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 @@ + + + + + + + + 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 @@ + + + + + + + + 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 @@ - - - - - - - - - - - - - - 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 @@ - 4 15 100 #FFFF3300 - #FFFF3300 #FFFFFFFF 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 @@ Notifications - Connect charger - - - The battery is getting low. + Battery is low %d%% remaining + + %d%% remaining. Battery saver is on. + USB charging not supported.\nUse only the supplied charger. + + USB charging not supported. + + + Use only the supplied charger. + - Battery use + Settings + + + Start battery saver? + + + Start + + + Start battery saver + + + 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. Settings @@ -595,4 +613,13 @@ For one hour For %d hours + + + Battery saver is on + + + Device performance is reduced. + + + Open battery saver settings 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); + } + } + } } -- cgit v1.1