Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0be7598c authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Show time remaining / to charge.

Rework the battery graph to include the time remaining or
time to charge.

Change-Id: Ib26b761cb10e01f5f3aa4189db10d44b8ce62f89
parent 4f700a3b
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -16,13 +16,15 @@

<com.android.settings.fuelgauge.BatteryHistoryChart
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.android.settings"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?android:attr/listPreferredItemHeight"
    android:minHeight="128dp"
    android:gravity="center_vertical"
    android:id="@+android:id/battery_history_chart"
    android:paddingEnd="?android:attr/scrollbarSize"
    android:textAppearance="?android:attr/textAppearanceMedium"
    app:headerAppearance="?android:attr/textAppearanceMedium"
    android:shadowRadius="4"
    android:shadowColor="?android:attr/colorBackground"
    android:shadowDx="2"
+2 −0
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@
        <attr name="android:shadowDy" />
        <!-- Radius of the shadow. -->
        <attr name="android:shadowRadius" />
        <!-- Text color, typeface, size, and style of header. -->
        <attr name="headerAppearance" format="reference" />
    </declare-styleable>

    <declare-styleable name="PercentageBarChart">
+28 −2
Original line number Diff line number Diff line
@@ -3279,6 +3279,27 @@
    <!-- Used to show an amount of time in the form "m minutes, s seconds" in BatteryHistory -->
    <string name="battery_history_minutes_no_seconds"><xliff:g id="minutes">%1$d</xliff:g>m</string>

    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "d days" in BatteryHistory -->
    <string name="battery_history_days_only"><xliff:g id="days">%1$d</xliff:g>d</string>

    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "d days, h hours" in BatteryHistory -->
    <string name="battery_history_days_and_hours"><xliff:g id="days">%1$d</xliff:g>d
        <xliff:g id="hours">%2$d</xliff:g>h</string>

    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "h hours" in BatteryHistory -->
    <string name="battery_history_hours_only"><xliff:g id="hours">%1$d</xliff:g>h</string>

    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "h hours, m minutes" in BatteryHistory -->
    <string name="battery_history_hours_and_minutes"><xliff:g id="hours">%1$d</xliff:g>h
        <xliff:g id="minutes">%2$d</xliff:g>m</string>

    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "m minutes" in BatteryHistory -->
    <string name="battery_history_minutes_only"><xliff:g id="minutes">%1$d</xliff:g>m</string>

    <!-- [CHAR LIMIT=20] Used to show an amount of time in the form "m minutes, s seconds" in BatteryHistory -->
    <string name="battery_history_minutes_and_seconds"><xliff:g id="minutes">%1$d</xliff:g>m
        <xliff:g id="seconds">%2$d</xliff:g>s</string>

    <!-- XXX remove? Strings used for displaying usage statistics -->
    <string name="usage_stats_label">Usage statistics</string>

@@ -3562,8 +3583,13 @@
    <string name="power_usage_summary">What has been using the battery</string>
    <!-- Message to show when battery usage data is not available [CHAR LIMIT=30] -->
    <string name="power_usage_not_available">Battery usage data isn\'t available.</string>
    <!-- Display the battery level and status [CHAR_LIMIT=30] -->
    <string name="power_usage_level_and_status"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="status">%2$s</xliff:g></string>
    <!-- Display the battery level and status [CHAR_LIMIT=60] -->
    <string name="power_usage_level_and_status"><xliff:g id="level">%1$s</xliff:g>
            - <xliff:g id="status">%2$s</xliff:g></string>
    <!-- Display time remaining until battery is discharged [CHAR_LIMIT=60] -->
    <string name="power_discharge_remaining"><xliff:g id="remain">%1$s</xliff:g> remaining</string>
    <!-- Display time remaining until battery is charged [CHAR_LIMIT=60] -->
    <string name="power_charge_remaining"><xliff:g id="until_charged">%1$s</xliff:g> to charge</string>
    <!-- Battery usage since unplugged -->
    <string name="battery_since_unplugged">Battery use since unplugged</string>
    <!-- Battery usage since user reset the stats -->
+0 −5
Original line number Diff line number Diff line
@@ -17,9 +17,4 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
        android:title="@string/power_usage_summary_title"
        android:key="app_list">
    <Preference
        style="?android:attr/preferenceInformationStyle"
        android:key="battery_status"
        android:persistent="false"
    />
</PreferenceScreen>
+179 −113
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settings.fuelgauge;

import android.content.Intent;
import android.os.BatteryManager;
import com.android.settings.R;

