diff options
8 files changed, 212 insertions, 5 deletions
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index c9bcd65f8d0d..70f9bb693602 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -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> diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java index fa59688d1523..43c97df24a58 100644 --- a/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java +++ b/packages/SettingsLib/src/com/android/settingslib/utils/PowerUtil.java @@ -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; } diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index 94189bb9594b..2000104ad0cd 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -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> diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index f6fec5456ed8..053ea67b92c8 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -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); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 792909928258..e3f85d93b8da 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -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(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 7f3537c2d41d..da2828eae7da 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -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; 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 6f4026db5633..f65f8261dcfb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -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; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index 7221efab0bb2..ddcfbf6f3c31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -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()); } |