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

Commit 4bf21dff authored by Evan Laird's avatar Evan Laird
Browse files

Add estimated time remaining text to QS

- Add an API to BatteryController to get an estimated time remaining
string.
- BatteryController will now check up to once per minute what the
estimated time will be and builds the string using PowerUtil.
- If the "show percentage" setting is on, the estimated time remaining
string (and battery icon) will show next to the system icons in QS
- Also make the battery percent in QS obey the setting

Test: visual
Bug: 116481529
Change-Id: Iaafa00127c8b8baae40956254a1237c8b7ac079b
parent b5138766
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -930,6 +930,8 @@
    <string name="power_discharge_by">Should last until about <xliff:g id="time">%1$s</xliff:g> (<xliff:g id="level">%2$s</xliff:g>)</string>
    <!-- [CHAR_LIMIT=100] Label for estimated time that phone will run out of battery -->
    <string name="power_discharge_by_only">Should last until about <xliff:g id="time">%1$s</xliff:g></string>
    <!-- [CHAR_LIMIT=100] Label for estimated time that phone will run out of battery -->
    <string name="power_discharge_by_only_short">Until <xliff:g id="time" example="12 PM">%1$s</xliff:g></string>

    <!-- [CHAR_LIMIT=60] label for estimated remaining duration of battery when under a certain amount -->
    <string name="power_remaining_less_than_duration_only">Less than <xliff:g id="threshold">%1$s</xliff:g> remaining</string>
+48 −0
Original line number Diff line number Diff line
@@ -81,6 +81,30 @@ public class PowerUtil {
        return null;
    }

    /**
     * Method to produce a shortened string describing the remaining battery. Suitable for Quick
     * Settings and other areas where space is constrained.
     *
     * @param context context to fetch descriptions from
     * @param drainTimeMs The estimated time remaining before the phone dies in milliseconds.
     *
     * @return a properly formatted and localized short string describing how much time remains
     * before the battery runs out.
     */
    @Nullable
    public static String getBatteryRemainingShortStringFormatted(
            Context context, long drainTimeMs) {
        if (drainTimeMs <= 0) {
            return null;
        }

        if (drainTimeMs <= ONE_DAY_MILLIS) {
            return getRegularTimeRemainingShortString(context, drainTimeMs);
        } else {
            return getMoreThanOneDayShortString(context, drainTimeMs);
        }
    }

    private static String getShutdownImminentString(Context context, String percentageString) {
        return TextUtils.isEmpty(percentageString)
                ? context.getString(R.string.power_remaining_duration_only_shutdown_imminent)
@@ -120,6 +144,14 @@ public class PowerUtil {
        }
    }

    private static String getMoreThanOneDayShortString(Context context, long drainTimeMs) {
        final long roundedTimeMs = roundTimeToNearestThreshold(drainTimeMs, ONE_HOUR_MILLIS);
        CharSequence timeString = StringUtil.formatElapsedTime(context, roundedTimeMs,
                false /* withSeconds */);

        return context.getString(R.string.power_remaining_duration_only_short, timeString);
    }

    private static String getMoreThanTwoDaysString(Context context, String percentageString) {
        final Locale currentLocale = context.getResources().getConfiguration().getLocales().get(0);
        final MeasureFormat frmt = MeasureFormat.getInstance(currentLocale, FormatWidth.SHORT);
@@ -162,6 +194,22 @@ public class PowerUtil {
        }
    }

    private static String getRegularTimeRemainingShortString(Context context, long drainTimeMs) {
        // Get the time of day we think device will die rounded to the nearest 15 min.
        final long roundedTimeOfDayMs =
                roundTimeToNearestThreshold(
                        System.currentTimeMillis() + drainTimeMs,
                        FIFTEEN_MINUTES_MILLIS);

        // convert the time to a properly formatted string.
        String skeleton = android.text.format.DateFormat.getTimeFormatString(context);
        DateFormat fmt = DateFormat.getInstanceForSkeleton(skeleton);
        Date date = Date.from(Instant.ofEpochMilli(roundedTimeOfDayMs));
        CharSequence timeString = fmt.format(date);

        return context.getString(R.string.power_discharge_by_only_short, timeString);
    }

    public static long convertUsToMs(long timeUs) {
        return timeUs / 1000;
    }
