Merge "Refactor battery usage page contollers interaction logic" into udc-qpr-dev
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);
+    }
+}