Move battery saver notification to QS

Remove the battery saver notification and instead create a detail
panel within QS that allows it to be turned on and off.

Change-Id: I54654d26183586fa171fda04877a840701f8ef33
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 40eb375..fd85953 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -27,7 +27,6 @@
  */
 public class MetricsLogger implements MetricsConstants {
     // Temporary constants go here, to await migration to MetricsConstants.
-    public static final int QS_LOCK_TILE = 257;
     public static final int QS_USER_TILE = 258;
     public static final int QS_BATTERY_TILE = 259;
     public static final int NOTIFICATION_ZEN_MODE_VISUAL_INTERRUPTIONS = 260;
@@ -62,6 +61,8 @@
      * credentials UI.
      */
     public static final int PROFILE_CHALLENGE = 271;
+    public static final int ACTION_WINDOW_DOCK_SWIPE = 272;
+    public static final int QS_BATTERY_DETAIL = 273;
 
     public static void visible(Context context, int category) throws IllegalArgumentException {
         if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f7e25db..ac19cf5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -726,4 +726,41 @@
     <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
     <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
 
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when discharging with duration -->
+    <string name="power_discharging_duration"><xliff:g id="level">%1$s</xliff:g>
+        - approx. <xliff:g id="time">%2$s</xliff:g> left</string>
+
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
+    <string name="power_charging"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="state">%2$s</xliff:g></string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration_ac"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full on AC</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration_usb"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full over USB</string>
+    <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
+    <string name="power_charging_duration_wireless"><xliff:g id="level">%1$s</xliff:g> -
+            <xliff:g id="time">%2$s</xliff:g> until full from wireless</string>
+
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_unknown">Unknown</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging from an unknown source.  -->
+    <string name="battery_info_status_charging">Charging</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging on AC.  -->
+    <string name="battery_info_status_charging_ac">Charging on AC</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging over USB.  -->
+    <string name="battery_info_status_charging_usb">Charging over USB</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging over a wireless connection.  -->
+    <string name="battery_info_status_charging_wireless">Charging wirelessly</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_discharging">Not charging</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_not_charging">Not charging</string>
+    <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
+    <string name="battery_info_status_full">Full</string>
+
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
new file mode 100644
index 0000000..d81bdeb
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/BatteryInfo.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 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.settingslib;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.format.Formatter;
+import com.android.internal.os.BatteryStatsHelper;
+
+public class BatteryInfo {
+
+    public String mChargeLabelString;
+    public int mBatteryLevel;
+    public boolean mDischarging = true;
+    public long remainingTimeUs = 0;
+
+    public interface Callback {
+        void onBatteryInfoLoaded(BatteryInfo info);
+    }
+
+    public static void getBatteryInfo(final Context context, final Callback callback) {
+        new AsyncTask<Void, Void, BatteryStats>() {
+            @Override
+            protected BatteryStats doInBackground(Void... params) {
+                BatteryStatsHelper statsHelper = new BatteryStatsHelper(context, true);
+                statsHelper.create((Bundle) null);
+                return statsHelper.getStats();
+            }
+
+            @Override
+            protected void onPostExecute(BatteryStats batteryStats) {
+                final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;
+                Intent batteryBroadcast = context.registerReceiver(null,
+                        new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+                BatteryInfo batteryInfo = BatteryInfo.getBatteryInfo(context,
+                        batteryBroadcast, batteryStats, elapsedRealtimeUs);
+                callback.onBatteryInfoLoaded(batteryInfo);
+            }
+        }.execute();
+    }
+
+    public static BatteryInfo getBatteryInfo(Context context, Intent batteryBroadcast,
+            BatteryStats stats, long elapsedRealtimeUs) {
+        BatteryInfo info = new BatteryInfo();
+        info.mBatteryLevel = Utils.getBatteryLevel(batteryBroadcast);
+        String batteryPercentString = Utils.formatPercentage(info.mBatteryLevel);
+        if (batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) == 0) {
+            final long drainTime = stats.computeBatteryTimeRemaining(elapsedRealtimeUs);
+            if (drainTime > 0) {
+                info.remainingTimeUs = drainTime;
+                String timeString = Formatter.formatShortElapsedTime(context,
+                        drainTime / 1000);
+                info.mChargeLabelString = context.getResources().getString(
+                        R.string.power_discharging_duration, batteryPercentString, timeString);
+            } else {
+                info.mChargeLabelString = batteryPercentString;
+            }
+        } else {
+            final long chargeTime = stats.computeChargeTimeRemaining(elapsedRealtimeUs);
+            final String statusLabel = Utils.getBatteryStatus(
+                    context.getResources(), batteryBroadcast);
+            final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
+                    BatteryManager.BATTERY_STATUS_UNKNOWN);
+            if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
+                info.mDischarging = false;
+                info.remainingTimeUs = chargeTime;
+                String timeString = Formatter.formatShortElapsedTime(context,
+                        chargeTime / 1000);
+                int plugType = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+                int resId;
+                if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
+                    resId = R.string.power_charging_duration_ac;
+                } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
+                    resId = R.string.power_charging_duration_usb;
+                } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+                    resId = R.string.power_charging_duration_wireless;
+                } else {
+                    resId = R.string.power_charging_duration;
+                }
+                info.mChargeLabelString = context.getResources().getString(
+                        resId, batteryPercentString, timeString);
+            } else {
+                info.mChargeLabelString = context.getResources().getString(
+                        R.string.power_charging, batteryPercentString, statusLabel);
+            }
+        }
+        return info;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 621a09cd..72df96d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,17 +1,21 @@
 package com.android.settingslib;
 
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Drawable;
 import android.net.ConnectivityManager;
