Merge "Listen for EXTRA_PRESENT changes" into rvc-qpr-dev

This commit is contained in:
Evan Laird
2021-01-12 18:57:58 +00:00
committed by Android (Google) Code Review
14 changed files with 446 additions and 83 deletions

View File

@@ -25,6 +25,7 @@ import static android.os.BatteryManager.EXTRA_LEVEL;
import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
import static android.os.BatteryManager.EXTRA_PLUGGED;
import static android.os.BatteryManager.EXTRA_PRESENT;
import static android.os.BatteryManager.EXTRA_STATUS;
import android.content.Context;
@@ -50,14 +51,16 @@ public class BatteryStatus {
public final int plugged;
public final int health;
public final int maxChargingWattage;
public final boolean present;
public BatteryStatus(int status, int level, int plugged, int health,
int maxChargingWattage) {
int maxChargingWattage, boolean present) {
this.status = status;
this.level = level;
this.plugged = plugged;
this.health = health;
this.maxChargingWattage = maxChargingWattage;
this.present = present;
}
public BatteryStatus(Intent batteryChangedIntent) {
@@ -65,6 +68,7 @@ public class BatteryStatus {
plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0);
level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0);
health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
present = batteryChangedIntent.getBooleanExtra(EXTRA_PRESENT, true);
final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT,
-1);

View File

@@ -0,0 +1,24 @@
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="24dp"
android:viewportWidth="12.0"
android:viewportHeight="24.0">
<path
android:pathData="M10.404,2.4L8.4,2.4L8.4,0L3.6,0L3.6,2.4L1.596,2.4C0.72,2.4 0,3.12 0,3.996L0,22.392C0,23.28 0.72,24 1.596,24L10.392,24C11.28,24 12,23.28 12,22.404L12,3.996C12,3.12 11.28,2.4 10.404,2.4ZM7.14,19.14L4.86,19.14L4.86,16.86L7.14,16.86L7.14,19.14ZM8.76,12.828C8.76,12.828 8.304,13.332 7.956,13.68C7.38,14.256 6.96,15.06 6.96,15.6L5.04,15.6C5.04,14.604 5.592,13.776 6.156,13.2L7.272,12.072C7.596,11.748 7.8,11.292 7.8,10.8C7.8,9.804 6.996,9 6,9C5.004,9 4.2,9.804 4.2,10.8L2.4,10.8C2.4,8.808 4.008,7.2 6,7.2C7.992,7.2 9.6,8.808 9.6,10.8C9.6,11.592 9.276,12.312 8.76,12.828L8.76,12.828Z"
android:fillColor="#ffffff" />
</vector>

View File

@@ -579,4 +579,9 @@
<integer name="controls_max_columns_adjust_below_width_dp">320</integer>
<!-- If the config font scale is >= this value, potentially adjust the number of columns-->
<item name="controls_max_columns_adjust_above_font_scale" translatable="false" format="float" type="dimen">1.25</item>
<!-- Whether or not to show a notification for an unknown battery state -->
<bool name="config_showNotificationForUnknownBatteryState">false</bool>
<!-- content URL in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false -->
<string translatable="false" name="config_batteryStateUnknownUrl"></string>
</resources>

View File

@@ -437,6 +437,8 @@
<string name="accessibility_battery_three_bars">Battery three bars.</string>
<!-- Content description of the battery when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_full">Battery full.</string>
<!-- Content description of the battery when battery state is unknown for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_battery_unknown">Battery percentage unknown.</string>
<!-- Content description of the phone signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_no_phone">No phone.</string>
@@ -2870,4 +2872,11 @@
<string name="media_output_dialog_connect_failed">Couldn\'t connect. Try again.</string>
<!-- Title for pairing item [CHAR LIMIT=60] -->
<string name="media_output_dialog_pairing_new">Pair new device</string>
<!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->
<string name="battery_state_unknown_notification_title">Problem reading your battery meter</string>
<!-- Text to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
[CHAR LIMIT=NONE] -->
<string name="battery_state_unknown_notification_text">Tap for more information</string>
</resources>

View File

