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

Commit 140650ce authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Refactor battery warning code to make it easier to read"

parents af597830 4387bd52
Loading
Loading
Loading
Loading
+55 −0
Original line number Original line Diff line number Diff line
package com.android.systemui.power

import com.android.systemui.power.PowerUI.NO_ESTIMATE_AVAILABLE

/**
 * A simple data class to snapshot battery state when a particular check for the
 * low battery warning is running in the background.
 */
data class BatteryStateSnapshot(
    val batteryLevel: Int,
    val isPowerSaver: Boolean,
    val plugged: Boolean,
    val bucket: Int,
    val batteryStatus: Int,
    val severeLevelThreshold: Int,
    val lowLevelThreshold: Int,
    val timeRemainingMillis: Long,
    val severeThresholdMillis: Long,
    val lowThresholdMillis: Long,
    val isBasedOnUsage: Boolean
) {
    /**
     * Returns whether hybrid warning logic/copy should be used for this snapshot
     */
    var isHybrid: Boolean = false
        private set

    init {
        this.isHybrid = true
    }

    constructor(
        batteryLevel: Int,
        isPowerSaver: Boolean,
        plugged: Boolean,
        bucket: Int,
        batteryStatus: Int,
        severeLevelThreshold: Int,
        lowLevelThreshold: Int
    ) : this(
        batteryLevel,
        isPowerSaver,
        plugged,
        bucket,
        batteryStatus,
        severeLevelThreshold,
        lowLevelThreshold,
        NO_ESTIMATE_AVAILABLE.toLong(),
        NO_ESTIMATE_AVAILABLE.toLong(),
        NO_ESTIMATE_AVAILABLE.toLong(),
        false
    ) {
        this.isHybrid = false
    }
}
+0 −11
Original line number Original line Diff line number Diff line
package com.android.systemui.power;

public class Estimate {
    public final long estimateMillis;
    public final boolean isBasedOnUsage;

    public Estimate(long estimateMillis, boolean isBasedOnUsage) {
        this.estimateMillis = estimateMillis;
        this.isBasedOnUsage = isBasedOnUsage;
    }
}
+3 −0
Original line number Original line Diff line number Diff line
package com.android.systemui.power

data class Estimate(val estimateMillis: Long, val isBasedOnUsage: Boolean)
 No newline at end of file
+17 −26
Original line number Original line Diff line number Diff line
@@ -134,10 +134,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
    private int mShowing;
    private int mShowing;


    private long mWarningTriggerTimeMs;
    private long mWarningTriggerTimeMs;

    private Estimate mEstimate;
    private long mLowWarningThreshold;
    private long mSevereWarningThreshold;
    private boolean mWarning;
    private boolean mWarning;
    private boolean mShowAutoSaverSuggestion;
    private boolean mShowAutoSaverSuggestion;
    private boolean mPlaySound;
    private boolean mPlaySound;
@@ -148,6 +144,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
    private SystemUIDialog mHighTempDialog;
    private SystemUIDialog mHighTempDialog;
    private SystemUIDialog mThermalShutdownDialog;
    private SystemUIDialog mThermalShutdownDialog;
    @VisibleForTesting SystemUIDialog mUsbHighTempDialog;
    @VisibleForTesting SystemUIDialog mUsbHighTempDialog;
    private BatteryStateSnapshot mCurrentBatterySnapshot;


    /**
    /**
     */
     */