+import android.os.BatteryManager;
 import android.os.UserManager;
-
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.CircleFramedDrawable;
 
-public final class Utils {
+import java.text.NumberFormat;
+
+public class Utils {
 
     /**
      * Return string resource that best describes combination of tethering
@@ -81,4 +85,57 @@
         return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
                 UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
     }
+
+    /** Formats the ratio of amount/total as a percentage. */
+    public static String formatPercentage(long amount, long total) {
+        return formatPercentage(((double) amount) / total);
+    }
+
+    /** Formats an integer from 0..100 as a percentage. */
+    public static String formatPercentage(int percentage) {
+        return formatPercentage(((double) percentage) / 100.0);
+    }
+
+    /** Formats a double from 0.0..1.0 as a percentage. */
+    private static String formatPercentage(double percentage) {
+      return NumberFormat.getPercentInstance().format(percentage);
+    }
+
+    public static int getBatteryLevel(Intent batteryChangedIntent) {
+        int level = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
+        int scale = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
+        return (level * 100) / scale;
+    }
+
+    public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
+        final Intent intent = batteryChangedIntent;
+
+        int plugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
+        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
+                BatteryManager.BATTERY_STATUS_UNKNOWN);
+        String statusString;
+        if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+            int resId;
+            if (plugType == BatteryManager.BATTERY_PLUGGED_AC) {
+                resId = R.string.battery_info_status_charging_ac;
+            } else if (plugType == BatteryManager.BATTERY_PLUGGED_USB) {
+                resId = R.string.battery_info_status_charging_usb;
+            } else if (plugType == BatteryManager.BATTERY_PLUGGED_WIRELESS) {
+                resId = R.string.battery_info_status_charging_wireless;
+            } else {
+                resId = R.string.battery_info_status_charging;
+            }
+            statusString = res.getString(resId);
+        } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+            statusString = res.getString(R.string.battery_info_status_discharging);
+        } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
+            statusString = res.getString(R.string.battery_info_status_not_charging);
+        } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
+            statusString = res.getString(R.string.battery_info_status_full);
+        } else {
+            statusString = res.getString(R.string.battery_info_status_unknown);
+        }
+
+        return statusString;
+    }
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 66a3336..6201fd6 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -143,6 +143,9 @@
     <!-- Block notifications inline notifications -->
     <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
 