+15 −1
Original line number Diff line number Diff line
@@ -42,6 +42,20 @@
        android:id="@+id/statusIcons"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />
        android:layout_weight="1"
        android:paddingEnd="@dimen/signal_cluster_battery_padding" />

    <com.android.systemui.BatteryMeterView
        android:id="@+id/batteryRemainingIcon"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:paddingEnd="2dp" />

    <TextView
        android:id="@+id/batteryRemainingText"
        android:textAppearance="@style/TextAppearance.QS.TileLabel"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:gravity="center_vertical" />

</LinearLayout>
+28 −3
Original line number Diff line number Diff line
@@ -19,7 +19,10 @@ import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
import static android.app.StatusBarManager.DISABLE_NONE;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;

import static java.lang.annotation.RetentionPolicy.SOURCE;

import android.animation.ArgbEvaluator;
import android.annotation.IntDef;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
@@ -55,15 +58,23 @@ import com.android.systemui.statusbar.policy.IconLogger;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
import com.android.systemui.util.Utils.DisableStateTracker;
import com.android.systemui.R;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.text.NumberFormat;

public class BatteryMeterView extends LinearLayout implements
        BatteryStateChangeCallback, Tunable, DarkReceiver, ConfigurationListener {


    @Retention(SOURCE)
    @IntDef({MODE_DEFAULT, MODE_ON, MODE_OFF})
    public @interface BatteryPercentMode {}
    public static final int MODE_DEFAULT = 0;
    public static final int MODE_ON = 1;
    public static final int MODE_OFF = 2;

    private final BatteryMeterDrawableBase mDrawable;
    private final String mSlotBattery;
    private final ImageView mBatteryIconView;
@@ -74,6 +85,7 @@ public class BatteryMeterView extends LinearLayout implements
    private SettingObserver mSettingObserver;
    private int mTextColor;
    private int mLevel;
    private int mShowPercentMode = MODE_DEFAULT;
    private boolean mForceShowPercent;
    private boolean mShowPercentAvailable;

@@ -154,7 +166,19 @@ public class BatteryMeterView extends LinearLayout implements
    }

    public void setForceShowPercent(boolean show) {
        mForceShowPercent = show;
        setPercentShowMode(show ? MODE_ON : MODE_DEFAULT);
    }

    /**
     * Force a particular mode of showing percent
     *
     * 0 - No preference
     * 1 - Force on
     * 2 - Force off
     * @param mode desired mode (none, on, off)
     */
    public void setPercentShowMode(@BatteryPercentMode int mode) {
        mShowPercentMode = mode;
        updateShowPercent();
    }

