Merge "Listen for EXTRA_PRESENT changes"

This commit is contained in:
Evan Laird
2020-10-30 02:20:25 +00:00
committed by Android (Google) Code Review
13 changed files with 398 additions and 79 deletions

View File

@@ -24,6 +24,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;
@@ -49,14 +50,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) {
@@ -64,6 +67,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

@@ -568,4 +568,9 @@
<!-- The duraction of the hide animation for the volume dialog in milliseconds -->
<integer name="config_dialogHideAnimationDurationMs">250</integer>
<!-- 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

@@ -439,6 +439,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>
@@ -2862,4 +2864,10 @@
<!-- Status for conversation without interaction data [CHAR LIMIT=120] -->
<string name="basic_status" translatable="false">Open conversation</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

@@ -1717,7 +1717,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();
@@ -2621,6 +2621,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) {
@@ -2637,6 +2639,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

@@ -31,6 +31,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;
@@ -91,12 +92,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;
@@ -341,6 +345,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;
}
@@ -381,9 +390,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
@@ -409,6 +422,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.
*/
@@ -449,6 +488,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) {
@@ -458,8 +501,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

@@ -121,6 +121,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;
@@ -389,86 +390,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) {
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) {
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) {
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) {
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) {
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);
}
}
@@ -647,6 +665,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());
}
@@ -685,6 +704,7 @@ public class KeyguardIndicationController implements StateListener,
mChargingWattage = status.maxChargingWattage;
mChargingSpeed = status.getChargingSpeed(mContext);
mBatteryLevel = status.level;
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;
@@ -75,6 +77,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;
@@ -138,6 +141,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
@@ -180,6 +184,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();
@@ -328,6 +339,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();
@@ -346,6 +366,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);
}
@@ -356,6 +377,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

@@ -494,7 +494,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();
@@ -506,7 +506,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();

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