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
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.service.quicksettings.Tile;
|
||||
import android.widget.Switch;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settingslib.graph.BatteryMeterDrawableBase;
|
||||
import com.android.systemui.Dependency;
|
||||
|
||||
@@ -55,4 +55,11 @@ public interface BatteryController extends DemoMode, Dumpable,
|
||||
default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {}
|
||||
default void onPowerSaveChanged(boolean isPowerSave) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* If available, get the estimated battery time remaining as a string
|
||||
*/
|
||||
default String getEstimatedTimeRemainingString() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,14 @@ import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.settingslib.fuelgauge.BatterySaverUtils;
|
||||
import com.android.settingslib.utils.PowerUtil;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.power.EnhancedEstimates;
|
||||
import com.android.systemui.power.Estimate;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
@@ -44,7 +49,9 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
|
||||
public static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
|
||||
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
private static final int UPDATE_GRANULARITY_MSEC = 1000 * 60;
|
||||
|
||||
private final EnhancedEstimates mEstimates = Dependency.get(EnhancedEstimates.class);
|
||||
private final ArrayList<BatteryController.BatteryStateChangeCallback> mChangeCallbacks = new ArrayList<>();
|
||||
private final PowerManager mPowerManager;
|
||||
private final Handler mHandler;
|
||||
@@ -58,6 +65,8 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
|
||||
protected boolean mAodPowerSave;
|
||||
private boolean mTestmode = false;
|
||||
private boolean mHasReceivedBattery = false;
|
||||
private Estimate mEstimate;
|
||||
private long mLastEstimateTimestamp = -1;
|
||||
|
||||
public BatteryControllerImpl(Context context) {
|
||||
this(context, context.getSystemService(PowerManager.class));
|
||||
@@ -71,6 +80,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
|
||||
|
||||
registerReceiver();
|
||||
updatePowerSave();
|
||||
updateEstimate();
|
||||
}
|
||||
|
||||
private void registerReceiver() {
|
||||
@@ -180,6 +190,26 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
|
||||
return mAodPowerSave;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEstimatedTimeRemainingString() {
|
||||
if (mEstimate == null
|
||||
|| System.currentTimeMillis() > mLastEstimateTimestamp + UPDATE_GRANULARITY_MSEC) {
|
||||
updateEstimate();
|
||||
}
|
||||
// Estimates may not exist yet even if we've checked
|
||||
if (mEstimate == null) {
|
||||
return null;
|
||||
}
|
||||
final String percentage = NumberFormat.getPercentInstance().format((double) mLevel / 100.0);
|
||||
return PowerUtil.getBatteryRemainingShortStringFormatted(
|
||||
mContext, mEstimate.estimateMillis);
|
||||
}
|
||||
|
||||
private void updateEstimate() {
|
||||
mEstimate = mEstimates.getEstimate();
|
||||
mLastEstimateTimestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private void updatePowerSave() {
|
||||
setPowerSave(mPowerManager.isPowerSaveMode());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user