+    <!-- Access battery information -->
+    <uses-permission android:name="android.permission.BATTERY_STATS" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
new file mode 100644
index 0000000..ea4db4b
--- /dev/null
+++ b/packages/SystemUI/res/layout/battery_detail.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="16dp"
+    android:background="?android:attr/selectableItemBackground"
+    android:clickable="true">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:scaleType="fitCenter"
+        android:adjustViewBounds="true"
+        android:layout_alignParentTop="true"
+        android:layout_alignParentStart="true"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="32dp" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_toStartOf="@android:id/toggle"
+        android:layout_toEndOf="@android:id/icon"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemPrimary"
+        android:text="@string/battery_detail_switch_title" />
+
+    <TextView
+        android:id="@android:id/summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@android:id/title"
+        android:layout_toStartOf="@android:id/toggle"
+        android:layout_toEndOf="@android:id/icon"
+        android:textAppearance="@style/TextAppearance.QS.DetailItemSecondary"
+        android:text="@string/battery_detail_switch_summary" />
+
+    <Switch
+        android:id="@android:id/toggle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentEnd="true"
+        android:layout_alignParentTop="true"
+        android:layout_marginEnd="16dp"
+        android:clickable="false"
+        android:textAppearance="@style/TextAppearance.QS.DetailHeader" />
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/values/arrays.xml b/packages/SystemUI/res/values/arrays.xml
index 6102aa6..bf0cba2 100644
--- a/packages/SystemUI/res/values/arrays.xml
+++ b/packages/SystemUI/res/values/arrays.xml
@@ -37,4 +37,18 @@
         <item>157</item><item>334</item>
         <item>0</item>  <item>334</item>
     </array>
+    <array name="batterymeter_plus_points">
+        <item>3</item><item>0</item>
+        <item>5</item><item>0</item>
+        <item>5</item><item>3</item>
+        <item>8</item><item>3</item>
+        <item>8</item><item>5</item>
+        <item>5</item><item>5</item>
+        <item>5</item><item>8</item>
+        <item>3</item><item>8</item>
+        <item>3</item><item>5</item>
+        <item>0</item><item>5</item>
+        <item>0</item><item>3</item>
+        <item>3</item><item>3</item>
+    </array>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 876c21e..4136c11 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1269,4 +1269,16 @@
     <string name="color_modification_g" translatable="false">G</string>
     <string name="color_modification_b" translatable="false">B</string>
 
+    <!-- Title of the battery settings detail panel [CHAR LIMIT=20] -->
+    <string name="battery_panel_title">Battery (<xliff:g name="pattery_percent" example="52">%1$d</xliff:g>%%)</string>
+
+    <!-- Summary of battery saver not available [CHAR LIMIT=NONE] -->
+    <string name="battery_detail_charging_summary">Battery Saver not available during charging</string>
+
+    <!-- Title of switch for battery saver [CHAR LIMIT=NONE] -->
+    <string name="battery_detail_switch_title">Battery Saver</string>
+
+    <!-- Summary of switch for battery saver [CHAR LIMIT=NONE] -->
+    <string name="battery_detail_switch_summary">Reduces performance and background data</string>
+
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 3eb1271..38ae345 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -49,7 +49,8 @@
     private float mButtonHeightFraction;
     private float mSubpixelSmoothingLeft;
     private float mSubpixelSmoothingRight;
-    private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint;
+    private final Paint mFramePaint, mBatteryPaint, mWarningTextPaint, mTextPaint, mBoltPaint,
+            mPlusPaint;
     private float mTextHeight, mWarningTextHeight;
     private int mIconTint = Color.WHITE;
 
@@ -60,10 +61,13 @@
     private int mChargeColor;
     private final float[] mBoltPoints;
     private final Path mBoltPath = new Path();
+    private final float[] mPlusPoints;
+    private final Path mPlusPath = new Path();
 
     private final RectF mFrame = new RectF();
     private final RectF mButtonFrame = new RectF();
     private final RectF mBoltFrame = new RectF();
+    private final RectF mPlusFrame = new RectF();
 
     private final Path mShapePath = new Path();
     private final Path mClipPath = new Path();
@@ -141,6 +145,9 @@
         mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color));
         mBoltPoints = loadBoltPoints(res);
 
+        mPlusPaint = new Paint(mBoltPaint);
+        mPlusPoints = loadPlusPoints(res);
+
         mDarkModeBackgroundColor =
                 context.getColor(R.color.dark_mode_icon_color_dual_tone_background);
         mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill);
@@ -187,8 +194,8 @@
     }
 
     @Override
-    public void onPowerSaveChanged() {
-        mPowerSaveEnabled = mBatteryController.isPowerSave();
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        mPowerSaveEnabled = isPowerSave;
         invalidateSelf();
     }
 
@@ -207,6 +214,21 @@
         return ptsF;
     }
 
