Update battery icon for incompatible charging

Bug: 293863612
Bug: 293275722
Test: atest com.android.systemui.statusbar.policy.BatteryControllerTest
Flag: INCOMPATIBLE_CHARGING_BATTERY_ICON

Change-Id: I3b5158e2dd680ce6623047b30f171a78abc5ca4e
This commit is contained in:
Jay Wang
2023-07-31 16:18:15 +00:00
parent 47e7d5c654
commit 33f1cd6666
9 changed files with 158 additions and 10 deletions

View File

@@ -178,6 +178,11 @@ class AccessorizedBatteryDrawable(
mainBatteryDrawable.charging = charging
}
/** Returns whether the battery is currently charging. */
fun getCharging(): Boolean {
return mainBatteryDrawable.charging
}
/** Sets the current level (out of 100) of the battery. */
fun setBatteryLevel(level: Int) {
mainBatteryDrawable.setBatteryLevel(level)

View File

@@ -77,8 +77,9 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
private int mShowPercentMode = MODE_DEFAULT;
private boolean mShowPercentAvailable;
private String mEstimateText = null;
private boolean mCharging;
private boolean mPluggedIn;
private boolean mIsBatteryDefender;
private boolean mIsIncompatibleCharging;
private boolean mDisplayShieldEnabled;
// Error state where we know nothing about the current battery state
private boolean mBatteryStateUnknown;
@@ -202,10 +203,10 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
* @param pluggedIn whether the device is plugged in or not
*/
public void onBatteryLevelChanged(@IntRange(from = 0, to = 100) int level, boolean pluggedIn) {
mDrawable.setCharging(pluggedIn);
mDrawable.setBatteryLevel(level);
mCharging = pluggedIn;
mPluggedIn = pluggedIn;
mLevel = level;
mDrawable.setCharging(isCharging());
mDrawable.setBatteryLevel(level);
updatePercentText();
}
@@ -224,6 +225,15 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
}
}
void onIsIncompatibleChargingChanged(boolean isIncompatibleCharging) {
boolean valueChanged = mIsIncompatibleCharging != isIncompatibleCharging;
mIsIncompatibleCharging = isIncompatibleCharging;
if (valueChanged) {
mDrawable.setCharging(isCharging());
updateContentDescription();
}
}
private TextView loadPercentView() {
return (TextView) LayoutInflater.from(getContext())
.inflate(R.layout.battery_percentage_view, null);
@@ -263,7 +273,7 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
}
if (mBatteryPercentView != null) {
if (mShowPercentMode == MODE_ESTIMATE && !mCharging) {
if (mShowPercentMode == MODE_ESTIMATE && !isCharging()) {
mBatteryEstimateFetcher.fetchBatteryTimeRemainingEstimate(
(String estimate) -> {
if (mBatteryPercentView == null) {
@@ -316,7 +326,7 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
} else if (mIsBatteryDefender) {
contentDescription =
context.getString(R.string.accessibility_battery_level_charging_paused, mLevel);
} else if (mCharging) {
} else if (isCharging()) {
contentDescription =
context.getString(R.string.accessibility_battery_level_charging, mLevel);
} else {
@@ -462,16 +472,24 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver {
}
}
private boolean isCharging() {
return mPluggedIn && !mIsIncompatibleCharging;
}
public void dump(PrintWriter pw, String[] args) {
String powerSave = mDrawable == null ? null : mDrawable.getPowerSaveEnabled() + "";
String displayShield = mDrawable == null ? null : mDrawable.getDisplayShield() + "";
String charging = mDrawable == null ? null : mDrawable.getCharging() + "";
CharSequence percent = mBatteryPercentView == null ? null : mBatteryPercentView.getText();
pw.println(" BatteryMeterView:");
pw.println(" mDrawable.getPowerSave: " + powerSave);
pw.println(" mDrawable.getDisplayShield: " + displayShield);
pw.println(" mDrawable.getCharging: " + charging);
pw.println(" mBatteryPercentView.getText(): " + percent);
pw.println(" mTextColor: #" + Integer.toHexString(mTextColor));
pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown);
pw.println(" mIsIncompatibleCharging: " + mIsIncompatibleCharging);
pw.println(" mPluggedIn: " + mPluggedIn);
pw.println(" mLevel: " + mLevel);
pw.println(" mMode: " + mShowPercentMode);
}

View File

@@ -32,6 +32,8 @@ import androidx.annotation.NonNull;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocation;
@@ -50,6 +52,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView>
private final TunerService mTunerService;
private final Handler mMainHandler;
private final ContentResolver mContentResolver;
private final FeatureFlags mFeatureFlags;
private final BatteryController mBatteryController;
private final String mSlotBattery;
@@ -98,6 +101,13 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView>
mView.onIsBatteryDefenderChanged(isBatteryDefender);
}
@Override
public void onIsIncompatibleChargingChanged(boolean isIncompatibleCharging) {
if (mFeatureFlags.isEnabled(Flags.INCOMPATIBLE_CHARGING_BATTERY_ICON)) {
mView.onIsIncompatibleChargingChanged(isIncompatibleCharging);
}
}
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.print(super.toString());
@@ -129,6 +139,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView>
TunerService tunerService,
@Main Handler mainHandler,
ContentResolver contentResolver,
FeatureFlags featureFlags,
BatteryController batteryController) {
super(view);
mLocation = location;
@@ -137,6 +148,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView>
mTunerService = tunerService;
mMainHandler = mainHandler;
mContentResolver = contentResolver;
mFeatureFlags = featureFlags;
mBatteryController = batteryController;
mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString);

View File

@@ -273,6 +273,7 @@ abstract class ShadeViewProviderModule {
tunerService: TunerService,
@Main mainHandler: Handler,
contentResolver: ContentResolver,
featureFlags: FeatureFlags,
batteryController: BatteryController,
): BatteryMeterViewController {
return BatteryMeterViewController(
@@ -283,6 +284,7 @@ abstract class ShadeViewProviderModule {
tunerService,
mainHandler,
contentResolver,
featureFlags,
batteryController,
)
}

View File

@@ -162,6 +162,9 @@ public interface BatteryController extends DemoMode,
default void onIsBatteryDefenderChanged(boolean isBatteryDefender) {
}
default void onIsIncompatibleChargingChanged(boolean isIncompatibleCharging) {
}
@Override
default void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println(this);

View File

@@ -29,6 +29,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbManager;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Handler;
@@ -42,6 +43,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.PowerUtil;
@@ -97,6 +99,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
private boolean mAodPowerSave;
private boolean mWirelessCharging;
private boolean mIsBatteryDefender = false;
private boolean mIsIncompatibleCharging = false;
private boolean mTestMode = false;
@VisibleForTesting
boolean mHasReceivedBattery = false;
@@ -136,6 +139,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
filter.addAction(ACTION_LEVEL_TEST);
filter.addAction(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED);
mBroadcastDispatcher.registerReceiver(this, filter);
}
@@ -169,6 +173,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
ipw.print("mCharging="); ipw.println(mCharging);
ipw.print("mCharged="); ipw.println(mCharged);
ipw.print("mIsBatteryDefender="); ipw.println(mIsBatteryDefender);
ipw.print("mIsIncompatibleCharging="); ipw.println(mIsIncompatibleCharging);
ipw.print("mPowerSave="); ipw.println(mPowerSave);
ipw.print("mStateUnknown="); ipw.println(mStateUnknown);
ipw.println("Callbacks:------------------");
@@ -214,6 +219,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
cb.onBatteryUnknownStateChanged(mStateUnknown);
cb.onWirelessChargingChanged(mWirelessCharging);
cb.onIsBatteryDefenderChanged(mIsBatteryDefender);
cb.onIsIncompatibleChargingChanged(mIsIncompatibleCharging);
}
@Override
@@ -229,7 +235,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
if (mTestMode && !intent.getBooleanExtra("testmode", false)) return;
mHasReceivedBattery = true;
mLevel = (int)(100f
mLevel = (int) (100f
* intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
/ intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
mPluggedChargingSource = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
@@ -262,6 +268,12 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
fireBatteryLevelChanged();
} else if (action.equals(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)) {
updatePowerSave();
} else if (action.equals(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED)) {
boolean isIncompatibleCharging = Utils.containsIncompatibleChargers(mContext, TAG);
if (isIncompatibleCharging != mIsIncompatibleCharging) {
mIsIncompatibleCharging = isIncompatibleCharging;
fireIsIncompatibleChargingChanged();
}
} else if (action.equals(ACTION_LEVEL_TEST)) {
mTestMode = true;
mMainHandler.post(new Runnable() {
@@ -270,6 +282,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
int mSavedLevel = mLevel;
boolean mSavedPluggedIn = mPluggedIn;
Intent mTestIntent = new Intent(Intent.ACTION_BATTERY_CHANGED);
@Override
public void run() {
if (mCurrentLevel < 0) {
@@ -333,6 +346,13 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
return mIsBatteryDefender;
}
/**
* Returns whether the charging adapter is incompatible.
*/
public boolean isIncompatibleCharging() {
return mIsIncompatibleCharging;
}
@Override
public void getEstimatedTimeRemainingString(EstimateFetchCompletion completion) {
// Need to fetch or refresh the estimate, but it may involve binder calls so offload the
@@ -453,6 +473,15 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
}
}
private void fireIsIncompatibleChargingChanged() {
synchronized (mChangeCallbacks) {
final int n = mChangeCallbacks.size();
for (int i = 0; i < n; i++) {
mChangeCallbacks.get(i).onIsIncompatibleChargingChanged(mIsIncompatibleCharging);
}
}
}
@Override
public void dispatchDemoCommand(String command, Bundle args) {
if (!mDemoModeController.isInDemoMode()) {
@@ -464,6 +493,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
String powerSave = args.getString("powersave");
String present = args.getString("present");
String defender = args.getString("defender");
String incompatible = args.getString("incompatible");
if (level != null) {
mLevel = Math.min(Math.max(Integer.parseInt(level), 0), 100);
}
@@ -482,6 +512,10 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mIsBatteryDefender = defender.equals("true");
fireIsBatteryDefenderChanged();
}
if (incompatible != null) {
mIsIncompatibleCharging = incompatible.equals("true");
fireIsIncompatibleChargingChanged();
}
fireBatteryLevelChanged();
}

View File

@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -63,6 +64,7 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase {
private ContentResolver mContentResolver;
@Mock
private BatteryController mBatteryController;
private FakeFeatureFlags mFakeFeatureFlags = new FakeFeatureFlags();
private BatteryMeterViewController mController;
@@ -160,6 +162,7 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase {
mTunerService,
mHandler,
mContentResolver,
mFakeFeatureFlags,
mBatteryController
);
}

View File

@@ -130,6 +130,16 @@ class BatteryMeterViewTest : SysuiTestCase() {
)
}
@Test
fun contentDescription_isIncompatibleCharging_notCharging() {
mBatteryMeterView.onBatteryLevelChanged(45, true)
mBatteryMeterView.onIsIncompatibleChargingChanged(true)
assertThat(mBatteryMeterView.contentDescription).isEqualTo(
context.getString(R.string.accessibility_battery_level, 45)
)
}
@Test
fun changesFromEstimateToPercent_textAndContentDescriptionChanges() {
mBatteryMeterView.onBatteryLevelChanged(15, false)
@@ -231,14 +241,33 @@ class BatteryMeterViewTest : SysuiTestCase() {
assertThat(drawable.displayShield).isFalse()
}
@Test
fun isIncompatibleChargingChanged_true_drawableGetsChargingFalse() {
mBatteryMeterView.onBatteryLevelChanged(45, true)
val drawable = getBatteryDrawable()
mBatteryMeterView.onIsIncompatibleChargingChanged(true)
assertThat(drawable.getCharging()).isFalse()
}
@Test
fun isIncompatibleChargingChanged_false_drawableGetsChargingTrue() {
mBatteryMeterView.onBatteryLevelChanged(45, true)
val drawable = getBatteryDrawable()
mBatteryMeterView.onIsIncompatibleChargingChanged(false)
assertThat(drawable.getCharging()).isTrue()
}
private fun getBatteryDrawable(): AccessorizedBatteryDrawable {
return (mBatteryMeterView.getChildAt(0) as ImageView)
.drawable as AccessorizedBatteryDrawable
}
private class Fetcher : BatteryEstimateFetcher {
override fun fetchBatteryTimeRemainingEstimate(
completion: EstimateFetchCompletion) {
override fun fetchBatteryTimeRemainingEstimate(completion: EstimateFetchCompletion) {
completion.onBatteryRemainingEstimateRetrieved(ESTIMATE)
}
}

View File

@@ -29,6 +29,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.PowerManager;
@@ -56,6 +59,9 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import java.util.ArrayList;
import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -65,8 +71,10 @@ public class BatteryControllerTest extends SysuiTestCase {
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private DemoModeController mDemoModeController;
@Mock private View mView;
@Mock private UsbPort mUsbPort;
@Mock private UsbManager mUsbManager;
@Mock private UsbPortStatus mUsbPortStatus;
private BatteryControllerImpl mBatteryController;
private MockitoSession mMockitoSession;
@Before
@@ -255,4 +263,38 @@ public class BatteryControllerTest extends SysuiTestCase {
Assert.assertFalse(mBatteryController.isBatteryDefender());
}
@Test
public void complianceChanged_complianceIncompatible_outputsTrue() {
mContext.addMockSystemService(UsbManager.class, mUsbManager);
setupIncompatibleCharging();
Intent intent = new Intent(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED);
mBatteryController.onReceive(getContext(), intent);
Assert.assertTrue(mBatteryController.isIncompatibleCharging());
}
@Test
public void complianceChanged_emptyComplianceWarnings_outputsFalse() {
mContext.addMockSystemService(UsbManager.class, mUsbManager);
setupIncompatibleCharging();
when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[1]);
Intent intent = new Intent(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED);
mBatteryController.onReceive(getContext(), intent);
Assert.assertFalse(mBatteryController.isIncompatibleCharging());
}
private void setupIncompatibleCharging() {
final List<UsbPort> usbPorts = new ArrayList<>();
usbPorts.add(mUsbPort);
when(mUsbManager.getPorts()).thenReturn(usbPorts);
when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus);
when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
when(mUsbPortStatus.isConnected()).thenReturn(true);
when(mUsbPortStatus.getComplianceWarnings())
.thenReturn(new int[]{UsbPortStatus.COMPLIANCE_WARNING_OTHER});
}
}