diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index b9440c5e3975b..66085ac0a9157 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -41,24 +41,22 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.StyleRes; +import androidx.annotation.VisibleForTesting; import com.android.settingslib.graph.ThemedBatteryDrawable; -import com.android.systemui.Dependency; import com.android.systemui.DualToneHandler; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.policy.BatteryController; -import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.text.NumberFormat; -public class BatteryMeterView extends LinearLayout implements - BatteryStateChangeCallback, DarkReceiver { +public class BatteryMeterView extends LinearLayout implements DarkReceiver { @Retention(SOURCE) @IntDef({MODE_DEFAULT, MODE_ON, MODE_OFF, MODE_ESTIMATE}) @@ -72,7 +70,6 @@ public class BatteryMeterView extends LinearLayout implements private final ImageView mBatteryIconView; private TextView mBatteryPercentView; - private BatteryController mBatteryController; private final @StyleRes int mPercentageStyleId; private int mTextColor; private int mLevel; @@ -90,6 +87,8 @@ public class BatteryMeterView extends LinearLayout implements private int mNonAdaptedForegroundColor; private int mNonAdaptedBackgroundColor; + private BatteryEstimateFetcher mBatteryEstimateFetcher; + public BatteryMeterView(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -178,22 +177,7 @@ public class BatteryMeterView extends LinearLayout implements return false; } - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - mBatteryController = Dependency.get(BatteryController.class); - mBatteryController.addCallback(this); - updateShowPercent(); - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mBatteryController.removeCallback(this); - } - - @Override - public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + void onBatteryLevelChanged(int level, boolean pluggedIn) { mDrawable.setCharging(pluggedIn); mDrawable.setBatteryLevel(level); mCharging = pluggedIn; @@ -201,8 +185,7 @@ public class BatteryMeterView extends LinearLayout implements updatePercentText(); } - @Override - public void onPowerSaveChanged(boolean isPowerSave) { + void onPowerSaveChanged(boolean isPowerSave) { mDrawable.setPowerSaveEnabled(isPowerSave); } @@ -222,19 +205,28 @@ public class BatteryMeterView extends LinearLayout implements updateShowPercent(); } + /** + * Sets the fetcher that should be used to get the estimated time remaining for the user's + * battery. + */ + void setBatteryEstimateFetcher(BatteryEstimateFetcher fetcher) { + mBatteryEstimateFetcher = fetcher; + } + void updatePercentText() { if (mBatteryStateUnknown) { setContentDescription(getContext().getString(R.string.accessibility_battery_unknown)); return; } - if (mBatteryController == null) { + if (mBatteryEstimateFetcher == null) { return; } if (mBatteryPercentView != null) { if (mShowPercentMode == MODE_ESTIMATE && !mCharging) { - mBatteryController.getEstimatedTimeRemainingString((String estimate) -> { + mBatteryEstimateFetcher.fetchBatteryTimeRemainingEstimate( + (String estimate) -> { if (mBatteryPercentView == null) { return; } @@ -310,8 +302,7 @@ public class BatteryMeterView extends LinearLayout implements return mUnknownStateDrawable; } - @Override - public void onBatteryUnknownStateChanged(boolean isUnknown) { + void onBatteryUnknownStateChanged(boolean isUnknown) { if (mBatteryStateUnknown == isUnknown) { return; } @@ -390,5 +381,16 @@ public class BatteryMeterView extends LinearLayout implements pw.println(" mLevel: " + mLevel); pw.println(" mMode: " + mShowPercentMode); } + + @VisibleForTesting + CharSequence getBatteryPercentViewText() { + return mBatteryPercentView.getText(); + } + + /** An interface that will fetch the estimated time remaining for the user's battery. */ + public interface BatteryEstimateFetcher { + void fetchBatteryTimeRemainingEstimate( + BatteryController.EstimateFetchCompletion completion); + } } diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index b7e29821a16a5..ae9a32309d45c 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -31,6 +31,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; @@ -42,6 +43,7 @@ public class BatteryMeterViewController extends ViewController private final ConfigurationController mConfigurationController; private final TunerService mTunerService; private final ContentResolver mContentResolver; + private final BatteryController mBatteryController; private final String mSlotBattery; private final SettingObserver mSettingObserver; @@ -66,6 +68,24 @@ public class BatteryMeterViewController extends ViewController } }; + private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback = + new BatteryController.BatteryStateChangeCallback() { + @Override + public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { + mView.onBatteryLevelChanged(level, pluggedIn); + } + + @Override + public void onPowerSaveChanged(boolean isPowerSave) { + mView.onPowerSaveChanged(isPowerSave); + } + + @Override + public void onBatteryUnknownStateChanged(boolean isUnknown) { + mView.onBatteryUnknownStateChanged(isUnknown); + } + }; + // Some places may need to show the battery conditionally, and not obey the tuner private boolean mIgnoreTunerUpdates; private boolean mIsSubscribedForTunerUpdates; @@ -77,11 +97,15 @@ public class BatteryMeterViewController extends ViewController TunerService tunerService, BroadcastDispatcher broadcastDispatcher, @Main Handler mainHandler, - ContentResolver contentResolver) { + ContentResolver contentResolver, + BatteryController batteryController) { super(view); mConfigurationController = configurationController; mTunerService = tunerService; mContentResolver = contentResolver; + mBatteryController = batteryController; + + mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString); mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery); mSettingObserver = new SettingObserver(mainHandler); @@ -99,16 +123,21 @@ public class BatteryMeterViewController extends ViewController protected void onViewAttached() { mConfigurationController.addCallback(mConfigurationListener); subscribeForTunerUpdates(); + mBatteryController.addCallback(mBatteryStateChangeCallback); registerShowBatteryPercentObserver(ActivityManager.getCurrentUser()); registerGlobalBatteryUpdateObserver(); mCurrentUserTracker.startTracking(); + + mView.updateShowPercent(); } @Override protected void onViewDetached() { mConfigurationController.removeCallback(mConfigurationListener); unsubscribeFromTunerUpdates(); + mBatteryController.removeCallback(mBatteryStateChangeCallback); + mCurrentUserTracker.stopTracking(); mContentResolver.unregisterContentObserver(mSettingObserver); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 8591e85ff4569..ccf2c5c24c10d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1143,7 +1143,8 @@ public class StatusBar extends SystemUI implements mTunerService, mBroadcastDispatcher, mMainHandler, - mContext.getContentResolver() + mContext.getContentResolver(), + mBatteryController ); mBatteryMeterViewController.init(); 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 903cbb522e4fe..1d038a43ec8e2 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.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; @@ -58,6 +59,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private Handler mHandler; @Mock private ContentResolver mContentResolver; + @Mock + private BatteryController mBatteryController; private BatteryMeterViewController mController; @@ -74,7 +77,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { mTunerService, mBroadcastDispatcher, mHandler, - mContentResolver + mContentResolver, + mBatteryController ); } @@ -92,6 +96,7 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { anyBoolean(), any() ); + verify(mBatteryController).addCallback(any()); } @Test @@ -104,6 +109,7 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { verify(mConfigurationController).removeCallback(any()); verify(mTunerService).removeTunable(any()); verify(mContentResolver).unregisterContentObserver(any()); + verify(mBatteryController).removeCallback(any()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt new file mode 100644 index 0000000000000..b4ff2a5e1fbf0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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.battery + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.battery.BatteryMeterView.BatteryEstimateFetcher +import com.android.systemui.statusbar.policy.BatteryController.EstimateFetchCompletion +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class BatteryMeterViewTest : SysuiTestCase() { + + private lateinit var mBatteryMeterView: BatteryMeterView + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + mBatteryMeterView = BatteryMeterView(mContext, null) + } + + @Test + fun updatePercentText_estimateModeAndNotCharging_estimateFetched() { + mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE) + mBatteryMeterView.setBatteryEstimateFetcher(Fetcher()) + + mBatteryMeterView.updatePercentText() + + assertThat(mBatteryMeterView.batteryPercentViewText).isEqualTo(ESTIMATE) + } + + @Test + fun updatePercentText_noBatteryEstimateFetcher_noCrash() { + mBatteryMeterView.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE) + + mBatteryMeterView.updatePercentText() + // No assert needed + } + + private class Fetcher : BatteryEstimateFetcher { + override fun fetchBatteryTimeRemainingEstimate( + completion: EstimateFetchCompletion) { + completion.onBatteryRemainingEstimateRetrieved(ESTIMATE) + } + } + + private companion object { + const val ESTIMATE = "2 hours 2 minutes" + } +} \ No newline at end of file