diff --git a/res/layout/preference_expand_divider.xml b/res/layout/preference_expand_divider.xml
deleted file mode 100644
index 9b766881183..00000000000
--- a/res/layout/preference_expand_divider.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/preference_tab.xml b/res/layout/preference_tab.xml
new file mode 100644
index 00000000000..f9a78819550
--- /dev/null
+++ b/res/layout/preference_tab.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0e64431a288..163b3e1f8ed 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4993,6 +4993,14 @@
Daily battery usage chart
Hourly battery usage chart
+
+ Usage proportional breakdown since last full charge
+
+ Usage proportional breakdown for %s
+
+ App
+
+ System
diff --git a/res/xml/power_usage_advanced.xml b/res/xml/power_usage_advanced.xml
index e9274ce3b2c..4371995b71d 100644
--- a/res/xml/power_usage_advanced.xml
+++ b/res/xml/power_usage_advanced.xml
@@ -22,14 +22,30 @@
settings:keywords="@string/keywords_battery_usage">
+ android:key="battery_chart"
+ settings:controller=
+ "com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController" />
+ android:key="battery_usage_breakdown"
+ settings:controller=
+ "com.android.settings.fuelgauge.batteryusage.BatteryUsageBreakdownController"
+ settings:isPreferenceVisible="false">
-
+
+
+
+
+
+
+
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index 124d4b4296c..7e1bb381224 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -21,30 +21,22 @@ import android.animation.AnimatorListenerAdapter;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.View;
-import android.view.accessibility.AccessibilityManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
-import com.android.settings.core.InstrumentedPreferenceFragment;
import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
-import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
@@ -54,73 +46,68 @@ import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
-import com.android.settingslib.utils.StringUtil;
-import com.android.settingslib.widget.FooterPreference;
import java.util.ArrayList;
import java.util.Calendar;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Controls the update for chart graph and the list items. */
public class BatteryChartPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
- OnSaveInstanceState, OnResume, ExpandDividerPreference.OnExpandListener {
+ OnSaveInstanceState, OnResume {
private static final String TAG = "BatteryChartPreferenceController";
- private static final String KEY_FOOTER_PREF = "battery_graph_footer";
- private static final String PACKAGE_NAME_NONE = "none";
- private static final int ENABLED_ICON_ALPHA = 255;
- private static final int DISABLED_ICON_ALPHA = 255 / 3;
+ private static final String PREFERENCE_KEY = "battery_chart";
private static final long FADE_IN_ANIMATION_DURATION = 400L;
private static final long FADE_OUT_ANIMATION_DURATION = 200L;
// Keys for bundle instance to restore configurations.
- private static final String KEY_EXPAND_SYSTEM_INFO = "expand_system_info";
private static final String KEY_DAILY_CHART_INDEX = "daily_chart_index";
private static final String KEY_HOURLY_CHART_INDEX = "hourly_chart_index";
private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
- @VisibleForTesting
- Map> mBatteryUsageMap;
+ /**
+ * 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);
+ }
@VisibleForTesting
Context mPrefContext;
- @VisibleForTesting
- BatteryUtils mBatteryUtils;
- @VisibleForTesting
- PreferenceGroup mAppListPrefGroup;
- @VisibleForTesting
- ExpandDividerPreference mExpandDividerPreference;
- @VisibleForTesting
- boolean mIsExpanded = false;
-
@VisibleForTesting
BatteryChartView mDailyChartView;
@VisibleForTesting
BatteryChartView mHourlyChartView;
-
@VisibleForTesting
int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
@VisibleForTesting
int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
+ @VisibleForTesting
+ Map> mBatteryUsageMap;
private boolean mIs24HourFormat;
- private boolean mIsFooterPrefAdded = false;
private boolean mHourlyChartVisible = true;
private View mBatteryChartViewGroup;
- private View mCategoryTitleView;
- private PreferenceScreen mPreferenceScreen;
- private FooterPreference mFooterPreference;
private TextView mChartSummaryTextView;
private BatteryChartViewModel mDailyViewModel;
private List mHourlyViewModels;
+ private OnBatteryUsageUpdatedListener mOnBatteryUsageUpdatedListener;
- private final String mPreferenceKey;
private final SettingsActivity mActivity;
- private final InstrumentedPreferenceFragment mFragment;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final AnimatorListenerAdapter mHourlyChartFadeInAdapter =
@@ -135,18 +122,10 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
final HourlyChartLabelTextGenerator mHourlyChartLabelTextGenerator =
new HourlyChartLabelTextGenerator();
- // Preference cache to avoid create new instance each time.
- @VisibleForTesting
- final Map mPreferenceCache = new HashMap<>();
-
public BatteryChartPreferenceController(
- Context context, String preferenceKey,
- Lifecycle lifecycle, SettingsActivity activity,
- InstrumentedPreferenceFragment fragment) {
+ Context context, Lifecycle lifecycle, SettingsActivity activity) {
super(context);
mActivity = activity;
- mFragment = fragment;
- mPreferenceKey = preferenceKey;
mIs24HourFormat = DateFormat.is24HourFormat(context);
mMetricsFeatureProvider =
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
@@ -164,10 +143,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
mHourlyChartIndex =
savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
- mIsExpanded =
- savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
- Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
- mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
+ Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d",
+ mDailyChartIndex, mHourlyChartIndex));
}
@Override
@@ -191,9 +168,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
}
savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
- savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
- Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
- mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
+ Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d",
+ mDailyChartIndex, mHourlyChartIndex));
}
@Override
@@ -202,25 +178,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
BatteryDiffEntry.clearCache();
}
mHandler.removeCallbacksAndMessages(/*token=*/ null);
- mPreferenceCache.clear();
- if (mAppListPrefGroup != null) {
- mAppListPrefGroup.removeAll();
- }
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
- mPreferenceScreen = screen;
mPrefContext = screen.getContext();
- mAppListPrefGroup = screen.findPreference(mPreferenceKey);
- mAppListPrefGroup.setOrderingAsAdded(false);
- mAppListPrefGroup.setTitle("");
- mFooterPreference = screen.findPreference(KEY_FOOTER_PREF);
- // Removes footer first until usage data is loaded to avoid flashing.
- if (mFooterPreference != null) {
- screen.removePreference(mFooterPreference);
- }
}
@Override
@@ -230,42 +193,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
@Override
public String getPreferenceKey() {
- return mPreferenceKey;
+ return PREFERENCE_KEY;
}
- @Override
- public boolean handlePreferenceTreeClick(Preference preference) {
- if (!(preference instanceof PowerGaugePreference)) {
- return false;
- }
- final PowerGaugePreference powerPref = (PowerGaugePreference) preference;
- final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
- final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
- final String packageName = histEntry.mPackageName;
- final boolean isAppEntry = histEntry.isAppEntry();
- mMetricsFeatureProvider.action(
- /* attribution */ SettingsEnums.OPEN_BATTERY_USAGE,
- /* action */ isAppEntry
- ? SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM
- : SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
- /* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
- TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName,
- (int) Math.round(diffEntry.getPercentOfTotal()));
- Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s",
- diffEntry.getAppLabel(), histEntry.getKey(), histEntry.mPackageName));
- AdvancedPowerUsageDetail.startBatteryDetailPage(
- mActivity, mFragment, diffEntry, powerPref.getPercent(), getSlotInformation());
- return true;
- }
-
- @Override
- public void onExpand(boolean isExpanded) {
- mIsExpanded = isExpanded;
- mMetricsFeatureProvider.action(
- mPrefContext,
- SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM,
- isExpanded);
- refreshExpandUi();
+ void setOnBatteryUsageUpdatedListener(OnBatteryUsageUpdatedListener listener) {
+ mOnBatteryUsageUpdatedListener = listener;
}
void setBatteryHistoryMap(
@@ -339,7 +271,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mDailyChartIndex = trapezoidIndex;
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
refreshUi();
- requestAccessibilityFocusForCategoryTitle(mDailyChartView);
mMetricsFeatureProvider.action(
mPrefContext,
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
@@ -355,7 +286,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex);
mHourlyChartIndex = trapezoidIndex;
refreshUi();
- requestAccessibilityFocusForCategoryTitle(mHourlyChartView);
mMetricsFeatureProvider.action(
mPrefContext,
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
@@ -385,14 +315,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return false;
}
- mHandler.post(() -> {
- final long start = System.currentTimeMillis();
- removeAndCacheAllPrefs();
- addAllPreferences();
- refreshCategoryTitle();
- Log.d(TAG, String.format("refreshUi is finished in %d/ms",
- (System.currentTimeMillis() - start)));
- });
+
+ if (mOnBatteryUsageUpdatedListener != null) {
+ final BatteryDiffData slotUsageData =
+ mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
+ mOnBatteryUsageUpdatedListener.onBatteryUsageUpdated(
+ slotUsageData, getSlotInformation(), isBatteryUsageMapNullOrEmpty());
+ }
return true;
}
@@ -414,9 +343,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mDailyChartView.setVisibility(View.GONE);
mHourlyChartView.setVisibility(View.VISIBLE);
mHourlyChartView.setViewModel(null);
- removeAndCacheAllPrefs();
- addFooterPreferenceIfNeeded(false);
- return false;
}
return true;
}
@@ -452,154 +378,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return true;
}
- private void addAllPreferences() {
- final BatteryDiffData batteryDiffData =
- mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
- addFooterPreferenceIfNeeded(batteryDiffData != null
- && (!batteryDiffData.getAppDiffEntryList().isEmpty()
- || !batteryDiffData.getSystemDiffEntryList().isEmpty()));
- if (batteryDiffData == null) {
- Log.w(TAG, "cannot find BatteryDiffEntry for daily_index: " + mDailyChartIndex
- + " hourly_index: " + mHourlyChartIndex);
- return;
- }
- // Adds app entries to the list if it is not empty.
- if (!batteryDiffData.getAppDiffEntryList().isEmpty()) {
- addPreferenceToScreen(batteryDiffData.getAppDiffEntryList());
- }
- // Adds the expandable divider if we have system entries data.
- if (!batteryDiffData.getSystemDiffEntryList().isEmpty()) {
- if (mExpandDividerPreference == null) {
- mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
- mExpandDividerPreference.setOnExpandListener(this);
- mExpandDividerPreference.setIsExpanded(mIsExpanded);
- }
- mExpandDividerPreference.setOrder(
- mAppListPrefGroup.getPreferenceCount());
- mAppListPrefGroup.addPreference(mExpandDividerPreference);
- }
- refreshExpandUi();
- }
-
- @VisibleForTesting
- void addPreferenceToScreen(List entries) {
- if (mAppListPrefGroup == null || entries.isEmpty()) {
- return;
- }
- int prefIndex = mAppListPrefGroup.getPreferenceCount();
- for (BatteryDiffEntry entry : entries) {
- boolean isAdded = false;
- final String appLabel = entry.getAppLabel();
- final Drawable appIcon = entry.getAppIcon();
- if (TextUtils.isEmpty(appLabel) || appIcon == null) {
- Log.w(TAG, "cannot find app resource for:" + entry.getPackageName());
- continue;
- }
- final String prefKey = entry.mBatteryHistEntry.getKey();
- PowerGaugePreference pref = mAppListPrefGroup.findPreference(prefKey);
- if (pref != null) {
- isAdded = true;
- Log.w(TAG, "preference should be removed for:" + entry.getPackageName());
- } else {
- pref = (PowerGaugePreference) mPreferenceCache.get(prefKey);
- }
- // Creates new innstance if cached preference is not found.
- if (pref == null) {
- pref = new PowerGaugePreference(mPrefContext);
- pref.setKey(prefKey);
- mPreferenceCache.put(prefKey, pref);
- }
- pref.setIcon(appIcon);
- pref.setTitle(appLabel);
- pref.setOrder(prefIndex);
- pref.setPercent(entry.getPercentOfTotal());
- pref.setSingleLineTitle(true);
- // Sets the BatteryDiffEntry to preference for launching detailed page.
- pref.setBatteryDiffEntry(entry);
- pref.setEnabled(entry.validForRestriction());
- setPreferenceSummary(pref, entry);
- if (!isAdded) {
- mAppListPrefGroup.addPreference(pref);
- }
- appIcon.setAlpha(pref.isEnabled() ? ENABLED_ICON_ALPHA : DISABLED_ICON_ALPHA);
- prefIndex++;
- }
- }
-
- private void removeAndCacheAllPrefs() {
- if (mAppListPrefGroup == null
- || mAppListPrefGroup.getPreferenceCount() == 0) {
- return;
- }
- final int prefsCount = mAppListPrefGroup.getPreferenceCount();
- for (int index = 0; index < prefsCount; index++) {
- final Preference pref = mAppListPrefGroup.getPreference(index);
- if (TextUtils.isEmpty(pref.getKey())) {
- continue;
- }
- mPreferenceCache.put(pref.getKey(), pref);
- }
- mAppListPrefGroup.removeAll();
- }
-
- private void refreshExpandUi() {
- final List systemEntries = mBatteryUsageMap.get(mDailyChartIndex).get(
- mHourlyChartIndex).getSystemDiffEntryList();
- if (mIsExpanded) {
- addPreferenceToScreen(systemEntries);
- } else {
- // Removes and recycles all system entries to hide all of them.
- for (BatteryDiffEntry entry : systemEntries) {
- final String prefKey = entry.mBatteryHistEntry.getKey();
- final Preference pref = mAppListPrefGroup.findPreference(prefKey);
- if (pref != null) {
- mAppListPrefGroup.removePreference(pref);
- mPreferenceCache.put(pref.getKey(), pref);
- }
- }
- }
- }
-
- @VisibleForTesting
- void refreshCategoryTitle() {
- final String slotInformation = getSlotInformation();
- Log.d(TAG, String.format("refreshCategoryTitle:%s", slotInformation));
- if (mAppListPrefGroup != null) {
- mAppListPrefGroup.setTitle(
- getSlotInformation(/*isApp=*/ true, slotInformation));
- }
- if (mExpandDividerPreference != null) {
- mExpandDividerPreference.setTitle(
- getSlotInformation(/*isApp=*/ false, slotInformation));
- }
- }
-
- private void requestAccessibilityFocusForCategoryTitle(View view) {
- if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
- return;
- }
- if (mCategoryTitleView == null) {
- mCategoryTitleView = view.getRootView().findViewById(com.android.internal.R.id.title);
- }
- if (mCategoryTitleView != null) {
- mCategoryTitleView.requestAccessibilityFocus();
- }
- }
-
- private String getSlotInformation(boolean isApp, String slotInformation) {
- // TODO: Updates the right slot information from daily and hourly chart selection.
- // Null means we show all information without a specific time slot.
- if (slotInformation == null) {
- return isApp
- ? mPrefContext.getString(R.string.battery_app_usage)
- : mPrefContext.getString(R.string.battery_system_usage);
- } else {
- return isApp
- ? mPrefContext.getString(R.string.battery_app_usage_for, slotInformation)
- : mPrefContext.getString(R.string.battery_system_usage_for, slotInformation);
- }
- }
-
@VisibleForTesting
String getSlotInformation() {
if (mDailyViewModel == null || mHourlyViewModels == null) {
@@ -624,50 +402,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return String.format("%s %s", selectedDayText, selectedHourText);
}
- @VisibleForTesting
- void setPreferenceSummary(
- PowerGaugePreference preference, BatteryDiffEntry entry) {
- final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs;
- final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
- final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
- String usageTimeSummary = null;
- // Not shows summary for some system components without usage time.
- if (totalUsageTimeInMs == 0) {
- preference.setSummary(null);
- // Shows background summary only if we don't have foreground usage time.
- } else if (foregroundUsageTimeInMs == 0 && backgroundUsageTimeInMs != 0) {
- usageTimeSummary = buildUsageTimeInfo(backgroundUsageTimeInMs, true);
- // Shows total usage summary only if total usage time is small.
- } else if (totalUsageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
- usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
- } else {
- usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
- // Shows background usage time if it is larger than a minute.
- if (backgroundUsageTimeInMs > 0) {
- usageTimeSummary +=
- "\n" + buildUsageTimeInfo(backgroundUsageTimeInMs, true);
- }
- }
- preference.setSummary(usageTimeSummary);
- }
-
- private String buildUsageTimeInfo(long usageTimeInMs, boolean isBackground) {
- if (usageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
- return mPrefContext.getString(
- isBackground
- ? R.string.battery_usage_background_less_than_one_minute
- : R.string.battery_usage_total_less_than_one_minute);
- }
- final CharSequence timeSequence =
- StringUtil.formatElapsedTime(mPrefContext, usageTimeInMs,
- /*withSeconds=*/ false, /*collapseTimeUnit=*/ false);
- final int resourceId =
- isBackground
- ? R.string.battery_usage_for_background_time
- : R.string.battery_usage_for_total_time;
- return mPrefContext.getString(resourceId, timeSequence);
- }
-
private void animateBatteryChartViewGroup() {
if (mBatteryChartViewGroup != null && mBatteryChartViewGroup.getAlpha() == 0) {
mBatteryChartViewGroup.animate().alpha(1f).setDuration(FADE_IN_ANIMATION_DURATION)
@@ -725,18 +459,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
};
}
- private void addFooterPreferenceIfNeeded(boolean containAppItems) {
- if (mIsFooterPrefAdded || mFooterPreference == null) {
- return;
- }
- mIsFooterPrefAdded = true;
- mFooterPreference.setTitle(mPrefContext.getString(
- containAppItems
- ? R.string.battery_usage_screen_footer
- : R.string.battery_usage_screen_footer_empty));
- mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference));
- }
-
private boolean isBatteryLevelDataInOneDay() {
return mHourlyViewModels != null && mHourlyViewModels.size() == 1;
}
@@ -747,6 +469,19 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
&& 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/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
new file mode 100644
index 00000000000..1452b54989a
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
@@ -0,0 +1,326 @@
+/*
+ * 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 android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
+import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.utils.StringUtil;
+import com.android.settingslib.widget.FooterPreference;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Controller for battery usage breakdown preference group. */
+public class BatteryUsageBreakdownController extends BasePreferenceController
+ implements LifecycleObserver, OnDestroy {
+ private static final String TAG = "BatteryUsageBreakdownController";
+ private static final String ROOT_PREFERENCE_KEY = "battery_usage_breakdown";
+ private static final String FOOTER_PREFERENCE_KEY = "battery_usage_footer";
+ private static final String TAB_PREFERENCE_KEY = "battery_usage_tab";
+ private static final String APP_LIST_PREFERENCE_KEY = "app_list";
+ private static final String PACKAGE_NAME_NONE = "none";
+ private static final int ENABLED_ICON_ALPHA = 255;
+ private static final int DISABLED_ICON_ALPHA = 255 / 3;
+
+ private final SettingsActivity mActivity;
+ private final InstrumentedPreferenceFragment mFragment;
+ private final MetricsFeatureProvider mMetricsFeatureProvider;
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+ @VisibleForTesting
+ final Map mPreferenceCache = new HashMap<>();
+
+ private int mTabPosition;
+ private String mSlotTimestamp;
+
+ @VisibleForTesting
+ Context mPrefContext;
+ @VisibleForTesting
+ PreferenceCategory mRootPreference;
+ @VisibleForTesting
+ TabPreference mTabPreference;
+ @VisibleForTesting
+ PreferenceGroup mAppListPreferenceGroup;
+ @VisibleForTesting
+ FooterPreference mFooterPreference;
+ @VisibleForTesting
+ BatteryDiffData mBatteryDiffData;
+
+ public BatteryUsageBreakdownController(
+ Context context, Lifecycle lifecycle, SettingsActivity activity,
+ InstrumentedPreferenceFragment fragment) {
+ super(context, ROOT_PREFERENCE_KEY);
+ mActivity = activity;
+ mFragment = fragment;
+ mMetricsFeatureProvider =
+ FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ mHandler.removeCallbacksAndMessages(/*token=*/ null);
+ mPreferenceCache.clear();
+ mAppListPreferenceGroup.removeAll();
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return AVAILABLE;
+ }
+
+ @Override
+ public boolean isSliceable() {
+ return false;
+ }
+
+ @Override
+ public boolean handlePreferenceTreeClick(Preference preference) {
+ if (!(preference instanceof PowerGaugePreference)) {
+ return false;
+ }
+ final PowerGaugePreference powerPref = (PowerGaugePreference) preference;
+ final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
+ final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
+ final String packageName = histEntry.mPackageName;
+ final boolean isAppEntry = histEntry.isAppEntry();
+ mMetricsFeatureProvider.action(
+ /* attribution */ SettingsEnums.OPEN_BATTERY_USAGE,
+ /* action */ isAppEntry
+ ? SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM
+ : SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
+ /* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
+ TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName,
+ (int) Math.round(diffEntry.getPercentOfTotal()));
+ Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s",
+ diffEntry.getAppLabel(), histEntry.getKey(), histEntry.mPackageName));
+ AdvancedPowerUsageDetail.startBatteryDetailPage(
+ mActivity, mFragment, diffEntry, powerPref.getPercent(), mSlotTimestamp);
+ return true;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPrefContext = screen.getContext();
+ mRootPreference = screen.findPreference(ROOT_PREFERENCE_KEY);
+ mTabPreference = screen.findPreference(TAB_PREFERENCE_KEY);
+ mAppListPreferenceGroup = screen.findPreference(APP_LIST_PREFERENCE_KEY);
+ mFooterPreference = screen.findPreference(FOOTER_PREFERENCE_KEY);
+
+ mAppListPreferenceGroup.setOrderingAsAdded(false);
+ mTabPreference.initializeTabs(mFragment, new String[]{
+ mPrefContext.getString(R.string.battery_usage_app_tab),
+ mPrefContext.getString(R.string.battery_usage_system_tab)
+ });
+ mTabPreference.setOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
+ @Override
+ public void onPageSelected(int position) {
+ super.onPageSelected(position);
+ mTabPosition = position;
+ mHandler.post(() -> {
+ removeAndCacheAllPreferences();
+ addAllPreferences();
+ });
+ }
+ });
+ }
+
+ /**
+ * Updates UI when the 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 handleBatteryUsageUpdated(
+ BatteryDiffData slotUsageData, String slotTimestamp, boolean isAllUsageDataEmpty) {
+ mBatteryDiffData = slotUsageData;
+ mSlotTimestamp = slotTimestamp;
+
+ showCategoryTitle(slotTimestamp);
+ showTabAndAppList();
+ showFooterPreference(isAllUsageDataEmpty);
+ }
+
+ // TODO: request accessibility focus on category title when slot selection updated.
+ private void showCategoryTitle(String slotTimestamp) {
+ mRootPreference.setTitle(slotTimestamp == null
+ ? mPrefContext.getString(
+ R.string.battery_usage_breakdown_title_since_last_full_charge)
+ : mPrefContext.getString(
+ R.string.battery_usage_breakdown_title_for_slot, slotTimestamp));
+ mRootPreference.setVisible(true);
+ }
+
+ private void showFooterPreference(boolean isAllBatteryUsageEmpty) {
+ mFooterPreference.setTitle(mPrefContext.getString(
+ isAllBatteryUsageEmpty
+ ? R.string.battery_usage_screen_footer_empty
+ : R.string.battery_usage_screen_footer));
+ mFooterPreference.setVisible(true);
+ }
+
+ private void showTabAndAppList() {
+ removeAndCacheAllPreferences();
+ if (mBatteryDiffData == null) {
+ return;
+ }
+ mTabPreference.setVisible(true);
+ mAppListPreferenceGroup.setVisible(true);
+ mHandler.post(() -> {
+ addAllPreferences();
+ });
+ }
+
+ @VisibleForTesting
+ void addAllPreferences() {
+ if (mBatteryDiffData == null) {
+ return;
+ }
+ final long start = System.currentTimeMillis();
+ final List entries = mTabPosition == 0
+ ? mBatteryDiffData.getAppDiffEntryList()
+ : mBatteryDiffData.getSystemDiffEntryList();
+ int prefIndex = mAppListPreferenceGroup.getPreferenceCount();
+ for (BatteryDiffEntry entry : entries) {
+ boolean isAdded = false;
+ final String appLabel = entry.getAppLabel();
+ final Drawable appIcon = entry.getAppIcon();
+ if (TextUtils.isEmpty(appLabel) || appIcon == null) {
+ Log.w(TAG, "cannot find app resource for:" + entry.getPackageName());
+ continue;
+ }
+ final String prefKey = entry.mBatteryHistEntry.getKey();
+ PowerGaugePreference pref = mAppListPreferenceGroup.findPreference(prefKey);
+ if (pref != null) {
+ isAdded = true;
+ Log.w(TAG, "preference should be removed for:" + entry.getPackageName());
+ } else {
+ pref = (PowerGaugePreference) mPreferenceCache.get(prefKey);
+ }
+ // Creates new innstance if cached preference is not found.
+ if (pref == null) {
+ pref = new PowerGaugePreference(mPrefContext);
+ pref.setKey(prefKey);
+ mPreferenceCache.put(prefKey, pref);
+ }
+ pref.setIcon(appIcon);
+ pref.setTitle(appLabel);
+ pref.setOrder(prefIndex);
+ pref.setPercent(entry.getPercentOfTotal());
+ pref.setSingleLineTitle(true);
+ // Sets the BatteryDiffEntry to preference for launching detailed page.
+ pref.setBatteryDiffEntry(entry);
+ pref.setEnabled(entry.validForRestriction());
+ setPreferenceSummary(pref, entry);
+ if (!isAdded) {
+ mAppListPreferenceGroup.addPreference(pref);
+ }
+ appIcon.setAlpha(pref.isEnabled() ? ENABLED_ICON_ALPHA : DISABLED_ICON_ALPHA);
+ prefIndex++;
+ }
+ Log.d(TAG, String.format("addAllPreferences() is finished in %d/ms",
+ (System.currentTimeMillis() - start)));
+ }
+
+ @VisibleForTesting
+ void removeAndCacheAllPreferences() {
+ final int prefsCount = mAppListPreferenceGroup.getPreferenceCount();
+ for (int index = 0; index < prefsCount; index++) {
+ final Preference pref = mAppListPreferenceGroup.getPreference(index);
+ if (TextUtils.isEmpty(pref.getKey())) {
+ continue;
+ }
+ mPreferenceCache.put(pref.getKey(), pref);
+ }
+ mAppListPreferenceGroup.removeAll();
+ }
+
+ @VisibleForTesting
+ void setPreferenceSummary(
+ PowerGaugePreference preference, BatteryDiffEntry entry) {
+ final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs;
+ final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
+ final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
+ String usageTimeSummary = null;
+ // Not shows summary for some system components without usage time.
+ if (totalUsageTimeInMs == 0) {
+ preference.setSummary(null);
+ // Shows background summary only if we don't have foreground usage time.
+ } else if (foregroundUsageTimeInMs == 0 && backgroundUsageTimeInMs != 0) {
+ usageTimeSummary = buildUsageTimeInfo(backgroundUsageTimeInMs, true);
+ // Shows total usage summary only if total usage time is small.
+ } else if (totalUsageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
+ usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
+ } else {
+ usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
+ // Shows background usage time if it is larger than a minute.
+ if (backgroundUsageTimeInMs > 0) {
+ usageTimeSummary +=
+ "\n" + buildUsageTimeInfo(backgroundUsageTimeInMs, true);
+ }
+ }
+ preference.setSummary(usageTimeSummary);
+ }
+
+ private String buildUsageTimeInfo(long usageTimeInMs, boolean isBackground) {
+ if (usageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
+ return mPrefContext.getString(
+ isBackground
+ ? R.string.battery_usage_background_less_than_one_minute
+ : R.string.battery_usage_total_less_than_one_minute);
+ }
+ final CharSequence timeSequence =
+ StringUtil.formatElapsedTime(mPrefContext, (double) usageTimeInMs,
+ /*withSeconds=*/ false, /*collapseTimeUnit=*/ false);
+ final int resourceId =
+ isBackground
+ ? R.string.battery_usage_for_background_time
+ : R.string.battery_usage_for_total_time;
+ return mPrefContext.getString(resourceId, timeSequence);
+ }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ExpandDividerPreference.java b/src/com/android/settings/fuelgauge/batteryusage/ExpandDividerPreference.java
deleted file mode 100644
index 8af842bc9c2..00000000000
--- a/src/com/android/settings/fuelgauge/batteryusage/ExpandDividerPreference.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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 android.content.Context;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceViewHolder;
-
-import com.android.settings.R;
-
-/** A preference for expandable section divider. */
-public class ExpandDividerPreference extends Preference {
- private static final String TAG = "ExpandDividerPreference";
- @VisibleForTesting
- static final String PREFERENCE_KEY = "expandable_divider";
-
- @VisibleForTesting
- TextView mTextView;
- @VisibleForTesting
- ImageView mImageView;
- private OnExpandListener mOnExpandListener;
-
- private boolean mIsExpanded = false;
- private String mTitleContent = null;
-
- /** A callback listener for expand state is changed by users. */
- public interface OnExpandListener {
- /** Callback function for expand state is changed by users. */
- void onExpand(boolean isExpanded);
- }
-
- public ExpandDividerPreference(Context context) {
- this(context, /*attrs=*/ null);
- }
-
- public ExpandDividerPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- setLayoutResource(R.layout.preference_expand_divider);
- setKey(PREFERENCE_KEY);
- }
-
- @Override
- public void onBindViewHolder(PreferenceViewHolder view) {
- super.onBindViewHolder(view);
- mTextView = (TextView) view.findViewById(R.id.expand_title);
- mImageView = (ImageView) view.findViewById(R.id.expand_icon);
- refreshState();
- }
-
- @Override
- public void onClick() {
- setIsExpanded(!mIsExpanded);
- if (mOnExpandListener != null) {
- mOnExpandListener.onExpand(mIsExpanded);
- }
- }
-
- void setTitle(final String titleContent) {
- mTitleContent = titleContent;
- refreshState();
- }
-
- void setIsExpanded(boolean isExpanded) {
- mIsExpanded = isExpanded;
- refreshState();
- }
-
- void setOnExpandListener(OnExpandListener listener) {
- mOnExpandListener = listener;
- }
-
- private void refreshState() {
- if (mImageView != null) {
- mImageView.setImageResource(mIsExpanded
- ? R.drawable.ic_settings_expand_less
- : R.drawable.ic_settings_expand_more);
- }
- if (mTextView != null) {
- mTextView.setText(mTitleContent);
- }
- }
-}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
index 2dba3c2ccf2..805821f9102 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
@@ -50,8 +50,7 @@ import java.util.Map;
public class PowerUsageAdvanced extends PowerUsageBase {
private static final String TAG = "AdvancedBatteryUsage";
private static final String KEY_REFRESH_TYPE = "refresh_type";
- private static final String KEY_BATTERY_GRAPH = "battery_graph";
- private static final String KEY_APP_LIST = "app_list";
+ private static final String KEY_BATTERY_CHART = "battery_chart";
@VisibleForTesting
BatteryHistoryPreference mHistPref;
@@ -81,7 +80,7 @@ public class PowerUsageAdvanced extends PowerUsageBase {
super.onCreate(icicle);
final Context context = getContext();
refreshFeatureFlag(context);
- mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
+ mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_CHART);
setBatteryChartPreferenceController();
}
@@ -134,9 +133,17 @@ public class PowerUsageAdvanced extends PowerUsageBase {
refreshFeatureFlag(context);
final List controllers = new ArrayList<>();
mBatteryChartPreferenceController =
- new BatteryChartPreferenceController(context, KEY_APP_LIST,
- getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
+ new BatteryChartPreferenceController(
+ context, getSettingsLifecycle(), (SettingsActivity) getActivity());
+ BatteryUsageBreakdownController batteryUsageBreakdownController =
+ new BatteryUsageBreakdownController(
+ context, getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
+
+ mBatteryChartPreferenceController.setOnBatteryUsageUpdatedListener(
+ batteryUsageBreakdownController::handleBatteryUsageUpdated);
+
controllers.add(mBatteryChartPreferenceController);
+ controllers.add(batteryUsageBreakdownController);
setBatteryChartPreferenceController();
return controllers;
}
@@ -196,8 +203,10 @@ public class PowerUsageAdvanced extends PowerUsageBase {
public List createPreferenceControllers(
Context context) {
final List controllers = new ArrayList<>();
- controllers.add(new BatteryChartPreferenceController(context,
- KEY_APP_LIST, null /* lifecycle */, null /* activity */,
+ controllers.add(new BatteryChartPreferenceController(
+ context, null /* lifecycle */, null /* activity */));
+ controllers.add(new BatteryUsageBreakdownController(
+ context, null /* lifecycle */, null /* activity */,
null /* fragment */));
return controllers;
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/TabPreference.java b/src/com/android/settings/fuelgauge/batteryusage/TabPreference.java
new file mode 100644
index 00000000000..517e01cf565
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/TabPreference.java
@@ -0,0 +1,152 @@
+/*
+ * 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 android.content.Context;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+
+import com.google.android.material.tabs.TabLayout;
+import com.google.android.material.tabs.TabLayoutMediator;
+
+/** A preference which contains a tab selection. */
+public class TabPreference extends Preference {
+ private static final String TAG = "TabPreference";
+
+ private Fragment mRootFragment;
+ private ViewPager2 mViewPager;
+ private ViewPager2.OnPageChangeCallback mOnPageChangeCallback;
+
+ @VisibleForTesting
+ String[] mTabTitles;
+ @VisibleForTesting
+ int mSavedTabPosition;
+ @VisibleForTesting
+ TabLayout mTabLayout;
+
+ public TabPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setLayoutResource(R.layout.preference_tab);
+ }
+
+ void initializeTabs(Fragment rootFragment, String[] tabTitles) {
+ mRootFragment = rootFragment;
+ mTabTitles = tabTitles;
+ }
+
+ void setOnPageChangeCallback(ViewPager2.OnPageChangeCallback callback) {
+ mOnPageChangeCallback = callback;
+ }
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder view) {
+ super.onBindViewHolder(view);
+ if (mViewPager != null && mTabLayout != null) {
+ return;
+ }
+
+ mViewPager = (ViewPager2) view.findViewById(R.id.view_pager);
+ mViewPager.setAdapter(new FragmentAdapter(mRootFragment, mTabTitles.length));
+ mViewPager.setUserInputEnabled(false);
+ if (mOnPageChangeCallback != null) {
+ mViewPager.registerOnPageChangeCallback(mOnPageChangeCallback);
+ }
+
+ mTabLayout = (TabLayout) view.findViewById(R.id.tabs);
+ new TabLayoutMediator(
+ mTabLayout, mViewPager, /* autoRefresh= */ true, /* smoothScroll= */ false,
+ (tab, position) -> tab.setText(mTabTitles[position])).attach();
+ mTabLayout.getTabAt(mSavedTabPosition).select();
+ }
+
+ @Override
+ public void onDetached() {
+ super.onDetached();
+ if (mOnPageChangeCallback != null) {
+ mViewPager.unregisterOnPageChangeCallback(mOnPageChangeCallback);
+ }
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Log.d(TAG, "onSaveInstanceState() tabPosition=" + mTabLayout.getSelectedTabPosition());
+ return new SavedState(super.onSaveInstanceState(), mTabLayout.getSelectedTabPosition());
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ if (state == null || !state.getClass().equals(SavedState.class)) {
+ super.onRestoreInstanceState(state);
+ return;
+ }
+ SavedState savedState = (SavedState) state;
+ super.onRestoreInstanceState(savedState.getSuperState());
+ mSavedTabPosition = savedState.getTabPosition();
+ Log.d(TAG, "onRestoreInstanceState() tabPosition=" + savedState.getTabPosition());
+ }
+
+ @VisibleForTesting
+ static class SavedState extends BaseSavedState {
+ private int mTabPosition;
+
+ SavedState(Parcelable superState, int tabPosition) {
+ super(superState);
+ mTabPosition = tabPosition;
+ }
+
+ int getTabPosition() {
+ return mTabPosition;
+ }
+ }
+
+ private static class FragmentAdapter extends FragmentStateAdapter {
+ private final int mItemCount;
+ private final Fragment[] mItemFragments;
+
+ FragmentAdapter(@NonNull Fragment rootFragment, int itemCount) {
+ super(rootFragment);
+ mItemCount = itemCount;
+ mItemFragments = new Fragment[mItemCount];
+ for (int i = 0; i < mItemCount; i++) {
+ // Empty tab pages.
+ mItemFragments[i] = new Fragment();
+ }
+ }
+
+ @NonNull
+ @Override
+ public Fragment createFragment(int position) {
+ return mItemFragments[position];
+ }
+
+ @Override
+ public int getItemCount() {
+ return mItemCount;
+ }
+ }
+}
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 b7984421cf2..42eb1c080ba 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceControllerTest.java
@@ -24,16 +24,13 @@ import static org.mockito.Mockito.any;
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;
-import android.app.settings.SettingsEnums;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.LocaleList;
import android.text.format.DateUtils;
@@ -41,27 +38,17 @@ import android.view.View;
import android.view.ViewPropertyAnimator;
import android.widget.LinearLayout;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceGroup;
-
import com.android.settings.SettingsActivity;
-import com.android.settings.core.InstrumentedPreferenceFragment;
-import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -70,18 +57,9 @@ import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class)
public final class BatteryChartPreferenceControllerTest {
- private static final String PREF_KEY = "pref_key";
- private static final String PREF_SUMMARY = "fake preference summary";
-
- @Mock
- private InstrumentedPreferenceFragment mFragment;
@Mock
private SettingsActivity mSettingsActivity;
@Mock
- private PreferenceGroup mAppListGroup;
- @Mock
- private Drawable mDrawable;
- @Mock
private BatteryHistEntry mBatteryHistEntry;
@Mock
private BatteryChartView mDailyChartView;
@@ -90,16 +68,11 @@ public final class BatteryChartPreferenceControllerTest {
@Mock
private ViewPropertyAnimator mViewPropertyAnimator;
@Mock
- private PowerGaugePreference mPowerGaugePreference;
- @Mock
- private BatteryUtils mBatteryUtils;
- @Mock
private LinearLayout.LayoutParams mLayoutParams;
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
private BatteryDiffEntry mBatteryDiffEntry;
- private MetricsFeatureProvider mMetricsFeatureProvider;
private BatteryChartPreferenceController mBatteryChartPreferenceController;
@Before
@@ -109,7 +82,6 @@ public final class BatteryChartPreferenceControllerTest {
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
mFeatureFactory = FakeFeatureFactory.setupForTest();
- mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
mContext = spy(RuntimeEnvironment.application);
final Resources resources = spy(mContext.getResources());
resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US")));
@@ -121,7 +93,6 @@ public final class BatteryChartPreferenceControllerTest {
setupHourlyChartViewAnimationMock();
mBatteryChartPreferenceController = createController();
mBatteryChartPreferenceController.mPrefContext = mContext;
- mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup;
mBatteryChartPreferenceController.mDailyChartView = mDailyChartView;
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
mBatteryDiffEntry = new BatteryDiffEntry(
@@ -161,24 +132,6 @@ public final class BatteryChartPreferenceControllerTest {
assertThat(BatteryDiffEntry.sResourceCache).isNotEmpty();
}
- @Test
- public void onDestroy_clearPreferenceCache() {
- // Ensures the testing environment is correct.
- mBatteryChartPreferenceController.mPreferenceCache.put(
- PREF_KEY, mPowerGaugePreference);
- assertThat(mBatteryChartPreferenceController.mPreferenceCache).hasSize(1);
-
- mBatteryChartPreferenceController.onDestroy();
- // Verifies the result after onDestroy.
- assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
- }
-
- @Test
- public void onDestroy_removeAllPreferenceFromPreferenceGroup() {
- mBatteryChartPreferenceController.onDestroy();
- verify(mAppListGroup).removeAll();
- }
-
@Test
public void setBatteryChartViewModel_6Hours() {
reset(mDailyChartView);
@@ -313,9 +266,9 @@ public final class BatteryChartPreferenceControllerTest {
}
@Test
- public void refreshUi_batteryIndexedMapIsNull_ignoreRefresh() {
+ public void refreshUi_batteryIndexedMapIsNull_returnTrue() {
mBatteryChartPreferenceController.setBatteryHistoryMap(null);
- assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
+ assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
}
@Test
@@ -330,261 +283,6 @@ public final class BatteryChartPreferenceControllerTest {
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
}
- @Test
- public void removeAndCacheAllPrefs_emptyContent_ignoreRemoveAll() {
- mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
- mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
- doReturn(0).when(mAppListGroup).getPreferenceCount();
-
- mBatteryChartPreferenceController.refreshUi();
- verify(mAppListGroup, never()).removeAll();
- }
-
- @Test
- public void removeAndCacheAllPrefs_buildCacheAndRemoveAllPreference() {
- mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
- mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
- doReturn(1).when(mAppListGroup).getPreferenceCount();
- doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0);
- doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
- doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
- doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
- // Ensures the testing data is correct.
- assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
-
- mBatteryChartPreferenceController.refreshUi();
-
- assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY))
- .isEqualTo(mPowerGaugePreference);
- verify(mAppListGroup).removeAll();
- }
-
- @Test
- public void addPreferenceToScreen_emptyContent_ignoreAddPreference() {
- mBatteryChartPreferenceController.addPreferenceToScreen(
- new ArrayList());
- verify(mAppListGroup, never()).addPreference(any());
- }
-
- @Test
- public void addPreferenceToScreen_addPreferenceIntoScreen() {
- final String appLabel = "fake app label";
- doReturn(1).when(mAppListGroup).getPreferenceCount();
- doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
- doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
- doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
- doReturn(null).when(mAppListGroup).findPreference(PREF_KEY);
- doReturn(false).when(mBatteryDiffEntry).validForRestriction();
-
- mBatteryChartPreferenceController.addPreferenceToScreen(
- Arrays.asList(mBatteryDiffEntry));
-
- // Verifies the preference cache.
- final PowerGaugePreference pref =
- (PowerGaugePreference) mBatteryChartPreferenceController.mPreferenceCache
- .get(PREF_KEY);
- assertThat(pref).isNotNull();
- // Verifies the added preference configuration.
- verify(mAppListGroup).addPreference(pref);
- assertThat(pref.getKey()).isEqualTo(PREF_KEY);
- assertThat(pref.getTitle()).isEqualTo(appLabel);
- assertThat(pref.getIcon()).isEqualTo(mDrawable);
- assertThat(pref.getOrder()).isEqualTo(1);
- assertThat(pref.getBatteryDiffEntry()).isSameInstanceAs(mBatteryDiffEntry);
- assertThat(pref.isSingleLineTitle()).isTrue();
- assertThat(pref.isEnabled()).isFalse();
- }
-
- @Test
- public void addPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
- final String appLabel = "fake app label";
- doReturn(1).when(mAppListGroup).getPreferenceCount();
- doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
- doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
- doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
- doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
-
- mBatteryChartPreferenceController.addPreferenceToScreen(
- Arrays.asList(mBatteryDiffEntry));
-
- verify(mAppListGroup, never()).addPreference(any());
- }
-
- @Test
- public void handlePreferenceTreeClick_notPowerGaugePreference_returnFalse() {
- assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(mAppListGroup))
- .isFalse();
-
- verify(mMetricsFeatureProvider, never())
- .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM);
- verify(mMetricsFeatureProvider, never())
- .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM);
- }
-
- @Test
- public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
- doReturn(false).when(mBatteryHistEntry).isAppEntry();
- doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
-
- assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(
- mPowerGaugePreference)).isTrue();
- verify(mMetricsFeatureProvider)
- .action(
- SettingsEnums.OPEN_BATTERY_USAGE,
- SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
- SettingsEnums.OPEN_BATTERY_USAGE,
- /* package name */ "none",
- /* percentage of total */ 0);
- }
-
- @Test
- public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
- mBatteryChartPreferenceController.mBatteryUtils = mBatteryUtils;
- doReturn(true).when(mBatteryHistEntry).isAppEntry();
- doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
-
- assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(
- mPowerGaugePreference)).isTrue();
- verify(mMetricsFeatureProvider)
- .action(
- SettingsEnums.OPEN_BATTERY_USAGE,
- SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
- SettingsEnums.OPEN_BATTERY_USAGE,
- /* package name */ "none",
- /* percentage of total */ 0);
- }
-
- @Test
- public void setPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
- final PowerGaugePreference pref = new PowerGaugePreference(mContext);
- pref.setSummary(PREF_SUMMARY);
-
- mBatteryChartPreferenceController.setPreferenceSummary(
- pref, createBatteryDiffEntry(
- /*foregroundUsageTimeInMs=*/ 0,
- /*backgroundUsageTimeInMs=*/ 0));
- assertThat(pref.getSummary()).isNull();
- }
-
- @Test
- public void setPreferenceSummary_setBackgroundUsageTimeOnly() {
- final PowerGaugePreference pref = new PowerGaugePreference(mContext);
- pref.setSummary(PREF_SUMMARY);
-
- mBatteryChartPreferenceController.setPreferenceSummary(
- pref, createBatteryDiffEntry(
- /*foregroundUsageTimeInMs=*/ 0,
- /*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
- assertThat(pref.getSummary()).isEqualTo("Background: 1 min");
- }
-
- @Test
- public void setPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
- final PowerGaugePreference pref = new PowerGaugePreference(mContext);
- pref.setSummary(PREF_SUMMARY);
-
- mBatteryChartPreferenceController.setPreferenceSummary(
- pref, createBatteryDiffEntry(
- /*foregroundUsageTimeInMs=*/ 100,
- /*backgroundUsageTimeInMs=*/ 200));
- assertThat(pref.getSummary()).isEqualTo("Total: less than a min");
- }
-
- @Test
- public void setPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
- final PowerGaugePreference pref = new PowerGaugePreference(mContext);
- pref.setSummary(PREF_SUMMARY);
-
- mBatteryChartPreferenceController.setPreferenceSummary(
- pref, createBatteryDiffEntry(
- /*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
- /*backgroundUsageTimeInMs=*/ 200));
- assertThat(pref.getSummary())
- .isEqualTo("Total: 1 min\nBackground: less than a min");
- }
-
- @Test
- public void setPreferenceSummary_setTotalAndBackgroundUsageTime() {
- final PowerGaugePreference pref = new PowerGaugePreference(mContext);
- pref.setSummary(PREF_SUMMARY);
-
- mBatteryChartPreferenceController.setPreferenceSummary(
- pref, createBatteryDiffEntry(
- /*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
- /*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
- assertThat(pref.getSummary()).isEqualTo("Total: 2 min\nBackground: 1 min");
- }
-
- @Test
- public void onExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() {
- doReturn(1).when(mAppListGroup).getPreferenceCount();
- mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
- doReturn("label").when(mBatteryDiffEntry).getAppLabel();
- doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
- doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
-
- mBatteryChartPreferenceController.onExpand(/*isExpanded=*/ true);
-
- final ArgumentCaptor captor = ArgumentCaptor.forClass(Preference.class);
- verify(mAppListGroup).addPreference(captor.capture());
- // Verifies the added preference.
- assertThat(captor.getValue().getKey()).isEqualTo(PREF_KEY);
- verify(mMetricsFeatureProvider)
- .action(
- mContext,
- SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM,
- true /*isExpanded*/);
- }
-
- @Test
- public void onExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() {
- doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
- doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
- mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
- // Verifies the cache is empty first.
- assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
-
- mBatteryChartPreferenceController.onExpand(/*isExpanded=*/ false);
-
- verify(mAppListGroup).findPreference(PREF_KEY);
- verify(mAppListGroup).removePreference(mPowerGaugePreference);
- assertThat(mBatteryChartPreferenceController.mPreferenceCache).hasSize(1);
- verify(mMetricsFeatureProvider)
- .action(
- mContext,
- SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM,
- false /*isExpanded*/);
- }
-
- @Test
- public void refreshCategoryTitle_setLastFullChargeIntoBothTitleTextView() {
- mBatteryChartPreferenceController = createController();
- mBatteryChartPreferenceController.mAppListPrefGroup =
- spy(new PreferenceCategory(mContext));
- mBatteryChartPreferenceController.mExpandDividerPreference =
- spy(new ExpandDividerPreference(mContext));
- // Simulates select all condition.
- mBatteryChartPreferenceController.mDailyChartIndex =
- BatteryChartViewModel.SELECTED_INDEX_ALL;
- mBatteryChartPreferenceController.mHourlyChartIndex =
- BatteryChartViewModel.SELECTED_INDEX_ALL;
-
- mBatteryChartPreferenceController.refreshCategoryTitle();
-
- ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
- // Verifies the title in the preference group.
- verify(mBatteryChartPreferenceController.mAppListPrefGroup)
- .setTitle(captor.capture());
- assertThat(captor.getValue())
- .isEqualTo("App usage since last full charge");
- // Verifies the title in the expandable divider.
- captor = ArgumentCaptor.forClass(String.class);
- verify(mBatteryChartPreferenceController.mExpandDividerPreference)
- .setTitle(captor.capture());
- assertThat(captor.getValue())
- .isEqualTo("System usage since last full charge");
- }
-
@Test
public void selectedSlotText_selectAllDaysAllHours_returnNull() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
@@ -640,16 +338,13 @@ public final class BatteryChartPreferenceControllerTest {
public void onSaveInstanceState_restoreSelectedIndexAndExpandState() {
final int expectedDailyIndex = 1;
final int expectedHourlyIndex = 2;
- final boolean isExpanded = true;
final Bundle bundle = new Bundle();
mBatteryChartPreferenceController.mDailyChartIndex = expectedDailyIndex;
mBatteryChartPreferenceController.mHourlyChartIndex = expectedHourlyIndex;
- mBatteryChartPreferenceController.mIsExpanded = isExpanded;
mBatteryChartPreferenceController.onSaveInstanceState(bundle);
// Replaces the original controller with other values.
mBatteryChartPreferenceController.mDailyChartIndex = -1;
mBatteryChartPreferenceController.mHourlyChartIndex = -1;
- mBatteryChartPreferenceController.mIsExpanded = false;
mBatteryChartPreferenceController.onCreate(bundle);
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25));
@@ -658,7 +353,6 @@ public final class BatteryChartPreferenceControllerTest {
.isEqualTo(expectedDailyIndex);
assertThat(mBatteryChartPreferenceController.mHourlyChartIndex)
.isEqualTo(expectedHourlyIndex);
- assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue();
}
@Test
@@ -704,36 +398,10 @@ public final class BatteryChartPreferenceControllerTest {
return batteryHistoryMap;
}
- private Map> createBatteryUsageMap() {
- final int selectedAll = BatteryChartViewModel.SELECTED_INDEX_ALL;
- return Map.of(
- selectedAll, Map.of(
- selectedAll, new BatteryDiffData(
- Arrays.asList(mBatteryDiffEntry),
- Arrays.asList(mBatteryDiffEntry))),
- 0, Map.of(
- selectedAll, new BatteryDiffData(
- Arrays.asList(mBatteryDiffEntry),
- Arrays.asList(mBatteryDiffEntry)),
- 0, new BatteryDiffData(
- Arrays.asList(mBatteryDiffEntry),
- Arrays.asList(mBatteryDiffEntry))));
- }
-
- private BatteryDiffEntry createBatteryDiffEntry(
- long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
- return new BatteryDiffEntry(
- mContext, foregroundUsageTimeInMs, backgroundUsageTimeInMs, /*consumePower=*/ 0,
- /*foregroundUsageConsumePower=*/ 0, /*foregroundServiceUsageConsumePower=*/ 0,
- /*backgroundUsageConsumePower=*/ 0, /*cachedUsageConsumePower=*/ 0,
- mBatteryHistEntry);
- }
-
private BatteryChartPreferenceController createController() {
final BatteryChartPreferenceController controller =
new BatteryChartPreferenceController(
- mContext, "app_list", /*lifecycle=*/ null,
- mSettingsActivity, mFragment);
+ mContext, /*lifecycle=*/ null, mSettingsActivity);
controller.mPrefContext = mContext;
return controller;
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
new file mode 100644
index 00000000000..d6b5a3eaafd
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
@@ -0,0 +1,310 @@
+/*
+ * 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.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.LocaleList;
+import android.text.format.DateUtils;
+
+import androidx.preference.PreferenceGroup;
+
+import com.android.settings.SettingsActivity;
+import com.android.settings.core.InstrumentedPreferenceFragment;
+import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.TimeZone;
+
+@RunWith(RobolectricTestRunner.class)
+public final class BatteryUsageBreakdownControllerTest {
+ private static final String PREF_KEY = "pref_key";
+ private static final String PREF_SUMMARY = "fake preference summary";
+
+ @Mock
+ private InstrumentedPreferenceFragment mFragment;
+ @Mock
+ private SettingsActivity mSettingsActivity;
+ @Mock
+ private PreferenceGroup mAppListPreferenceGroup;
+ @Mock
+ private Drawable mDrawable;
+ @Mock
+ private BatteryHistEntry mBatteryHistEntry;
+ @Mock
+ private PowerGaugePreference mPowerGaugePreference;
+
+ private Context mContext;
+ private FakeFeatureFactory mFeatureFactory;
+ private BatteryDiffEntry mBatteryDiffEntry;
+ private MetricsFeatureProvider mMetricsFeatureProvider;
+ private BatteryUsageBreakdownController mBatteryUsageBreakdownController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ Locale.setDefault(new Locale("en_US"));
+ org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
+ TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+ mFeatureFactory = FakeFeatureFactory.setupForTest();
+ mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
+ mContext = spy(RuntimeEnvironment.application);
+ final Resources resources = spy(mContext.getResources());
+ resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US")));
+ doReturn(resources).when(mContext).getResources();
+ doReturn(new String[]{"com.android.gms.persistent"})
+ .when(mFeatureFactory.powerUsageFeatureProvider)
+ .getHideApplicationEntries(mContext);
+ mBatteryUsageBreakdownController = createController();
+ mBatteryUsageBreakdownController.mAppListPreferenceGroup = mAppListPreferenceGroup;
+ mBatteryDiffEntry = new BatteryDiffEntry(
+ mContext,
+ /*foregroundUsageTimeInMs=*/ 1,
+ /*backgroundUsageTimeInMs=*/ 2,
+ /*consumePower=*/ 3,
+ /*foregroundUsageConsumePower=*/ 0,
+ /*foregroundServiceUsageConsumePower=*/ 1,
+ /*backgroundUsageConsumePower=*/ 2,
+ /*cachedUsageConsumePower=*/ 0,
+ mBatteryHistEntry);
+ mBatteryDiffEntry = spy(mBatteryDiffEntry);
+ mBatteryUsageBreakdownController.mBatteryDiffData =
+ new BatteryDiffData(Arrays.asList(mBatteryDiffEntry), Arrays.asList());
+ // Adds fake testing data.
+ BatteryDiffEntry.sResourceCache.put(
+ "fakeBatteryDiffEntryKey",
+ new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
+ }
+
+ @Test
+ public void onDestroy_clearPreferenceCacheAndPreferenceGroupRemoveAll() {
+ // Ensures the testing environment is correct.
+ mBatteryUsageBreakdownController.mPreferenceCache.put(
+ PREF_KEY, mPowerGaugePreference);
+ assertThat(mBatteryUsageBreakdownController.mPreferenceCache).hasSize(1);
+
+ mBatteryUsageBreakdownController.onDestroy();
+
+ assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
+ }
+
+ @Test
+ public void onDestroy_removeAllPreferenceFromPreferenceGroup() {
+ mBatteryUsageBreakdownController.onDestroy();
+ verify(mAppListPreferenceGroup).removeAll();
+ }
+
+ @Test
+ public void addAllPreferences_addAllPreferences() {
+ final String appLabel = "fake app label";
+ doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
+ doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
+ doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
+ doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
+ doReturn(null).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
+ doReturn(false).when(mBatteryDiffEntry).validForRestriction();
+
+ mBatteryUsageBreakdownController.addAllPreferences();
+
+ // Verifies the preference cache.
+ final PowerGaugePreference pref =
+ (PowerGaugePreference) mBatteryUsageBreakdownController.mPreferenceCache
+ .get(PREF_KEY);
+ assertThat(pref).isNotNull();
+ // Verifies the added preference configuration.
+ verify(mAppListPreferenceGroup).addPreference(pref);
+ assertThat(pref.getKey()).isEqualTo(PREF_KEY);
+ assertThat(pref.getTitle().toString()).isEqualTo(appLabel);
+ assertThat(pref.getIcon()).isEqualTo(mDrawable);
+ assertThat(pref.getOrder()).isEqualTo(1);
+ assertThat(pref.getBatteryDiffEntry()).isSameInstanceAs(mBatteryDiffEntry);
+ assertThat(pref.isSingleLineTitle()).isTrue();
+ assertThat(pref.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void addPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
+ final String appLabel = "fake app label";
+ doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
+ doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
+ doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
+ doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
+ doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
+
+ mBatteryUsageBreakdownController.addAllPreferences();
+
+ verify(mAppListPreferenceGroup, never()).addPreference(any());
+ }
+
+ @Test
+ public void removeAndCacheAllPreferences_buildCacheAndRemoveAllPreference() {
+ doReturn(1).when(mAppListPreferenceGroup).getPreferenceCount();
+ doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).getPreference(0);
+ doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
+ doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
+ doReturn(mPowerGaugePreference).when(mAppListPreferenceGroup).findPreference(PREF_KEY);
+ // Ensures the testing data is correct.
+ assertThat(mBatteryUsageBreakdownController.mPreferenceCache).isEmpty();
+
+ mBatteryUsageBreakdownController.removeAndCacheAllPreferences();
+
+ assertThat(mBatteryUsageBreakdownController.mPreferenceCache.get(PREF_KEY))
+ .isEqualTo(mPowerGaugePreference);
+ verify(mAppListPreferenceGroup).removeAll();
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_notPowerGaugePreference_returnFalse() {
+ assertThat(mBatteryUsageBreakdownController
+ .handlePreferenceTreeClick(mAppListPreferenceGroup)).isFalse();
+
+ verify(mMetricsFeatureProvider, never())
+ .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM);
+ verify(mMetricsFeatureProvider, never())
+ .action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM);
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
+ doReturn(false).when(mBatteryHistEntry).isAppEntry();
+ doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
+
+ assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
+ mPowerGaugePreference)).isTrue();
+ verify(mMetricsFeatureProvider)
+ .action(
+ SettingsEnums.OPEN_BATTERY_USAGE,
+ SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
+ SettingsEnums.OPEN_BATTERY_USAGE,
+ /* package name */ "none",
+ /* percentage of total */ 0);
+ }
+
+ @Test
+ public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
+ doReturn(true).when(mBatteryHistEntry).isAppEntry();
+ doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
+
+ assertThat(mBatteryUsageBreakdownController.handlePreferenceTreeClick(
+ mPowerGaugePreference)).isTrue();
+ verify(mMetricsFeatureProvider)
+ .action(
+ SettingsEnums.OPEN_BATTERY_USAGE,
+ SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
+ SettingsEnums.OPEN_BATTERY_USAGE,
+ /* package name */ "none",
+ /* percentage of total */ 0);
+ }
+
+ @Test
+ public void setPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
+ final PowerGaugePreference pref = new PowerGaugePreference(mContext);
+ pref.setSummary(PREF_SUMMARY);
+
+ mBatteryUsageBreakdownController.setPreferenceSummary(
+ pref, createBatteryDiffEntry(
+ /*foregroundUsageTimeInMs=*/ 0,
+ /*backgroundUsageTimeInMs=*/ 0));
+ assertThat(pref.getSummary()).isNull();
+ }
+
+ @Test
+ public void setPreferenceSummary_setBackgroundUsageTimeOnly() {
+ final PowerGaugePreference pref = new PowerGaugePreference(mContext);
+ pref.setSummary(PREF_SUMMARY);
+
+ mBatteryUsageBreakdownController.setPreferenceSummary(
+ pref, createBatteryDiffEntry(
+ /*foregroundUsageTimeInMs=*/ 0,
+ /*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
+ assertThat(pref.getSummary().toString()).isEqualTo("Background: 1 min");
+ }
+
+ @Test
+ public void setPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
+ final PowerGaugePreference pref = new PowerGaugePreference(mContext);
+ pref.setSummary(PREF_SUMMARY);
+
+ mBatteryUsageBreakdownController.setPreferenceSummary(
+ pref, createBatteryDiffEntry(
+ /*foregroundUsageTimeInMs=*/ 100,
+ /*backgroundUsageTimeInMs=*/ 200));
+ assertThat(pref.getSummary().toString()).isEqualTo("Total: less than a min");
+ }
+
+ @Test
+ public void setPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
+ final PowerGaugePreference pref = new PowerGaugePreference(mContext);
+ pref.setSummary(PREF_SUMMARY);
+
+ mBatteryUsageBreakdownController.setPreferenceSummary(
+ pref, createBatteryDiffEntry(
+ /*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
+ /*backgroundUsageTimeInMs=*/ 200));
+ assertThat(pref.getSummary().toString())
+ .isEqualTo("Total: 1 min\nBackground: less than a min");
+ }
+
+ @Test
+ public void setPreferenceSummary_setTotalAndBackgroundUsageTime() {
+ final PowerGaugePreference pref = new PowerGaugePreference(mContext);
+ pref.setSummary(PREF_SUMMARY);
+
+ mBatteryUsageBreakdownController.setPreferenceSummary(
+ pref, createBatteryDiffEntry(
+ /*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
+ /*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
+ assertThat(pref.getSummary().toString()).isEqualTo("Total: 2 min\nBackground: 1 min");
+ }
+
+ private BatteryDiffEntry createBatteryDiffEntry(
+ long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
+ return new BatteryDiffEntry(
+ mContext, foregroundUsageTimeInMs, backgroundUsageTimeInMs, /*consumePower=*/ 0,
+ /*foregroundUsageConsumePower=*/ 0, /*foregroundServiceUsageConsumePower=*/ 0,
+ /*backgroundUsageConsumePower=*/ 0, /*cachedUsageConsumePower=*/ 0,
+ mBatteryHistEntry);
+ }
+
+ private BatteryUsageBreakdownController createController() {
+ final BatteryUsageBreakdownController controller =
+ new BatteryUsageBreakdownController(
+ mContext, /*lifecycle=*/ null, mSettingsActivity, mFragment);
+ controller.mPrefContext = mContext;
+ return controller;
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ExpandDividerPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ExpandDividerPreferenceTest.java
deleted file mode 100644
index e36f94854e7..00000000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ExpandDividerPreferenceTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.doReturn;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-@RunWith(RobolectricTestRunner.class)
-public final class ExpandDividerPreferenceTest {
-
- private Context mContext;
- private ExpandDividerPreference mExpandDividerPreference;
-
- private ImageView mImageView;
- private TextView mTextView;
-
- @Before
- public void setUp() {
- mContext = spy(RuntimeEnvironment.application);
- mImageView = spy(new ImageView(mContext));
- mTextView = spy(new TextView(mContext));
- mExpandDividerPreference = new ExpandDividerPreference(mContext);
- doReturn(R.id.expand_title).when(mTextView).getId();
- doReturn(R.id.expand_icon).when(mImageView).getId();
- }
-
- @Test
- public void testConstructor_returnExpectedResult() {
- assertThat(mExpandDividerPreference.getKey())
- .isEqualTo(ExpandDividerPreference.PREFERENCE_KEY);
- assertThat(mExpandDividerPreference.getLayoutResource())
- .isEqualTo(R.layout.preference_expand_divider);
- }
-
- @Test
- public void testSetTitle_setTitleContentIntoTextView() {
- final String titleContent = "title content";
- mExpandDividerPreference.mTextView = mTextView;
-
- mExpandDividerPreference.setTitle(titleContent);
-
- verify(mTextView).setText(titleContent);
- }
-
- @Test
- public void testOnClick_switchExpandStateAndInvokeCallback() {
- final boolean[] isExpandedArray = new boolean[]{false};
- mExpandDividerPreference.mImageView = mImageView;
- mExpandDividerPreference.setOnExpandListener(
- isExpanded -> isExpandedArray[0] = isExpanded);
-
- // Click the item first time from false -> true.
- mExpandDividerPreference.onClick();
- // Verifies the first time click result.
- verify(mImageView).setImageResource(R.drawable.ic_settings_expand_less);
- assertThat(isExpandedArray[0]).isTrue();
-
- // Clicks the item second time from true -> false.
- mExpandDividerPreference.onClick();
- // Verifies the second time click result.
- verify(mImageView).setImageResource(R.drawable.ic_settings_expand_more);
- assertThat(isExpandedArray[0]).isFalse();
- }
-
- @Test
- public void testSetIsExpanded_updateStateButNotInvokeCallback() {
- final boolean[] isExpandedArray = new boolean[]{false};
- mExpandDividerPreference.mImageView = mImageView;
- mExpandDividerPreference.setOnExpandListener(
- isExpanded -> isExpandedArray[0] = isExpanded);
-
- mExpandDividerPreference.setIsExpanded(true);
-
- verify(mImageView).setImageResource(R.drawable.ic_settings_expand_less);
- assertThat(isExpandedArray[0]).isFalse();
- }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TabPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TabPreferenceTest.java
new file mode 100644
index 00000000000..c106b48d457
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/TabPreferenceTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.Context;
+
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+
+import com.android.settings.R;
+
+import com.google.android.material.tabs.TabLayout;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public final class TabPreferenceTest {
+
+ private Context mContext;
+ private TabPreference mTabPreference;
+
+ @Mock
+ private Fragment mMockFragment;
+ @Mock
+ private TabLayout mMockTabLayout;
+
+ private final String[] mTabTitles = new String[]{"tab1", "tab2"};
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+ mTabPreference = new TabPreference(mContext, /*attrs=*/ null);
+ }
+
+ @Test
+ public void constructor_returnExpectedResult() {
+ assertThat(mTabPreference.getLayoutResource()).isEqualTo(R.layout.preference_tab);
+ }
+
+ @Test
+ public void initializeTabs_returnExpectedResult() {
+ mTabPreference.initializeTabs(mMockFragment, mTabTitles);
+ assertThat(mTabPreference.mTabTitles).isEqualTo(mTabTitles);
+ }
+
+ @Test
+ public void onSaveInstanceState_returnExpectedResult() {
+ doReturn(1).when(mMockTabLayout).getSelectedTabPosition();
+ mTabPreference.mTabLayout = mMockTabLayout;
+ TabPreference.SavedState savedState =
+ (TabPreference.SavedState) mTabPreference.onSaveInstanceState();
+ assertThat(savedState.getTabPosition()).isEqualTo(1);
+ }
+
+ @Test
+ public void onRestoreInstanceState_returnExpectedResult() {
+ TabPreference.SavedState savedState =
+ new TabPreference.SavedState(Preference.BaseSavedState.EMPTY_STATE, 2);
+ mTabPreference.onRestoreInstanceState(savedState);
+ assertThat(mTabPreference.mSavedTabPosition).isEqualTo(2);
+ }
+}