+    private static float[] loadPlusPoints(Resources res) {
+        final int[] pts = res.getIntArray(R.array.batterymeter_plus_points);
+        int maxX = 0, maxY = 0;
+        for (int i = 0; i < pts.length; i += 2) {
+            maxX = Math.max(maxX, pts[i]);
+            maxY = Math.max(maxY, pts[i + 1]);
+        }
+        final float[] ptsF = new float[pts.length];
+        for (int i = 0; i < pts.length; i += 2) {
+            ptsF[i] = (float)pts[i] / maxX;
+            ptsF[i + 1] = (float)pts[i + 1] / maxY;
+        }
+        return ptsF;
+    }
+
     @Override
     public void setBounds(int left, int top, int right, int bottom) {
         super.setBounds(left, top, right, bottom);
@@ -328,9 +350,9 @@
 
         if (mPluggedIn) {
             // define the bolt shape
-            final float bl = mFrame.left + mFrame.width() / 4.5f;
+            final float bl = mFrame.left + mFrame.width() / 4f;
             final float bt = mFrame.top + mFrame.height() / 6f;
-            final float br = mFrame.right - mFrame.width() / 7f;
+            final float br = mFrame.right - mFrame.width() / 4f;
             final float bb = mFrame.bottom - mFrame.height() / 10f;
             if (mBoltFrame.left != bl || mBoltFrame.top != bt
                     || mBoltFrame.right != br || mBoltFrame.bottom != bb) {
@@ -358,6 +380,39 @@
                 // otherwise cut the bolt out of the overall shape
                 mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
             }
+        } else if (mPowerSaveEnabled) {
+            // define the plus shape
+            final float pw = mFrame.width() * 2 / 3;
+            final float pl = mFrame.left + (mFrame.width() - pw) / 2;
+            final float pt = mFrame.top + (mFrame.height() - pw) / 2;
+            final float pr = mFrame.right - (mFrame.width() - pw) / 2;
+            final float pb = mFrame.bottom - (mFrame.height() - pw) / 2;
+            if (mPlusFrame.left != pl || mPlusFrame.top != pt
+                    || mPlusFrame.right != pr || mPlusFrame.bottom != pb) {
+                mPlusFrame.set(pl, pt, pr, pb);
+                mPlusPath.reset();
+                mPlusPath.moveTo(
+                        mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
+                        mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
+                for (int i = 2; i < mPlusPoints.length; i += 2) {
+                    mPlusPath.lineTo(
+                            mPlusFrame.left + mPlusPoints[i] * mPlusFrame.width(),
+                            mPlusFrame.top + mPlusPoints[i + 1] * mPlusFrame.height());
+                }
+                mPlusPath.lineTo(
+                        mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
+                        mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
+            }
+
+            float boltPct = (mPlusFrame.bottom - levelTop) / (mPlusFrame.bottom - mPlusFrame.top);
+            boltPct = Math.min(Math.max(boltPct, 0), 1);
+            if (boltPct <= BOLT_LEVEL_THRESHOLD) {
+                // draw the bolt if opaque
+                c.drawPath(mPlusPath, mPlusPaint);
+            } else {
+                // otherwise cut the bolt out of the overall shape
+                mShapePath.op(mPlusPath, Path.Op.DIFFERENCE);
+            }
         }
 
         // compute percentage text
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 6cb8da4..cdbdc22 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 19b65f7..ea1c9bf 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -52,7 +52,6 @@
 
     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",
@@ -63,7 +62,6 @@
 
     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 String ACTION_DISMISSED_WARNING = "PNW.dismissedWarning";
 
     private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
@@ -77,7 +75,6 @@
     private final Handler mHandler = new Handler();
     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;
@@ -86,7 +83,6 @@
 
     private long mBucketDroppedNegativeTimeMs;
 
-    private boolean mSaver;
     private boolean mWarning;
     private boolean mPlaySound;
     private boolean mInvalidCharger;
@@ -101,7 +97,6 @@
 
     @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);
@@ -121,27 +116,15 @@
         mScreenOffTime = screenOffTime;
     }
 
-    @Override
-    public void showSaverMode(boolean mode) {
-        mSaver = mode;
-        if (mSaver && mSaverConfirmation != null) {
-            mSaverConfirmation.dismiss();
-        }
-        updateNotification();
-    }
-
     private void updateNotification() {
         if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning + " mPlaySound="
-                + mPlaySound + " mSaver=" + mSaver + " mInvalidCharger=" + mInvalidCharger);
+                + mPlaySound + " mInvalidCharger=" + mInvalidCharger);
         if (mInvalidCharger) {
             showInvalidChargerNotification();
             mShowing = SHOWING_INVALID_CHARGER;
         } else if (mWarning) {
             showWarningNotification();
             mShowing = SHOWING_WARNING;
-        } else if (mSaver) {
-            showSaverNotification();
-            mShowing = SHOWING_SAVER;
         } else {
             mNoMan.cancelAsUser(TAG_NOTIFICATION, R.id.notification_power, UserHandle.ALL);
             mShowing = SHOWING_NOTHING;
@@ -165,8 +148,7 @@
     }
 
     private void showWarningNotification() {
-        final int textRes = mSaver ? R.string.battery_low_percent_format_saver_started
-                : R.string.battery_low_percent_format;
+        final int textRes = R.string.battery_low_percent_format;
         final String percentage = NumberFormat.getPercentInstance().format((double) mBatteryLevel / 100.0);
         final Notification.Builder nb = new Notification.Builder(mContext)
                 .setSmallIcon(R.drawable.ic_power_low)
@@ -184,13 +166,9 @@
         if (hasBatterySettings()) {
             nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
         }
-        if (!mSaver) {
-            nb.addAction(0,
-                    mContext.getString(R.string.battery_saver_start_action),
-                    pendingBroadcast(ACTION_START_SAVER));
-        } else {
-            addStopSaverAction(nb);
-        }
+        nb.addAction(0,
+                mContext.getString(R.string.battery_saver_start_action),
+                pendingBroadcast(ACTION_START_SAVER));
         if (mPlaySound) {
             attachLowBatterySound(nb);
             mPlaySound = false;
@@ -199,35 +177,6 @@
         mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, n, UserHandle.ALL);
     }
 