@@ -1696,7 +1696,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
// Take a guess at initial SIM state, battery status and PLMN until we get an update
mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0);
mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0, true);
// Watch for interesting updates
final IntentFilter filter = new IntentFilter();
@@ -2563,6 +2563,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean wasPluggedIn = old.isPluggedIn();
final boolean stateChangedWhilePluggedIn = wasPluggedIn && nowPluggedIn
&& (old.status != current.status);
final boolean nowPresent = current.present;
final boolean wasPresent = old.present;
// change in plug state is always interesting
if (wasPluggedIn != nowPluggedIn || stateChangedWhilePluggedIn) {
@@ -2584,6 +2586,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return true;
}
// Battery either showed up or disappeared
if (wasPresent != nowPresent) {
return true;
}
return false;
}

View File

@@ -33,6 +33,7 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;
@@ -95,12 +96,15 @@ public class BatteryMeterView extends LinearLayout implements
private int mTextColor;
private int mLevel;
private int mShowPercentMode = MODE_DEFAULT;
private boolean mForceShowPercent;
private boolean mShowPercentAvailable;
// Some places may need to show the battery conditionally, and not obey the tuner
private boolean mIgnoreTunerUpdates;
private boolean mIsSubscribedForTunerUpdates;
private boolean mCharging;
// Error state where we know nothing about the current battery state
private boolean mBatteryStateUnknown;
// Lazily-loaded since this is expected to be a rare-if-ever state
private Drawable mUnknownStateDrawable;
private DualToneHandler mDualToneHandler;
private int mUser;
@@ -350,6 +354,11 @@ public class BatteryMeterView extends LinearLayout implements
}
private void updatePercentText() {
if (mBatteryStateUnknown) {
setContentDescription(getContext().getString(R.string.accessibility_battery_unknown));
return;
}
if (mBatteryController == null) {
return;
}
@@ -390,9 +399,13 @@ public class BatteryMeterView extends LinearLayout implements
final boolean systemSetting = 0 != whitelistIpcs(() -> Settings.System
.getIntForUser(getContext().getContentResolver(),
SHOW_BATTERY_PERCENT, 0, mUser));
boolean shouldShow =
(mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
|| mShowPercentMode == MODE_ON
|| mShowPercentMode == MODE_ESTIMATE;
shouldShow = shouldShow && !mBatteryStateUnknown;
if ((mShowPercentAvailable && systemSetting && mShowPercentMode != MODE_OFF)
|| mShowPercentMode == MODE_ON || mShowPercentMode == MODE_ESTIMATE) {
if (shouldShow) {
if (!showing) {
mBatteryPercentView = loadPercentView();
if (mPercentageStyleId != 0) { // Only set if specified as attribute
@@ -418,6 +431,32 @@ public class BatteryMeterView extends LinearLayout implements
scaleBatteryMeterViews();
}
private Drawable getUnknownStateDrawable() {
if (mUnknownStateDrawable == null) {
mUnknownStateDrawable = mContext.getDrawable(R.drawable.ic_battery_unknown);
mUnknownStateDrawable.setTint(mTextColor);
}
return mUnknownStateDrawable;
}
@Override
public void onBatteryUnknownStateChanged(boolean isUnknown) {
if (mBatteryStateUnknown == isUnknown) {
return;
}
mBatteryStateUnknown = isUnknown;
if (mBatteryStateUnknown) {
mBatteryIconView.setImageDrawable(getUnknownStateDrawable());
} else {
mBatteryIconView.setImageDrawable(mDrawable);
}
updateShowPercent();
}
/**
* Looks up the scale factor for status bar icons and scales the battery view by that amount.
*/
@@ -458,6 +497,10 @@ public class BatteryMeterView extends LinearLayout implements
if (mBatteryPercentView != null) {
mBatteryPercentView.setTextColor(singleToneColor);
}
if (mUnknownStateDrawable != null) {
mUnknownStateDrawable.setTint(singleToneColor);
}
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -467,8 +510,8 @@ public class BatteryMeterView extends LinearLayout implements
pw.println(" mDrawable.getPowerSave: " + powerSave);
pw.println(" mBatteryPercentView.getText(): " + percent);
pw.println(" mTextColor: #" + Integer.toHexString(mTextColor));
pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown);
pw.println(" mLevel: " + mLevel);
pw.println(" mForceShowPercent: " + mForceShowPercent);
}
private final class SettingObserver extends ContentObserver {

View File

@@ -32,6 +32,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.LogBufferFreezer;
import com.android.systemui.dump.SystemUIAuxiliaryDumpService;
import com.android.systemui.statusbar.policy.BatteryStateNotifier;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -44,18 +45,21 @@ public class SystemUIService extends Service {
private final DumpHandler mDumpHandler;
private final BroadcastDispatcher mBroadcastDispatcher;
private final LogBufferFreezer mLogBufferFreezer;
private final BatteryStateNotifier mBatteryStateNotifier;
@Inject
public SystemUIService(
@Main Handler mainHandler,
DumpHandler dumpHandler,
BroadcastDispatcher broadcastDispatcher,
LogBufferFreezer logBufferFreezer) {
LogBufferFreezer logBufferFreezer,
BatteryStateNotifier batteryStateNotifier) {
super();
mMainHandler = mainHandler;
mDumpHandler = dumpHandler;
mBroadcastDispatcher = broadcastDispatcher;
mLogBufferFreezer = logBufferFreezer;
mBatteryStateNotifier = batteryStateNotifier;
}
@Override
@@ -68,6 +72,11 @@ public class SystemUIService extends Service {
// Finish initializing dump logic
mLogBufferFreezer.attach(mBroadcastDispatcher);
// If configured, set up a battery notification
if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) {
mBatteryStateNotifier.startListening();
}
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
throw new RuntimeException();

View File

@@ -123,6 +123,7 @@ public class KeyguardIndicationController implements StateListener,
private int mChargingSpeed;
private int mChargingWattage;
private int mBatteryLevel;
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
private float mDisclosureMaxAlpha;
private String mMessageToShowOnScreenOn;
@@ -391,86 +392,103 @@ public class KeyguardIndicationController implements StateListener,
mWakeLock.setAcquired(false);
}
if (mVisible) {
// Walk down a precedence-ordered list of what indication
// should be shown based on user or device state
if (mDozing) {
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
mTextView.setTextColor(Color.WHITE);
if (!TextUtils.isEmpty(mTransientIndication)) {
mTextView.switchIndication(mTransientIndication);
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
mTextView.switchIndication(mAlignmentIndication);
mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
} else if (mPowerPluggedIn || mEnableBatteryDefender) {
String indication = computePowerIndication();
if (animate) {
animateText(mTextView, indication);
} else {
mTextView.switchIndication(indication);
}
} else {
String percentage = NumberFormat.getPercentInstance()
.format(mBatteryLevel / 100f);
mTextView.switchIndication(percentage);
}
return;
}
if (!mVisible) {
return;
}
int userId = KeyguardUpdateMonitor.getCurrentUser();
String trustGrantedIndication = getTrustGrantedIndication();
String trustManagedIndication = getTrustManagedIndication();
// A few places might need to hide the indication, so always start by making it visible
mIndicationArea.setVisibility(View.VISIBLE);
String powerIndication = null;
if (mPowerPluggedIn || mEnableBatteryDefender) {
powerIndication = computePowerIndication();
}
boolean isError = false;
if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
} else if (!TextUtils.isEmpty(mTransientIndication)) {
if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
String indication = mContext.getResources().getString(
R.string.keyguard_indication_trust_unlocked_plugged_in,
mTransientIndication, powerIndication);
mTextView.switchIndication(indication);
} else {
mTextView.switchIndication(mTransientIndication);
}
isError = mTransientTextIsError;
} else if (!TextUtils.isEmpty(trustGrantedIndication)
&& mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
if (powerIndication != null) {
String indication = mContext.getResources().getString(
R.string.keyguard_indication_trust_unlocked_plugged_in,
trustGrantedIndication, powerIndication);
mTextView.switchIndication(indication);
} else {
mTextView.switchIndication(trustGrantedIndication);
}
// Walk down a precedence-ordered list of what indication
// should be shown based on user or device state
if (mDozing) {
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
mTextView.setTextColor(Color.WHITE);
if (!TextUtils.isEmpty(mTransientIndication)) {
mTextView.switchIndication(mTransientIndication);
} else if (!mBatteryPresent) {
// If there is no battery detected, hide the indication and bail
mIndicationArea.setVisibility(View.GONE);
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
mTextView.switchIndication(mAlignmentIndication);
isError = true;
mTextView.setTextColor(mContext.getColor(R.color.misalignment_text_color));
} else if (mPowerPluggedIn || mEnableBatteryDefender) {
if (DEBUG_CHARGING_SPEED) {
powerIndication += ", " + (mChargingWattage / 1000) + " mW";
}
String indication = computePowerIndication();
if (animate) {
animateText(mTextView, powerIndication);
animateText(mTextView, indication);
} else {
mTextView.switchIndication(powerIndication);
mTextView.switchIndication(indication);
}
} else if (!TextUtils.isEmpty(trustManagedIndication)
&& mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
&& !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
mTextView.switchIndication(trustManagedIndication);
} else {
mTextView.switchIndication(mRestingIndication);
String percentage = NumberFormat.getPercentInstance()
.format(mBatteryLevel / 100f);
mTextView.switchIndication(percentage);
}
mTextView.setTextColor(isError ? Utils.getColorError(mContext)
: mInitialTextColorState);
return;
}
int userId = KeyguardUpdateMonitor.getCurrentUser();
String trustGrantedIndication = getTrustGrantedIndication();
String trustManagedIndication = getTrustManagedIndication();
String powerIndication = null;
if (mPowerPluggedIn || mEnableBatteryDefender) {
powerIndication = computePowerIndication();
}
// Some cases here might need to hide the indication (if the battery is not present)
boolean hideIndication = false;
boolean isError = false;
if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) {
mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
} else if (!TextUtils.isEmpty(mTransientIndication)) {
if (powerIndication != null && !mTransientIndication.equals(powerIndication)) {
String indication = mContext.getResources().getString(
R.string.keyguard_indication_trust_unlocked_plugged_in,
mTransientIndication, powerIndication);
mTextView.switchIndication(indication);
hideIndication = !mBatteryPresent;
} else {
mTextView.switchIndication(mTransientIndication);
}
isError = mTransientTextIsError;
} else if (!TextUtils.isEmpty(trustGrantedIndication)
&& mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
if (powerIndication != null) {
String indication = mContext.getResources().getString(
R.string.keyguard_indication_trust_unlocked_plugged_in,
trustGrantedIndication, powerIndication);
mTextView.switchIndication(indication);
hideIndication = !mBatteryPresent;
} else {
mTextView.switchIndication(trustGrantedIndication);
}
} else if (!TextUtils.isEmpty(mAlignmentIndication)) {
mTextView.switchIndication(mAlignmentIndication);
isError = true;
hideIndication = !mBatteryPresent;
} else if (mPowerPluggedIn || mEnableBatteryDefender) {
if (DEBUG_CHARGING_SPEED) {
powerIndication += ", " + (mChargingWattage / 1000) + " mW";
}
if (animate) {
animateText(mTextView, powerIndication);
} else {
mTextView.switchIndication(powerIndication);
}
hideIndication = !mBatteryPresent;
} else if (!TextUtils.isEmpty(trustManagedIndication)
&& mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
&& !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
mTextView.switchIndication(trustManagedIndication);
} else {
mTextView.switchIndication(mRestingIndication);
}
mTextView.setTextColor(isError ? Utils.getColorError(mContext)
: mInitialTextColorState);
if (hideIndication) {
mIndicationArea.setVisibility(View.GONE);
}
}
@@ -654,6 +672,7 @@ public class KeyguardIndicationController implements StateListener,
pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
pw.println(" mDozing: " + mDozing);
pw.println(" mBatteryLevel: " + mBatteryLevel);
pw.println(" mBatteryPresent: " + mBatteryPresent);
pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
pw.println(" computePowerIndication(): " + computePowerIndication());
}
@@ -694,6 +713,7 @@ public class KeyguardIndicationController implements StateListener,
mBatteryLevel = status.level;
mBatteryOverheated = status.isOverheated();
mEnableBatteryDefender = mBatteryOverheated && status.isPluggedIn();
mBatteryPresent = status.present;
try {
mChargingTimeRemaining = mPowerPluggedIn
? mBatteryInfo.computeChargeTimeRemaining() : -1;

View File

@@ -97,6 +97,9 @@ public interface BatteryController extends DemoMode, Dumpable,
default void onPowerSaveChanged(boolean isPowerSave) {
}
default void onBatteryUnknownStateChanged(boolean isUnknown) {
}
default void onReverseChanged(boolean isReverse, int level, String name) {
}

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
import static android.os.BatteryManager.EXTRA_PRESENT;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -70,6 +72,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
protected int mLevel;
protected boolean mPluggedIn;
protected boolean mCharging;
private boolean mStateUnknown = false;
private boolean mCharged;
protected boolean mPowerSave;
private boolean mAodPowerSave;
@@ -126,6 +129,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
pw.print(" mCharging="); pw.println(mCharging);
pw.print(" mCharged="); pw.println(mCharged);
pw.print(" mPowerSave="); pw.println(mPowerSave);
pw.print(" mStateUnknown="); pw.println(mStateUnknown);
}
@Override
@@ -139,8 +143,11 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mChangeCallbacks.add(cb);
}
if (!mHasReceivedBattery) return;
// Make sure new callbacks get the correct initial state
cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
cb.onPowerSaveChanged(mPowerSave);
cb.onBatteryUnknownStateChanged(mStateUnknown);
}
@Override
@@ -168,6 +175,13 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mWirelessCharging = mCharging && intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
== BatteryManager.BATTERY_PLUGGED_WIRELESS;
boolean present = intent.getBooleanExtra(EXTRA_PRESENT, true);
boolean unknown = !present;
if (unknown != mStateUnknown) {
mStateUnknown = unknown;
fireBatteryUnknownStateChanged();
}
fireBatteryLevelChanged();
} else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
updatePowerSave();
@@ -316,6 +330,15 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
}
}
private void fireBatteryUnknownStateChanged() {
synchronized (mChangeCallbacks) {
final int n = mChangeCallbacks.size();
for (int i = 0; i < n; i++) {
mChangeCallbacks.get(i).onBatteryUnknownStateChanged(mStateUnknown);
}
}
}
private void firePowerSaveChanged() {
synchronized (mChangeCallbacks) {
final int N = mChangeCallbacks.size();
@@ -340,6 +363,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
String level = args.getString("level");
String plugged = args.getString("plugged");
String powerSave = args.getString("powersave");
String present = args.getString("present");
if (level != null) {
mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
}
@@ -350,6 +374,10 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mPowerSave = powerSave.equals("true");
firePowerSaveChanged();
}
if (present != null) {
mStateUnknown = !present.equals("true");
fireBatteryUnknownStateChanged();
}
fireBatteryLevelChanged();
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.policy
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.net.Uri
import com.android.systemui.R
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
/**
* Listens for important battery states and sends non-dismissible system notifications if there is a
* problem
*/
class BatteryStateNotifier @Inject constructor(
val controller: BatteryController,
val noMan: NotificationManager,
val delayableExecutor: DelayableExecutor,
val context: Context
) : BatteryController.BatteryStateChangeCallback {
var stateUnknown = false
fun startListening() {
controller.addCallback(this)
}
fun stopListening() {
controller.removeCallback(this)
}
override fun onBatteryUnknownStateChanged(isUnknown: Boolean) {
stateUnknown = isUnknown
if (stateUnknown) {
val channel = NotificationChannel("battery_status", "Battery status",
NotificationManager.IMPORTANCE_DEFAULT)
noMan.createNotificationChannel(channel)
val intent = Intent(Intent.ACTION_VIEW,
Uri.parse(context.getString(R.string.config_batteryStateUnknownUrl)))
val pi = PendingIntent.getActivity(context, 0, intent, 0)
val builder = Notification.Builder(context, channel.id)
.setAutoCancel(false)
.setContentTitle(
context.getString(R.string.battery_state_unknown_notification_title))
.setContentText(
context.getString(R.string.battery_state_unknown_notification_text))
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setContentIntent(pi)
.setAutoCancel(true)
.setOngoing(true)
noMan.notify(TAG, ID, builder.build())
} else {
scheduleNotificationCancel()
}
}
private fun scheduleNotificationCancel() {
val r = {
if (!stateUnknown) {
noMan.cancel(ID)
}
}
delayableExecutor.executeDelayed(r, DELAY_MILLIS)
}
}
private const val TAG = "BatteryStateNotifier"
private const val ID = 666
private const val DELAY_MILLIS: Long = 4 * 60 * 60 * 1000

View File

@@ -495,7 +495,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
80 /* level */, BatteryManager.BATTERY_PLUGGED_WIRELESS, 100 /* health */,
0 /* maxChargingWattage */);
0 /* maxChargingWattage */, true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
verify(mIBatteryStats).computeChargeTimeRemaining();
@@ -507,7 +507,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
80 /* level */, 0 /* plugged */, 100 /* health */,
0 /* maxChargingWattage */);
0 /* maxChargingWattage */, true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
verify(mIBatteryStats, never()).computeChargeTimeRemaining();
@@ -553,7 +553,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
80 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */);
BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */,
true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
mController.setVisible(true);
@@ -569,7 +570,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING,
80 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */);
BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */,
true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
mController.setVisible(true);
@@ -585,7 +587,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_CHARGING,
100 /* level */, BatteryManager.BATTERY_PLUGGED_AC,
BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */);
BatteryManager.BATTERY_HEALTH_OVERHEAT, 0 /* maxChargingWattage */,
true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
mController.setVisible(true);
@@ -599,7 +602,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
createController();
BatteryStatus status = new BatteryStatus(BatteryManager.BATTERY_STATUS_DISCHARGING,
90 /* level */, 0 /* plugged */, BatteryManager.BATTERY_HEALTH_OVERHEAT,
0 /* maxChargingWattage */);
0 /* maxChargingWattage */, true /* present */);
mController.getKeyguardCallback().onRefreshBatteryInfo(status);
mController.setDozing(true);

