Refactor battery usage page contollers interaction logic
(1) Move controllers interaction logic from BatteryChartPreferenceController to main page PowerUsageAdvanced.
(2) Move query power anomaly logic to DataProcessManager async job.
Bug: 284893240
Test: manual
Change-Id: Ib23b338fe3946e68ff73a372342ec5d86494c566
Merged-In: Ib23b338fe3946e68ff73a372342ec5d86494c566
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index 5294dd5..d04ab0b 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -36,7 +36,6 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
@@ -44,7 +43,6 @@
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
-import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
@@ -52,17 +50,12 @@
import java.util.ArrayList;
import java.util.Calendar;
-import java.util.Comparator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicBoolean;
/** Controls the update for chart graph and the list items. */
public class BatteryChartPreferenceController extends AbstractPreferenceController
- implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy, OnPause,
+ implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
OnSaveInstanceState, OnResume {
private static final String TAG = "BatteryChartPreferenceController";
private static final String PREFERENCE_KEY = "battery_chart";
@@ -74,53 +67,17 @@
private static final String KEY_DAILY_CHART_INDEX = "daily_chart_index";
private static final String KEY_HOURLY_CHART_INDEX = "hourly_chart_index";
- /**
- * A callback listener for battery usage is updated.
- * This happens when battery usage data is ready or the selected index is changed.
- */
- public interface OnBatteryUsageUpdatedListener {
- /**
- * The callback function for battery usage is updated.
- * @param slotUsageData The battery usage diff data for the selected slot. This is used in
- * the app list.
- * @param slotTimestamp The selected slot timestamp information. This is used in the battery
- * usage breakdown category.
- * @param isAllUsageDataEmpty Whether all the battery usage data is null or empty. This is
- * used when showing the footer.
- */
- void onBatteryUsageUpdated(
- BatteryDiffData slotUsageData, String slotTimestamp, boolean isAllUsageDataEmpty);
+ /** A callback listener for the selected index is updated. */
+ interface OnSelectedIndexUpdatedListener {
+ /** The callback function for the selected index is updated. */
+ void onSelectedIndexUpdated();
}
- /**
- * A callback listener for the device screen on time is updated.
- * This happens when screen on time data is ready or the selected index is changed.
- */
- public interface OnScreenOnTimeUpdatedListener {
- /**
- * The callback function for the device screen on time is updated.
- * @param screenOnTime The selected slot device screen on time.
- * @param slotTimestamp The selected slot timestamp information.
- */
- void onScreenOnTimeUpdated(Long screenOnTime, String slotTimestamp);
- }
-
- /**
- * A callback listener for the battery tips card is updated.
- * This happens when battery tips card is ready.
- */
- public interface OnBatteryTipsUpdatedListener {
- /**
- * The callback function for the battery tips card is updated.
- * @param powerAnomalyEvent the power anomaly event with highest score
- */
- void onBatteryTipsUpdated(PowerAnomalyEvent powerAnomalyEvent);
- }
-
-
@VisibleForTesting
Context mPrefContext;
@VisibleForTesting
+ TextView mChartSummaryTextView;
+ @VisibleForTesting
BatteryChartView mDailyChartView;
@VisibleForTesting
BatteryChartView mHourlyChartView;
@@ -128,28 +85,20 @@
int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
@VisibleForTesting
int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
- @VisibleForTesting
- Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
private boolean mIs24HourFormat;
private View mBatteryChartViewGroup;
- private TextView mChartSummaryTextView;
private BatteryChartViewModel mDailyViewModel;
private List<BatteryChartViewModel> mHourlyViewModels;
- private OnBatteryUsageUpdatedListener mOnBatteryUsageUpdatedListener;
- private OnScreenOnTimeUpdatedListener mOnScreenOnTimeUpdatedListener;
- private OnBatteryTipsUpdatedListener mOnBatteryTipsUpdatedListener;
- private AtomicBoolean mIsAppResume = new AtomicBoolean(false);
+ private OnSelectedIndexUpdatedListener mOnSelectedIndexUpdatedListener;
private final SettingsActivity mActivity;
private final MetricsFeatureProvider mMetricsFeatureProvider;
- private final PowerUsageFeatureProvider mPowerUsageFeatureProvider;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final AnimatorListenerAdapter mHourlyChartFadeInAdapter =
createHourlyChartAnimatorListenerAdapter(/*visible=*/ true);
private final AnimatorListenerAdapter mHourlyChartFadeOutAdapter =
createHourlyChartAnimatorListenerAdapter(/*visible=*/ false);
- private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
@VisibleForTesting
final DailyChartLabelTextGenerator mDailyChartLabelTextGenerator =
@@ -165,8 +114,6 @@
mIs24HourFormat = DateFormat.is24HourFormat(context);
mMetricsFeatureProvider =
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
- mPowerUsageFeatureProvider =
- FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(context);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
@@ -184,15 +131,9 @@
Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d",
mDailyChartIndex, mHourlyChartIndex));
}
- @Override
- public void onPause() {
- mIsAppResume.compareAndSet(/* expect= */ true, /* update= */ false);
- }
-
@Override
public void onResume() {
- mIsAppResume.compareAndSet(/* expect= */ false, /* update= */ true);
mIs24HourFormat = DateFormat.is24HourFormat(mContext);
mMetricsFeatureProvider.action(mPrefContext, SettingsEnums.OPEN_BATTERY_USAGE);
}
@@ -232,16 +173,16 @@
return PREFERENCE_KEY;
}
- void setOnBatteryUsageUpdatedListener(OnBatteryUsageUpdatedListener listener) {
- mOnBatteryUsageUpdatedListener = listener;
+ int getDailyChartIndex() {
+ return mDailyChartIndex;
}
- void setOnScreenOnTimeUpdatedListener(OnScreenOnTimeUpdatedListener listener) {
- mOnScreenOnTimeUpdatedListener = listener;
+ int getHourlyChartIndex() {
+ return mHourlyChartIndex;
}
- void setOnBatteryTipsUpdatedListener(OnBatteryTipsUpdatedListener listener) {
- mOnBatteryTipsUpdatedListener = listener;
+ void setOnSelectedIndexUpdatedListener(OnSelectedIndexUpdatedListener listener) {
+ mOnSelectedIndexUpdatedListener = listener;
}
void onBatteryLevelDataUpdate(final BatteryLevelData batteryLevelData) {
@@ -276,13 +217,6 @@
refreshUi();
}
- void onBatteryUsageMapUpdate(Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
- Log.d(TAG, "onBatteryUsageMapUpdate: " + batteryUsageMap);
- mBatteryUsageMap = batteryUsageMap;
- logScreenUsageTime();
- refreshUi();
- }
-
void setBatteryChartView(@NonNull final BatteryChartView dailyChartView,
@NonNull final BatteryChartView hourlyChartView) {
final View parentView = (View) dailyChartView.getParent();
@@ -319,6 +253,9 @@
? SettingsEnums.ACTION_BATTERY_USAGE_DAILY_SHOW_ALL
: SettingsEnums.ACTION_BATTERY_USAGE_DAILY_TIME_SLOT,
mDailyChartIndex);
+ if (mOnSelectedIndexUpdatedListener != null) {
+ mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
+ }
});
mHourlyChartView = hourlyChartView;
mHourlyChartView.setOnSelectListener(trapezoidIndex -> {
@@ -340,102 +277,37 @@
? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
: SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT,
mHourlyChartIndex);
+ if (mOnSelectedIndexUpdatedListener != null) {
+ mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
+ }
});
refreshUi();
}
+ // Show empty hourly chart view only if there is no valid battery usage data.
+ void showEmptyChart() {
+ setChartSummaryVisible(true);
+ mDailyChartView.setVisibility(View.GONE);
+ mHourlyChartView.setVisibility(View.VISIBLE);
+ mHourlyChartView.setViewModel(null);
+ }
+
@VisibleForTesting
- boolean refreshUi() {
+ void refreshUi() {
if (mDailyChartView == null || mHourlyChartView == null) {
// Chart views are not initialized.
- return false;
+ return;
}
- // When mDailyViewModel or mHourlyViewModels is null, there is no battery level data.
- // This is mainly in 2 cases:
- // 1) battery data is within 2 hours
- // 2) no battery data in the latest 7 days (power off >= 7 days)
- final boolean refreshUiResult = mDailyViewModel == null || mHourlyViewModels == null
- ? refreshUiWithNoLevelDataCase()
- : refreshUiWithLevelDataCase();
-
- if (!refreshUiResult) {
- return false;
- }
-
- if (mOnBatteryUsageUpdatedListener != null && mBatteryUsageMap != null
- && mBatteryUsageMap.get(mDailyChartIndex) != null) {
- final BatteryDiffData slotUsageData =
- mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
- if (slotUsageData != null) {
- mOnScreenOnTimeUpdatedListener.onScreenOnTimeUpdated(
- slotUsageData.getScreenOnTime(),
- getSlotInformation());
- }
- mOnBatteryUsageUpdatedListener.onBatteryUsageUpdated(
- slotUsageData, getSlotInformation(), isBatteryUsageMapNullOrEmpty());
- if (mOnBatteryTipsUpdatedListener != null) {
- mExecutor.execute(() -> {
- final PowerAnomalyEventList anomalyEventList = mPowerUsageFeatureProvider
- .detectSettingsAnomaly(mContext, /* displayDrain= */ 0);
- Log.d(TAG, "anomalyEventList = " + anomalyEventList);
- final PowerAnomalyEvent displayEvent =
- getHighestScoreAnomalyEvent(anomalyEventList);
- mHandler.post(() -> {
- if (mIsAppResume.get()) {
- mOnBatteryTipsUpdatedListener
- .onBatteryTipsUpdated(displayEvent);
- }
- }
- );
- });
- }
- }
- return true;
- }
-
- @VisibleForTesting
- PowerAnomalyEvent getHighestScoreAnomalyEvent(PowerAnomalyEventList anomalyEventList) {
- if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) {
- return null;
- }
- final Set<String> dismissedPowerAnomalyKeys =
- DatabaseUtils.getDismissedPowerAnomalyKeys(mContext);
- Log.d(TAG, "dismissedPowerAnomalyKeys = " + dismissedPowerAnomalyKeys);
-
- final PowerAnomalyEvent highestScoreEvent = anomalyEventList.getPowerAnomalyEventsList()
- .stream()
- .filter(event -> event.hasKey()
- && !dismissedPowerAnomalyKeys.contains(event.getKey().name()))
- .max(Comparator.comparing(PowerAnomalyEvent::getScore))
- .orElse(null);
- Log.d(TAG, "highestScoreAnomalyEvent = " + highestScoreEvent);
- return highestScoreEvent;
- }
-
- private boolean refreshUiWithNoLevelDataCase() {
- setChartSummaryVisible(false);
- if (mBatteryUsageMap == null) {
- // There is no battery level data and battery usage data is not ready, wait for data
- // ready to refresh UI. Show nothing temporarily.
+ if (mDailyViewModel == null || mHourlyViewModels == null) {
+ setChartSummaryVisible(false);
mDailyChartView.setVisibility(View.GONE);
mHourlyChartView.setVisibility(View.GONE);
mDailyChartView.setViewModel(null);
mHourlyChartView.setViewModel(null);
- return false;
- } else if (mBatteryUsageMap
- .get(BatteryChartViewModel.SELECTED_INDEX_ALL)
- .get(BatteryChartViewModel.SELECTED_INDEX_ALL) == null) {
- // There is no battery level data and battery usage data, show an empty hourly chart
- // view.
- mDailyChartView.setVisibility(View.GONE);
- mHourlyChartView.setVisibility(View.VISIBLE);
- mHourlyChartView.setViewModel(null);
+ return;
}
- return true;
- }
- private boolean refreshUiWithLevelDataCase() {
setChartSummaryVisible(true);
// Gets valid battery level data.
if (isBatteryLevelDataInOneDay()) {
@@ -464,15 +336,8 @@
hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
mHourlyChartView.setViewModel(hourlyViewModel);
}
-
- if (mBatteryUsageMap == null) {
- // Battery usage data is not ready, wait for data ready to refresh UI.
- return false;
- }
- return true;
}
- @VisibleForTesting
String getSlotInformation() {
if (mDailyViewModel == null || mHourlyViewModels == null) {
// No data
@@ -563,44 +428,6 @@
};
}
- private void logScreenUsageTime() {
- if (mBatteryUsageMap == null) {
- return;
- }
- final BatteryDiffData allBatteryDiffData = mBatteryUsageMap.get(
- BatteryChartViewModel.SELECTED_INDEX_ALL).get(
- BatteryChartViewModel.SELECTED_INDEX_ALL);
- if (allBatteryDiffData == null) {
- return;
- }
- mMetricsFeatureProvider.action(
- mPrefContext,
- SettingsEnums.ACTION_BATTERY_USAGE_SCREEN_ON_TIME,
- (int) allBatteryDiffData.getScreenOnTime());
- mMetricsFeatureProvider.action(
- mPrefContext,
- SettingsEnums.ACTION_BATTERY_USAGE_FOREGROUND_USAGE_TIME,
- (int) getTotalForegroundUsageTime());
- }
-
- private long getTotalForegroundUsageTime() {
- if (mBatteryUsageMap == null) {
- return 0;
- }
- final BatteryDiffData totalBatteryUsageDiffData =
- mBatteryUsageMap
- .get(BatteryChartViewModel.SELECTED_INDEX_ALL)
- .get(BatteryChartViewModel.SELECTED_INDEX_ALL);
- if (totalBatteryUsageDiffData == null) {
- return 0;
- }
- long totalValue = 0;
- for (final BatteryDiffEntry entry : totalBatteryUsageDiffData.getAppDiffEntryList()) {
- totalValue += entry.mForegroundUsageTimeInMs;
- }
- return totalValue;
- }
-
private boolean isBatteryLevelDataInOneDay() {
return mHourlyViewModels != null && mHourlyViewModels.size() == 1;
}
@@ -611,19 +438,6 @@
&& mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
}
- private boolean isBatteryUsageMapNullOrEmpty() {
- if (mBatteryUsageMap == null) {
- return true;
- }
- BatteryDiffData allBatteryDiffData = mBatteryUsageMap
- .get(BatteryChartViewModel.SELECTED_INDEX_ALL)
- .get(BatteryChartViewModel.SELECTED_INDEX_ALL);
- // If all data is null or empty, each slot must be null or empty.
- return allBatteryDiffData == null
- || (allBatteryDiffData.getAppDiffEntryList().isEmpty()
- && allBatteryDiffData.getSystemDiffEntryList().isEmpty());
- }
-
@VisibleForTesting
static int getTotalHours(final BatteryLevelData batteryLevelData) {
if (batteryLevelData == null) {
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
index ae74689..ccfc1e2 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
@@ -35,6 +35,8 @@
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.fuelgauge.BatteryBroadcastReceiver;
+import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
+import com.android.settings.overlay.FeatureFactory;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
@@ -42,9 +44,13 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
/** Advanced power usage. */
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
@@ -61,9 +67,14 @@
private boolean mIsChartDataLoaded = false;
private long mResumeTimestamp;
+ private BatteryTipsController mBatteryTipsController;
private BatteryChartPreferenceController mBatteryChartPreferenceController;
+ private ScreenOnTimeController mScreenOnTimeController;
+ private BatteryUsageBreakdownController mBatteryUsageBreakdownController;
private Optional<BatteryLevelData> mBatteryLevelData;
+ private Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
+ private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final ContentObserver mBatteryObserver =
new ContentObserver(mHandler) {
@@ -90,6 +101,7 @@
if (getActivity().isChangingConfigurations()) {
BatteryEntry.clearUidCache();
}
+ mExecutor.shutdown();
}
@Override
@@ -112,7 +124,6 @@
super.onPause();
// Resets the flag to reload usage data in onResume() callback.
mIsChartDataLoaded = false;
- mBatteryLevelData = null;
final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI;
if (uri != null) {
getContext().getContentResolver().unregisterContentObserver(mBatteryObserver);
@@ -133,28 +144,25 @@
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
+ mBatteryTipsController = new BatteryTipsController(context);
mBatteryChartPreferenceController =
new BatteryChartPreferenceController(
context, getSettingsLifecycle(), (SettingsActivity) getActivity());
- final ScreenOnTimeController screenOnTimeController = new ScreenOnTimeController(context);
- final BatteryUsageBreakdownController batteryUsageBreakdownController =
+ mScreenOnTimeController = new ScreenOnTimeController(context);
+ mBatteryUsageBreakdownController =
new BatteryUsageBreakdownController(
context, getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
- final BatteryTipsController batteryTipsController = new BatteryTipsController(context);
- mBatteryChartPreferenceController.setOnScreenOnTimeUpdatedListener(
- screenOnTimeController::handleSceenOnTimeUpdated);
- mBatteryChartPreferenceController.setOnBatteryUsageUpdatedListener(
- batteryUsageBreakdownController::handleBatteryUsageUpdated);
- mBatteryChartPreferenceController.setOnBatteryTipsUpdatedListener(
- batteryTipsController::handleBatteryTipsCardUpdated);
-
+ controllers.add(mBatteryTipsController);
controllers.add(mBatteryChartPreferenceController);
- controllers.add(screenOnTimeController);
- controllers.add(batteryUsageBreakdownController);
- controllers.add(batteryTipsController);
+ controllers.add(mScreenOnTimeController);
+ controllers.add(mBatteryUsageBreakdownController);
setBatteryChartPreferenceController();
+ mBatteryChartPreferenceController.setOnSelectedIndexUpdatedListener(
+ this::onSelectedSlotDataUpdated);
+ // Force UI refresh if battery usage data was loaded before UI initialization.
+ onSelectedSlotDataUpdated();
return controllers;
}
@@ -169,12 +177,17 @@
bundle.putInt(KEY_REFRESH_TYPE, refreshType);
if (!mIsChartDataLoaded) {
mIsChartDataLoaded = true;
+ mBatteryLevelData = null;
+ mBatteryUsageMap = null;
restartLoader(LoaderIndex.BATTERY_LEVEL_DATA_LOADER, bundle,
mBatteryLevelDataLoaderCallbacks);
}
}
private void onBatteryLevelDataUpdate(BatteryLevelData batteryLevelData) {
+ if (!isResumed()) {
+ return;
+ }
mBatteryLevelData = Optional.ofNullable(batteryLevelData);
if (mBatteryChartPreferenceController != null) {
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(batteryLevelData);
@@ -184,23 +197,131 @@
}
private void onBatteryDiffDataMapUpdate(Map<Long, BatteryDiffData> batteryDiffDataMap) {
- if (mBatteryLevelData != null && mBatteryChartPreferenceController != null) {
- Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap =
- DataProcessor.generateBatteryUsageMap(
- getContext(), batteryDiffDataMap, mBatteryLevelData.orElse(null));
- DataProcessor.loadLabelAndIcon(batteryUsageMap);
- mBatteryChartPreferenceController.onBatteryUsageMapUpdate(batteryUsageMap);
+ if (!isResumed() || mBatteryLevelData == null) {
+ return;
}
+ mBatteryUsageMap = DataProcessor.generateBatteryUsageMap(
+ getContext(), batteryDiffDataMap, mBatteryLevelData.orElse(null));
+ Log.d(TAG, "onBatteryDiffDataMapUpdate: " + mBatteryUsageMap);
+ DataProcessor.loadLabelAndIcon(mBatteryUsageMap);
+ onSelectedSlotDataUpdated();
+ detectAnomaly();
+ logScreenUsageTime();
+ if (mBatteryChartPreferenceController != null
+ && mBatteryLevelData.isEmpty() && isBatteryUsageMapNullOrEmpty()) {
+ // No available battery usage and battery level data.
+ mBatteryChartPreferenceController.showEmptyChart();
+ }
+ }
+
+ private void onSelectedSlotDataUpdated() {
+ if (mBatteryChartPreferenceController == null
+ || mScreenOnTimeController == null
+ || mBatteryUsageBreakdownController == null
+ || mBatteryUsageMap == null) {
+ return;
+ }
+ final int dailyIndex = mBatteryChartPreferenceController.getDailyChartIndex();
+ final int hourlyIndex = mBatteryChartPreferenceController.getHourlyChartIndex();
+ final String slotInformation = mBatteryChartPreferenceController.getSlotInformation();
+ final BatteryDiffData slotUsageData = mBatteryUsageMap.get(dailyIndex).get(hourlyIndex);
+ if (slotUsageData != null) {
+ mScreenOnTimeController.handleSceenOnTimeUpdated(
+ slotUsageData.getScreenOnTime(), slotInformation);
+ }
+ mBatteryUsageBreakdownController.handleBatteryUsageUpdated(
+ slotUsageData, slotInformation, isBatteryUsageMapNullOrEmpty());
Log.d(TAG, String.format("Battery usage list shows in %d millis",
System.currentTimeMillis() - mResumeTimestamp));
}
+ private void detectAnomaly() {
+ mExecutor.execute(() -> {
+ final PowerUsageFeatureProvider powerUsageFeatureProvider =
+ FeatureFactory.getFactory(getContext())
+ .getPowerUsageFeatureProvider(getContext());
+ final PowerAnomalyEventList anomalyEventList =
+ powerUsageFeatureProvider.detectSettingsAnomaly(
+ getContext(), /* displayDrain= */ 0);
+ mHandler.post(() -> onAnomalyDetected(anomalyEventList));
+ });
+ }
+
+ private void onAnomalyDetected(PowerAnomalyEventList anomalyEventList) {
+ if (!isResumed() || anomalyEventList == null) {
+ return;
+ }
+ Log.d(TAG, "anomalyEventList = " + anomalyEventList);
+ final PowerAnomalyEvent displayEvent =
+ getHighestScoreAnomalyEvent(getContext(), anomalyEventList);
+ if (displayEvent == null) {
+ return;
+ }
+ if (mBatteryTipsController != null) {
+ mBatteryTipsController.handleBatteryTipsCardUpdated(displayEvent);
+ }
+ }
+
private void setBatteryChartPreferenceController() {
if (mHistPref != null && mBatteryChartPreferenceController != null) {
mHistPref.setChartPreferenceController(mBatteryChartPreferenceController);
}
}
+ private boolean isBatteryUsageMapNullOrEmpty() {
+ final BatteryDiffData allBatteryDiffData = getAllBatteryDiffData(mBatteryUsageMap);
+ // If all data is null or empty, each slot must be null or empty.
+ return allBatteryDiffData == null
+ || (allBatteryDiffData.getAppDiffEntryList().isEmpty()
+ && allBatteryDiffData.getSystemDiffEntryList().isEmpty());
+ }
+
+ private void logScreenUsageTime() {
+ final BatteryDiffData allBatteryDiffData = getAllBatteryDiffData(mBatteryUsageMap);
+ if (allBatteryDiffData == null) {
+ return;
+ }
+ long totalForegroundUsageTime = 0;
+ for (final BatteryDiffEntry entry : allBatteryDiffData.getAppDiffEntryList()) {
+ totalForegroundUsageTime += entry.mForegroundUsageTimeInMs;
+ }
+ mMetricsFeatureProvider.action(
+ getContext(),
+ SettingsEnums.ACTION_BATTERY_USAGE_SCREEN_ON_TIME,
+ (int) allBatteryDiffData.getScreenOnTime());
+ mMetricsFeatureProvider.action(
+ getContext(),
+ SettingsEnums.ACTION_BATTERY_USAGE_FOREGROUND_USAGE_TIME,
+ (int) totalForegroundUsageTime);
+ }
+
+ @VisibleForTesting
+ static PowerAnomalyEvent getHighestScoreAnomalyEvent(
+ Context context, PowerAnomalyEventList anomalyEventList) {
+ if (anomalyEventList == null || anomalyEventList.getPowerAnomalyEventsCount() == 0) {
+ return null;
+ }
+ final Set<String> dismissedPowerAnomalyKeys =
+ DatabaseUtils.getDismissedPowerAnomalyKeys(context);
+ Log.d(TAG, "dismissedPowerAnomalyKeys = " + dismissedPowerAnomalyKeys);
+
+ final PowerAnomalyEvent highestScoreEvent = anomalyEventList.getPowerAnomalyEventsList()
+ .stream()
+ .filter(event -> event.hasKey()
+ && !dismissedPowerAnomalyKeys.contains(event.getKey().name()))
+ .max(Comparator.comparing(PowerAnomalyEvent::getScore))
+ .orElse(null);
+ Log.d(TAG, "highestScoreAnomalyEvent = " + highestScoreEvent);
+ return highestScoreEvent;
+ }
+
+ private static BatteryDiffData getAllBatteryDiffData(
+ Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap) {
+ return batteryUsageMap == null ? null : batteryUsageMap
+ .get(BatteryChartViewModel.SELECTED_INDEX_ALL)
+ .get(BatteryChartViewModel.SELECTED_INDEX_ALL);
+ }
+
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
@@ -237,7 +358,7 @@
public BatteryLevelData loadInBackground() {
return DataProcessManager.getBatteryLevelData(
getContext(), mHandler, /*isFromPeriodJob=*/ false,
- map -> PowerUsageAdvanced.this.onBatteryDiffDataMapUpdate(map));
+ PowerUsageAdvanced.this::onBatteryDiffDataMapUpdate);
}
};
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
index 786a529..cd4e599 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -44,9 +45,9 @@
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.widget.LinearLayout;
+import android.widget.TextView;
import com.android.settings.SettingsActivity;
-import com.android.settings.testutils.BatteryTestUtils;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
@@ -72,6 +73,8 @@
@Mock
private SettingsActivity mSettingsActivity;
@Mock
+ private TextView mChartSummaryTextView;
+ @Mock
private BatteryChartView mDailyChartView;
@Mock
private BatteryChartView mHourlyChartView;
@@ -112,6 +115,7 @@
setupHourlyChartViewAnimationMock();
mBatteryChartPreferenceController = createController();
mBatteryChartPreferenceController.mPrefContext = mContext;
+ mBatteryChartPreferenceController.mChartSummaryTextView = mChartSummaryTextView;
mBatteryChartPreferenceController.mDailyChartView = mDailyChartView;
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
BatteryDiffEntry.clearCache();
@@ -180,7 +184,6 @@
mBatteryChartPreferenceController.mDailyChartLabelTextGenerator);
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
- mBatteryChartPreferenceController.onBatteryUsageMapUpdate(getEmptyBatteryUsageMap());
verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
verify(mViewPropertyAnimator, atLeastOnce()).alpha(0f);
@@ -275,29 +278,78 @@
}
@Test
- public void refreshUi_normalCase_returnTrue() {
+ public void onBatteryLevelDataUpdate_oneDay_showHourlyChartOnly() {
+ doReturn(View.GONE).when(mHourlyChartView).getVisibility();
+
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(6));
- mBatteryChartPreferenceController.onBatteryUsageMapUpdate(getEmptyBatteryUsageMap());
- assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
+
+ verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
+ verify(mDailyChartView).setVisibility(View.GONE);
+ verify(mHourlyChartView).setVisibility(View.VISIBLE);
}
@Test
- public void refreshUi_batteryIndexedMapIsNull_returnTrue() {
+ public void onBatteryLevelDataUpdate_selectAllForMultipleDays_showDailyChartOnly() {
+ doReturn(View.GONE).when(mHourlyChartView).getVisibility();
+
+ mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL;
+ mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
+
+ verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
+ verify(mDailyChartView).setVisibility(View.VISIBLE);
+ verify(mHourlyChartView, never()).setVisibility(View.VISIBLE);
+ }
+
+ @Test
+ public void onBatteryLevelDataUpdate_selectOneDayForMultipleDays_showBothCharts() {
+ doReturn(View.GONE).when(mHourlyChartView).getVisibility();
+
+ mBatteryChartPreferenceController.mDailyChartIndex = 0;
+ mBatteryChartPreferenceController.onBatteryLevelDataUpdate(createBatteryLevelData(60));
+
+ verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
+ verify(mDailyChartView).setVisibility(View.VISIBLE);
+ verify(mHourlyChartView).setVisibility(View.VISIBLE);
+ }
+
+ @Test
+ public void onBatteryLevelDataUpdate_batteryLevelDataIsNull_showNoChart() {
+ doReturn(View.GONE).when(mHourlyChartView).getVisibility();
+
mBatteryChartPreferenceController.onBatteryLevelDataUpdate(null);
- mBatteryChartPreferenceController.onBatteryUsageMapUpdate(getEmptyBatteryUsageMap());
- assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
+
+ verify(mChartSummaryTextView).setVisibility(View.GONE);
+ verify(mDailyChartView).setVisibility(View.GONE);
+ verify(mHourlyChartView).setVisibility(View.GONE);
+ }
+
+ @Test
+ public void showEmptyChart_normalCase_showEmptyChart() {
+ doReturn(View.GONE).when(mHourlyChartView).getVisibility();
+
+ mBatteryChartPreferenceController.showEmptyChart();
+
+ verify(mChartSummaryTextView).setVisibility(View.VISIBLE);
+ verify(mDailyChartView).setVisibility(View.GONE);
+ verify(mHourlyChartView).setVisibility(View.VISIBLE);
}
@Test
public void refreshUi_dailyChartViewIsNull_ignoreRefresh() {
mBatteryChartPreferenceController.mDailyChartView = null;
- assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
+
+ mBatteryChartPreferenceController.refreshUi();
+
+ verify(mChartSummaryTextView, never()).setVisibility(anyInt());
}
@Test
public void refreshUi_hourlyChartViewIsNull_ignoreRefresh() {
mBatteryChartPreferenceController.mHourlyChartView = null;
- assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
+
+ mBatteryChartPreferenceController.refreshUi();
+
+ verify(mChartSummaryTextView, never()).setVisibility(anyInt());
}
@Test
@@ -408,57 +460,6 @@
assertThat(totalHour).isEqualTo(59);
}
- @Test
- public void getHighestScoreAnomalyEvent_withEmptyOrNullList_getNull() {
- assertThat(mBatteryChartPreferenceController.getHighestScoreAnomalyEvent(null))
- .isEqualTo(null);
- assertThat(mBatteryChartPreferenceController.getHighestScoreAnomalyEvent(
- BatteryTestUtils.createEmptyPowerAnomalyEventList()))
- .isEqualTo(null);
- }
-
- @Test
- public void getHighestScoreAnomalyEvent_withoutDismissed_getHighestScoreEvent() {
- final PowerAnomalyEventList eventList =
- BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
-
- final PowerAnomalyEvent highestScoreEvent =
- mBatteryChartPreferenceController.getHighestScoreAnomalyEvent(eventList);
-
- assertThat(highestScoreEvent)
- .isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent());
- }
-
- @Test
- public void getHighestScoreAnomalyEvent_withBrightnessDismissed_getScreenTimeout() {
- final PowerAnomalyEventList eventList =
- BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
- DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
- DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, PowerAnomalyKey.KEY_BRIGHTNESS.name());
-
- final PowerAnomalyEvent highestScoreEvent =
- mBatteryChartPreferenceController.getHighestScoreAnomalyEvent(eventList);
-
- assertThat(highestScoreEvent)
- .isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent());
- }
-
- @Test
- public void getHighestScoreAnomalyEvent_withAllDismissed_getNull() {
- final PowerAnomalyEventList eventList =
- BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
- DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
- for (PowerAnomalyKey key : PowerAnomalyKey.values()) {
- DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, key.name());
- }
-
- final PowerAnomalyEvent highestScoreEvent =
- mBatteryChartPreferenceController.getHighestScoreAnomalyEvent(eventList);
-
- assertThat(highestScoreEvent).isEqualTo(null);
- }
-
-
private static Long generateTimestamp(int index) {
// "2021-04-23 07:00:00 UTC" + index hours
return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS;
@@ -481,11 +482,6 @@
return new BatteryLevelData(batteryLevelMap);
}
- private static Map<Integer, Map<Integer, BatteryDiffData>> getEmptyBatteryUsageMap() {
- return Map.of(SELECTED_INDEX_ALL, Map.of(SELECTED_INDEX_ALL, new BatteryDiffData(
- null, 0, 0, 0, 0, 0, List.of(), List.of(), Set.of(), Set.of(), false)));
- }
-
private BatteryChartPreferenceController createController() {
final BatteryChartPreferenceController controller =
new BatteryChartPreferenceController(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java
new file mode 100644
index 0000000..ee2a8b2
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvancedTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 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.settings.fuelgauge.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+
+import com.android.settings.testutils.BatteryTestUtils;
+import com.android.settings.testutils.shadow.ShadowDashboardFragment;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowDashboardFragment.class)
+public final class PowerUsageAdvancedTest {
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = spy(RuntimeEnvironment.application);
+ }
+
+ @Test
+ public void getHighestScoreAnomalyEvent_withEmptyOrNullList_getNull() {
+ assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, null)).isNull();
+ assertThat(PowerUsageAdvanced.getHighestScoreAnomalyEvent(
+ mContext, BatteryTestUtils.createEmptyPowerAnomalyEventList())).isNull();
+ }
+
+ @Test
+ public void getHighestScoreAnomalyEvent_withoutDismissed_getHighestScoreEvent() {
+ final PowerAnomalyEventList powerAnomalyEventList =
+ BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
+
+ final PowerAnomalyEvent highestScoreEvent =
+ PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList);
+
+ assertThat(highestScoreEvent)
+ .isEqualTo(BatteryTestUtils.createAdaptiveBrightnessAnomalyEvent());
+ }
+
+ @Test
+ public void getHighestScoreAnomalyEvent_withBrightnessDismissed_getScreenTimeout() {
+ final PowerAnomalyEventList powerAnomalyEventList =
+ BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
+ DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
+ DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, PowerAnomalyKey.KEY_BRIGHTNESS.name());
+
+ final PowerAnomalyEvent highestScoreEvent =
+ PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList);
+
+ assertThat(highestScoreEvent)
+ .isEqualTo(BatteryTestUtils.createScreenTimeoutAnomalyEvent());
+ }
+
+ @Test
+ public void getHighestScoreAnomalyEvent_withAllDismissed_getNull() {
+ final PowerAnomalyEventList powerAnomalyEventList =
+ BatteryTestUtils.createNonEmptyPowerAnomalyEventList();
+ DatabaseUtils.removeDismissedPowerAnomalyKeys(mContext);
+ for (PowerAnomalyKey key : PowerAnomalyKey.values()) {
+ DatabaseUtils.setDismissedPowerAnomalyKeys(mContext, key.name());
+ }
+
+ final PowerAnomalyEvent highestScoreEvent =
+ PowerUsageAdvanced.getHighestScoreAnomalyEvent(mContext, powerAnomalyEventList);
+
+ assertThat(highestScoreEvent).isEqualTo(null);
+ }
+}