-    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)
-                .setVisibility(Notification.VISIBILITY_PUBLIC)
-                .setColor(mContext.getColor(
-                        com.android.internal.R.color.battery_saver_mode_color));
-        addStopSaverAction(nb);
-        if (hasSaverSettings()) {
-            nb.setContentIntent(pendingActivity(mOpenSaverSettings));
-        }
-        mNoMan.notifyAsUser(TAG_NOTIFICATION, R.id.notification_power, nb.build(), UserHandle.ALL);
-    }
-
-    private void addStopSaverAction(Notification.Builder nb) {
-        nb.addAction(0,
-                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);
@@ -272,10 +221,6 @@
         return mOpenBatterySettings.resolveActivity(mContext.getPackageManager()) != null;
     }
 
-    private boolean hasSaverSettings() {
-        return mOpenSaverSettings.resolveActivity(mContext.getPackageManager()) != null;
-    }
-
     @Override
     public void showLowBatteryWarning(boolean playSound) {
         Slog.i(TAG,
@@ -367,7 +312,6 @@
             IntentFilter filter = new IntentFilter();
             filter.addAction(ACTION_SHOW_BATTERY_SETTINGS);
             filter.addAction(ACTION_START_SAVER);
-            filter.addAction(ACTION_STOP_SAVER);
             filter.addAction(ACTION_DISMISSED_WARNING);
             mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
                     android.Manifest.permission.STATUS_BAR_SERVICE, mHandler);
@@ -383,10 +327,6 @@
             } else if (action.equals(ACTION_START_SAVER)) {
                 dismissLowBatteryNotification();
                 showStartSaverConfirmation();
-            } else if (action.equals(ACTION_STOP_SAVER)) {
-                dismissSaverNotification();
-                dismissLowBatteryNotification();
-                setSaverMode(false);
             } else if (action.equals(ACTION_DISMISSED_WARNING)) {
                 dismissLowBatteryWarning();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 9459740..522d533 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -76,10 +76,6 @@
         mReceiver.init();
     }
 
-    private void setSaverMode(boolean mode) {
-        mWarnings.showSaverMode(mode);
-    }
-
     void updateBatteryWarningLevels() {
         int critLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
@@ -141,11 +137,6 @@
             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
             filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
             mContext.registerReceiver(this, filter, null, mHandler);
-            updateSaverMode();
-        }
-
-        private void updateSaverMode() {
-            setSaverMode(mPowerManager.isPowerSaveMode());
         }
 
         @Override
