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
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7929099..e3f85d9 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.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.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 @@
     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 @@
     };
     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 @@
         // 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 @@
         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 @@
                 .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 @@
         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 void onDetachedFromWindow() {
         setListening(false);
         Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
+        mContext.getContentResolver().unregisterContentObserver(mPercentSettingObserver);
         super.onDetachedFromWindow();
     }
 
@@ -491,6 +527,7 @@
             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 @@
         // 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 @@
             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();
+        }
+    }
 }