import android.content.Context;
@@ -120,6 +122,7 @@ public class BatteryHistoryChart extends View {
    final Paint mCpuRunningPaint = new Paint();
    final ChartData mPhoneSignalChart = new ChartData();
    final TextPaint mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
    final TextPaint mHeaderTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);

    final Path mBatLevelPath = new Path();
    final Path mBatGoodPath = new Path();
@@ -131,12 +134,13 @@ public class BatteryHistoryChart extends View {
    final Path mWifiRunningPath = new Path();
    final Path mCpuRunningPath = new Path();
    
    int mFontSize;
    
    BatteryStats mStats;
    Intent mBatteryBroadcast;
    long mStatsPeriod;
    String mDurationString;
    String mTotalDurationString;
    String mChargeLabelString;
    String mChargeDurationString;
    String mDrainString;
    String mChargingLabel;
    String mScreenOnLabel;
    String mGpsOnLabel;
@@ -146,8 +150,12 @@ public class BatteryHistoryChart extends View {
    
    int mTextAscent;
    int mTextDescent;
    int mHeaderTextAscent;
    int mHeaderTextDescent;
    int mDurationStringWidth;
    int mTotalDurationStringWidth;
    int mChargeLabelStringWidth;
    int mChargeDurationStringWidth;
    int mDrainStringWidth;

    boolean mLargeMode;

@@ -175,47 +183,18 @@ public class BatteryHistoryChart extends View {
    boolean mHaveGps;
    boolean mHavePhoneSignal;

    public BatteryHistoryChart(Context context, AttributeSet attrs) {
        super(context, attrs);
        
        mBatteryBackgroundPaint.setARGB(255, 128, 128, 128);
        mBatteryBackgroundPaint.setStyle(Paint.Style.FILL);
        mBatteryGoodPaint.setARGB(128, 0, 255, 0);
        mBatteryGoodPaint.setStyle(Paint.Style.STROKE);
        mBatteryWarnPaint.setARGB(128, 255, 255, 0);
        mBatteryWarnPaint.setStyle(Paint.Style.STROKE);
        mBatteryCriticalPaint.setARGB(192, 255, 0, 0);
        mBatteryCriticalPaint.setStyle(Paint.Style.STROKE);
        mChargingPaint.setARGB(255, 0, 128, 0);
        mChargingPaint.setStyle(Paint.Style.STROKE);
        mScreenOnPaint.setStyle(Paint.Style.STROKE);
        mGpsOnPaint.setStyle(Paint.Style.STROKE);
        mWifiRunningPaint.setStyle(Paint.Style.STROKE);
        mCpuRunningPaint.setStyle(Paint.Style.STROKE);
        mPhoneSignalChart.setColors(new int[] {
                0x00000000, 0xffa00000, 0xffa07000, 0xffa0a000,
                0xff80a000, 0xff409000, 0xff008000
        });
        
        mTextPaint.density = getResources().getDisplayMetrics().density;
        mTextPaint.setCompatibilityScaling(
                getResources().getCompatibilityInfo().applicationScale);
        
        TypedArray a =
            context.obtainStyledAttributes(
                attrs, R.styleable.BatteryHistoryChart, 0, 0);
        
    static class TextAttrs {
        ColorStateList textColor = null;
        int textSize = 15;
        int typefaceIndex = -1;
        int styleIndex = -1;

        void retrieve(Context context, TypedArray from, int index) {
            TypedArray appearance = null;
        int ap = a.getResourceId(R.styleable.BatteryHistoryChart_android_textAppearance, -1);
            int ap = from.getResourceId(index, -1);
            if (ap != -1) {
                appearance = context.obtainStyledAttributes(ap,
                                com.android.internal.R.styleable.
                                TextAppearance);
                                    com.android.internal.R.styleable.TextAppearance);
            }
            if (appearance != null) {
                int n = appearance.getIndexCount();
@@ -243,6 +222,86 @@ public class BatteryHistoryChart extends View {

                appearance.recycle();
            }
        }

        void apply(Context context, TextPaint paint) {
            paint.density = context.getResources().getDisplayMetrics().density;
            paint.setCompatibilityScaling(
                    context.getResources().getCompatibilityInfo().applicationScale);

            paint.setColor(textColor.getDefaultColor());
            paint.setTextSize(textSize);

            Typeface tf = null;
            switch (typefaceIndex) {
                case SANS:
                    tf = Typeface.SANS_SERIF;
                    break;

                case SERIF:
                    tf = Typeface.SERIF;
                    break;

                case MONOSPACE:
                    tf = Typeface.MONOSPACE;
                    break;
            }

            setTypeface(paint, tf, styleIndex);
        }

        public void setTypeface(TextPaint paint, Typeface tf, int style) {
            if (style > 0) {
                if (tf == null) {
                    tf = Typeface.defaultFromStyle(style);
                } else {
                    tf = Typeface.create(tf, style);
                }

                paint.setTypeface(tf);
                // now compute what (if any) algorithmic styling is needed
                int typefaceStyle = tf != null ? tf.getStyle() : 0;
                int need = style & ~typefaceStyle;
                paint.setFakeBoldText((need & Typeface.BOLD) != 0);
                paint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
            } else {
                paint.setFakeBoldText(false);
                paint.setTextSkewX(0);
                paint.setTypeface(tf);
            }
        }
    }

    public BatteryHistoryChart(Context context, AttributeSet attrs) {
        super(context, attrs);
        
        mBatteryBackgroundPaint.setARGB(255, 128, 128, 128);
        mBatteryBackgroundPaint.setStyle(Paint.Style.FILL);
        mBatteryGoodPaint.setARGB(128, 0, 255, 0);
        mBatteryGoodPaint.setStyle(Paint.Style.STROKE);
        mBatteryWarnPaint.setARGB(128, 255, 255, 0);
        mBatteryWarnPaint.setStyle(Paint.Style.STROKE);
        mBatteryCriticalPaint.setARGB(192, 255, 0, 0);
        mBatteryCriticalPaint.setStyle(Paint.Style.STROKE);
        mChargingPaint.setARGB(255, 0, 128, 0);
        mChargingPaint.setStyle(Paint.Style.STROKE);
        mScreenOnPaint.setStyle(Paint.Style.STROKE);
        mGpsOnPaint.setStyle(Paint.Style.STROKE);
        mWifiRunningPaint.setStyle(Paint.Style.STROKE);
        mCpuRunningPaint.setStyle(Paint.Style.STROKE);
        mPhoneSignalChart.setColors(new int[] {
                0x00000000, 0xffa00000, 0xffa07000, 0xffa0a000,
                0xff80a000, 0xff409000, 0xff008000
        });
        
        TypedArray a =
            context.obtainStyledAttributes(
                attrs, R.styleable.BatteryHistoryChart, 0, 0);

        final TextAttrs mainTextAttrs = new TextAttrs();
        final TextAttrs headTextAttrs = new TextAttrs();
        mainTextAttrs.retrieve(context, a, R.styleable.BatteryHistoryChart_android_textAppearance);
        headTextAttrs.retrieve(context, a, R.styleable.BatteryHistoryChart_headerAppearance);

        int shadowcolor = 0;
        float dx=0, dy=0, r=0;
@@ -269,80 +328,47 @@ public class BatteryHistoryChart extends View {
                    break;

                case R.styleable.BatteryHistoryChart_android_textColor:
                    textColor = a.getColorStateList(attr);
                    mainTextAttrs.textColor = a.getColorStateList(attr);
                    headTextAttrs.textColor = a.getColorStateList(attr);
                    break;

                case R.styleable.BatteryHistoryChart_android_textSize:
                    textSize = a.getDimensionPixelSize(attr, textSize);
                    mainTextAttrs.textSize = a.getDimensionPixelSize(attr, mainTextAttrs.textSize);
                    headTextAttrs.textSize = a.getDimensionPixelSize(attr, headTextAttrs.textSize);
                    break;

                case R.styleable.BatteryHistoryChart_android_typeface:
                    typefaceIndex = a.getInt(attr, typefaceIndex);
                    mainTextAttrs.typefaceIndex = a.getInt(attr, mainTextAttrs.typefaceIndex);
                    headTextAttrs.typefaceIndex = a.getInt(attr, headTextAttrs.typefaceIndex);
                    break;

                case R.styleable.BatteryHistoryChart_android_textStyle:
                    styleIndex = a.getInt(attr, styleIndex);
                    mainTextAttrs.styleIndex = a.getInt(attr, mainTextAttrs.styleIndex);
                    headTextAttrs.styleIndex = a.getInt(attr, headTextAttrs.styleIndex);
                    break;
            }
        }
        
        a.recycle();
        
        mTextPaint.setColor(textColor.getDefaultColor());
        mTextPaint.setTextSize(textSize);
        
        Typeface tf = null;
        switch (typefaceIndex) {
            case SANS:
                tf = Typeface.SANS_SERIF;
                break;

            case SERIF:
                tf = Typeface.SERIF;
                break;

            case MONOSPACE:
                tf = Typeface.MONOSPACE;
                break;
        }
        
        setTypeface(tf, styleIndex);
        mainTextAttrs.apply(context, mTextPaint);
        headTextAttrs.apply(context, mHeaderTextPaint);

        if (shadowcolor != 0) {
            mTextPaint.setShadowLayer(r, dx, dy, shadowcolor);
            mHeaderTextPaint.setShadowLayer(r, dx, dy, shadowcolor);
        }
    }
    
    public void setTypeface(Typeface tf, int style) {
        if (style > 0) {
            if (tf == null) {
                tf = Typeface.defaultFromStyle(style);
            } else {
                tf = Typeface.create(tf, style);
            }

            mTextPaint.setTypeface(tf);
            // now compute what (if any) algorithmic styling is needed
            int typefaceStyle = tf != null ? tf.getStyle() : 0;
            int need = style & ~typefaceStyle;
            mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
            mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
        } else {
            mTextPaint.setFakeBoldText(false);
            mTextPaint.setTextSkewX(0);
            mTextPaint.setTypeface(tf);
        }
    }
    
    void setStats(BatteryStats stats) {
    void setStats(BatteryStats stats, Intent broadcast) {
        mStats = stats;
        mBatteryBroadcast = broadcast;

        final long elapsedRealtimeUs = SystemClock.elapsedRealtime() * 1000;

        long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000,
        long uSecTime = mStats.computeBatteryRealtime(elapsedRealtimeUs,
                BatteryStats.STATS_SINCE_CHARGED);
        mStatsPeriod = uSecTime;
        String durationString = Utils.formatElapsedTime(getContext(), mStatsPeriod / 1000, true);
        mDurationString = getContext().getString(R.string.battery_stats_on_battery,
                durationString);
        mChargingLabel = getContext().getString(R.string.battery_stats_charging_label);
        mScreenOnLabel = getContext().getString(R.string.battery_stats_screen_on_label);
        mGpsOnLabel = getContext().getString(R.string.battery_stats_gps_on_label);
@@ -382,16 +408,42 @@ public class BatteryHistoryChart extends View {
            mHavePhoneSignal = true;
        }
        if (mHistEnd <= mHistStart) mHistEnd = mHistStart+1;
        mTotalDurationString = Utils.formatElapsedTime(getContext(), mHistEnd - mHistStart, true);

        //String durationString = Utils.formatElapsedTime(getContext(), mStatsPeriod / 1000, true);
        //mDurationString = getContext().getString(R.string.battery_stats_on_battery,
        //        durationString);
        mDurationString = Utils.formatElapsedTime(getContext(), mHistEnd - mHistStart, true);
        mDrainString = com.android.settings.Utils.getBatteryPercentage(mBatteryBroadcast);
        mChargeLabelString = com.android.settings.Utils.getBatteryStatus(getResources(),
                mBatteryBroadcast);
        final long drainTime = mStats.computeBatteryTimeRemaining(elapsedRealtimeUs);
        final long chargeTime = mStats.computeChargeTimeRemaining(elapsedRealtimeUs);
        final int status = mBatteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
                BatteryManager.BATTERY_STATUS_UNKNOWN);
        if (drainTime > 0) {
            String timeString = Utils.formatShortElapsedTime(getContext(),drainTime / 1000);
            mChargeDurationString = getContext().getResources().getString(
                    R.string.power_discharge_remaining, timeString);
        } else if (chargeTime > 0 && status != BatteryManager.BATTERY_STATUS_FULL) {
            String timeString = Utils.formatShortElapsedTime(getContext(), chargeTime / 1000);
            mChargeDurationString = getContext().getResources().getString(
                    R.string.power_charge_remaining, timeString);
        } else {
            mChargeDurationString = "";
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mDurationStringWidth = (int)mTextPaint.measureText(mDurationString);
        mTotalDurationStringWidth = (int)mTextPaint.measureText(mTotalDurationString);
        mDrainStringWidth = (int)mHeaderTextPaint.measureText(mDrainString);
        mChargeLabelStringWidth = (int)mHeaderTextPaint.measureText(mChargeLabelString);
        mChargeDurationStringWidth = (int)mHeaderTextPaint.measureText(mChargeDurationString);
        mTextAscent = (int)mTextPaint.ascent();
        mTextDescent = (int)mTextPaint.descent();
        mHeaderTextAscent = (int)mHeaderTextPaint.ascent();
        mHeaderTextDescent = (int)mHeaderTextPaint.descent();
    }

    void finishPaths(int w, int h, int levelh, int startX, int y, Path curLevelPath,
@@ -434,9 +486,10 @@ public class BatteryHistoryChart extends View {
        super.onSizeChanged(w, h, oldw, oldh);
        
        int textHeight = mTextDescent - mTextAscent;
        int headerTextHeight = mHeaderTextDescent - mHeaderTextAscent;
        mThinLineWidth = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                2, getResources().getDisplayMetrics());
        if (h > (textHeight*6)) {
        if (h > (textHeight*12)) {
            mLargeMode = true;
            if (h > (textHeight*15)) {
                // Plenty of room for the chart.
@@ -445,7 +498,7 @@ public class BatteryHistoryChart extends View {
                // Compress lines to make more room for chart.
                mLineWidth = textHeight/3;
            }
            mLevelTop = textHeight + mLineWidth;
            mLevelTop = headerTextHeight*2 + mLineWidth;
            mScreenOnPaint.setARGB(255, 32, 64, 255);
            mGpsOnPaint.setARGB(255, 32, 64, 255);
            mWifiRunningPaint.setARGB(255, 32, 64, 255);
@@ -453,7 +506,7 @@ public class BatteryHistoryChart extends View {
        } else {
            mLargeMode = false;
            mLineWidth = mThinLineWidth;
            mLevelTop = 0;
            mLevelTop = headerTextHeight*2 + mLineWidth;
            mScreenOnPaint.setARGB(255, 0, 0, 255);
            mGpsOnPaint.setARGB(255, 0, 0, 255);
            mWifiRunningPaint.setARGB(255, 0, 0, 255);
@@ -659,22 +712,35 @@ public class BatteryHistoryChart extends View {
        final int height = getHeight();
        final boolean layoutRtl = isLayoutRtl();
        final int textStartX = layoutRtl ? width : 0;
        mTextPaint.setTextAlign(layoutRtl ? Paint.Align.RIGHT : Paint.Align.LEFT);
        final int textEndX = layoutRtl ? 0 : width;
        final Paint.Align textAlignLeft = layoutRtl ? Paint.Align.RIGHT : Paint.Align.LEFT;
        final Paint.Align textAlignRight = layoutRtl ? Paint.Align.LEFT : Paint.Align.RIGHT;
        mTextPaint.setTextAlign(textAlignLeft);

        canvas.drawPath(mBatLevelPath, mBatteryBackgroundPaint);
        if (mLargeMode) {
            int durationHalfWidth = mTotalDurationStringWidth / 2;
        int durationHalfWidth = mDurationStringWidth / 2;
        if (layoutRtl) durationHalfWidth = -durationHalfWidth;
            canvas.drawText(mDurationString, textStartX, -mTextAscent + (mLineWidth / 2),
                    mTextPaint);
            canvas.drawText(mTotalDurationString, (width / 2) - durationHalfWidth,
        if (mLargeMode) {
            canvas.drawText(mDurationString, (width / 2) - durationHalfWidth,
                    mLevelBottom - mTextAscent + mThinLineWidth, mTextPaint);
        } else {
            int durationHalfWidth = mDurationStringWidth / 2;
            if (layoutRtl) durationHalfWidth = -durationHalfWidth;
            canvas.drawText(mDurationString, (width / 2) - durationHalfWidth,
                    (height / 2) - ((mTextDescent - mTextAscent) / 2) - mTextAscent, mTextPaint);
                    mLevelTop + ((height-mLevelTop) / 2) - ((mTextDescent - mTextAscent) / 2)
                            - mTextAscent, mTextPaint);
        }

        int headerTop = mLevelTop/2 + (mHeaderTextDescent-mHeaderTextAscent)/2;
        mHeaderTextPaint.setTextAlign(textAlignLeft);
        canvas.drawText(mChargeLabelString, textStartX, headerTop, mHeaderTextPaint);
        durationHalfWidth = mChargeDurationStringWidth / 2;
        if (layoutRtl) durationHalfWidth = -durationHalfWidth;
        int headerCenter = ((width-mChargeDurationStringWidth-mDrainStringWidth)/2)
                + (layoutRtl ? mDrainStringWidth : mChargeLabelStringWidth);
        canvas.drawText(mChargeDurationString, headerCenter - durationHalfWidth, headerTop,
                mHeaderTextPaint);
        mHeaderTextPaint.setTextAlign(textAlignRight);
        canvas.drawText(mDrainString, textEndX, headerTop, mHeaderTextPaint);

        if (!mBatGoodPath.isEmpty()) {
            canvas.drawPath(mBatGoodPath, mBatteryGoodPaint);
        }
Loading