@@ -210,10 +201,6 @@
                 mScreenOffTime = -1;
             } else if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 mWarnings.userSwitched();
-            } 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);
             }
@@ -251,7 +238,6 @@
 
     public interface WarningsUI {
         void update(int batteryLevel, int bucket, long screenOffTime);
-        void showSaverMode(boolean mode);
         void dismissLowBatteryWarning();
         void showLowBatteryWarning(boolean playSound);
         void dismissInvalidChargerWarning();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 16fd9eb..91f88b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -466,7 +466,7 @@
             MetricsLogger.visible(mContext, detailAdapter.getMetricsCategory());
             announceForAccessibility(mContext.getString(
                     R.string.accessibility_quick_settings_detail,
-                    mContext.getString(detailAdapter.getTitle())));
+                    detailAdapter.getTitle()));
             setDetailRecord(r);
             listener = mHideGridContentWhenDone;
             if (r instanceof TileRecord && visibleDiff) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index b6776bb..8ce6da2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -109,7 +109,7 @@
     }
 
     public interface DetailAdapter {
-        int getTitle();
+        CharSequence getTitle();
         Boolean getToggleState();
         View createDetailView(Context context, View convertView, ViewGroup parent);
         Intent getSettingsIntent();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 84eac65..60238fc3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -19,7 +19,14 @@
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Checkable;
+import android.widget.ImageView;
+import android.widget.TextView;
 import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.BatteryInfo;
 import com.android.systemui.BatteryMeterDrawable;
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -31,8 +38,11 @@
 
     private final BatteryMeterDrawable mDrawable;
     private final BatteryController mBatteryController;
+    private final BatteryDetail mBatteryDetail = new BatteryDetail();
 
     private int mLevel;
+    private boolean mPowerSave;
+    private boolean mCharging;
 
     public BatteryTile(Host host) {
         super(host);
@@ -48,6 +58,11 @@
     }
 
     @Override
+    public DetailAdapter getDetailAdapter() {
+        return mBatteryDetail;
+    }
+
+    @Override
     public int getMetricsCategory() {
         return MetricsLogger.QS_BATTERY_TILE;
     }
@@ -64,8 +79,16 @@
     }
 
     @Override
+    public void setDetailListening(boolean listening) {
+        super.setDetailListening(listening);
+        if (!listening) {
+            mBatteryDetail.mCurrentView = null;
+        }
+    }
+
+    @Override
     protected void handleClick() {
-        mHost.startActivityDismissingKeyguard(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY));
+        showDetail(true);
     }
 
     @Override
@@ -85,11 +108,98 @@
     @Override
     public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
         mLevel = level;
+        mCharging = charging;
         refreshState((Integer) level);
+        if (mBatteryDetail.mCurrentView != null) {
+            mBatteryDetail.bindView();
+        }
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
+        mPowerSave = isPowerSave;
+        if (mBatteryDetail.mCurrentView != null) {
+            mBatteryDetail.bindView();
+        }
+    }
 