View File

@@ -16,7 +16,11 @@
package com.android.systemui.statusbar.policy;
import static android.os.BatteryManager.EXTRA_PRESENT;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
@@ -30,6 +34,7 @@ import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.power.EnhancedEstimates;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import org.junit.Assert;
import org.junit.Before;
@@ -93,4 +98,36 @@ public class BatteryControllerTest extends SysuiTestCase {
Assert.assertFalse(mBatteryController.isAodPowerSave());
}
@Test
public void testBatteryPresentState_notPresent() {
// GIVEN a battery state callback listening for changes
BatteryStateChangeCallback cb = mock(BatteryStateChangeCallback.class);
mBatteryController.addCallback(cb);
// WHEN the state of the battery becomes unknown
Intent i = new Intent(Intent.ACTION_BATTERY_CHANGED);
i.putExtra(EXTRA_PRESENT, false);
mBatteryController.onReceive(getContext(), i);
// THEN the callback is notified
verify(cb, atLeastOnce()).onBatteryUnknownStateChanged(true);
}
@Test
public void testBatteryPresentState_callbackAddedAfterStateChange() {
// GIVEN a battery state callback
BatteryController.BatteryStateChangeCallback cb =
mock(BatteryController.BatteryStateChangeCallback.class);
// GIVEN the state has changed before adding a new callback
Intent i = new Intent(Intent.ACTION_BATTERY_CHANGED);
i.putExtra(EXTRA_PRESENT, false);
mBatteryController.onReceive(getContext(), i);
// WHEN a callback is added
mBatteryController.addCallback(cb);
// THEN it is informed about the battery state
verify(cb, atLeastOnce()).onBatteryUnknownStateChanged(true);
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.policy
import android.app.NotificationManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
private fun <T> anyObject(): T {
return Mockito.anyObject<T>()
}
@RunWith(AndroidTestingRunner::class)
@RunWithLooper()
@SmallTest
class BatteryStateNotifierTest : SysuiTestCase() {
@Mock private lateinit var batteryController: BatteryController
@Mock private lateinit var noMan: NotificationManager
private val clock = FakeSystemClock()
private val executor = FakeExecutor(clock)
private lateinit var notifier: BatteryStateNotifier
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
notifier = BatteryStateNotifier(batteryController, noMan, executor, context)
notifier.startListening()
context.ensureTestableResources()
}
@Test
fun testNotifyWhenStateUnknown() {
notifier.onBatteryUnknownStateChanged(true)
verify(noMan).notify(anyString(), anyInt(), anyObject())
}
@Test
fun testCancelAfterDelay() {
notifier.onBatteryUnknownStateChanged(true)
notifier.onBatteryUnknownStateChanged(false)
clock.advanceTime(DELAY_MILLIS + 1)
verify(noMan).cancel(anyInt())
}
}
// From BatteryStateNotifier.kt
private const val DELAY_MILLIS: Long = 40 * 60 * 60 * 1000