= "android.os.action.POWER_SAVE_MODE_CHANGED";
/**
+ * Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change.
+ * This broadcast is only sent to registered receivers.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_POWER_SAVE_MODE_CHANGING
+ = "android.os.action.POWER_SAVE_MODE_CHANGING";
+
+ /** @hide */
+ public static final String EXTRA_POWER_SAVE_MODE = "mode";
+
+ /**
* A wake lock is a mechanism to indicate that your application needs
* to have the device stay on.
* <p>
<protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
<protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
+ <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
<protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
<protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
<string name="description_krtv_12">12세미만의 청소년이 시청하기에 부적절한 내용이 포함되어 있어 보호자의 시청지도가 필요한 등급을 말한다.</string>
<string name="description_krtv_15">15세미만의 청소년이 시청하기에 부적절한 내용이 포함되어 있어 보호자의 시청지도가 필요한 등급을 말한다.</string>
<string name="description_krtv_19">19세미만의 청소년이 시청하기에 부적절한 내용이 포함되어 있어 청소년이 시청할 수 없는 등급을 말한다.</string>
+
+ <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
+ <string name="battery_saver_description">To help improve battery life, battery saver will reduce your device’s performance and restrict background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
</resources>
<java-symbol type="string" name="timepicker_numbers_radius_multiplier_normal" />
<java-symbol type="string" name="timepicker_transition_mid_radius_multiplier" />
<java-symbol type="string" name="timepicker_transition_end_radius_multiplier" />
+ <java-symbol type="string" name="battery_saver_description" />
<java-symbol type="string" name="item_is_selected" />
<java-symbol type="string" name="day_of_week_label_typeface" />
<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>
+ <string name="battery_saver_confirmation_title">Turn on battery saver?</string>
<!-- Battery saver confirmation dialog ok text [CHAR LIMIT=40]-->
- <string name="battery_saver_confirmation_ok">Start</string>
+ <string name="battery_saver_confirmation_ok">Turn on</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>
+ <string name="battery_saver_start_action">Turn on battery saver</string>
<!-- Name of the button that links to the Settings app. [CHAR LIMIT=NONE] -->
<string name="status_bar_settings_settings_button">Settings</string>
<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>
+ <string name="battery_saver_notification_text">Reduces performance and background data</string>
<!-- Battery saver notification action text. [CHAR LIMIT=60] -->
- <string name="battery_saver_notification_action_text">Open battery saver settings</string>
+ <string name="battery_saver_notification_action_text">Turn off battery saver</string>
<!-- Battery level for expanded quick settings [CHAR LIMIT=2] -->
<string name="battery_level_template"><xliff:g id="level" example="45">%d</xliff:g>%%</string>
private int mBucket;
private long mScreenOffTime;
private boolean mSaver;
- private int mSaverTriggerLevel;
private AlertDialog mInvalidChargerDialog;
private AlertDialog mLowBatteryDialog;
public void showSaverMode(boolean mode) {
mSaver = mode;
}
-
- @Override
- public void setSaverTrigger(int level) {
- mSaverTriggerLevel = level;
- }
}
package com.android.systemui.power;
-import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.content.IntentFilter;
-import android.media.AudioManager;
+import android.media.AudioAttributes;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
-import android.view.ContextThemeWrapper;
import android.view.View;
-import android.view.WindowManager;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import java.io.PrintWriter;
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 static final String ACTION_STOP_SAVER = "PNW.stopSaver";
+
+ private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
+ .build();
private final Context mContext;
- private final Context mLightContext;
private final NotificationManager mNoMan;
private final Handler mHandler = new Handler();
private final PowerDialogWarnings mFallbackDialogs;
private long mBucketDroppedNegativeTimeMs;
private boolean mSaver;
- private int mSaverTriggerLevel;
private boolean mWarning;
private boolean mPlaySound;
private boolean mInvalidCharger;
+ private SystemUIDialog mSaverConfirmation;
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();
pw.print("mPlaySound="); pw.println(mPlaySound);
pw.print("mInvalidCharger="); pw.println(mInvalidCharger);
pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]);
+ pw.print("mSaverConfirmation="); pw.println(mSaverConfirmation != null ? "not null" : null);
}
@Override
@Override
public void showSaverMode(boolean mode) {
mSaver = mode;
- updateNotification();
- }
-
- @Override
- public void setSaverTrigger(int level) {
- mSaverTriggerLevel = level;
+ if (mSaver && mSaverConfirmation != null) {
+ mSaverConfirmation.dismiss();
+ }
updateNotification();
}
.setContentTitle(mContext.getString(R.string.battery_low_title))
.setContentText(mContext.getString(textRes, mBatteryLevel))
.setOngoing(true)
+ .setOnlyAlertOnce(true)
.setPriority(Notification.PRIORITY_MAX)
.setCategory(Notification.CATEGORY_SYSTEM)
.setVisibility(Notification.VISIBILITY_PUBLIC)
if (hasBatterySettings()) {
nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
}
- if (!mSaver && mSaverTriggerLevel <= 0) {
+ if (!mSaver) {
nb.addAction(R.drawable.ic_power_saver,
mContext.getString(R.string.battery_saver_start_action),
pendingBroadcast(ACTION_START_SAVER));
+ } else {
+ addStopSaverAction(nb);
}
if (mPlaySound) {
attachLowBatterySound(nb);
.setContentTitle(mContext.getString(R.string.battery_saver_notification_title))
.setContentText(mContext.getString(R.string.battery_saver_notification_text))
.setOngoing(true)
- .setWhen(0)
.setShowWhen(false)
.setCategory(Notification.CATEGORY_SYSTEM)
.setVisibility(Notification.VISIBILITY_PUBLIC);
+ addStopSaverAction(nb);
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 void addStopSaverAction(Notification.Builder nb) {
+ nb.addAction(R.drawable.ic_power_saver,
+ mContext.getString(R.string.battery_saver_notification_action_text),
+ pendingBroadcast(ACTION_STOP_SAVER));
+ }
+
+ private void dismissSaverNotification() {
+ if (mSaver) Slog.i(TAG, "dismissing saver notification");
+ mSaver = false;
+ updateNotification();
+ }
+
private PendingIntent pendingActivity(Intent intent) {
return PendingIntent.getActivityAsUser(mContext,
0, intent, 0, null, UserHandle.CURRENT);
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);
+ b.setSound(soundUri, AUDIO_ATTRIBUTES);
+ if (DEBUG) Slog.d(TAG, "playing sound " + soundUri);
}
}
}
}
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;
+ if (mSaverConfirmation != null) return;
+ final SystemUIDialog d = new SystemUIDialog(mContext);
+ d.setTitle(R.string.battery_saver_confirmation_title);
+ d.setMessage(com.android.internal.R.string.battery_saver_description);
+ d.setNegativeButton(android.R.string.cancel, null);
+ d.setPositiveButton(R.string.battery_saver_confirmation_ok, mStartSaverMode);
+ d.setShowForAllUsers(true);
+ d.setOnDismissListener(new OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mSaverConfirmation = null;
+ }
+ });
d.show();
+ mSaverConfirmation = d;
}
private void setSaverSetting(boolean mode) {
filter.addAction(ACTION_SHOW_FALLBACK_CHARGER);
filter.addAction(ACTION_SHOW_BATTERY_SETTINGS);
filter.addAction(ACTION_START_SAVER);
+ filter.addAction(ACTION_STOP_SAVER);
mContext.registerReceiver(this, filter, null, mHandler);
}
} else if (action.equals(ACTION_START_SAVER)) {
dismissLowBatteryNotification();
showStartSaverConfirmation();
+ } else if (action.equals(ACTION_STOP_SAVER)) {
+ dismissSaverNotification();
+ dismissLowBatteryNotification();
+ setSaverSetting(false);
}
}
}
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;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Log;
import android.util.Slog;
import com.android.systemui.SystemUI;
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
- static final boolean DEBUG = false;
-
+ static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final Handler mHandler = new Handler();
- private final SettingsObserver mObserver = new SettingsObserver(mHandler);
private final Receiver mReceiver = new Receiver();
private PowerManager mPowerManager;
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevels();
mReceiver.init();
- mObserver.init();
}
private void setSaverMode(boolean mode) {
mWarnings.showSaverMode(mode);
}
- private void setSaverTrigger(int level) {
- mWarnings.setSaverTrigger(level);
- }
-
void updateBatteryWarningLevels() {
int critLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
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_CHANGING);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
mContext.registerReceiver(this, filter, null, mHandler);
updateSaverMode();
mScreenOffTime = -1;
} else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
updateSaverMode();
+ } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGING.equals(action)) {
+ setSaverMode(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
} else {
Slog.w(TAG, "unknown intent: " + intent);
}
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);
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);
- }
- }
- }
}
final boolean powerSave = mBatteryController.isPowerSave();
final boolean anim = (mScreenOn == null || mScreenOn) && windowState != WINDOW_STATE_HIDDEN
&& !powerSave;
- if (powerSave && getBarState() != StatusBarState.KEYGUARD) {
+ if (powerSave && getBarState() == StatusBarState.SHADE) {
mode = MODE_WARNING;
}
transitions.transitionTo(mode, anim);
*/
public class SystemUIDialog extends AlertDialog {
+ private final Context mContext;
+
public SystemUIDialog(Context context) {
super(context, R.style.Theme_SystemUI_Dialog);
+ mContext = context;
+
getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
attrs.setTitle(getClass().getSimpleName());
getWindow().setAttributes(attrs);
}
+
+ public void setShowForAllUsers(boolean show) {
+ if (show) {
+ getWindow().getAttributes().privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ } else {
+ getWindow().getAttributes().privateFlags &=
+ ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ }
+ }
+
+ public void setMessage(int resId) {
+ setMessage(mContext.getString(resId));
+ }
+
+ public void setPositiveButton(int resId, OnClickListener onClick) {
+ setButton(BUTTON_POSITIVE, mContext.getString(resId), onClick);
+ }
+
+ public void setNegativeButton(int resId, OnClickListener onClick) {
+ setButton(BUTTON_NEGATIVE, mContext.getString(resId), onClick);
+ }
}
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
context.registerReceiver(this, filter);
updatePowerSave();
fireBatteryLevelChanged();
} else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
updatePowerSave();
+ } else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)) {
+ setPowerSave(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
}
}
}
private void updatePowerSave() {
- final boolean powerSave = mPowerManager.isPowerSaveMode();
+ setPowerSave(mPowerManager.isPowerSaveMode());
+ }
+
+ private void setPowerSave(boolean powerSave) {
if (powerSave == mPowerSave) return;
mPowerSave = powerSave;
if (DEBUG) Log.d(TAG, "Power save is " + (mPowerSave ? "on" : "off"));
// Current state of whether the settings are allowing auto low power mode.
private boolean mAutoLowPowerModeEnabled;
+ // The user turned off low power mode below the trigger level
+ private boolean mAutoLowPowerModeSnoozing;
+
// True if the battery level is currently considered low.
private boolean mBatteryLevelLow;
final boolean lowPowerModeEnabled = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE, 0) != 0;
final boolean autoLowPowerModeEnabled = Settings.Global.getInt(resolver,
- Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 15) != 0;
+ Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) != 0;
if (lowPowerModeEnabled != mLowPowerModeSetting
|| autoLowPowerModeEnabled != mAutoLowPowerModeEnabled) {
+ if (lowPowerModeEnabled != mLowPowerModeSetting) {
+ if (!mAutoLowPowerModeSnoozing && !lowPowerModeEnabled && !mIsPowered
+ && mAutoLowPowerModeEnabled) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateSettingsLocked: snoozing low power mode");
+ }
+ mAutoLowPowerModeSnoozing = true;
+ } else if (mAutoLowPowerModeSnoozing && lowPowerModeEnabled) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateSettingsLocked: no longer snoozing low power mode");
+ }
+ mAutoLowPowerModeSnoozing = true;
+ }
+ }
mLowPowerModeSetting = lowPowerModeEnabled;
mAutoLowPowerModeEnabled = autoLowPowerModeEnabled;
updateLowPowerModeLocked();
}
void updateLowPowerModeLocked() {
- final boolean lowPowerModeEnabled = !mIsPowered
- && (mLowPowerModeSetting || (mAutoLowPowerModeEnabled && mBatteryLevelLow));
+ if (mIsPowered && mLowPowerModeSetting) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateLowPowerModeLocked: powered, turning setting off");
+ }
+ // Turn setting off if powered
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.LOW_POWER_MODE, 0);
+ mLowPowerModeSetting = false;
+ } else if (!mIsPowered && mAutoLowPowerModeEnabled && !mAutoLowPowerModeSnoozing
+ && mBatteryLevelLow && !mLowPowerModeSetting) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateLowPowerModeLocked: trigger level reached, turning setting on");
+ }
+ // Turn setting on if trigger level is enabled, and we're now below it
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.LOW_POWER_MODE, 1);
+ mLowPowerModeSetting = true;
+ }
+ final boolean lowPowerModeEnabled = mLowPowerModeSetting;
if (mLowPowerModeEnabled != lowPowerModeEnabled) {
mLowPowerModeEnabled = lowPowerModeEnabled;
powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
BackgroundThread.getHandler().post(new Runnable() {
@Override
public void run() {
+ Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING)
+ .putExtra(PowerManager.EXTRA_POWER_SAVE_MODE, mLowPowerModeEnabled)
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcast(intent);
ArrayList<PowerManagerInternal.LowPowerModeListener> listeners;
synchronized (mLock) {
listeners = new ArrayList<PowerManagerInternal.LowPowerModeListener>(
for (int i=0; i<listeners.size(); i++) {
listeners.get(i).onLowPowerModeChanged(lowPowerModeEnabled);
}
- Intent intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+ intent = new Intent(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
mContext.sendBroadcast(intent);
}
}
if (wasPowered != mIsPowered || oldLevelLow != mBatteryLevelLow) {
+ if (oldLevelLow != mBatteryLevelLow && !mBatteryLevelLow) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "updateIsPoweredLocked: resetting low power snooze");
+ }
+ mAutoLowPowerModeSnoozing = false;
+ }
updateLowPowerModeLocked();
}
}
pw.println(" mDreamsActivateOnDockSetting=" + mDreamsActivateOnDockSetting);
pw.println(" mLowPowerModeSetting=" + mLowPowerModeSetting);
pw.println(" mAutoLowPowerModeEnabled=" + mAutoLowPowerModeEnabled);
+ pw.println(" mAutoLowPowerModeSnoozing=" + mAutoLowPowerModeSnoozing);
pw.println(" mMinimumScreenOffTimeoutConfig=" + mMinimumScreenOffTimeoutConfig);
pw.println(" mMaximumScreenDimDurationConfig=" + mMaximumScreenDimDurationConfig);
pw.println(" mMaximumScreenDimRatioConfig=" + mMaximumScreenDimRatioConfig);