Merge "Listen for EXTRA_PRESENT changes" into rvc-qpr-dev am: 623049adac
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13083016 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: Iad025254e0c1d8e3400ee9554594bc28d5b20b84
This commit is contained in:
@@ -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);
|
||||
|
||||
24
packages/SystemUI/res/drawable/ic_battery_unknown.xml
Normal file
24
packages/SystemUI/res/drawable/ic_battery_unknown.xml
Normal 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>
|
||||
@@ -581,4 +581,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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user