@@ -273,7 +297,8 @@ public class BatteryMeterView extends LinearLayout implements
                .getIntForUser(getContext().getContentResolver(),
                SHOW_BATTERY_PERCENT, 0, mUser);

        if ((mShowPercentAvailable && systemSetting) || mForceShowPercent) {
        if ((mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
                || mShowPercentMode == MODE_ON) {
            if (!showing) {
                mBatteryPercentView = loadPercentView();
                if (mTextColor != 0) mBatteryPercentView.setTextColor(mTextColor);
+81 −1
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
package com.android.systemui.qs;

import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -28,12 +29,15 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Color;
import android.graphics.Rect;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.provider.AlarmClock;
import android.provider.Settings;
import android.service.notification.ZenModeConfig;
import android.text.format.DateUtils;
import android.util.AttributeSet;
@@ -68,6 +72,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
import com.android.systemui.statusbar.phone.StatusIconContainer;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.DarkIconDispatcher;
import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
@@ -132,6 +137,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    private DateView mDateView;
    private OngoingPrivacyChip mPrivacyChip;
    private Space mSpace;
    private BatteryMeterView mBatteryRemainingIcon;
    private TextView mBatteryRemainingText;
    private boolean mShowBatteryPercentAndEstimate;

    private NextAlarmController mAlarmController;
    private ZenModeController mZenController;
@@ -148,6 +156,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    };
    private boolean mHasTopCutout = false;

    private final PercentSettingObserver mPercentSettingObserver =
            new PercentSettingObserver(new Handler(mContext.getMainLooper()));

    /**
     * Runnable for automatically fading out the long press tooltip (as if it were animating away).
     */
@@ -204,8 +215,12 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        // Set the correct tint for the status icons so they contrast
        mIconManager.setTint(fillColor);

        mShowBatteryPercentAndEstimate = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_battery_percentage_setting_available);

        mBatteryMeterView = findViewById(R.id.battery);
        mBatteryMeterView.setForceShowPercent(true);
        mBatteryMeterView.setPercentShowMode(mShowBatteryPercentAndEstimate
                ? BatteryMeterView.MODE_ON : BatteryMeterView.MODE_OFF);
        mBatteryMeterView.setOnClickListener(this);
        mClockView = findViewById(R.id.clock);
        mClockView.setOnClickListener(this);
@@ -213,6 +228,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        mPrivacyChip = findViewById(R.id.privacy_chip);
        mPrivacyChip.setOnClickListener(this);
        mSpace = findViewById(R.id.space);

        // Tint for the battery icons are handled in setupHost()
        mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
        mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_OFF);

        mBatteryRemainingText = findViewById(R.id.batteryRemainingText);
        mBatteryRemainingText.setTextColor(fillColor);

        updateShowPercent();
    }

    private void updateStatusText() {
@@ -371,6 +395,14 @@ public class QuickStatusBarHeader extends RelativeLayout implements
                .build();
    }

    private void updateBatteryRemainingText() {
        if (!mShowBatteryPercentAndEstimate) {
            return;
        }
        mBatteryRemainingText.setText(
                Dependency.get(BatteryController.class).getEstimatedTimeRemainingString());
    }

    public void setExpanded(boolean expanded) {
        if (mExpanded == expanded) return;
        mExpanded = expanded;
@@ -436,6 +468,9 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        super.onAttachedToWindow();
        Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
        requestApplyInsets();
        mContext.getContentResolver().registerContentObserver(
                Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mPercentSettingObserver,
                ActivityManager.getCurrentUser());
    }

    @Override
@@ -475,6 +510,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
    public void onDetachedFromWindow() {
        setListening(false);
        Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
        mContext.getContentResolver().unregisterContentObserver(mPercentSettingObserver);
        super.onDetachedFromWindow();
    }

@@ -491,6 +527,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements
            mAlarmController.addCallback(this);
            mContext.registerReceiver(mRingerReceiver,
                    new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
            updateBatteryRemainingText();
        } else {
            mZenController.removeCallback(this);
            mAlarmController.removeCallback(this);
@@ -660,6 +697,14 @@ public class QuickStatusBarHeader extends RelativeLayout implements
        // Use SystemUI context to get battery meter colors, and let it use the default tint (white)
        mBatteryMeterView.setColorsFromContext(mHost.getContext());
        mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);

        Rect tintArea = new Rect(0, 0, 0, 0);
        int colorForeground = Utils.getColorAttrDefaultColor(getContext(),
                android.R.attr.colorForeground);
        float intensity = getColorIntensity(colorForeground);
        int fillColor = fillColorForIntensity(intensity, getContext());
        mBatteryRemainingIcon.setColorsFromContext(mHost.getContext());
        mBatteryRemainingIcon.onDarkChanged(tintArea, intensity, fillColor);
    }

    public void setCallback(Callback qsPanelCallback) {
@@ -692,4 +737,39 @@ public class QuickStatusBarHeader extends RelativeLayout implements
            lp.rightMargin = sideMargins;
        }
    }

    private void updateShowPercent() {
        final boolean systemSetting = 0 != Settings.System
                .getIntForUser(getContext().getContentResolver(),
                        SHOW_BATTERY_PERCENT, 0, ActivityManager.getCurrentUser());

        mShowBatteryPercentAndEstimate = systemSetting;

        updateBatteryViews();
    }

    private void updateBatteryViews() {
        if (mShowBatteryPercentAndEstimate) {
            mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ON);
            mBatteryRemainingIcon.setVisibility(View.VISIBLE);
            mBatteryRemainingText.setVisibility(View.VISIBLE);
            updateBatteryRemainingText();
        } else {
            mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_OFF);
            mBatteryRemainingIcon.setVisibility(View.GONE);
            mBatteryRemainingText.setVisibility(View.GONE);
        }
    }

    private final class PercentSettingObserver extends ContentObserver {
        PercentSettingObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);
            updateShowPercent();
        }
    }
}
Loading