@@ -195,17 +192,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
    }
    }


    @Override
    @Override
    public void updateEstimate(Estimate estimate) {
    public void updateSnapshot(BatteryStateSnapshot snapshot) {
        mEstimate = estimate;
        mCurrentBatterySnapshot = snapshot;
        if (estimate.estimateMillis <= mLowWarningThreshold) {
            mWarningTriggerTimeMs = System.currentTimeMillis();
        }
    }

    @Override
    public void updateThresholds(long lowThreshold, long severeThreshold) {
        mLowWarningThreshold = lowThreshold;
        mSevereWarningThreshold = severeThreshold;
    }
    }


    private void updateNotification() {
    private void updateNotification() {
@@ -254,15 +242,17 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {


    protected void showWarningNotification() {
    protected void showWarningNotification() {
        final String percentage = NumberFormat.getPercentInstance()
        final String percentage = NumberFormat.getPercentInstance()
                .format((double) mBatteryLevel / 100.0);
                .format((double) mCurrentBatterySnapshot.getBatteryLevel() / 100.0);


        // get standard notification copy
        // get shared standard notification copy
        String title = mContext.getString(R.string.battery_low_title);
        String title = mContext.getString(R.string.battery_low_title);
        String contentText = mContext.getString(R.string.battery_low_percent_format, percentage);
        String contentText;


        // override notification copy if hybrid notification enabled
        // get correct content text if notification is hybrid or not
        if (mEstimate != null) {
        if (mCurrentBatterySnapshot.isHybrid()) {
            contentText = getHybridContentString(percentage);
            contentText = getHybridContentString(percentage);
        } else {
            contentText = mContext.getString(R.string.battery_low_percent_format, percentage);
        }
        }


        final Notification.Builder nb =
        final Notification.Builder nb =
@@ -282,8 +272,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
        }
        }
        // Make the notification red if the percentage goes below a certain amount or the time
        // Make the notification red if the percentage goes below a certain amount or the time
        // remaining estimate is disabled
        // remaining estimate is disabled
        if (mEstimate == null || mBucket < 0
        if (!mCurrentBatterySnapshot.isHybrid() || mBucket < 0
                || mEstimate.estimateMillis < mSevereWarningThreshold) {
                || mCurrentBatterySnapshot.getTimeRemainingMillis()
                        < mCurrentBatterySnapshot.getSevereThresholdMillis()) {
            nb.setColor(Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError));
            nb.setColor(Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError));
        }
        }


@@ -325,9 +316,9 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
    private String getHybridContentString(String percentage) {
    private String getHybridContentString(String percentage) {
        return PowerUtil.getBatteryRemainingStringFormatted(
        return PowerUtil.getBatteryRemainingStringFormatted(
                mContext,
                mContext,
            mEstimate.estimateMillis,
                mCurrentBatterySnapshot.getTimeRemainingMillis(),
                percentage,
                percentage,
            mEstimate.isBasedOnUsage);
                mCurrentBatterySnapshot.isBasedOnUsage());
    }
    }


    private PendingIntent pendingBroadcast(String action) {
    private PendingIntent pendingBroadcast(String action) {
+172 −91
Original line number Original line Diff line number Diff line
@@ -55,6 +55,7 @@ import java.util.Arrays;
import java.util.concurrent.Future;
import java.util.concurrent.Future;


public class PowerUI extends SystemUI {
public class PowerUI extends SystemUI {

    static final String TAG = "PowerUI";
    static final String TAG = "PowerUI";
    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
    private static final long TEMPERATURE_INTERVAL = 30 * DateUtils.SECOND_IN_MILLIS;
@@ -63,6 +64,7 @@ public class PowerUI extends SystemUI {
    static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
    static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
    private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
    private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
    private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
    private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
    public static final int NO_ESTIMATE_AVAILABLE = -1;


    private final Handler mHandler = new Handler();
    private final Handler mHandler = new Handler();
    @VisibleForTesting
    @VisibleForTesting
@@ -71,13 +73,9 @@ public class PowerUI extends SystemUI {
    private PowerManager mPowerManager;
    private PowerManager mPowerManager;
    private WarningsUI mWarnings;
    private WarningsUI mWarnings;
    private final Configuration mLastConfiguration = new Configuration();
    private final Configuration mLastConfiguration = new Configuration();
    private long mTimeRemaining = Long.MAX_VALUE;
    private int mPlugType = 0;
    private int mPlugType = 0;
    private int mInvalidCharger = 0;
    private int mInvalidCharger = 0;
    private EnhancedEstimates mEnhancedEstimates;
    private EnhancedEstimates mEnhancedEstimates;
    private Estimate mLastEstimate;
    private boolean mLowWarningShownThisChargeCycle;
    private boolean mSevereWarningShownThisChargeCycle;
    private Future mLastShowWarningTask;
    private Future mLastShowWarningTask;
    private boolean mEnableSkinTemperatureWarning;
    private boolean mEnableSkinTemperatureWarning;
    private boolean mEnableUsbTemperatureAlarm;
    private boolean mEnableUsbTemperatureAlarm;
@@ -87,6 +85,10 @@ public class PowerUI extends SystemUI {


    private long mScreenOffTime = -1;
    private long mScreenOffTime = -1;


    @VisibleForTesting boolean mLowWarningShownThisChargeCycle;
    @VisibleForTesting boolean mSevereWarningShownThisChargeCycle;
    @VisibleForTesting BatteryStateSnapshot mCurrentBatteryStateSnapshot;
    @VisibleForTesting BatteryStateSnapshot mLastBatteryStateSnapshot;
    @VisibleForTesting IThermalService mThermalService;
    @VisibleForTesting IThermalService mThermalService;


    @VisibleForTesting int mBatteryLevel = 100;
    @VisibleForTesting int mBatteryLevel = 100;
@@ -205,6 +207,7 @@ public class PowerUI extends SystemUI {
                mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
                mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
                final int oldInvalidCharger = mInvalidCharger;
                final int oldInvalidCharger = mInvalidCharger;
                mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
                mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
                mLastBatteryStateSnapshot = mCurrentBatteryStateSnapshot;


                final boolean plugged = mPlugType != 0;
                final boolean plugged = mPlugType != 0;
                final boolean oldPlugged = oldPlugType != 0;
                final boolean oldPlugged = oldPlugType != 0;
@@ -233,16 +236,22 @@ public class PowerUI extends SystemUI {
                    mWarnings.dismissInvalidChargerWarning();
                    mWarnings.dismissInvalidChargerWarning();
                } else if (mWarnings.isInvalidChargerWarningShowing()) {
                } else if (mWarnings.isInvalidChargerWarningShowing()) {
                    // if invalid charger is showing, don't show low battery
                    // if invalid charger is showing, don't show low battery
                    if (DEBUG) {
                        Slog.d(TAG, "Bad Charger");
                    }
                    return;
                    return;
                }
                }


                // Show the correct version of low battery warning if needed
                // Show the correct version of low battery warning if needed
                if (mLastShowWarningTask != null) {
                if (mLastShowWarningTask != null) {
                    mLastShowWarningTask.cancel(true);
                    mLastShowWarningTask.cancel(true);
                    if (DEBUG) {
                        Slog.d(TAG, "cancelled task");
                    }
                }
                }
                mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
                mLastShowWarningTask = ThreadUtils.postOnBackgroundThread(() -> {
                    maybeShowBatteryWarning(
                    maybeShowBatteryWarningV2(
                            oldBatteryLevel, plugged, oldPlugged, oldBucket, bucket);
                            plugged, bucket);
                });
                });


            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
@@ -257,118 +266,176 @@ public class PowerUI extends SystemUI {
        }
        }
    }
    }


    protected void maybeShowBatteryWarning(int oldBatteryLevel, boolean plugged, boolean oldPlugged,
    protected void maybeShowBatteryWarningV2(boolean plugged, int bucket) {
            int oldBucket, int bucket) {
        boolean isPowerSaver = mPowerManager.isPowerSaveMode();
        // only play SFX when the dialog comes up or the bucket changes
        final boolean playSound = bucket != oldBucket || oldPlugged;
        final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
        final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
        final boolean isPowerSaverMode = mPowerManager.isPowerSaveMode();

        // Stick current battery state into an immutable container to determine if we should show
        // a warning.
        if (DEBUG) {
            Slog.d(TAG, "evaluating which notification to show");
        }
        if (hybridEnabled) {
        if (hybridEnabled) {
            Estimate estimate = mLastEstimate;
            if (DEBUG) {
            if (estimate == null || mBatteryLevel != oldBatteryLevel) {
                Slog.d(TAG, "using hybrid");
                estimate = mEnhancedEstimates.getEstimate();
            }
                mLastEstimate = estimate;
            Estimate estimate = refreshEstimateIfNeeded();
            }
            mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
            // Turbo is not always booted once SysUI is running so we have to make sure we actually
                    plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
            // get data back
                    mLowBatteryReminderLevels[0], estimate.getEstimateMillis(),
            if (estimate != null) {
                    mEnhancedEstimates.getSevereWarningThreshold(),
                mTimeRemaining = estimate.estimateMillis;
                    mEnhancedEstimates.getLowWarningThreshold(), estimate.isBasedOnUsage());
                mWarnings.updateEstimate(estimate);
        } else {
                mWarnings.updateThresholds(mEnhancedEstimates.getLowWarningThreshold(),
            if (DEBUG) {
                        mEnhancedEstimates.getSevereWarningThreshold());
                Slog.d(TAG, "using standard");

            }
                // if we are now over 45% battery & 6 hours remaining we can trigger hybrid
            mCurrentBatteryStateSnapshot = new BatteryStateSnapshot(mBatteryLevel, isPowerSaverMode,
                    plugged, bucket, mBatteryStatus, mLowBatteryReminderLevels[1],
                    mLowBatteryReminderLevels[0]);
        }

        mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
        if (mCurrentBatteryStateSnapshot.isHybrid()) {
            maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
        } else {
            maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
        }
    }

    // updates the time estimate if we don't have one or battery level has changed.
    @VisibleForTesting
    Estimate refreshEstimateIfNeeded() {
        if (mLastBatteryStateSnapshot == null
                || mLastBatteryStateSnapshot.getTimeRemainingMillis() == NO_ESTIMATE_AVAILABLE
                || mBatteryLevel != mLastBatteryStateSnapshot.getBatteryLevel()) {
            final Estimate estimate = mEnhancedEstimates.getEstimate();
            if (DEBUG) {
                Slog.d(TAG, "updated estimate: " + estimate.getEstimateMillis());
            }
            return estimate;
        }
        return new Estimate(mLastBatteryStateSnapshot.getTimeRemainingMillis(),
                mLastBatteryStateSnapshot.isBasedOnUsage());
    }

    @VisibleForTesting
    void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
            BatteryStateSnapshot lastSnapshot) {
        // if we are now over 45% battery & 6 hours remaining so we can trigger hybrid
        // notification again
        // notification again
                if (mBatteryLevel >= CHARGE_CYCLE_PERCENT_RESET
        if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET
                        && mTimeRemaining > SIX_HOURS_MILLIS) {
                && currentSnapshot.getTimeRemainingMillis() > SIX_HOURS_MILLIS) {
            mLowWarningShownThisChargeCycle = false;
            mLowWarningShownThisChargeCycle = false;
            mSevereWarningShownThisChargeCycle = false;
            mSevereWarningShownThisChargeCycle = false;
                }
            if (DEBUG) {
                Slog.d(TAG, "Charge cycle reset! Can show warnings again");
            }
            }
        }
        }


        if (shouldShowLowBatteryWarning(plugged, oldPlugged, oldBucket, bucket,
        final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
                mTimeRemaining, isPowerSaver, mBatteryStatus)) {
                || lastSnapshot.getPlugged();
            mWarnings.showLowBatteryWarning(playSound);


        if (shouldShowHybridWarning(currentSnapshot)) {
            mWarnings.showLowBatteryWarning(playSound);
            // mark if we've already shown a warning this cycle. This will prevent the notification
            // mark if we've already shown a warning this cycle. This will prevent the notification
            // trigger from spamming users by only showing low/critical warnings once per cycle
            // trigger from spamming users by only showing low/critical warnings once per cycle
            if (hybridEnabled) {
            if (currentSnapshot.getTimeRemainingMillis()
                if (mTimeRemaining <= mEnhancedEstimates.getSevereWarningThreshold()
                    <= currentSnapshot.getSevereLevelThreshold()
                        || mBatteryLevel <= mLowBatteryReminderLevels[1]) {
                    || currentSnapshot.getBatteryLevel() <= mLowBatteryReminderLevels[1]) {
                mSevereWarningShownThisChargeCycle = true;
                mSevereWarningShownThisChargeCycle = true;
                mLowWarningShownThisChargeCycle = true;
                mLowWarningShownThisChargeCycle = true;
                if (DEBUG) {
                    Slog.d(TAG, "Severe warning marked as shown this cycle");
                }
            } else {
            } else {
                Slog.d(TAG, "Low warning marked as shown this cycle");
                mLowWarningShownThisChargeCycle = true;
                mLowWarningShownThisChargeCycle = true;
            }
            }

        } else if (shouldDismissHybridWarning(currentSnapshot)) {
            if (DEBUG) {
                Slog.d(TAG, "Dismissing warning");
            }
            }
        } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
                isPowerSaver)) {
            mWarnings.dismissLowBatteryWarning();
            mWarnings.dismissLowBatteryWarning();
        } else {
        } else {
            mWarnings.updateLowBatteryWarning();
            if (DEBUG) {
        }
                Slog.d(TAG, "Updating warning");
            }
            }

            mWarnings.updateLowBatteryWarning();
    @VisibleForTesting
    boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
            int bucket, long timeRemaining, boolean isPowerSaver, int batteryStatus) {
        if (mEnhancedEstimates.isHybridNotificationEnabled()) {
            // triggering logic when enhanced estimate is available
            return isEnhancedTrigger(plugged, timeRemaining, isPowerSaver, batteryStatus);
        }
        }
        // legacy triggering logic
        return !plugged
                && !isPowerSaver
                && (((bucket < oldBucket || oldPlugged) && bucket < 0))
                && batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
    }
    }


    @VisibleForTesting
    @VisibleForTesting
    boolean shouldDismissLowBatteryWarning(boolean plugged, int oldBucket, int bucket,
    boolean shouldShowHybridWarning(BatteryStateSnapshot snapshot) {
            long timeRemaining, boolean isPowerSaver) {
        if (snapshot.getPlugged()
        final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
                || snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN) {
        final boolean hybridWouldDismiss = hybridEnabled
            Slog.d(TAG, "can't show warning due to - plugged: " + snapshot.getPlugged()
                && timeRemaining > mEnhancedEstimates.getLowWarningThreshold();
                    + " status unknown: "
        final boolean standardWouldDismiss = (bucket > oldBucket && bucket > 0);
                    + (snapshot.getBatteryStatus() == BatteryManager.BATTERY_STATUS_UNKNOWN));
        return (isPowerSaver && !hybridEnabled)
                || plugged
                || (standardWouldDismiss && (!mEnhancedEstimates.isHybridNotificationEnabled()
                        || hybridWouldDismiss));
    }

    private boolean isEnhancedTrigger(boolean plugged, long timeRemaining, boolean isPowerSaver,
            int batteryStatus) {
        if (plugged || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
            return false;
            return false;
        }
        }
        int warnLevel = mLowBatteryReminderLevels[0];
        int critLevel = mLowBatteryReminderLevels[1];


        // Only show the low warning once per charge cycle & no battery saver
        // Only show the low warning once per charge cycle & no battery saver
        final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !isPowerSaver
        final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
                && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
                && (snapshot.getTimeRemainingMillis() < snapshot.getLowThresholdMillis()
                || mBatteryLevel <= warnLevel);
                || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());


        // Only show the severe warning once per charge cycle
        // Only show the severe warning once per charge cycle
        final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
        final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
                && (timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
                && (snapshot.getTimeRemainingMillis() < snapshot.getSevereThresholdMillis()
                || mBatteryLevel <= critLevel);
                || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());


        final boolean canShow = canShowWarning || canShowSevereWarning;
        final boolean canShow = canShowWarning || canShowSevereWarning;
        if (DEBUG) {
        if (DEBUG) {
            Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith values: "
            Slog.d(TAG, "Enhanced trigger is: " + canShow + "\nwith battery snapshot:"
                    + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
                    + " mLowWarningShownThisChargeCycle: " + mLowWarningShownThisChargeCycle
                    + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
                    + " mSevereWarningShownThisChargeCycle: " + mSevereWarningShownThisChargeCycle
                    + " mEnhancedEstimates.timeremaining: " + timeRemaining
                    + "\n" + snapshot.toString());
                    + " mBatteryLevel: " + mBatteryLevel
        }
                    + " canShowWarning: " + canShowWarning
        return canShow;
                    + " canShowSevereWarning: " + canShowSevereWarning
    }
                    + " plugged: " + plugged

                    + " batteryStatus: " + batteryStatus
    @VisibleForTesting
                    + " isPowerSaver: " + isPowerSaver);
    boolean shouldDismissHybridWarning(BatteryStateSnapshot snapshot) {
        return snapshot.getPlugged()
                || snapshot.getTimeRemainingMillis() > snapshot.getLowThresholdMillis();
    }

    protected void maybeShowBatteryWarning(
            BatteryStateSnapshot currentSnapshot,
            BatteryStateSnapshot lastSnapshot) {
        final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
                || lastSnapshot.getPlugged();

        if (shouldShowLowBatteryWarning(currentSnapshot, lastSnapshot)) {
            mWarnings.showLowBatteryWarning(playSound);
        } else if (shouldDismissLowBatteryWarning(currentSnapshot, lastSnapshot)) {
            mWarnings.dismissLowBatteryWarning();
        } else {
            mWarnings.updateLowBatteryWarning();
        }
        }
        return canShowWarning || canShowSevereWarning;
    }

    @VisibleForTesting
    boolean shouldShowLowBatteryWarning(
            BatteryStateSnapshot currentSnapshot,
            BatteryStateSnapshot lastSnapshot) {
        return !currentSnapshot.getPlugged()
                && !currentSnapshot.isPowerSaver()
                && (((currentSnapshot.getBucket() < lastSnapshot.getBucket()
                        || lastSnapshot.getPlugged())
                && currentSnapshot.getBucket() < 0))
                && currentSnapshot.getBatteryStatus() != BatteryManager.BATTERY_STATUS_UNKNOWN;
    }

    @VisibleForTesting
    boolean shouldDismissLowBatteryWarning(
            BatteryStateSnapshot currentSnapshot,
            BatteryStateSnapshot lastSnapshot) {
        return currentSnapshot.isPowerSaver()
                || currentSnapshot.getPlugged()
                || (currentSnapshot.getBucket() > lastSnapshot.getBucket()
                        && currentSnapshot.getBucket() > 0);
    }
    }


    private void initTemperature() {
    private void initTemperature() {
@@ -453,12 +520,20 @@ public class PowerUI extends SystemUI {
        mWarnings.dump(pw);
        mWarnings.dump(pw);
    }
    }


    /**
     * The interface to allow PowerUI to communicate with whatever implementation of WarningsUI
     * is being used by the system.
     */
    public interface WarningsUI {
    public interface WarningsUI {
        void update(int batteryLevel, int bucket, long screenOffTime);

        void updateEstimate(Estimate estimate);


        void updateThresholds(long lowThreshold, long severeThreshold);
        /**
         * Updates battery and screen info for determining whether to trigger battery warnings or
         * not.
         * @param batteryLevel The current battery level
         * @param bucket The current battery bucket
         * @param screenOffTime How long the screen has been off in millis
         */
        void update(int batteryLevel, int bucket, long screenOffTime);


        void dismissLowBatteryWarning();
        void dismissLowBatteryWarning();


@@ -486,6 +561,12 @@ public class PowerUI extends SystemUI {
        void dump(PrintWriter pw);
        void dump(PrintWriter pw);


        void userSwitched();
        void userSwitched();

        /**
         * Updates the snapshot of battery state used for evaluating battery warnings
         * @param snapshot object containing relevant values for making battery warning decisions.
         */
        void updateSnapshot(BatteryStateSnapshot snapshot);
    }
    }


    // Thermal event received from thermal service manager subsystem
    // Thermal event received from thermal service manager subsystem
Loading