+    private final class BatteryDetail implements DetailAdapter, View.OnClickListener {
+        private final BatteryMeterDrawable mDrawable = new BatteryMeterDrawable(mHost.getContext(),
+                new Handler(), mHost.getContext().getColor(R.color.batterymeter_frame_color));
+        private View mCurrentView;
+
+        @Override
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.battery_panel_title, mLevel);
+        }
+
+        @Override
+        public Boolean getToggleState() {
+            return null;
+        }
+
+        @Override
+        public View createDetailView(Context context, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(mContext).inflate(R.layout.battery_detail, parent,
+                        false);
+            }
+            mCurrentView = convertView;
+            bindView();
+            return convertView;
+        }
+
+        private void bindView() {
+            mDrawable.onBatteryLevelChanged(100, false, false);
+            mDrawable.onPowerSaveChanged(true);
+            ((ImageView) mCurrentView.findViewById(android.R.id.icon)).setImageDrawable(mDrawable);
+            Checkable checkbox = (Checkable) mCurrentView.findViewById(android.R.id.toggle);
+            checkbox.setChecked(mPowerSave);
+            if (mCharging) {
+                BatteryInfo.getBatteryInfo(mContext, new BatteryInfo.Callback() {
+                    @Override
+                    public void onBatteryInfoLoaded(BatteryInfo info) {
+                        if (mCurrentView != null && mCharging) {
+                            ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
+                                    info.mChargeLabelString);
+                        }
+                    }
+                });
+                ((TextView) mCurrentView.findViewById(android.R.id.summary)).setText(
+                        R.string.battery_detail_charging_summary);
+                mCurrentView.setClickable(false);
+                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.INVISIBLE);
+                mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.INVISIBLE);
+            } else {
+                ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
+                        R.string.battery_detail_switch_title);
+                ((TextView) mCurrentView.findViewById(android.R.id.summary)).setText(
+                        R.string.battery_detail_switch_summary);
+                mCurrentView.setClickable(true);
+                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.VISIBLE);
+                mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.VISIBLE);
+                mCurrentView.setOnClickListener(this);
+            }
+        }
+
+        @Override
+        public void onClick(View v) {
+            mBatteryController.setPowerSaveMode(!mPowerSave);
+        }
+
+        @Override
+        public Intent getSettingsIntent() {
+            return new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+        }
+
+        @Override
+        public void setToggleState(boolean state) {
+            // No toggle state.
+        }
+
+        @Override
+        public int getMetricsCategory() {
+            return MetricsLogger.QS_BATTERY_DETAIL;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index cfc09a0..3750290 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -165,8 +165,8 @@
         private QSDetailItems mItems;
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_bluetooth_label;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_bluetooth_label);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index a8e139c..de4c21c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -160,8 +160,8 @@
         private QSDetailItems mItems;
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_cast_title;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_cast_title);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 6c7b337..c1dcfea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -223,8 +223,8 @@
     private final class CellularDetailAdapter implements DetailAdapter {
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_cellular_detail_title;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_cellular_detail_title);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 4f9f46d..4d9b266 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -218,8 +218,8 @@
     private final class DndDetailAdapter implements DetailAdapter, OnAttachStateChangeListener {
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_dnd_label;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_dnd_label);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 48b4096..95ea3f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -240,8 +240,8 @@
         private AccessPoint[] mAccessPoints;
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_wifi_label;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_wifi_label);
         }
 
         public Intent getSettingsIntent() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index b93fc76..f41e47b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -166,7 +166,7 @@
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
         // could not care less
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 75337e1..4370c38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -810,10 +810,10 @@
         mBatteryController = new BatteryController(mContext);
         mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() {
             @Override
-            public void onPowerSaveChanged() {
+            public void onPowerSaveChanged(boolean isPowerSave) {
                 mHandler.post(mCheckBarModes);
                 if (mDozeServiceHost != null) {
-                    mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave());
+                    mDozeServiceHost.firePowerSaveChanged(isPowerSave);
                 }
             }
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 3d21f44..d2f1ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -419,7 +419,7 @@
     }
 
     @Override
-    public void onPowerSaveChanged() {
+    public void onPowerSaveChanged(boolean isPowerSave) {
         // could not care less
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 5071df0..bb3e116 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -70,9 +70,14 @@
         pw.print("  mPowerSave="); pw.println(mPowerSave);
     }
 
+    public void setPowerSaveMode(boolean powerSave) {
+        mPowerManager.setPowerSaveMode(powerSave);
+    }
+
     public void addStateChangedCallback(BatteryStateChangeCallback cb) {
         mChangeCallbacks.add(cb);
         cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
+        cb.onPowerSaveChanged(mPowerSave);
     }
 
     public void removeStateChangedCallback(BatteryStateChangeCallback cb) {
@@ -158,12 +163,12 @@
     private void firePowerSaveChanged() {
         final int N = mChangeCallbacks.size();
         for (int i = 0; i < N; i++) {
-            mChangeCallbacks.get(i).onPowerSaveChanged();
+            mChangeCallbacks.get(i).onPowerSaveChanged(mPowerSave);
         }
     }
 
     public interface BatteryStateChangeCallback {
         void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging);
-        void onPowerSaveChanged();
+        void onPowerSaveChanged(boolean isPowerSave);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index b010761..f3a3554 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -643,8 +643,8 @@
         private final Intent USER_SETTINGS_INTENT = new Intent("android.settings.USER_SETTINGS");
 
         @Override
-        public int getTitle() {
-            return R.string.quick_settings_user_title;
+        public CharSequence getTitle() {
+            return mContext.getString(R.string.quick_settings_user_title);
         }
 
         @Override