From 33f1cd666675046c210e7cf040b99cf6ff7e0ae6 Mon Sep 17 00:00:00 2001 From: Jay Wang Date: Mon, 31 Jul 2023 16:18:15 +0000 Subject: [PATCH] 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 --- .../battery/AccessorizedBatteryDrawable.kt | 5 +++ .../systemui/battery/BatteryMeterView.java | 30 ++++++++++--- .../battery/BatteryMeterViewController.java | 12 +++++ .../systemui/shade/ShadeViewProviderModule.kt | 2 + .../statusbar/policy/BatteryController.java | 3 ++ .../policy/BatteryControllerImpl.java | 36 ++++++++++++++- .../BatteryMeterViewControllerTest.java | 3 ++ .../systemui/battery/BatteryMeterViewTest.kt | 33 +++++++++++++- .../policy/BatteryControllerTest.java | 44 ++++++++++++++++++- 9 files changed, 158 insertions(+), 10 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt index b34f1b45d7634..b81d7fced5e4b 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt @@ -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) diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index 4e8383c3e39ec..ca43705f0d138 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -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); } diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index 6a5749cc55714..0ca38834960c2 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -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 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 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 TunerService tunerService, @Main Handler mainHandler, ContentResolver contentResolver, + FeatureFlags featureFlags, BatteryController batteryController) { super(view); mLocation = location; @@ -137,6 +148,7 @@ public class BatteryMeterViewController extends ViewController mTunerService = tunerService; mMainHandler = mainHandler; mContentResolver = contentResolver; + mFeatureFlags = featureFlags; mBatteryController = batteryController; mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt index 29551188c5c77..6e7678407805b 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt @@ -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, ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 7df083afcd198..37eda6490ec28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -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); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index d5d8f4d7598ee..4b515115dd77a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -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(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java index 40b572934f747..ec8be8ef3047f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java @@ -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 ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt index c84efac86db5c..f0f4ca7f3e668 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt @@ -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) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java index c886f9bee07e5..cdeb592788512 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java @@ -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 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}); + } }