Merge "Port new version battery usage chart implementation from master to tm-qpr-dev." into tm-qpr-dev

This commit is contained in:
YK Hung
2022-08-19 01:30:51 +00:00
committed by Android (Google) Code Review
22 changed files with 3095 additions and 800 deletions

View File

@@ -29,14 +29,24 @@
android:layout_marginVertical="16dp" android:layout_marginVertical="16dp"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:text="@string/battery_usage_chart_graph_hint" /> android:text="@string/battery_usage_chart_graph_hint_last_full_charge" />
<com.android.settings.fuelgauge.batteryusage.BatteryChartView <com.android.settings.fuelgauge.batteryusage.BatteryChartView
android:id="@+id/battery_chart" android:id="@+id/daily_battery_chart"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="170dp" android:layout_height="170dp"
android:layout_marginBottom="6dp" android:layout_marginBottom="16dp"
android:visibility="invisible" android:visibility="gone"
android:contentDescription="@string/battery_usage_chart"
android:textAppearance="?android:attr/textAppearanceSmall"
settings:textColor="?android:attr/textColorSecondary" />
<com.android.settings.fuelgauge.batteryusage.BatteryChartView
android:id="@+id/hourly_battery_chart"
android:layout_width="match_parent"
android:layout_height="170dp"
android:layout_marginBottom="16dp"
android:visibility="visible"
android:contentDescription="@string/battery_usage_chart" android:contentDescription="@string/battery_usage_chart"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
settings:textColor="?android:attr/textColorSecondary" /> settings:textColor="?android:attr/textColorSecondary" />

View File

@@ -6749,10 +6749,16 @@
<!-- [CHAR_LIMIT=NONE] Battery percentage: Description for preference --> <!-- [CHAR_LIMIT=NONE] Battery percentage: Description for preference -->
<string name="battery_percentage_description">Show battery percentage in status bar</string> <string name="battery_percentage_description">Show battery percentage in status bar</string>
<!-- [CHAR_LIMIT=NONE] Battery usage main screen chart graph hint since last full charge -->
<string name="battery_usage_chart_graph_hint_last_full_charge">Battery level since last full charge</string>
<!-- [CHAR_LIMIT=NONE] Battery usage main screen chart graph hint --> <!-- [CHAR_LIMIT=NONE] Battery usage main screen chart graph hint -->
<string name="battery_usage_chart_graph_hint">Battery level for past 24 hr</string> <string name="battery_usage_chart_graph_hint">Battery level for past 24 hr</string>
<!-- [CHAR_LIMIT=NONE] Battery app usage section header since last full charge -->
<string name="battery_app_usage">App usage since last full charge</string>
<!-- [CHAR_LIMIT=NONE] Battery app usage section header for past 24 hour --> <!-- [CHAR_LIMIT=NONE] Battery app usage section header for past 24 hour -->
<string name="battery_app_usage_for_past_24">App usage for past 24 hr</string> <string name="battery_app_usage_for_past_24">App usage for past 24 hr</string>
<!-- [CHAR_LIMIT=NONE] Battery system usage section header since last full charge -->
<string name="battery_system_usage">System usage since last full charge</string>
<!-- [CHAR_LIMIT=NONE] Battery system usage section header for past 24 hour --> <!-- [CHAR_LIMIT=NONE] Battery system usage section header for past 24 hour -->
<string name="battery_system_usage_for_past_24">System usage for past 24 hr</string> <string name="battery_system_usage_for_past_24">System usage for past 24 hr</string>
<!-- [CHAR_LIMIT=NONE] Battery system usage section header --> <!-- [CHAR_LIMIT=NONE] Battery system usage section header -->

View File

@@ -179,7 +179,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController
return null; return null;
} }
final BatteryDiffEntry entry = final BatteryDiffEntry entry =
BatteryChartPreferenceController.getBatteryLast24HrUsageData( BatteryChartPreferenceController.getAppBatteryUsageData(
mContext, mPackageName, mUserId); mContext, mPackageName, mUserId);
Log.d(TAG, "loadBatteryDiffEntries():\n" + entry); Log.d(TAG, "loadBatteryDiffEntries():\n" + entry);
return entry; return entry;
@@ -200,10 +200,10 @@ public class AppBatteryPreferenceController extends BasePreferenceController
mBatteryPercent = Utils.formatPercentage( mBatteryPercent = Utils.formatPercentage(
mBatteryDiffEntry.getPercentOfTotal(), /* round */ true); mBatteryDiffEntry.getPercentOfTotal(), /* round */ true);
mPreference.setSummary(mContext.getString( mPreference.setSummary(mContext.getString(
R.string.battery_summary_24hr, mBatteryPercent)); R.string.battery_summary, mBatteryPercent));
} else { } else {
mPreference.setSummary( mPreference.setSummary(
mContext.getString(R.string.no_battery_summary_24hr)); mContext.getString(R.string.no_battery_summary));
} }
} }

View File

@@ -539,16 +539,13 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
return null; return null;
} }
if (totalTimeMs == 0) { if (totalTimeMs == 0) {
final int batteryWithoutUsageTime = consumedPower > 0 usageTimeSummary = getText(
? R.string.battery_usage_without_time : R.string.battery_not_usage_24hr; isChartGraphEnabled && consumedPower > 0 ? R.string.battery_usage_without_time
usageTimeSummary = getText(isChartGraphEnabled : R.string.battery_not_usage);
? batteryWithoutUsageTime : R.string.battery_not_usage);
} else if (slotTime == null) { } else if (slotTime == null) {
// Shows summary text with past 24 hr or full charge if slot time is null. // Shows summary text with last full charge if slot time is null.
usageTimeSummary = isChartGraphEnabled usageTimeSummary = getAppFullChargeActiveSummary(
? getAppPast24HrActiveSummary(foregroundTimeMs, backgroundTimeMs, totalTimeMs) foregroundTimeMs, backgroundTimeMs, totalTimeMs);
: getAppFullChargeActiveSummary(
foregroundTimeMs, backgroundTimeMs, totalTimeMs);
} else { } else {
// Shows summary text with slot time. // Shows summary text with slot time.
usageTimeSummary = getAppActiveSummaryWithSlotTime( usageTimeSummary = getAppActiveSummaryWithSlotTime(

View File

@@ -20,7 +20,6 @@ import android.app.settings.SettingsEnums;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@@ -28,7 +27,9 @@ import android.text.TextUtils;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.util.Log; import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
@@ -53,8 +54,6 @@ import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.FooterPreference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -62,27 +61,23 @@ import java.util.Map;
/** Controls the update for chart graph and the list items. */ /** Controls the update for chart graph and the list items. */
public class BatteryChartPreferenceController extends AbstractPreferenceController public class BatteryChartPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy, implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
OnSaveInstanceState, BatteryChartView.OnSelectListener, OnResume, OnSaveInstanceState, OnResume, ExpandDividerPreference.OnExpandListener {
ExpandDividerPreference.OnExpandListener {
private static final String TAG = "BatteryChartPreferenceController"; private static final String TAG = "BatteryChartPreferenceController";
private static final String KEY_FOOTER_PREF = "battery_graph_footer"; private static final String KEY_FOOTER_PREF = "battery_graph_footer";
private static final String PACKAGE_NAME_NONE = "none"; private static final String PACKAGE_NAME_NONE = "none";
/** Desired battery history size for timestamp slots. */
public static final int DESIRED_HISTORY_SIZE = 25;
private static final int CHART_LEVEL_ARRAY_SIZE = 13;
private static final int CHART_KEY_ARRAY_SIZE = DESIRED_HISTORY_SIZE;
private static final long VALID_USAGE_TIME_DURATION = DateUtils.HOUR_IN_MILLIS * 2; private static final long VALID_USAGE_TIME_DURATION = DateUtils.HOUR_IN_MILLIS * 2;
private static final long VALID_DIFF_DURATION = DateUtils.MINUTE_IN_MILLIS * 3; private static final long VALID_DIFF_DURATION = DateUtils.MINUTE_IN_MILLIS * 3;
// Keys for bundle instance to restore configurations. // Keys for bundle instance to restore configurations.
private static final String KEY_EXPAND_SYSTEM_INFO = "expand_system_info"; private static final String KEY_EXPAND_SYSTEM_INFO = "expand_system_info";
private static final String KEY_CURRENT_TIME_SLOT = "current_time_slot"; 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; private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
@VisibleForTesting @VisibleForTesting
Map<Integer, List<BatteryDiffEntry>> mBatteryIndexedMap; Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
@VisibleForTesting @VisibleForTesting
Context mPrefContext; Context mPrefContext;
@@ -91,28 +86,34 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
@VisibleForTesting @VisibleForTesting
PreferenceGroup mAppListPrefGroup; PreferenceGroup mAppListPrefGroup;
@VisibleForTesting @VisibleForTesting
BatteryChartView mBatteryChartView;
@VisibleForTesting
ExpandDividerPreference mExpandDividerPreference; ExpandDividerPreference mExpandDividerPreference;
@VisibleForTesting @VisibleForTesting
boolean mIsExpanded = false; boolean mIsExpanded = false;
@VisibleForTesting
int[] mBatteryHistoryLevels;
@VisibleForTesting
long[] mBatteryHistoryKeys;
@VisibleForTesting
int mTrapezoidIndex = BatteryChartView.SELECTED_INDEX_INVALID;
private boolean mIs24HourFormat = false; @VisibleForTesting
BatteryChartView mDailyChartView;
@VisibleForTesting
BatteryChartView mHourlyChartView;
@VisibleForTesting
int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
@VisibleForTesting
int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
private boolean mIs24HourFormat;
private boolean mIsFooterPrefAdded = false; private boolean mIsFooterPrefAdded = false;
private PreferenceScreen mPreferenceScreen; private PreferenceScreen mPreferenceScreen;
private FooterPreference mFooterPreference; private FooterPreference mFooterPreference;
// Daily view model only saves abbreviated day of week texts (e.g. MON). This field saves the
// full day of week texts (e.g. Monday), which is used in category title and battery detail
// page.
private List<String> mDailyTimestampFullTexts;
private BatteryChartViewModel mDailyViewModel;
private List<BatteryChartViewModel> mHourlyViewModels;
private final String mPreferenceKey; private final String mPreferenceKey;
private final SettingsActivity mActivity; private final SettingsActivity mActivity;
private final InstrumentedPreferenceFragment mFragment; private final InstrumentedPreferenceFragment mFragment;
private final CharSequence[] mNotAllowShowEntryPackages;
private final CharSequence[] mNotAllowShowSummaryPackages; private final CharSequence[] mNotAllowShowSummaryPackages;
private final MetricsFeatureProvider mMetricsFeatureProvider; private final MetricsFeatureProvider mMetricsFeatureProvider;
private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -120,8 +121,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
// Preference cache to avoid create new instance each time. // Preference cache to avoid create new instance each time.
@VisibleForTesting @VisibleForTesting
final Map<String, Preference> mPreferenceCache = new HashMap<>(); final Map<String, Preference> mPreferenceCache = new HashMap<>();
@VisibleForTesting
final List<BatteryDiffEntry> mSystemEntries = new ArrayList<>();
public BatteryChartPreferenceController( public BatteryChartPreferenceController(
Context context, String preferenceKey, Context context, String preferenceKey,
@@ -134,10 +133,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mIs24HourFormat = DateFormat.is24HourFormat(context); mIs24HourFormat = DateFormat.is24HourFormat(context);
mMetricsFeatureProvider = mMetricsFeatureProvider =
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider(); FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
mNotAllowShowEntryPackages =
FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context)
.getHideApplicationEntries(context);
mNotAllowShowSummaryPackages = mNotAllowShowSummaryPackages =
FeatureFactory.getFactory(context) FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context) .getPowerUsageFeatureProvider(context)
@@ -152,12 +147,14 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (savedInstanceState == null) { if (savedInstanceState == null) {
return; return;
} }
mTrapezoidIndex = mDailyChartIndex =
savedInstanceState.getInt(KEY_CURRENT_TIME_SLOT, mTrapezoidIndex); savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
mHourlyChartIndex =
savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
mIsExpanded = mIsExpanded =
savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded); savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
Log.d(TAG, String.format("onCreate() slotIndex=%d isExpanded=%b", Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
mTrapezoidIndex, mIsExpanded)); mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
} }
@Override @Override
@@ -179,10 +176,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (savedInstance == null) { if (savedInstance == null) {
return; return;
} }
savedInstance.putInt(KEY_CURRENT_TIME_SLOT, mTrapezoidIndex); savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded); savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
Log.d(TAG, String.format("onSaveInstanceState() slotIndex=%d isExpanded=%b", Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
mTrapezoidIndex, mIsExpanded)); mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
} }
@Override @Override
@@ -204,8 +202,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mPrefContext = screen.getContext(); mPrefContext = screen.getContext();
mAppListPrefGroup = screen.findPreference(mPreferenceKey); mAppListPrefGroup = screen.findPreference(mPreferenceKey);
mAppListPrefGroup.setOrderingAsAdded(false); mAppListPrefGroup.setOrderingAsAdded(false);
mAppListPrefGroup.setTitle( mAppListPrefGroup.setTitle(mPrefContext.getString(R.string.battery_app_usage));
mPrefContext.getString(R.string.battery_app_usage_for_past_24));
mFooterPreference = screen.findPreference(KEY_FOOTER_PREF); mFooterPreference = screen.findPreference(KEY_FOOTER_PREF);
// Removes footer first until usage data is loaded to avoid flashing. // Removes footer first until usage data is loaded to avoid flashing.
if (mFooterPreference != null) { if (mFooterPreference != null) {
@@ -249,17 +246,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return true; return true;
} }
@Override
public void onSelect(int trapezoidIndex) {
Log.d(TAG, "onChartSelect:" + trapezoidIndex);
refreshUi(trapezoidIndex, /*isForce=*/ false);
mMetricsFeatureProvider.action(
mPrefContext,
trapezoidIndex == BatteryChartView.SELECTED_INDEX_ALL
? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
: SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT);
}
@Override @Override
public void onExpand(boolean isExpanded) { public void onExpand(boolean isExpanded) {
mIsExpanded = isExpanded; mIsExpanded = isExpanded;
@@ -272,81 +258,120 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
void setBatteryHistoryMap( void setBatteryHistoryMap(
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) { final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
// Resets all battery history data relative variables. Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null"
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) { : ("size=" + batteryHistoryMap.size())));
mBatteryIndexedMap = null; final BatteryLevelData batteryLevelData =
mBatteryHistoryKeys = null; DataProcessor.getBatteryLevelData(mContext, mHandler, batteryHistoryMap,
mBatteryHistoryLevels = null; batteryUsageMap -> {
addFooterPreferenceIfNeeded(false); mBatteryUsageMap = batteryUsageMap;
refreshUi();
});
Log.d(TAG, "getBatteryLevelData: " + batteryLevelData);
if (batteryLevelData == null) {
mDailyTimestampFullTexts = null;
mDailyViewModel = null;
mHourlyViewModels = null;
refreshUi();
return; return;
} }
mBatteryHistoryKeys = getBatteryHistoryKeys(batteryHistoryMap); mDailyTimestampFullTexts = generateTimestampDayOfWeekTexts(
mBatteryHistoryLevels = new int[CHART_LEVEL_ARRAY_SIZE]; mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(),
for (int index = 0; index < CHART_LEVEL_ARRAY_SIZE; index++) { /* isAbbreviation= */ false);
final long timestamp = mBatteryHistoryKeys[index * 2]; mDailyViewModel = new BatteryChartViewModel(
final Map<String, BatteryHistEntry> entryMap = batteryHistoryMap.get(timestamp); batteryLevelData.getDailyBatteryLevels().getLevels(),
if (entryMap == null || entryMap.isEmpty()) { generateTimestampDayOfWeekTexts(
Log.e(TAG, "abnormal entry list in the timestamp:" mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(),
+ ConvertUtils.utcToLocalTime(mPrefContext, timestamp)); /* isAbbreviation= */ true),
continue; BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS);
mHourlyViewModels = new ArrayList<>();
for (BatteryLevelData.PeriodBatteryLevelData hourlyBatteryLevelsPerDay :
batteryLevelData.getHourlyBatteryLevelsPerDay()) {
mHourlyViewModels.add(new BatteryChartViewModel(
hourlyBatteryLevelsPerDay.getLevels(),
generateTimestampHourTexts(
mContext, hourlyBatteryLevelsPerDay.getTimestamps()),
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
}
refreshUi();
}
void setBatteryChartView(@NonNull final BatteryChartView dailyChartView,
@NonNull final BatteryChartView hourlyChartView) {
if (mDailyChartView != dailyChartView || mHourlyChartView != hourlyChartView) {
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
}
}
private void setBatteryChartViewInner(@NonNull final BatteryChartView dailyChartView,
@NonNull final BatteryChartView hourlyChartView) {
mDailyChartView = dailyChartView;
mDailyChartView.setOnSelectListener(trapezoidIndex -> {
if (mDailyChartIndex == trapezoidIndex) {
return;
} }
// Averages the battery level in each time slot to avoid corner conditions. Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex);
float batteryLevelCounter = 0; mDailyChartIndex = trapezoidIndex;
for (BatteryHistEntry entry : entryMap.values()) { mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
batteryLevelCounter += entry.mBatteryLevel; refreshUi();
// TODO: Change to log daily data.
});
mHourlyChartView = hourlyChartView;
mHourlyChartView.setOnSelectListener(trapezoidIndex -> {
if (mHourlyChartIndex == trapezoidIndex) {
return;
} }
mBatteryHistoryLevels[index] = Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex);
Math.round(batteryLevelCounter / entryMap.size()); mHourlyChartIndex = trapezoidIndex;
} refreshUi();
forceRefreshUi(); mMetricsFeatureProvider.action(
Log.d(TAG, String.format( mPrefContext,
"setBatteryHistoryMap() size=%d key=%s\nlevels=%s", trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
batteryHistoryMap.size(), ? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
ConvertUtils.utcToLocalTime(mPrefContext, : SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT);
mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1]), });
Arrays.toString(mBatteryHistoryLevels))); refreshUi();
// Loads item icon and label in the background.
new LoadAllItemsInfoTask(batteryHistoryMap).execute();
}
void setBatteryChartView(final BatteryChartView batteryChartView) {
if (mBatteryChartView != batteryChartView) {
mHandler.post(() -> setBatteryChartViewInner(batteryChartView));
}
}
private void setBatteryChartViewInner(final BatteryChartView batteryChartView) {
mBatteryChartView = batteryChartView;
mBatteryChartView.setOnSelectListener(this);
forceRefreshUi();
}
private void forceRefreshUi() {
final int refreshIndex =
mTrapezoidIndex == BatteryChartView.SELECTED_INDEX_INVALID
? BatteryChartView.SELECTED_INDEX_ALL
: mTrapezoidIndex;
if (mBatteryChartView != null) {
mBatteryChartView.setLevels(mBatteryHistoryLevels);
mBatteryChartView.setSelectedIndex(refreshIndex);
setTimestampLabel();
}
refreshUi(refreshIndex, /*isForce=*/ true);
} }
@VisibleForTesting @VisibleForTesting
boolean refreshUi(int trapezoidIndex, boolean isForce) { boolean refreshUi() {
// Invalid refresh condition. if (mDailyChartView == null || mHourlyChartView == null) {
if (mBatteryIndexedMap == null // Chart views are not initialized.
|| mBatteryChartView == null return false;
|| (mTrapezoidIndex == trapezoidIndex && !isForce)) { }
if (mDailyViewModel == null || mHourlyViewModels == null) {
// Fail to get battery level data, show an empty hourly chart view.
mDailyChartView.setVisibility(View.GONE);
mHourlyChartView.setVisibility(View.VISIBLE);
mHourlyChartView.setViewModel(null);
removeAndCacheAllPrefs();
addFooterPreferenceIfNeeded(false);
return false;
}
if (mBatteryUsageMap == null) {
// Battery usage data is not ready, wait for data ready to refresh UI.
return false; return false;
} }
Log.d(TAG, String.format("refreshUi: index=%d size=%d isForce:%b",
trapezoidIndex, mBatteryIndexedMap.size(), isForce));
mTrapezoidIndex = trapezoidIndex; if (isBatteryLevelDataInOneDay()) {
// Only 1 day data, hide the daily chart view.
mDailyChartView.setVisibility(View.GONE);
mDailyChartIndex = 0;
} else {
mDailyChartView.setVisibility(View.VISIBLE);
mDailyViewModel.setSelectedIndex(mDailyChartIndex);
mDailyChartView.setViewModel(mDailyViewModel);
}
if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
// Multiple days are selected, hide the hourly chart view.
mHourlyChartView.setVisibility(View.GONE);
} else {
mHourlyChartView.setVisibility(View.VISIBLE);
final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex);
hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
mHourlyChartView.setViewModel(hourlyViewModel);
}
mHandler.post(() -> { mHandler.post(() -> {
final long start = System.currentTimeMillis(); final long start = System.currentTimeMillis();
removeAndCacheAllPrefs(); removeAndCacheAllPrefs();
@@ -359,43 +384,22 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
private void addAllPreferences() { private void addAllPreferences() {
final List<BatteryDiffEntry> entries = final BatteryDiffData batteryDiffData =
mBatteryIndexedMap.get(Integer.valueOf(mTrapezoidIndex)); mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
addFooterPreferenceIfNeeded(entries != null && !entries.isEmpty()); addFooterPreferenceIfNeeded(batteryDiffData != null
if (entries == null) { && (!batteryDiffData.getAppDiffEntryList().isEmpty()
Log.w(TAG, "cannot find BatteryDiffEntry for:" + mTrapezoidIndex); || !batteryDiffData.getSystemDiffEntryList().isEmpty()));
if (batteryDiffData == null) {
Log.w(TAG, "cannot find BatteryDiffEntry for daily_index: " + mDailyChartIndex
+ " hourly_index: " + mHourlyChartIndex);
return; return;
} }
// Separates data into two groups and sort them individually.
final List<BatteryDiffEntry> appEntries = new ArrayList<>();
mSystemEntries.clear();
entries.forEach(entry -> {
final String packageName = entry.getPackageName();
if (!isValidToShowEntry(packageName)) {
Log.w(TAG, "ignore showing item:" + packageName);
return;
}
if (entry.isSystemEntry()) {
mSystemEntries.add(entry);
} else {
appEntries.add(entry);
}
// Validates the usage time if users click a specific slot.
if (mTrapezoidIndex >= 0) {
validateUsageTime(entry);
}
});
Collections.sort(appEntries, BatteryDiffEntry.COMPARATOR);
Collections.sort(mSystemEntries, BatteryDiffEntry.COMPARATOR);
Log.d(TAG, String.format("addAllPreferences() app=%d system=%d",
appEntries.size(), mSystemEntries.size()));
// Adds app entries to the list if it is not empty. // Adds app entries to the list if it is not empty.
if (!appEntries.isEmpty()) { if (!batteryDiffData.getAppDiffEntryList().isEmpty()) {
addPreferenceToScreen(appEntries); addPreferenceToScreen(batteryDiffData.getAppDiffEntryList());
} }
// Adds the expabable divider if we have system entries data. // Adds the expabable divider if we have system entries data.
if (!mSystemEntries.isEmpty()) { if (!batteryDiffData.getSystemDiffEntryList().isEmpty()) {
if (mExpandDividerPreference == null) { if (mExpandDividerPreference == null) {
mExpandDividerPreference = new ExpandDividerPreference(mPrefContext); mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
mExpandDividerPreference.setOnExpandListener(this); mExpandDividerPreference.setOnExpandListener(this);
@@ -469,11 +473,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
private void refreshExpandUi() { private void refreshExpandUi() {
final List<BatteryDiffEntry> systemEntries = mBatteryUsageMap.get(mDailyChartIndex).get(
mHourlyChartIndex).getSystemDiffEntryList();
if (mIsExpanded) { if (mIsExpanded) {
addPreferenceToScreen(mSystemEntries); addPreferenceToScreen(systemEntries);
} else { } else {
// Removes and recycles all system entries to hide all of them. // Removes and recycles all system entries to hide all of them.
for (BatteryDiffEntry entry : mSystemEntries) { for (BatteryDiffEntry entry : systemEntries) {
final String prefKey = entry.mBatteryHistEntry.getKey(); final String prefKey = entry.mBatteryHistEntry.getKey();
final Preference pref = mAppListPrefGroup.findPreference(prefKey); final Preference pref = mAppListPrefGroup.findPreference(prefKey);
if (pref != null) { if (pref != null) {
@@ -499,11 +505,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
private String getSlotInformation(boolean isApp, String slotInformation) { 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. // Null means we show all information without a specific time slot.
if (slotInformation == null) { if (slotInformation == null) {
return isApp return isApp
? mPrefContext.getString(R.string.battery_app_usage_for_past_24) ? mPrefContext.getString(R.string.battery_app_usage)
: mPrefContext.getString(R.string.battery_system_usage_for_past_24); : mPrefContext.getString(R.string.battery_system_usage);
} else { } else {
return isApp return isApp
? mPrefContext.getString(R.string.battery_app_usage_for, slotInformation) ? mPrefContext.getString(R.string.battery_app_usage_for, slotInformation)
@@ -511,17 +518,33 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
} }
private String getSlotInformation() { @VisibleForTesting
if (mTrapezoidIndex < 0) { String getSlotInformation() {
if (mDailyTimestampFullTexts == null || mDailyViewModel == null
|| mHourlyViewModels == null) {
// No data
return null; return null;
} }
final String fromHour = ConvertUtils.utcToLocalTimeHour(mPrefContext, if (isAllSelected()) {
mBatteryHistoryKeys[mTrapezoidIndex * 2], mIs24HourFormat); return null;
final String toHour = ConvertUtils.utcToLocalTimeHour(mPrefContext, }
mBatteryHistoryKeys[(mTrapezoidIndex + 1) * 2], mIs24HourFormat);
return mIs24HourFormat final String selectedDayText = mDailyTimestampFullTexts.get(mDailyChartIndex);
? String.format("%s%s", fromHour, toHour) if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
: String.format("%s %s", fromHour, toHour); return selectedDayText;
}
final String fromHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get(
mHourlyChartIndex);
final String toHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get(
mHourlyChartIndex + 1);
final String selectedHourText =
String.format("%s%s%s", fromHourText, mIs24HourFormat ? "-" : " - ", toHourText);
if (isBatteryLevelDataInOneDay()) {
return selectedHourText;
}
return String.format("%s %s", selectedDayText, selectedHourText);
} }
@VisibleForTesting @VisibleForTesting
@@ -575,22 +598,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
@VisibleForTesting @VisibleForTesting
boolean isValidToShowSummary(String packageName) { boolean isValidToShowSummary(String packageName) {
return !contains(packageName, mNotAllowShowSummaryPackages); return !DataProcessor.contains(packageName, mNotAllowShowSummaryPackages);
}
@VisibleForTesting
boolean isValidToShowEntry(String packageName) {
return !contains(packageName, mNotAllowShowEntryPackages);
}
@VisibleForTesting
void setTimestampLabel() {
if (mBatteryChartView == null || mBatteryHistoryKeys == null) {
return;
}
final long latestTimestamp =
mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1];
mBatteryChartView.setLatestTimestamp(latestTimestamp);
} }
private void addFooterPreferenceIfNeeded(boolean containAppItems) { private void addFooterPreferenceIfNeeded(boolean containAppItems) {
@@ -605,60 +613,65 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference)); mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference));
} }
private static boolean contains(String target, CharSequence[] packageNames) { private boolean isBatteryLevelDataInOneDay() {
if (target != null && packageNames != null) { return mHourlyViewModels != null && mHourlyViewModels.size() == 1;
for (CharSequence packageName : packageNames) {
if (TextUtils.equals(target, packageName)) {
return true;
}
}
}
return false;
} }
@VisibleForTesting private boolean isAllSelected() {
static boolean validateUsageTime(BatteryDiffEntry entry) { return (isBatteryLevelDataInOneDay()
final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs; || mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL)
final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs; && mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs; }
if (foregroundUsageTimeInMs > VALID_USAGE_TIME_DURATION
|| backgroundUsageTimeInMs > VALID_USAGE_TIME_DURATION private static List<String> generateTimestampDayOfWeekTexts(@NonNull final Context context,
|| totalUsageTimeInMs > VALID_USAGE_TIME_DURATION) { @NonNull final List<Long> timestamps, final boolean isAbbreviation) {
Log.e(TAG, "validateUsageTime() fail for\n" + entry); final ArrayList<String> texts = new ArrayList<>();
return false; for (Long timestamp : timestamps) {
texts.add(ConvertUtils.utcToLocalTimeDayOfWeek(context, timestamp, isAbbreviation));
} }
return true; return texts;
}
private static List<String> generateTimestampHourTexts(
@NonNull final Context context, @NonNull final List<Long> timestamps) {
final boolean is24HourFormat = DateFormat.is24HourFormat(context);
final ArrayList<String> texts = new ArrayList<>();
for (Long timestamp : timestamps) {
texts.add(ConvertUtils.utcToLocalTimeHour(context, timestamp, is24HourFormat));
}
return texts;
} }
/** Used for {@link AppBatteryPreferenceController}. */ /** Used for {@link AppBatteryPreferenceController}. */
public static List<BatteryDiffEntry> getBatteryLast24HrUsageData(Context context) { public static List<BatteryDiffEntry> getAppBatteryUsageData(Context context) {
final long start = System.currentTimeMillis(); final long start = System.currentTimeMillis();
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
FeatureFactory.getFactory(context) FeatureFactory.getFactory(context)
.getPowerUsageFeatureProvider(context) .getPowerUsageFeatureProvider(context)
.getBatteryHistory(context); .getBatteryHistorySinceLastFullCharge(context);
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) { if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
return null; return null;
} }
Log.d(TAG, String.format("getBatteryLast24HrData() size=%d time=&d/ms", Log.d(TAG, String.format("getBatterySinceLastFullChargeUsageData() size=%d time=%d/ms",
batteryHistoryMap.size(), (System.currentTimeMillis() - start))); batteryHistoryMap.size(), (System.currentTimeMillis() - start)));
final Map<Integer, List<BatteryDiffEntry>> batteryIndexedMap =
ConvertUtils.getIndexedUsageMap( final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageData =
context, DataProcessor.getBatteryUsageData(context, batteryHistoryMap);
/*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1, return batteryUsageData == null
getBatteryHistoryKeys(batteryHistoryMap), ? null
batteryHistoryMap, : batteryUsageData
/*purgeLowPercentageAndFakeData=*/ true); .get(BatteryChartViewModel.SELECTED_INDEX_ALL)
return batteryIndexedMap.get(BatteryChartView.SELECTED_INDEX_ALL); .get(BatteryChartViewModel.SELECTED_INDEX_ALL)
.getAppDiffEntryList();
} }
/** Used for {@link AppBatteryPreferenceController}. */ /** Used for {@link AppBatteryPreferenceController}. */
public static BatteryDiffEntry getBatteryLast24HrUsageData( public static BatteryDiffEntry getAppBatteryUsageData(
Context context, String packageName, int userId) { Context context, String packageName, int userId) {
if (packageName == null) { if (packageName == null) {
return null; return null;
} }
final List<BatteryDiffEntry> entries = getBatteryLast24HrUsageData(context); final List<BatteryDiffEntry> entries = getAppBatteryUsageData(context);
if (entries == null) { if (entries == null) {
return null; return null;
} }
@@ -673,65 +686,4 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
} }
return null; return null;
} }
private static long[] getBatteryHistoryKeys(
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
final List<Long> batteryHistoryKeyList =
new ArrayList<>(batteryHistoryMap.keySet());
Collections.sort(batteryHistoryKeyList);
final long[] batteryHistoryKeys = new long[CHART_KEY_ARRAY_SIZE];
for (int index = 0; index < CHART_KEY_ARRAY_SIZE; index++) {
batteryHistoryKeys[index] = batteryHistoryKeyList.get(index);
}
return batteryHistoryKeys;
}
// Loads all items icon and label in the background.
private final class LoadAllItemsInfoTask
extends AsyncTask<Void, Void, Map<Integer, List<BatteryDiffEntry>>> {
private long[] mBatteryHistoryKeysCache;
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
private LoadAllItemsInfoTask(
Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
this.mBatteryHistoryMap = batteryHistoryMap;
this.mBatteryHistoryKeysCache = mBatteryHistoryKeys;
}
@Override
protected Map<Integer, List<BatteryDiffEntry>> doInBackground(Void... voids) {
if (mPrefContext == null || mBatteryHistoryKeysCache == null) {
return null;
}
final long startTime = System.currentTimeMillis();
final Map<Integer, List<BatteryDiffEntry>> indexedUsageMap =
ConvertUtils.getIndexedUsageMap(
mPrefContext, /*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1,
mBatteryHistoryKeysCache, mBatteryHistoryMap,
/*purgeLowPercentageAndFakeData=*/ true);
// Pre-loads each BatteryDiffEntry relative icon and label for all slots.
for (List<BatteryDiffEntry> entries : indexedUsageMap.values()) {
entries.forEach(entry -> entry.loadLabelAndIcon());
}
Log.d(TAG, String.format("execute LoadAllItemsInfoTask in %d/ms",
(System.currentTimeMillis() - startTime)));
return indexedUsageMap;
}
@Override
protected void onPostExecute(
Map<Integer, List<BatteryDiffEntry>> indexedUsageMap) {
mBatteryHistoryMap = null;
mBatteryHistoryKeysCache = null;
if (indexedUsageMap == null) {
return;
}
// Posts results back to main thread to refresh UI.
mHandler.post(() -> {
mBatteryIndexedMap = indexedUsageMap;
forceRefreshUi();
});
}
}
} }

View File

@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batteryusage;
import static com.android.settings.Utils.formatPercentage; import static com.android.settings.Utils.formatPercentage;
import static java.lang.Math.round; import static java.lang.Math.round;
import static java.util.Objects.requireNonNull;
import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context; import android.content.Context;
@@ -29,8 +30,6 @@ import android.graphics.Paint;
import android.graphics.Path; import android.graphics.Path;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Handler; import android.os.Handler;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.HapticFeedbackConstants; import android.view.HapticFeedbackConstants;
@@ -39,6 +38,7 @@ import android.view.View;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.appcompat.widget.AppCompatImageView; import androidx.appcompat.widget.AppCompatImageView;
@@ -46,7 +46,7 @@ import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory; import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.Utils; import com.android.settingslib.Utils;
import java.time.Clock; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -58,36 +58,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private static final List<String> ACCESSIBILITY_SERVICE_NAMES = private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService"); Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
private static final int DEFAULT_TRAPEZOID_COUNT = 12;
private static final int DEFAULT_TIMESTAMP_COUNT = 4;
private static final int TIMESTAMP_GAPS_COUNT = DEFAULT_TIMESTAMP_COUNT - 1;
private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5"); private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5");
private static final long UPDATE_STATE_DELAYED_TIME = 500L; private static final long UPDATE_STATE_DELAYED_TIME = 500L;
/** Selects all trapezoid shapes. */
public static final int SELECTED_INDEX_ALL = -1;
public static final int SELECTED_INDEX_INVALID = -2;
/** A callback listener for selected group index is updated. */ /** A callback listener for selected group index is updated. */
public interface OnSelectListener { public interface OnSelectListener {
/** The callback function for selected group index is updated. */ /** The callback function for selected group index is updated. */
void onSelect(int trapezoidIndex); void onSelect(int trapezoidIndex);
} }
private BatteryChartViewModel mViewModel;
private int mDividerWidth; private int mDividerWidth;
private int mDividerHeight; private int mDividerHeight;
private int mTrapezoidCount;
private float mTrapezoidVOffset; private float mTrapezoidVOffset;
private float mTrapezoidHOffset; private float mTrapezoidHOffset;
private boolean mIsSlotsClickabled; private boolean mIsSlotsClickabled;
private String[] mPercentages = getPercentages(); private String[] mPercentages = getPercentages();
@VisibleForTesting @VisibleForTesting
int mHoveredIndex = SELECTED_INDEX_INVALID; int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
@VisibleForTesting
int mSelectedIndex = SELECTED_INDEX_INVALID;
@VisibleForTesting
String[] mTimestamps;
// Colors for drawing the trapezoid shape and dividers. // Colors for drawing the trapezoid shape and dividers.
private int mTrapezoidColor; private int mTrapezoidColor;
@@ -98,25 +88,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private final Rect mIndent = new Rect(); private final Rect mIndent = new Rect();
private final Rect[] mPercentageBounds = private final Rect[] mPercentageBounds =
new Rect[]{new Rect(), new Rect(), new Rect()}; new Rect[]{new Rect(), new Rect(), new Rect()};
// For drawing the timestamp information. // For drawing the axis label information.
private final Rect[] mTimestampsBounds = private final List<Rect> mAxisLabelsBounds = new ArrayList<>();
new Rect[]{new Rect(), new Rect(), new Rect(), new Rect()};
@VisibleForTesting @VisibleForTesting
Handler mHandler = new Handler(); Handler mHandler = new Handler();
@VisibleForTesting @VisibleForTesting
final Runnable mUpdateClickableStateRun = () -> updateClickableState(); final Runnable mUpdateClickableStateRun = () -> updateClickableState();
private int[] mLevels;
private Paint mTextPaint; private Paint mTextPaint;
private Paint mDividerPaint; private Paint mDividerPaint;
private Paint mTrapezoidPaint; private Paint mTrapezoidPaint;
@VisibleForTesting @VisibleForTesting
Paint mTrapezoidCurvePaint = null; Paint mTrapezoidCurvePaint = null;
private TrapezoidSlot[] mTrapezoidSlots; @VisibleForTesting
TrapezoidSlot[] mTrapezoidSlots;
// Records the location to calculate selected index. // Records the location to calculate selected index.
private float mTouchUpEventX = Float.MIN_VALUE; @VisibleForTesting
float mTouchUpEventX = Float.MIN_VALUE;
private BatteryChartView.OnSelectListener mOnSelectListener; private BatteryChartView.OnSelectListener mOnSelectListener;
public BatteryChartView(Context context) { public BatteryChartView(Context context) {
@@ -128,57 +119,25 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
initializeColors(context); initializeColors(context);
// Registers the click event listener. // Registers the click event listener.
setOnClickListener(this); setOnClickListener(this);
setSelectedIndex(SELECTED_INDEX_ALL);
setTrapezoidCount(DEFAULT_TRAPEZOID_COUNT);
setClickable(false); setClickable(false);
setLatestTimestamp(0); requestLayout();
} }
/** Sets the total trapezoid count for drawing. */ /** Sets the data model of this view. */
public void setTrapezoidCount(int trapezoidCount) { public void setViewModel(BatteryChartViewModel viewModel) {
Log.i(TAG, "trapezoidCount:" + trapezoidCount); if (viewModel == null) {
mTrapezoidCount = trapezoidCount; mViewModel = null;
mTrapezoidSlots = new TrapezoidSlot[trapezoidCount];
// Allocates the trapezoid slot array.
for (int index = 0; index < trapezoidCount; index++) {
mTrapezoidSlots[index] = new TrapezoidSlot();
}
invalidate();
}
/** Sets all levels value to draw the trapezoid shape */
public void setLevels(int[] levels) {
Log.d(TAG, "setLevels() " + (levels == null ? "null" : levels.length));
if (levels == null) {
mLevels = null;
return;
}
// We should provide trapezoid count + 1 data to draw all trapezoids.
mLevels = levels.length == mTrapezoidCount + 1 ? levels : null;
setClickable(false);
invalidate();
if (mLevels == null) {
return;
}
// Sets the chart is clickable if there is at least one valid item in it.
for (int index = 0; index < mLevels.length - 1; index++) {
if (mLevels[index] != 0 && mLevels[index + 1] != 0) {
setClickable(true);
break;
}
}
}
/** Sets the selected group index to draw highlight effect. */
public void setSelectedIndex(int index) {
if (mSelectedIndex != index) {
mSelectedIndex = index;
invalidate(); invalidate();
// Callbacks to the listener if we have. return;
if (mOnSelectListener != null) {
mOnSelectListener.onSelect(mSelectedIndex);
}
} }
Log.d(TAG, String.format("setViewModel(): size: %d, selectedIndex: %d.",
viewModel.size(), viewModel.selectedIndex()));
mViewModel = viewModel;
initializeAxisLabelsBounds();
initializeTrapezoidSlots(viewModel.size() - 1);
setClickable(hasAnyValidTrapezoid(viewModel));
requestLayout();
} }
/** Sets the callback to monitor the selected group index. */ /** Sets the callback to monitor the selected group index. */
@@ -195,29 +154,6 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
} else { } else {
mTextPaint = null; mTextPaint = null;
} }
setVisibility(View.VISIBLE);
requestLayout();
}
/** Sets the latest timestamp for drawing into x-axis information. */
public void setLatestTimestamp(long latestTimestamp) {
if (latestTimestamp == 0) {
latestTimestamp = Clock.systemUTC().millis();
}
if (mTimestamps == null) {
mTimestamps = new String[DEFAULT_TIMESTAMP_COUNT];
}
final long timeSlotOffset =
DateUtils.HOUR_IN_MILLIS * (/*total 24 hours*/ 24 / TIMESTAMP_GAPS_COUNT);
final boolean is24HourFormat = DateFormat.is24HourFormat(getContext());
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) {
mTimestamps[index] =
ConvertUtils.utcToLocalTimeHour(
getContext(),
latestTimestamp - (TIMESTAMP_GAPS_COUNT - index)
* timeSlotOffset,
is24HourFormat);
}
requestLayout(); requestLayout();
} }
@@ -226,6 +162,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
super.onMeasure(widthMeasureSpec, heightMeasureSpec); super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Measures text bounds and updates indent configuration. // Measures text bounds and updates indent configuration.
if (mTextPaint != null) { if (mTextPaint != null) {
mTextPaint.setTextAlign(Paint.Align.LEFT);
for (int index = 0; index < mPercentages.length; index++) { for (int index = 0; index < mPercentages.length; index++) {
mTextPaint.getTextBounds( mTextPaint.getTextBounds(
mPercentages[index], 0, mPercentages[index].length(), mPercentages[index], 0, mPercentages[index].length(),
@@ -235,15 +172,14 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
mIndent.top = mPercentageBounds[0].height(); mIndent.top = mPercentageBounds[0].height();
mIndent.right = mPercentageBounds[0].width() + mTextPadding; mIndent.right = mPercentageBounds[0].width() + mTextPadding;
if (mTimestamps != null) { if (mViewModel != null) {
int maxHeight = 0; int maxTop = 0;
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { for (int index = 0; index < mViewModel.size(); index++) {
mTextPaint.getTextBounds( final String text = mViewModel.texts().get(index);
mTimestamps[index], 0, mTimestamps[index].length(), mTextPaint.getTextBounds(text, 0, text.length(), mAxisLabelsBounds.get(index));
mTimestampsBounds[index]); maxTop = Math.max(maxTop, -mAxisLabelsBounds.get(index).top);
maxHeight = Math.max(maxHeight, mTimestampsBounds[index].height());
} }
mIndent.bottom = maxHeight + round(mTextPadding * 1.5f); mIndent.bottom = maxTop + round(mTextPadding * 2f);
} }
Log.d(TAG, "setIndent:" + mPercentageBounds[0]); Log.d(TAG, "setIndent:" + mPercentageBounds[0]);
} else { } else {
@@ -254,7 +190,12 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
@Override @Override
public void draw(Canvas canvas) { public void draw(Canvas canvas) {
super.draw(canvas); super.draw(canvas);
// Before mLevels initialized, the count of trapezoids is unknown. Only draws the
// horizontal percentages and dividers.
drawHorizontalDividers(canvas); drawHorizontalDividers(canvas);
if (mViewModel == null) {
return;
}
drawVerticalDividers(canvas); drawVerticalDividers(canvas);
drawTrapezoids(canvas); drawTrapezoids(canvas);
} }
@@ -294,7 +235,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
public void onHoverChanged(boolean hovered) { public void onHoverChanged(boolean hovered) {
super.onHoverChanged(hovered); super.onHoverChanged(hovered);
if (!hovered) { if (!hovered) {
mHoveredIndex = SELECTED_INDEX_INVALID; // reset mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
invalidate(); invalidate();
} }
} }
@@ -307,15 +248,15 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
} }
final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX); final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX);
// Ignores the click event if the level is zero. // Ignores the click event if the level is zero.
if (trapezoidIndex == SELECTED_INDEX_INVALID if (trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID
|| !isValidToDraw(trapezoidIndex)) { || !isValidToDraw(mViewModel, trapezoidIndex)) {
return; return;
} }
// Selects all if users click the same trapezoid item two times. if (mOnSelectListener != null) {
if (trapezoidIndex == mSelectedIndex) { // Selects all if users click the same trapezoid item two times.
setSelectedIndex(SELECTED_INDEX_ALL); mOnSelectListener.onSelect(
} else { trapezoidIndex == mViewModel.selectedIndex()
setSelectedIndex(trapezoidIndex); ? BatteryChartViewModel.SELECTED_INDEX_ALL : trapezoidIndex);
} }
view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK); view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
} }
@@ -364,8 +305,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2); mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
} else if (mIsSlotsClickabled) { } else if (mIsSlotsClickabled) {
mTrapezoidCurvePaint = null; mTrapezoidCurvePaint = null;
// Sets levels again to force update the click state. // Sets view model again to force update the click state.
setLevels(mLevels); setViewModel(mViewModel);
} }
invalidate(); invalidate();
} }
@@ -380,6 +321,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
super.setClickable(clickable); super.setClickable(clickable);
} }
private void initializeTrapezoidSlots(int count) {
mTrapezoidSlots = new TrapezoidSlot[count];
for (int index = 0; index < mTrapezoidSlots.length; index++) {
mTrapezoidSlots[index] = new TrapezoidSlot();
}
}
private void initializeColors(Context context) { private void initializeColors(Context context) {
setBackgroundColor(Color.TRANSPARENT); setBackgroundColor(Color.TRANSPARENT);
mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context); mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
@@ -434,10 +382,10 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private void drawPercentage(Canvas canvas, int index, float offsetY) { private void drawPercentage(Canvas canvas, int index, float offsetY) {
if (mTextPaint != null) { if (mTextPaint != null) {
mTextPaint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText( canvas.drawText(
mPercentages[index], mPercentages[index],
getWidth() - mPercentageBounds[index].width() getWidth(),
- mPercentageBounds[index].left,
offsetY + mPercentageBounds[index].height() * .5f, offsetY + mPercentageBounds[index].height() * .5f,
mTextPaint); mTextPaint);
} }
@@ -445,9 +393,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
private void drawVerticalDividers(Canvas canvas) { private void drawVerticalDividers(Canvas canvas) {
final int width = getWidth() - mIndent.right; final int width = getWidth() - mIndent.right;
final int dividerCount = mTrapezoidCount + 1; final int dividerCount = mTrapezoidSlots.length + 1;
final float dividerSpace = dividerCount * mDividerWidth; final float dividerSpace = dividerCount * mDividerWidth;
final float unitWidth = (width - dividerSpace) / (float) mTrapezoidCount; final float unitWidth = (width - dividerSpace) / (float) mTrapezoidSlots.length;
final float bottomY = getHeight() - mIndent.bottom; final float bottomY = getHeight() - mIndent.bottom;
final float startY = bottomY - mDividerHeight; final float startY = bottomY - mDividerHeight;
final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f; final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f;
@@ -463,66 +411,140 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
} }
startX = nextX; startX = nextX;
} }
// Draws the timestamp slot information. // Draws the axis label slot information.
if (mTimestamps != null) { if (mViewModel != null) {
final float[] xOffsets = new float[DEFAULT_TIMESTAMP_COUNT]; final float baselineY = getHeight() - mTextPadding * 1.5f;
final float baselineX = mDividerWidth * .5f; Rect[] axisLabelDisplayAreas;
final float offsetX = mDividerWidth + unitWidth; switch (mViewModel.axisLabelPosition()) {
final int slotBarOffset = (/*total 12 bars*/ 12) / TIMESTAMP_GAPS_COUNT; case CENTER_OF_TRAPEZOIDS:
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { axisLabelDisplayAreas = getAxisLabelDisplayAreas(
xOffsets[index] = baselineX + index * offsetX * slotBarOffset; /* size= */ mViewModel.size() - 1,
/* baselineX= */ mDividerWidth + unitWidth * .5f,
/* offsetX= */ mDividerWidth + unitWidth,
baselineY,
/* shiftFirstAndLast= */ false);
break;
case BETWEEN_TRAPEZOIDS:
default:
axisLabelDisplayAreas = getAxisLabelDisplayAreas(
/* size= */ mViewModel.size(),
/* baselineX= */ mDividerWidth * .5f,
/* offsetX= */ mDividerWidth + unitWidth,
baselineY,
/* shiftFirstAndLast= */ true);
break;
} }
drawTimestamp(canvas, xOffsets); drawAxisLabels(canvas, axisLabelDisplayAreas, baselineY);
} }
} }
private void drawTimestamp(Canvas canvas, float[] xOffsets) { /** Gets all the axis label texts displaying area positions if they are shown. */
// Draws the 1st timestamp info. private Rect[] getAxisLabelDisplayAreas(final int size, final float baselineX,
canvas.drawText( final float offsetX, final float baselineY, final boolean shiftFirstAndLast) {
mTimestamps[0], final Rect[] result = new Rect[size];
xOffsets[0] - mTimestampsBounds[0].left, for (int index = 0; index < result.length; index++) {
getTimestampY(0), mTextPaint); final float width = mAxisLabelsBounds.get(index).width();
final int latestIndex = DEFAULT_TIMESTAMP_COUNT - 1; float middle = baselineX + index * offsetX;
// Draws the last timestamp info. if (shiftFirstAndLast) {
canvas.drawText( if (index == 0) {
mTimestamps[latestIndex], middle += width * .5f;
xOffsets[latestIndex] - mTimestampsBounds[latestIndex].width() }
- mTimestampsBounds[latestIndex].left, if (index == size - 1) {
getTimestampY(latestIndex), mTextPaint); middle -= width * .5f;
// Draws the rest of timestamp info since it is located in the center. }
for (int index = 1; index <= DEFAULT_TIMESTAMP_COUNT - 2; index++) { }
canvas.drawText( final float left = middle - width * .5f;
mTimestamps[index], final float right = left + width;
xOffsets[index] final float top = baselineY + mAxisLabelsBounds.get(index).top;
- (mTimestampsBounds[index].width() - mTimestampsBounds[index].left) final float bottom = top + mAxisLabelsBounds.get(index).height();
* .5f, result[index] = new Rect(round(left), round(top), round(right), round(bottom));
getTimestampY(index), mTextPaint); }
return result;
}
private void drawAxisLabels(Canvas canvas, final Rect[] displayAreas, final float baselineY) {
final int lastIndex = displayAreas.length - 1;
// Suppose first and last labels are always able to draw.
drawAxisLabelText(canvas, 0, displayAreas[0], baselineY);
drawAxisLabelText(canvas, lastIndex, displayAreas[lastIndex], baselineY);
drawAxisLabelsBetweenStartIndexAndEndIndex(canvas, displayAreas, 0, lastIndex, baselineY);
}
/**
* Recursively draws axis labels between the start index and the end index. If the inner number
* can be exactly divided into 2 parts, check and draw the middle index label and then
* recursively draw the 2 parts. Otherwise, divide into 3 parts. Check and draw the middle two
* labels and then recursively draw the 3 parts. If there are any overlaps, skip drawing and go
* back to the uplevel of the recursion.
*/
private void drawAxisLabelsBetweenStartIndexAndEndIndex(Canvas canvas,
final Rect[] displayAreas, final int startIndex, final int endIndex,
final float baselineY) {
if (endIndex - startIndex <= 1) {
return;
}
if ((endIndex - startIndex) % 2 == 0) {
int middleIndex = (startIndex + endIndex) / 2;
if (hasOverlap(displayAreas, startIndex, middleIndex)
|| hasOverlap(displayAreas, middleIndex, endIndex)) {
return;
}
drawAxisLabelText(canvas, middleIndex, displayAreas[middleIndex], baselineY);
drawAxisLabelsBetweenStartIndexAndEndIndex(
canvas, displayAreas, startIndex, middleIndex, baselineY);
drawAxisLabelsBetweenStartIndexAndEndIndex(
canvas, displayAreas, middleIndex, endIndex, baselineY);
} else {
int middleIndex1 = startIndex + round((endIndex - startIndex) / 3f);
int middleIndex2 = startIndex + round((endIndex - startIndex) * 2 / 3f);
if (hasOverlap(displayAreas, startIndex, middleIndex1)
|| hasOverlap(displayAreas, middleIndex1, middleIndex2)
|| hasOverlap(displayAreas, middleIndex2, endIndex)) {
return;
}
drawAxisLabelText(canvas, middleIndex1, displayAreas[middleIndex1], baselineY);
drawAxisLabelText(canvas, middleIndex2, displayAreas[middleIndex2], baselineY);
drawAxisLabelsBetweenStartIndexAndEndIndex(
canvas, displayAreas, startIndex, middleIndex1, baselineY);
drawAxisLabelsBetweenStartIndexAndEndIndex(
canvas, displayAreas, middleIndex1, middleIndex2, baselineY);
drawAxisLabelsBetweenStartIndexAndEndIndex(
canvas, displayAreas, middleIndex2, endIndex, baselineY);
} }
} }
private int getTimestampY(int index) { private boolean hasOverlap(
return getHeight() - mTimestampsBounds[index].height() final Rect[] displayAreas, final int leftIndex, final int rightIndex) {
+ (mTimestampsBounds[index].height() + mTimestampsBounds[index].top) return displayAreas[leftIndex].right + mTextPadding * 2f > displayAreas[rightIndex].left;
+ round(mTextPadding * 1.5f); }
private void drawAxisLabelText(
Canvas canvas, final int index, final Rect displayArea, final float baselineY) {
mTextPaint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(
mViewModel.texts().get(index),
displayArea.centerX(),
baselineY,
mTextPaint);
} }
private void drawTrapezoids(Canvas canvas) { private void drawTrapezoids(Canvas canvas) {
// Ignores invalid trapezoid data. // Ignores invalid trapezoid data.
if (mLevels == null) { if (mViewModel == null) {
return; return;
} }
final float trapezoidBottom = final float trapezoidBottom =
getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth
- mTrapezoidVOffset; - mTrapezoidVOffset;
final float availableSpace = trapezoidBottom - mDividerWidth * .5f - mIndent.top; final float availableSpace =
trapezoidBottom - mDividerWidth * .5f - mIndent.top - mTrapezoidVOffset;
final float unitHeight = availableSpace / 100f; final float unitHeight = availableSpace / 100f;
// Draws all trapezoid shapes into the canvas. // Draws all trapezoid shapes into the canvas.
final Path trapezoidPath = new Path(); final Path trapezoidPath = new Path();
Path trapezoidCurvePath = null; Path trapezoidCurvePath = null;
for (int index = 0; index < mTrapezoidCount; index++) { for (int index = 0; index < mTrapezoidSlots.length; index++) {
// Not draws the trapezoid for corner or not initialization cases. // Not draws the trapezoid for corner or not initialization cases.
if (!isValidToDraw(index)) { if (!isValidToDraw(mViewModel, index)) {
if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) { if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) {
canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint); canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint);
trapezoidCurvePath = null; trapezoidCurvePath = null;
@@ -530,17 +552,18 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
continue; continue;
} }
// Configures the trapezoid paint color. // Configures the trapezoid paint color.
final int trapezoidColor = final int trapezoidColor = mIsSlotsClickabled && (mViewModel.selectedIndex() == index
!mIsSlotsClickabled || mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL)
? mTrapezoidColor ? mTrapezoidSolidColor : mTrapezoidColor;
: mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
? mTrapezoidSolidColor : mTrapezoidColor;
final boolean isHoverState = final boolean isHoverState =
mIsSlotsClickabled && mHoveredIndex == index && isValidToDraw(mHoveredIndex); mIsSlotsClickabled && mHoveredIndex == index
&& isValidToDraw(mViewModel, mHoveredIndex);
mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor); mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor);
final float leftTop = round(trapezoidBottom - mLevels[index] * unitHeight); final float leftTop = round(
final float rightTop = round(trapezoidBottom - mLevels[index + 1] * unitHeight); trapezoidBottom - requireNonNull(mViewModel.levels().get(index)) * unitHeight);
final float rightTop = round(trapezoidBottom
- requireNonNull(mViewModel.levels().get(index + 1)) * unitHeight);
trapezoidPath.reset(); trapezoidPath.reset();
trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom); trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom);
trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop); trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop);
@@ -579,15 +602,37 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
return index; return index;
} }
} }
return SELECTED_INDEX_INVALID; return BatteryChartViewModel.SELECTED_INDEX_INVALID;
} }
private boolean isValidToDraw(int trapezoidIndex) { private void initializeAxisLabelsBounds() {
return mLevels != null mAxisLabelsBounds.clear();
for (int i = 0; i < mViewModel.size(); i++) {
mAxisLabelsBounds.add(new Rect());
}
}
private static boolean isTrapezoidValid(
@NonNull BatteryChartViewModel viewModel, int trapezoidIndex) {
return viewModel.levels().get(trapezoidIndex) != null
&& viewModel.levels().get(trapezoidIndex + 1) != null;
}
private static boolean isValidToDraw(BatteryChartViewModel viewModel, int trapezoidIndex) {
return viewModel != null
&& trapezoidIndex >= 0 && trapezoidIndex >= 0
&& trapezoidIndex < mLevels.length - 1 && trapezoidIndex < viewModel.size() - 1
&& mLevels[trapezoidIndex] != 0 && isTrapezoidValid(viewModel, trapezoidIndex);
&& mLevels[trapezoidIndex + 1] != 0; }
private static boolean hasAnyValidTrapezoid(@NonNull BatteryChartViewModel viewModel) {
// Sets the chart is clickable if there is at least one valid item in it.
for (int trapezoidIndex = 0; trapezoidIndex < viewModel.size() - 1; trapezoidIndex++) {
if (isTrapezoidValid(viewModel, trapezoidIndex)) {
return true;
}
}
return false;
} }
private static String[] getPercentages() { private static String[] getPercentages() {
@@ -621,7 +666,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
} }
// A container class for each trapezoid left and right location. // A container class for each trapezoid left and right location.
private static final class TrapezoidSlot { @VisibleForTesting
static final class TrapezoidSlot {
public float mLeft; public float mLeft;
public float mRight; public float mRight;

View File

@@ -0,0 +1,109 @@
/*
* 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 androidx.annotation.NonNull;
import androidx.core.util.Preconditions;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
/** The view model of {@code BatteryChartView} */
class BatteryChartViewModel {
private static final String TAG = "BatteryChartViewModel";
public static final int SELECTED_INDEX_ALL = -1;
public static final int SELECTED_INDEX_INVALID = -2;
// We need at least 2 levels to draw a trapezoid.
private static final int MIN_LEVELS_DATA_SIZE = 2;
enum AxisLabelPosition {
BETWEEN_TRAPEZOIDS,
CENTER_OF_TRAPEZOIDS,
}
private final List<Integer> mLevels;
private final List<String> mTexts;
private final AxisLabelPosition mAxisLabelPosition;
private int mSelectedIndex = SELECTED_INDEX_ALL;
BatteryChartViewModel(
@NonNull List<Integer> levels, @NonNull List<String> texts,
@NonNull AxisLabelPosition axisLabelPosition) {
Preconditions.checkArgument(
levels.size() == texts.size() && levels.size() >= MIN_LEVELS_DATA_SIZE,
String.format(Locale.ENGLISH,
"Invalid BatteryChartViewModel levels.size: %d, texts.size: %d.",
levels.size(), texts.size()));
mLevels = levels;
mTexts = texts;
mAxisLabelPosition = axisLabelPosition;
}
public int size() {
return mLevels.size();
}
public List<Integer> levels() {
return mLevels;
}
public List<String> texts() {
return mTexts;
}
public AxisLabelPosition axisLabelPosition() {
return mAxisLabelPosition;
}
public int selectedIndex() {
return mSelectedIndex;
}
public void setSelectedIndex(int index) {
mSelectedIndex = index;
}
@Override
public int hashCode() {
return Objects.hash(mLevels, mTexts, mSelectedIndex, mAxisLabelPosition);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (!(other instanceof BatteryChartViewModel)) {
return false;
}
final BatteryChartViewModel batteryChartViewModel = (BatteryChartViewModel) other;
return Objects.equals(mLevels, batteryChartViewModel.mLevels)
&& Objects.equals(mTexts, batteryChartViewModel.mTexts)
&& mAxisLabelPosition == batteryChartViewModel.mAxisLabelPosition
&& mSelectedIndex == batteryChartViewModel.mSelectedIndex;
}
@Override
public String toString() {
return String.format(Locale.ENGLISH,
"levels: %s,\ntexts: %s,\naxisLabelPosition: %s, selectedIndex: %d",
Objects.toString(mLevels), Objects.toString(mTexts), mAxisLabelPosition,
mSelectedIndex);
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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 androidx.annotation.NonNull;
import java.util.Collections;
import java.util.List;
/** Wraps the battery usage diff data for each entry used for battery usage app list. */
public class BatteryDiffData {
private final List<BatteryDiffEntry> mAppEntries;
private final List<BatteryDiffEntry> mSystemEntries;
/** Constructor for the diff entries which already have totalConsumePower value. */
public BatteryDiffData(
@NonNull List<BatteryDiffEntry> appDiffEntries,
@NonNull List<BatteryDiffEntry> systemDiffEntries) {
mAppEntries = appDiffEntries;
mSystemEntries = systemDiffEntries;
sortEntries();
}
/** Constructor for the diff entries which have not set totalConsumePower value. */
public BatteryDiffData(
@NonNull List<BatteryDiffEntry> appDiffEntries,
@NonNull List<BatteryDiffEntry> systemDiffEntries,
final double totalConsumePower) {
mAppEntries = appDiffEntries;
mSystemEntries = systemDiffEntries;
setTotalConsumePowerForAllEntries(totalConsumePower);
sortEntries();
}
public List<BatteryDiffEntry> getAppDiffEntryList() {
return mAppEntries;
}
public List<BatteryDiffEntry> getSystemDiffEntryList() {
return mSystemEntries;
}
// Sets total consume power for each entry.
private void setTotalConsumePowerForAllEntries(final double totalConsumePower) {
mAppEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
mSystemEntries.forEach(diffEntry -> diffEntry.setTotalConsumePower(totalConsumePower));
}
// Sorts entries based on consumed percentage.
private void sortEntries() {
Collections.sort(mAppEntries, BatteryDiffEntry.COMPARATOR);
Collections.sort(mSystemEntries, BatteryDiffEntry.COMPARATOR);
}
}

View File

@@ -43,6 +43,6 @@ public class BatteryHistoryLoader
public Map<Long, Map<String, BatteryHistEntry>> loadInBackground() { public Map<Long, Map<String, BatteryHistEntry>> loadInBackground() {
final PowerUsageFeatureProvider powerUsageFeatureProvider = final PowerUsageFeatureProvider powerUsageFeatureProvider =
FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(mContext); FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(mContext);
return powerUsageFeatureProvider.getBatteryHistory(mContext); return powerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext);
} }
} }

View File

@@ -49,7 +49,8 @@ public class BatteryHistoryPreference extends Preference {
private TextView mSummaryView; private TextView mSummaryView;
private CharSequence mSummaryContent; private CharSequence mSummaryContent;
private BatteryChartView mBatteryChartView; private BatteryChartView mDailyChartView;
private BatteryChartView mHourlyChartView;
private BatteryChartPreferenceController mChartPreferenceController; private BatteryChartPreferenceController mChartPreferenceController;
public BatteryHistoryPreference(Context context, AttributeSet attrs) { public BatteryHistoryPreference(Context context, AttributeSet attrs) {
@@ -92,8 +93,8 @@ public class BatteryHistoryPreference extends Preference {
void setChartPreferenceController(BatteryChartPreferenceController controller) { void setChartPreferenceController(BatteryChartPreferenceController controller) {
mChartPreferenceController = controller; mChartPreferenceController = controller;
if (mBatteryChartView != null) { if (mDailyChartView != null && mHourlyChartView != null) {
mChartPreferenceController.setBatteryChartView(mBatteryChartView); mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView);
} }
} }
@@ -105,11 +106,14 @@ public class BatteryHistoryPreference extends Preference {
return; return;
} }
if (mIsChartGraphEnabled) { if (mIsChartGraphEnabled) {
mBatteryChartView = (BatteryChartView) view.findViewById(R.id.battery_chart); mDailyChartView = (BatteryChartView) view.findViewById(R.id.daily_battery_chart);
mBatteryChartView.setCompanionTextView( mDailyChartView.setCompanionTextView(
(TextView) view.findViewById(R.id.companion_text));
mHourlyChartView = (BatteryChartView) view.findViewById(R.id.hourly_battery_chart);
mHourlyChartView.setCompanionTextView(
(TextView) view.findViewById(R.id.companion_text)); (TextView) view.findViewById(R.id.companion_text));
if (mChartPreferenceController != null) { if (mChartPreferenceController != null) {
mChartPreferenceController.setBatteryChartView(mBatteryChartView); mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView);
} }
} else { } else {
final TextView chargeView = (TextView) view.findViewById(R.id.charge); final TextView chargeView = (TextView) view.findViewById(R.id.charge);

View File

@@ -0,0 +1,98 @@
/*
* 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 androidx.annotation.NonNull;
import androidx.core.util.Preconditions;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
/** Wraps the battery timestamp and level data used for battery usage chart. */
public final class BatteryLevelData {
/** A container for the battery timestamp and level data. */
public static final class PeriodBatteryLevelData {
// The length of mTimestamps and mLevels must be the same. mLevels[index] might be null when
// there is no level data for the corresponding timestamp.
private final List<Long> mTimestamps;
private final List<Integer> mLevels;
public PeriodBatteryLevelData(
@NonNull List<Long> timestamps, @NonNull List<Integer> levels) {
Preconditions.checkArgument(timestamps.size() == levels.size(),
/* errorMessage= */ "Timestamp: " + timestamps.size() + ", Level: "
+ levels.size());
mTimestamps = timestamps;
mLevels = levels;
}
public List<Long> getTimestamps() {
return mTimestamps;
}
public List<Integer> getLevels() {
return mLevels;
}
@Override
public String toString() {
return String.format(Locale.ENGLISH, "timestamps: %s; levels: %s",
Objects.toString(mTimestamps), Objects.toString(mLevels));
}
}
/**
* There could be 2 cases for the daily battery levels:
* 1) length is 2: The usage data is within 1 day. Only contains start and end data, such as
* data of 2022-01-01 06:00 and 2022-01-01 16:00.
* 2) length > 2: The usage data is more than 1 days. The data should be the start, end and 0am
* data of every day between the start and end, such as data of 2022-01-01 06:00,
* 2022-01-02 00:00, 2022-01-03 00:00 and 2022-01-03 08:00.
*/
private final PeriodBatteryLevelData mDailyBatteryLevels;
// The size of hourly data must be the size of daily data - 1.
private final List<PeriodBatteryLevelData> mHourlyBatteryLevelsPerDay;
public BatteryLevelData(
@NonNull PeriodBatteryLevelData dailyBatteryLevels,
@NonNull List<PeriodBatteryLevelData> hourlyBatteryLevelsPerDay) {
final long dailySize = dailyBatteryLevels.getTimestamps().size();
final long hourlySize = hourlyBatteryLevelsPerDay.size();
Preconditions.checkArgument(hourlySize == dailySize - 1,
/* errorMessage= */ "DailySize: " + dailySize + ", HourlySize: " + hourlySize);
mDailyBatteryLevels = dailyBatteryLevels;
mHourlyBatteryLevelsPerDay = hourlyBatteryLevelsPerDay;
}
public PeriodBatteryLevelData getDailyBatteryLevels() {
return mDailyBatteryLevels;
}
public List<PeriodBatteryLevelData> getHourlyBatteryLevelsPerDay() {
return mHourlyBatteryLevelsPerDay;
}
@Override
public String toString() {
return String.format(Locale.ENGLISH,
"dailyBatteryLevels: %s; hourlyBatteryLevelsPerDay: %s",
Objects.toString(mDailyBatteryLevels),
Objects.toString(mHourlyBatteryLevelsPerDay));
}
}

View File

@@ -140,7 +140,7 @@ public final class ConvertUtils {
/** Converts UTC timestamp to local time hour data. */ /** Converts UTC timestamp to local time hour data. */
public static String utcToLocalTimeHour( public static String utcToLocalTimeHour(
Context context, long timestamp, boolean is24HourFormat) { final Context context, final long timestamp, final boolean is24HourFormat) {
final Locale locale = getLocale(context); final Locale locale = getLocale(context);
// e.g. for 12-hour format: 9 pm // e.g. for 12-hour format: 9 pm
// e.g. for 24-hour format: 09:00 // e.g. for 24-hour format: 09:00
@@ -149,6 +149,15 @@ public final class ConvertUtils {
return DateFormat.format(pattern, timestamp).toString().toLowerCase(locale); return DateFormat.format(pattern, timestamp).toString().toLowerCase(locale);
} }
/** Converts UTC timestamp to local time day of week data. */
public static String utcToLocalTimeDayOfWeek(
final Context context, final long timestamp, final boolean isAbbreviation) {
final Locale locale = getLocale(context);
final String pattern = DateFormat.getBestDateTimePattern(locale,
isAbbreviation ? "E" : "EEEE");
return DateFormat.format(pattern, timestamp).toString();
}
/** Gets indexed battery usage data for each corresponding time slot. */ /** Gets indexed battery usage data for each corresponding time slot. */
public static Map<Integer, List<BatteryDiffEntry>> getIndexedUsageMap( public static Map<Integer, List<BatteryDiffEntry>> getIndexedUsageMap(
final Context context, final Context context,
@@ -267,7 +276,7 @@ public final class ConvertUtils {
diffEntry.setTotalConsumePower(totalConsumePower); diffEntry.setTotalConsumePower(totalConsumePower);
} }
} }
insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap); insert24HoursData(BatteryChartViewModel.SELECTED_INDEX_ALL, resultMap);
resolveMultiUsersData(context, resultMap); resolveMultiUsersData(context, resultMap);
if (purgeLowPercentageAndFakeData) { if (purgeLowPercentageAndFakeData) {
purgeLowPercentageAndFakeData(context, resultMap); purgeLowPercentageAndFakeData(context, resultMap);

File diff suppressed because it is too large Load Diff

View File

@@ -259,10 +259,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
@VisibleForTesting @VisibleForTesting
void initPreference() { void initPreference() {
mBatteryUsagePreference = findPreference(KEY_BATTERY_USAGE); mBatteryUsagePreference = findPreference(KEY_BATTERY_USAGE);
mBatteryUsagePreference.setSummary( mBatteryUsagePreference.setSummary(getString(R.string.advanced_battery_preference_summary));
mPowerFeatureProvider.isChartGraphEnabled(getContext())
? getString(R.string.advanced_battery_preference_summary_with_hours)
: getString(R.string.advanced_battery_preference_summary));
mHelpPreference = findPreference(KEY_BATTERY_ERROR); mHelpPreference = findPreference(KEY_BATTERY_ERROR);
mHelpPreference.setVisible(false); mHelpPreference.setVisible(false);

View File

@@ -162,7 +162,8 @@ public class AppBatteryPreferenceControllerTest {
mController.updateBatteryWithDiffEntry(); mController.updateBatteryWithDiffEntry();
assertThat(mBatteryPreference.getSummary()).isEqualTo("No battery use for past 24 hours"); assertThat(mBatteryPreference.getSummary().toString()).isEqualTo(
"No battery use since last full charge");
} }
@Test @Test
@@ -175,7 +176,8 @@ public class AppBatteryPreferenceControllerTest {
mController.updateBatteryWithDiffEntry(); mController.updateBatteryWithDiffEntry();
assertThat(mBatteryPreference.getSummary()).isEqualTo("60% use for past 24 hours"); assertThat(mBatteryPreference.getSummary().toString()).isEqualTo(
"60% use since last full charge");
} }
@Test @Test

View File

@@ -232,7 +232,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testGetPreferenceScreenResId_returnNewLayout() { public void setPreferenceScreenResId_returnNewLayout() {
assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(R.xml.power_usage_detail); assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(R.xml.power_usage_detail);
} }
@@ -252,7 +252,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_HasAppEntry_BuildByAppEntry() { public void initHeader_HasAppEntry_BuildByAppEntry() {
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
new InstantAppDataProvider() { new InstantAppDataProvider() {
@Override @Override
@@ -269,7 +269,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_HasAppEntry_InstantApp() { public void initHeader_HasAppEntry_InstantApp() {
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider", ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
new InstantAppDataProvider() { new InstantAppDataProvider() {
@Override @Override
@@ -286,7 +286,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_noUsageTimeAndGraphDisabled_hasCorrectSummary() { public void initHeader_noUsageTimeAndGraphDisabled_hasCorrectSummary() {
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext)) when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
.thenReturn(false); .thenReturn(false);
@@ -304,7 +304,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_bgTwoMinFgZeroAndGraphDisabled_hasCorrectSummary() { public void initHeader_bgTwoMinFgZeroAndGraphDisabled_hasCorrectSummary() {
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext)) when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
.thenReturn(false); .thenReturn(false);
@@ -324,7 +324,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_bgLessThanAMinFgZeroAndGraphDisabled_hasCorrectSummary() { public void initHeader_bgLessThanAMinFgZeroAndGraphDisabled_hasCorrectSummary() {
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext)) when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
.thenReturn(false); .thenReturn(false);
@@ -345,7 +345,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_totalUsageLessThanAMinAndGraphDisabled_hasCorrectSummary() { public void initHeader_totalUsageLessThanAMinAndGraphDisabled_hasCorrectSummary() {
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext)) when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
.thenReturn(false); .thenReturn(false);
@@ -367,7 +367,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_TotalAMinutesBgLessThanAMinAndGraphDisabled_hasCorrectSummary() { public void initHeader_TotalAMinutesBgLessThanAMinAndGraphDisabled_hasCorrectSummary() {
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext)) when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
.thenReturn(false); .thenReturn(false);
@@ -387,7 +387,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_TotalAMinBackgroundZeroAndGraphDisabled_hasCorrectSummary() { public void initHeader_TotalAMinBackgroundZeroAndGraphDisabled_hasCorrectSummary() {
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext)) when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
.thenReturn(false); .thenReturn(false);
final long backgroundTimeZero = 0; final long backgroundTimeZero = 0;
@@ -406,7 +406,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_fgTwoMinBgFourMinAndGraphDisabled_hasCorrectSummary() { public void initHeader_fgTwoMinBgFourMinAndGraphDisabled_hasCorrectSummary() {
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext)) when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
.thenReturn(false); .thenReturn(false);
final long backgroundTimeFourMinute = 240000; final long backgroundTimeFourMinute = 240000;
@@ -424,7 +424,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_noUsageTime_hasCorrectSummary() { public void initHeader_noUsageTime_hasCorrectSummary() {
Bundle bundle = new Bundle(2); Bundle bundle = new Bundle(2);
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, /* value */ 0); bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, /* value */ 0);
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, /* value */ 0); bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, /* value */ 0);
@@ -435,11 +435,11 @@ public class AdvancedPowerUsageDetailTest {
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class); ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
verify(mEntityHeaderController).setSummary(captor.capture()); verify(mEntityHeaderController).setSummary(captor.capture());
assertThat(captor.getValue().toString()) assertThat(captor.getValue().toString())
.isEqualTo("No usage for past 24 hr"); .isEqualTo("No usage from last full charge");
} }
@Test @Test
public void testInitHeader_noUsageTimeButConsumedPower_hasEmptySummary() { public void initHeader_noUsageTimeButConsumedPower_hasEmptySummary() {
Bundle bundle = new Bundle(3); Bundle bundle = new Bundle(3);
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, /* value */ 0); bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, /* value */ 0);
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, /* value */ 0); bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, /* value */ 0);
@@ -454,7 +454,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_backgroundTwoMinForegroundZero_hasCorrectSummary() { public void initHeader_backgroundTwoMinForegroundZero_hasCorrectSummary() {
final long backgroundTimeTwoMinutes = 120000; final long backgroundTimeTwoMinutes = 120000;
final long foregroundTimeZero = 0; final long foregroundTimeZero = 0;
Bundle bundle = new Bundle(2); Bundle bundle = new Bundle(2);
@@ -467,11 +467,11 @@ public class AdvancedPowerUsageDetailTest {
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class); ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
verify(mEntityHeaderController).setSummary(captor.capture()); verify(mEntityHeaderController).setSummary(captor.capture());
assertThat(captor.getValue().toString()) assertThat(captor.getValue().toString())
.isEqualTo("2 min background for past 24 hr"); .isEqualTo("2 min background from last full charge");
} }
@Test @Test
public void testInitHeader_backgroundLessThanAMinForegroundZero_hasCorrectSummary() { public void initHeader_backgroundLessThanAMinForegroundZero_hasCorrectSummary() {
final long backgroundTimeLessThanAMinute = 59999; final long backgroundTimeLessThanAMinute = 59999;
final long foregroundTimeZero = 0; final long foregroundTimeZero = 0;
Bundle bundle = new Bundle(2); Bundle bundle = new Bundle(2);
@@ -485,11 +485,11 @@ public class AdvancedPowerUsageDetailTest {
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class); ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
verify(mEntityHeaderController).setSummary(captor.capture()); verify(mEntityHeaderController).setSummary(captor.capture());
assertThat(captor.getValue().toString()) assertThat(captor.getValue().toString())
.isEqualTo("Background less than a minute for past 24 hr"); .isEqualTo("Background less than a minute from last full charge");
} }
@Test @Test
public void testInitHeader_totalUsageLessThanAMin_hasCorrectSummary() { public void initHeader_totalUsageLessThanAMin_hasCorrectSummary() {
final long backgroundTimeLessThanHalfMinute = 20000; final long backgroundTimeLessThanHalfMinute = 20000;
final long foregroundTimeLessThanHalfMinute = 20000; final long foregroundTimeLessThanHalfMinute = 20000;
Bundle bundle = new Bundle(2); Bundle bundle = new Bundle(2);
@@ -504,11 +504,11 @@ public class AdvancedPowerUsageDetailTest {
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class); ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
verify(mEntityHeaderController).setSummary(captor.capture()); verify(mEntityHeaderController).setSummary(captor.capture());
assertThat(captor.getValue().toString()) assertThat(captor.getValue().toString())
.isEqualTo("Total less than a minute for past 24 hr"); .isEqualTo("Total less than a minute from last full charge");
} }
@Test @Test
public void testInitHeader_TotalAMinutesBackgroundLessThanAMin_hasCorrectSummary() { public void initHeader_TotalAMinutesBackgroundLessThanAMin_hasCorrectSummary() {
final long backgroundTimeZero = 59999; final long backgroundTimeZero = 59999;
final long foregroundTimeTwoMinutes = 1; final long foregroundTimeTwoMinutes = 1;
Bundle bundle = new Bundle(2); Bundle bundle = new Bundle(2);
@@ -521,11 +521,11 @@ public class AdvancedPowerUsageDetailTest {
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class); ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
verify(mEntityHeaderController).setSummary(captor.capture()); verify(mEntityHeaderController).setSummary(captor.capture());
assertThat(captor.getValue().toString()) assertThat(captor.getValue().toString())
.isEqualTo("1 min total • background less than a minute\nfor past 24 hr"); .isEqualTo("1 min total • background less than a minute\nfrom last full charge");
} }
@Test @Test
public void testInitHeader_TotalAMinBackgroundZero_hasCorrectSummary() { public void initHeader_TotalAMinBackgroundZero_hasCorrectSummary() {
final long backgroundTimeZero = 0; final long backgroundTimeZero = 0;
final long foregroundTimeAMinutes = 60000; final long foregroundTimeAMinutes = 60000;
Bundle bundle = new Bundle(2); Bundle bundle = new Bundle(2);
@@ -538,11 +538,11 @@ public class AdvancedPowerUsageDetailTest {
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class); ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
verify(mEntityHeaderController).setSummary(captor.capture()); verify(mEntityHeaderController).setSummary(captor.capture());
assertThat(captor.getValue().toString()) assertThat(captor.getValue().toString())
.isEqualTo("1 min total for past 24 hr"); .isEqualTo("1 min total from last full charge");
} }
@Test @Test
public void testInitHeader_foregroundTwoMinBackgroundFourMin_hasCorrectSummary() { public void initHeader_foregroundTwoMinBackgroundFourMin_hasCorrectSummary() {
final long backgroundTimeFourMinute = 240000; final long backgroundTimeFourMinute = 240000;
final long foregroundTimeTwoMinutes = 120000; final long foregroundTimeTwoMinutes = 120000;
Bundle bundle = new Bundle(2); Bundle bundle = new Bundle(2);
@@ -555,11 +555,11 @@ public class AdvancedPowerUsageDetailTest {
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class); ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
verify(mEntityHeaderController).setSummary(captor.capture()); verify(mEntityHeaderController).setSummary(captor.capture());
assertThat(captor.getValue().toString()) assertThat(captor.getValue().toString())
.isEqualTo("6 min total • 4 min background\nfor past 24 hr"); .isEqualTo("6 min total • 4 min background\nfrom last full charge");
} }
@Test @Test
public void testInitHeader_totalUsageLessThanAMinWithSlotTime_hasCorrectSummary() { public void initHeader_totalUsageLessThanAMinWithSlotTime_hasCorrectSummary() {
final long backgroundTimeLessThanHalfMinute = 20000; final long backgroundTimeLessThanHalfMinute = 20000;
final long foregroundTimeLessThanHalfMinute = 20000; final long foregroundTimeLessThanHalfMinute = 20000;
Bundle bundle = new Bundle(3); Bundle bundle = new Bundle(3);
@@ -579,7 +579,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_TotalAMinBackgroundLessThanAMinWithSlotTime_hasCorrectSummary() { public void initHeader_TotalAMinBackgroundLessThanAMinWithSlotTime_hasCorrectSummary() {
final long backgroundTimeZero = 59999; final long backgroundTimeZero = 59999;
final long foregroundTimeTwoMinutes = 1; final long foregroundTimeTwoMinutes = 1;
Bundle bundle = new Bundle(3); Bundle bundle = new Bundle(3);
@@ -597,7 +597,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_TotalAMinBackgroundZeroWithSlotTime_hasCorrectSummary() { public void initHeader_TotalAMinBackgroundZeroWithSlotTime_hasCorrectSummary() {
final long backgroundTimeZero = 0; final long backgroundTimeZero = 0;
final long foregroundTimeAMinutes = 60000; final long foregroundTimeAMinutes = 60000;
Bundle bundle = new Bundle(3); Bundle bundle = new Bundle(3);
@@ -615,7 +615,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_foregroundTwoMinBackgroundFourMinWithSlotTime_hasCorrectSummary() { public void initHeader_foregroundTwoMinBackgroundFourMinWithSlotTime_hasCorrectSummary() {
final long backgroundTimeFourMinute = 240000; final long backgroundTimeFourMinute = 240000;
final long foregroundTimeTwoMinutes = 120000; final long foregroundTimeTwoMinutes = 120000;
Bundle bundle = new Bundle(3); Bundle bundle = new Bundle(3);
@@ -633,7 +633,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_systemUidWithChartIsDisabled_nullSummary() { public void initHeader_systemUidWithChartIsDisabled_nullSummary() {
Bundle bundle = new Bundle(3); Bundle bundle = new Bundle(3);
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, 240000); bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, 240000);
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, 120000); bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, 120000);
@@ -650,7 +650,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitHeader_systemUidWithChartIsEnabled_notNullSummary() { public void initHeader_systemUidWithChartIsEnabled_notNullSummary() {
Bundle bundle = new Bundle(3); Bundle bundle = new Bundle(3);
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, 240000); bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, 240000);
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, 120000); bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, 120000);
@@ -665,21 +665,21 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testStartBatteryDetailPage_hasBasicData() { public void startBatteryDetailPage_hasBasicData() {
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment, AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
mBatteryEntry, USAGE_PERCENT, /*isValidToShowSummary=*/ true); mBatteryEntry, USAGE_PERCENT, /*isValidToShowSummary=*/ true);
assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID); assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID);
assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME)) assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME))
.isEqualTo(BACKGROUND_TIME_MS); .isEqualTo(BACKGROUND_TIME_MS);
assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME)) assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME))
.isEqualTo(FOREGROUND_TIME_MS); .isEqualTo(FOREGROUND_TIME_MS);
assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT)) assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT))
.isEqualTo(USAGE_PERCENT); .isEqualTo(USAGE_PERCENT);
} }
@Test @Test
public void testStartBatteryDetailPage_invalidToShowSummary_noFGBDData() { public void startBatteryDetailPage_invalidToShowSummary_noFGBDData() {
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment, AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
mBatteryEntry, USAGE_PERCENT, /*isValidToShowSummary=*/ false); mBatteryEntry, USAGE_PERCENT, /*isValidToShowSummary=*/ false);
@@ -693,7 +693,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testStartBatteryDetailPage_NormalApp() { public void startBatteryDetailPage_NormalApp() {
when(mBatteryEntry.getDefaultPackageName()).thenReturn(PACKAGE_NAME[0]); when(mBatteryEntry.getDefaultPackageName()).thenReturn(PACKAGE_NAME[0]);
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment, AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
@@ -704,7 +704,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testStartBatteryDetailPage_SystemApp() { public void startBatteryDetailPage_SystemApp() {
when(mBatteryEntry.getDefaultPackageName()).thenReturn(null); when(mBatteryEntry.getDefaultPackageName()).thenReturn(null);
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment, AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
@@ -716,7 +716,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testStartBatteryDetailPage_WorkApp() { public void startBatteryDetailPage_WorkApp() {
final int appUid = 1010019; final int appUid = 1010019;
doReturn(appUid).when(mBatteryEntry).getUid(); doReturn(appUid).when(mBatteryEntry).getUid();
@@ -727,7 +727,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testStartBatteryDetailPage_typeUser_startByCurrentUser() { public void startBatteryDetailPage_typeUser_startByCurrentUser() {
when(mBatteryEntry.isUserEntry()).thenReturn(true); when(mBatteryEntry.isUserEntry()).thenReturn(true);
final int currentUser = 20; final int currentUser = 20;
@@ -739,7 +739,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testStartBatteryDetailPage_noBatteryUsage_hasBasicData() { public void startBatteryDetailPage_noBatteryUsage_hasBasicData() {
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment, PACKAGE_NAME[0]); AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment, PACKAGE_NAME[0]);
@@ -747,16 +747,16 @@ public class AdvancedPowerUsageDetailTest {
verify(mActivity).startActivity(captor.capture()); verify(mActivity).startActivity(captor.capture());
assertThat(captor.getValue().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS) assertThat(captor.getValue().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS)
.getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME)) .getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME))
.isEqualTo(PACKAGE_NAME[0]); .isEqualTo(PACKAGE_NAME[0]);
assertThat(captor.getValue().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS) assertThat(captor.getValue().getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS)
.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT)) .getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT))
.isEqualTo("0%"); .isEqualTo("0%");
} }
@Test @Test
public void testStartBatteryDetailPage_batteryEntryNotExisted_extractUidFromPackageName() throws public void startBatteryDetailPage_batteryEntryNotExisted_extractUidFromPackageName() throws
PackageManager.NameNotFoundException { PackageManager.NameNotFoundException {
doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME[0], 0 /* no flag */); doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME[0], 0 /* no flag */);
@@ -796,7 +796,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitPreferenceForTriState_isSystemOrDefaultApp_hasCorrectString() { public void initPreferenceForTriState_isSystemOrDefaultApp_hasCorrectString() {
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
@@ -807,7 +807,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testInitPreferenceForTriState_hasCorrectString() { public void initPreferenceForTriState_hasCorrectString() {
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true); when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false);
@@ -818,7 +818,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testOnRadioButtonClicked_clickOptimizePref_optimizePreferenceChecked() { public void onRadioButtonClicked_clickOptimizePref_optimizePreferenceChecked() {
mOptimizePreference.setKey(KEY_PREF_OPTIMIZED); mOptimizePreference.setKey(KEY_PREF_OPTIMIZED);
mRestrictedPreference.setKey(KEY_PREF_RESTRICTED); mRestrictedPreference.setKey(KEY_PREF_RESTRICTED);
mUnrestrictedPreference.setKey(KEY_PREF_UNRESTRICTED); mUnrestrictedPreference.setKey(KEY_PREF_UNRESTRICTED);
@@ -830,7 +830,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testOnPause_optimizationModeChanged_logPreference() { public void onPause_optimizationModeChanged_logPreference() {
final int mode = BatteryOptimizeUtils.MODE_RESTRICTED; final int mode = BatteryOptimizeUtils.MODE_RESTRICTED;
mFragment.mOptimizationMode = mode; mFragment.mOptimizationMode = mode;
when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode); when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode);
@@ -849,7 +849,7 @@ public class AdvancedPowerUsageDetailTest {
} }
@Test @Test
public void testOnPause_optimizationModeIsNotChanged_notInvokeLogging() { public void onPause_optimizationModeIsNotChanged_notInvokeLogging() {
final int mode = BatteryOptimizeUtils.MODE_OPTIMIZED; final int mode = BatteryOptimizeUtils.MODE_OPTIMIZED;
mFragment.mOptimizationMode = mode; mFragment.mOptimizationMode = mode;
when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode); when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode);

View File

@@ -18,23 +18,24 @@ package com.android.settings.fuelgauge.batteryusage;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.app.settings.SettingsEnums; import android.app.settings.SettingsEnums;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.LocaleList; import android.os.LocaleList;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.View;
import android.widget.LinearLayout;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceCategory;
@@ -58,15 +59,15 @@ import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
public final class BatteryChartPreferenceControllerTest { public final class BatteryChartPreferenceControllerTest {
private static final String PREF_KEY = "pref_key"; private static final String PREF_KEY = "pref_key";
private static final String PREF_SUMMARY = "fake preference summary"; private static final String PREF_SUMMARY = "fake preference summary";
private static final int DESIRED_HISTORY_SIZE =
BatteryChartPreferenceController.DESIRED_HISTORY_SIZE;
@Mock @Mock
private InstrumentedPreferenceFragment mFragment; private InstrumentedPreferenceFragment mFragment;
@@ -79,11 +80,15 @@ public final class BatteryChartPreferenceControllerTest {
@Mock @Mock
private BatteryHistEntry mBatteryHistEntry; private BatteryHistEntry mBatteryHistEntry;
@Mock @Mock
private BatteryChartView mBatteryChartView; private BatteryChartView mDailyChartView;
@Mock
private BatteryChartView mHourlyChartView;
@Mock @Mock
private PowerGaugePreference mPowerGaugePreference; private PowerGaugePreference mPowerGaugePreference;
@Mock @Mock
private BatteryUtils mBatteryUtils; private BatteryUtils mBatteryUtils;
@Mock
private LinearLayout.LayoutParams mLayoutParams;
private Context mContext; private Context mContext;
private FakeFeatureFactory mFeatureFactory; private FakeFeatureFactory mFeatureFactory;
@@ -96,6 +101,7 @@ public final class BatteryChartPreferenceControllerTest {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
Locale.setDefault(new Locale("en_US")); Locale.setDefault(new Locale("en_US"));
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false); org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
mFeatureFactory = FakeFeatureFactory.setupForTest(); mFeatureFactory = FakeFeatureFactory.setupForTest();
mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
mContext = spy(RuntimeEnvironment.application); mContext = spy(RuntimeEnvironment.application);
@@ -108,10 +114,12 @@ public final class BatteryChartPreferenceControllerTest {
doReturn(new String[]{"com.android.gms.persistent"}) doReturn(new String[]{"com.android.gms.persistent"})
.when(mFeatureFactory.powerUsageFeatureProvider) .when(mFeatureFactory.powerUsageFeatureProvider)
.getHideApplicationEntries(mContext); .getHideApplicationEntries(mContext);
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController = createController();
mBatteryChartPreferenceController.mPrefContext = mContext; mBatteryChartPreferenceController.mPrefContext = mContext;
mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup; mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup;
mBatteryChartPreferenceController.mBatteryChartView = mBatteryChartView; mBatteryChartPreferenceController.mDailyChartView = mDailyChartView;
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
mBatteryDiffEntry = new BatteryDiffEntry( mBatteryDiffEntry = new BatteryDiffEntry(
mContext, mContext,
/*foregroundUsageTimeInMs=*/ 1, /*foregroundUsageTimeInMs=*/ 1,
@@ -123,12 +131,10 @@ public final class BatteryChartPreferenceControllerTest {
BatteryDiffEntry.sResourceCache.put( BatteryDiffEntry.sResourceCache.put(
"fakeBatteryDiffEntryKey", "fakeBatteryDiffEntryKey",
new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1)); new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
mBatteryChartPreferenceController.setBatteryHistoryMap(
createBatteryHistoryMap());
} }
@Test @Test
public void testOnDestroy_activityIsChanging_clearBatteryEntryCache() { public void onDestroy_activityIsChanging_clearBatteryEntryCache() {
doReturn(true).when(mSettingsActivity).isChangingConfigurations(); doReturn(true).when(mSettingsActivity).isChangingConfigurations();
// Ensures the testing environment is correct. // Ensures the testing environment is correct.
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
@@ -138,7 +144,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testOnDestroy_activityIsNotChanging_notClearBatteryEntryCache() { public void onDestroy_activityIsNotChanging_notClearBatteryEntryCache() {
doReturn(false).when(mSettingsActivity).isChangingConfigurations(); doReturn(false).when(mSettingsActivity).isChangingConfigurations();
// Ensures the testing environment is correct. // Ensures the testing environment is correct.
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1); assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
@@ -148,7 +154,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testOnDestroy_clearPreferenceCache() { public void onDestroy_clearPreferenceCache() {
// Ensures the testing environment is correct. // Ensures the testing environment is correct.
mBatteryChartPreferenceController.mPreferenceCache.put( mBatteryChartPreferenceController.mPreferenceCache.put(
PREF_KEY, mPowerGaugePreference); PREF_KEY, mPowerGaugePreference);
@@ -160,113 +166,135 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testOnDestroy_removeAllPreferenceFromPreferenceGroup() { public void onDestroy_removeAllPreferenceFromPreferenceGroup() {
mBatteryChartPreferenceController.onDestroy(); mBatteryChartPreferenceController.onDestroy();
verify(mAppListGroup).removeAll(); verify(mAppListGroup).removeAll();
} }
@Test @Test
public void testSetBatteryHistoryMap_createExpectedKeysAndLevels() { public void setBatteryChartViewModel_6Hours() {
mBatteryChartPreferenceController.setBatteryHistoryMap( mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
createBatteryHistoryMap());
// Verifies the created battery keys array. verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE);
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) { verify(mHourlyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index]) verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
// These values is are calculated by hand from createBatteryHistoryMap(). List.of(100, 97, 95),
.isEqualTo(index + 1); List.of("8 am", "10 am", "12 pm"),
} BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
// Verifies the created battery levels array.
for (int index = 0; index < 13; index++) {
assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index])
// These values is are calculated by hand from createBatteryHistoryMap().
.isEqualTo(100 - index * 2);
}
assertThat(mBatteryChartPreferenceController.mBatteryIndexedMap).hasSize(13);
} }
@Test @Test
public void testSetBatteryHistoryMap_largeSize_createExpectedKeysAndLevels() { public void setBatteryChartViewModel_60Hours() {
mBatteryChartPreferenceController.setBatteryHistoryMap( BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel(
createBatteryHistoryMap()); List.of(100, 83, 59, 41),
List.of("Sat", "Sun", "Mon", "Mon"),
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS);
// Verifies the created battery keys array. mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index]) verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
// These values is are calculated by hand from createBatteryHistoryMap(). verify(mHourlyChartView, atLeastOnce()).setVisibility(View.GONE);
.isEqualTo(index + 1); verify(mDailyChartView).setViewModel(expectedDailyViewModel);
}
// Verifies the created battery levels array. reset(mDailyChartView);
for (int index = 0; index < 13; index++) { reset(mHourlyChartView);
assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index]) doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
// These values is are calculated by hand from createBatteryHistoryMap(). mBatteryChartPreferenceController.mDailyChartIndex = 0;
.isEqualTo(100 - index * 2); mBatteryChartPreferenceController.refreshUi();
} verify(mDailyChartView).setVisibility(View.VISIBLE);
assertThat(mBatteryChartPreferenceController.mBatteryIndexedMap).hasSize(13); verify(mHourlyChartView).setVisibility(View.VISIBLE);
expectedDailyViewModel.setSelectedIndex(0);
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
List.of(100, 97, 95, 93, 91, 89, 87, 85, 83),
List.of("8 am", "10 am", "12 pm", "2 pm", "4 pm", "6 pm", "8 pm", "10 pm",
"12 am"),
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
reset(mDailyChartView);
reset(mHourlyChartView);
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
mBatteryChartPreferenceController.mDailyChartIndex = 1;
mBatteryChartPreferenceController.mHourlyChartIndex = 6;
mBatteryChartPreferenceController.refreshUi();
verify(mDailyChartView).setVisibility(View.VISIBLE);
verify(mHourlyChartView).setVisibility(View.VISIBLE);
expectedDailyViewModel.setSelectedIndex(1);
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
BatteryChartViewModel expectedHourlyViewModel = new BatteryChartViewModel(
List.of(83, 81, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59),
List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm",
"4 pm", "6 pm", "8 pm", "10 pm", "12 am"),
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS);
expectedHourlyViewModel.setSelectedIndex(6);
verify(mHourlyChartView).setViewModel(expectedHourlyViewModel);
reset(mDailyChartView);
reset(mHourlyChartView);
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
mBatteryChartPreferenceController.mDailyChartIndex = 2;
mBatteryChartPreferenceController.mHourlyChartIndex =
BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.refreshUi();
verify(mDailyChartView).setVisibility(View.VISIBLE);
verify(mHourlyChartView).setVisibility(View.VISIBLE);
expectedDailyViewModel.setSelectedIndex(2);
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
List.of(59, 57, 55, 53, 51, 49, 47, 45, 43, 41),
List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm",
"4 pm", "6 pm"),
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
} }
@Test @Test
public void testRefreshUi_batteryIndexedMapIsNull_ignoreRefresh() { public void refreshUi_normalCase_returnTrue() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
}
@Test
public void refreshUi_batteryIndexedMapIsNull_ignoreRefresh() {
mBatteryChartPreferenceController.setBatteryHistoryMap(null); mBatteryChartPreferenceController.setBatteryHistoryMap(null);
assertThat(mBatteryChartPreferenceController.refreshUi( assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
/*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
} }
@Test @Test
public void testRefreshUi_batteryChartViewIsNull_ignoreRefresh() { public void refreshUi_dailyChartViewIsNull_ignoreRefresh() {
mBatteryChartPreferenceController.mBatteryChartView = null; mBatteryChartPreferenceController.mDailyChartView = null;
assertThat(mBatteryChartPreferenceController.refreshUi( assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
/*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
} }
@Test @Test
public void testRefreshUi_trapezoidIndexIsNotChanged_ignoreRefresh() { public void refreshUi_hourlyChartViewIsNull_ignoreRefresh() {
final int trapezoidIndex = 1; mBatteryChartPreferenceController.mHourlyChartView = null;
mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex; assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
assertThat(mBatteryChartPreferenceController.refreshUi(
trapezoidIndex, /*isForce=*/ false)).isFalse();
} }
@Test @Test
public void testRefreshUi_forceUpdate_refreshUi() { public void removeAndCacheAllPrefs_emptyContent_ignoreRemoveAll() {
final int trapezoidIndex = 1; mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex; mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
assertThat(mBatteryChartPreferenceController.refreshUi(
trapezoidIndex, /*isForce=*/ true)).isTrue();
}
@Test
public void testForceRefreshUi_updateTrapezoidIndexIntoSelectAll() {
mBatteryChartPreferenceController.mTrapezoidIndex =
BatteryChartView.SELECTED_INDEX_INVALID;
mBatteryChartPreferenceController.setBatteryHistoryMap(
createBatteryHistoryMap());
assertThat(mBatteryChartPreferenceController.mTrapezoidIndex)
.isEqualTo(BatteryChartView.SELECTED_INDEX_ALL);
}
@Test
public void testRemoveAndCacheAllPrefs_emptyContent_ignoreRemoveAll() {
final int trapezoidIndex = 1;
doReturn(0).when(mAppListGroup).getPreferenceCount(); doReturn(0).when(mAppListGroup).getPreferenceCount();
mBatteryChartPreferenceController.refreshUi( mBatteryChartPreferenceController.refreshUi();
trapezoidIndex, /*isForce=*/ true);
verify(mAppListGroup, never()).removeAll(); verify(mAppListGroup, never()).removeAll();
} }
@Test @Test
public void testRemoveAndCacheAllPrefs_buildCacheAndRemoveAllPreference() { public void removeAndCacheAllPrefs_buildCacheAndRemoveAllPreference() {
final int trapezoidIndex = 1; mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
doReturn(1).when(mAppListGroup).getPreferenceCount(); doReturn(1).when(mAppListGroup).getPreferenceCount();
doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0); doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0);
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey(); doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
// Ensures the testing data is correct. // Ensures the testing data is correct.
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty(); assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
mBatteryChartPreferenceController.refreshUi( mBatteryChartPreferenceController.refreshUi();
trapezoidIndex, /*isForce=*/ true);
assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY)) assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY))
.isEqualTo(mPowerGaugePreference); .isEqualTo(mPowerGaugePreference);
@@ -274,14 +302,14 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testAddPreferenceToScreen_emptyContent_ignoreAddPreference() { public void addPreferenceToScreen_emptyContent_ignoreAddPreference() {
mBatteryChartPreferenceController.addPreferenceToScreen( mBatteryChartPreferenceController.addPreferenceToScreen(
new ArrayList<BatteryDiffEntry>()); new ArrayList<BatteryDiffEntry>());
verify(mAppListGroup, never()).addPreference(any()); verify(mAppListGroup, never()).addPreference(any());
} }
@Test @Test
public void testAddPreferenceToScreen_addPreferenceIntoScreen() { public void addPreferenceToScreen_addPreferenceIntoScreen() {
final String appLabel = "fake app label"; final String appLabel = "fake app label";
doReturn(1).when(mAppListGroup).getPreferenceCount(); doReturn(1).when(mAppListGroup).getPreferenceCount();
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
@@ -310,7 +338,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testAddPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() { public void addPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
final String appLabel = "fake app label"; final String appLabel = "fake app label";
doReturn(1).when(mAppListGroup).getPreferenceCount(); doReturn(1).when(mAppListGroup).getPreferenceCount();
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
@@ -325,7 +353,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testHandlePreferenceTreeiClick_notPowerGaugePreference_returnFalse() { public void handlePreferenceTreeClick_notPowerGaugePreference_returnFalse() {
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(mAppListGroup)) assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(mAppListGroup))
.isFalse(); .isFalse();
@@ -336,7 +364,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testHandlePreferenceTreeClick_forAppEntry_returnTrue() { public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
doReturn(false).when(mBatteryHistEntry).isAppEntry(); doReturn(false).when(mBatteryHistEntry).isAppEntry();
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
@@ -352,7 +380,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testHandlePreferenceTreeClick_forSystemEntry_returnTrue() { public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
mBatteryChartPreferenceController.mBatteryUtils = mBatteryUtils; mBatteryChartPreferenceController.mBatteryUtils = mBatteryUtils;
doReturn(true).when(mBatteryHistEntry).isAppEntry(); doReturn(true).when(mBatteryHistEntry).isAppEntry();
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry(); doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
@@ -369,7 +397,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testSetPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() { public void setPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
final PowerGaugePreference pref = new PowerGaugePreference(mContext); final PowerGaugePreference pref = new PowerGaugePreference(mContext);
pref.setSummary(PREF_SUMMARY); pref.setSummary(PREF_SUMMARY);
@@ -381,7 +409,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testSetPreferenceSummary_setBackgroundUsageTimeOnly() { public void setPreferenceSummary_setBackgroundUsageTimeOnly() {
final PowerGaugePreference pref = new PowerGaugePreference(mContext); final PowerGaugePreference pref = new PowerGaugePreference(mContext);
pref.setSummary(PREF_SUMMARY); pref.setSummary(PREF_SUMMARY);
@@ -393,7 +421,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testSetPreferenceSummary_setTotalUsageTimeLessThanAMinute() { public void setPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
final PowerGaugePreference pref = new PowerGaugePreference(mContext); final PowerGaugePreference pref = new PowerGaugePreference(mContext);
pref.setSummary(PREF_SUMMARY); pref.setSummary(PREF_SUMMARY);
@@ -405,7 +433,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testSetPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() { public void setPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
final PowerGaugePreference pref = new PowerGaugePreference(mContext); final PowerGaugePreference pref = new PowerGaugePreference(mContext);
pref.setSummary(PREF_SUMMARY); pref.setSummary(PREF_SUMMARY);
@@ -418,7 +446,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testSetPreferenceSummary_setTotalAndBackgroundUsageTime() { public void setPreferenceSummary_setTotalAndBackgroundUsageTime() {
final PowerGaugePreference pref = new PowerGaugePreference(mContext); final PowerGaugePreference pref = new PowerGaugePreference(mContext);
pref.setSummary(PREF_SUMMARY); pref.setSummary(PREF_SUMMARY);
@@ -430,7 +458,7 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testSetPreferenceSummary_notAllowShownPackage_setSummayAsNull() { public void setPreferenceSummary_notAllowShownPackage_setSummayAsNull() {
final PowerGaugePreference pref = new PowerGaugePreference(mContext); final PowerGaugePreference pref = new PowerGaugePreference(mContext);
pref.setSummary(PREF_SUMMARY); pref.setSummary(PREF_SUMMARY);
final BatteryDiffEntry batteryDiffEntry = final BatteryDiffEntry batteryDiffEntry =
@@ -445,36 +473,9 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testValidateUsageTime_returnTrueIfBatteryDiffEntryIsValid() { public void onExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() {
assertThat(BatteryChartPreferenceController.validateUsageTime(
createBatteryDiffEntry(
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS)))
.isTrue();
}
@Test
public void testValidateUsageTime_foregroundTimeExceedThreshold_returnFalse() {
assertThat(BatteryChartPreferenceController.validateUsageTime(
createBatteryDiffEntry(
/*foregroundUsageTimeInMs=*/ DateUtils.HOUR_IN_MILLIS * 3,
/*backgroundUsageTimeInMs=*/ 0)))
.isFalse();
}
@Test
public void testValidateUsageTime_backgroundTimeExceedThreshold_returnFalse() {
assertThat(BatteryChartPreferenceController.validateUsageTime(
createBatteryDiffEntry(
/*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ DateUtils.HOUR_IN_MILLIS * 3)))
.isFalse();
}
@Test
public void testOnExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() {
doReturn(1).when(mAppListGroup).getPreferenceCount(); doReturn(1).when(mAppListGroup).getPreferenceCount();
mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry); mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
doReturn("label").when(mBatteryDiffEntry).getAppLabel(); doReturn("label").when(mBatteryDiffEntry).getAppLabel();
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon(); doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey(); doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
@@ -493,10 +494,10 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testOnExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() { public void onExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() {
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey(); doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY); doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry); mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
// Verifies the cache is empty first. // Verifies the cache is empty first.
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty(); assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
@@ -513,57 +514,17 @@ public final class BatteryChartPreferenceControllerTest {
} }
@Test @Test
public void testOnSelect_selectSpecificTimeSlot_logMetric() { public void refreshCategoryTitle_setLastFullChargeIntoBothTitleTextView() {
mBatteryChartPreferenceController.onSelect(1 /*slot index*/);
verify(mMetricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT);
}
@Test
public void testOnSelect_selectAll_logMetric() {
mBatteryChartPreferenceController.onSelect(
BatteryChartView.SELECTED_INDEX_ALL /*slot index*/);
verify(mMetricsFeatureProvider)
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL);
}
@Test
public void testRefreshCategoryTitle_setHourIntoBothTitleTextView() {
mBatteryChartPreferenceController = createController();
setUpBatteryHistoryKeys();
mBatteryChartPreferenceController.mAppListPrefGroup =
spy(new PreferenceCategory(mContext));
mBatteryChartPreferenceController.mExpandDividerPreference =
spy(new ExpandDividerPreference(mContext));
// Simulates select the first slot.
mBatteryChartPreferenceController.mTrapezoidIndex = 0;
mBatteryChartPreferenceController.refreshCategoryTitle();
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
// Verifies the title in the preference group.
verify(mBatteryChartPreferenceController.mAppListPrefGroup)
.setTitle(captor.capture());
assertThat(captor.getValue()).isNotEqualTo("App usage for past 24 hr");
// Verifies the title in the expandable divider.
captor = ArgumentCaptor.forClass(String.class);
verify(mBatteryChartPreferenceController.mExpandDividerPreference)
.setTitle(captor.capture());
assertThat(captor.getValue()).isNotEqualTo("System usage for past 24 hr");
}
@Test
public void testRefreshCategoryTitle_setLast24HrIntoBothTitleTextView() {
mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController = createController();
mBatteryChartPreferenceController.mAppListPrefGroup = mBatteryChartPreferenceController.mAppListPrefGroup =
spy(new PreferenceCategory(mContext)); spy(new PreferenceCategory(mContext));
mBatteryChartPreferenceController.mExpandDividerPreference = mBatteryChartPreferenceController.mExpandDividerPreference =
spy(new ExpandDividerPreference(mContext)); spy(new ExpandDividerPreference(mContext));
// Simulates select all condition. // Simulates select all condition.
mBatteryChartPreferenceController.mTrapezoidIndex = mBatteryChartPreferenceController.mDailyChartIndex =
BatteryChartView.SELECTED_INDEX_ALL; BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.mHourlyChartIndex =
BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.refreshCategoryTitle(); mBatteryChartPreferenceController.refreshCategoryTitle();
@@ -572,76 +533,93 @@ public final class BatteryChartPreferenceControllerTest {
verify(mBatteryChartPreferenceController.mAppListPrefGroup) verify(mBatteryChartPreferenceController.mAppListPrefGroup)
.setTitle(captor.capture()); .setTitle(captor.capture());
assertThat(captor.getValue()) assertThat(captor.getValue())
.isEqualTo("App usage for past 24 hr"); .isEqualTo("App usage since last full charge");
// Verifies the title in the expandable divider. // Verifies the title in the expandable divider.
captor = ArgumentCaptor.forClass(String.class); captor = ArgumentCaptor.forClass(String.class);
verify(mBatteryChartPreferenceController.mExpandDividerPreference) verify(mBatteryChartPreferenceController.mExpandDividerPreference)
.setTitle(captor.capture()); .setTitle(captor.capture());
assertThat(captor.getValue()) assertThat(captor.getValue())
.isEqualTo("System usage for past 24 hr"); .isEqualTo("System usage since last full charge");
} }
@Test @Test
public void testSetTimestampLabel_nullBatteryHistoryKeys_ignore() { public void selectedSlotText_selectAllDaysAllHours_returnNull() {
mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
mBatteryChartPreferenceController.mBatteryHistoryKeys = null; mBatteryChartPreferenceController.mDailyChartIndex =
mBatteryChartPreferenceController.mBatteryChartView = BatteryChartViewModel.SELECTED_INDEX_ALL;
spy(new BatteryChartView(mContext)); mBatteryChartPreferenceController.mHourlyChartIndex =
mBatteryChartPreferenceController.setTimestampLabel(); BatteryChartViewModel.SELECTED_INDEX_ALL;
verify(mBatteryChartPreferenceController.mBatteryChartView, never()) assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
.setLatestTimestamp(anyLong());
} }
@Test @Test
public void testSetTimestampLabel_setExpectedTimestampData() { public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() {
mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.mBatteryChartView = mBatteryChartPreferenceController.mDailyChartIndex = 0;
spy(new BatteryChartView(mContext)); mBatteryChartPreferenceController.mHourlyChartIndex =
setUpBatteryHistoryKeys(); BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.setTimestampLabel(); assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
verify(mBatteryChartPreferenceController.mBatteryChartView)
.setLatestTimestamp(1619247636826L);
} }
@Test @Test
public void testSetTimestampLabel_withoutValidTimestamp_setExpectedTimestampData() { public void selectedSlotText_selectADayAllHours_onlyDayText() {
mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
mBatteryChartPreferenceController.mBatteryChartView = mBatteryChartPreferenceController.mDailyChartIndex = 1;
spy(new BatteryChartView(mContext)); mBatteryChartPreferenceController.mHourlyChartIndex =
mBatteryChartPreferenceController.mBatteryHistoryKeys = new long[]{0L}; BatteryChartViewModel.SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.setTimestampLabel(); assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo("Sunday");
verify(mBatteryChartPreferenceController.mBatteryChartView)
.setLatestTimestamp(anyLong());
} }
@Test @Test
public void testOnSaveInstanceState_restoreSelectedIndexAndExpandState() { public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() {
final int expectedIndex = 1; mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 1;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(
"10 am - 12 pm");
}
@Test
public void selectedSlotText_SelectADayAnHour_dayAndHourText() {
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
mBatteryChartPreferenceController.mDailyChartIndex = 1;
mBatteryChartPreferenceController.mHourlyChartIndex = 8;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(
"Sunday 4 pm - 6 pm");
}
@Test
public void onSaveInstanceState_restoreSelectedIndexAndExpandState() {
final int expectedDailyIndex = 1;
final int expectedHourlyIndex = 2;
final boolean isExpanded = true; final boolean isExpanded = true;
final Bundle bundle = new Bundle(); final Bundle bundle = new Bundle();
mBatteryChartPreferenceController.mTrapezoidIndex = expectedIndex; mBatteryChartPreferenceController.mDailyChartIndex = expectedDailyIndex;
mBatteryChartPreferenceController.mHourlyChartIndex = expectedHourlyIndex;
mBatteryChartPreferenceController.mIsExpanded = isExpanded; mBatteryChartPreferenceController.mIsExpanded = isExpanded;
mBatteryChartPreferenceController.onSaveInstanceState(bundle); mBatteryChartPreferenceController.onSaveInstanceState(bundle);
// Replaces the original controller with other values. // Replaces the original controller with other values.
mBatteryChartPreferenceController.mTrapezoidIndex = -1; mBatteryChartPreferenceController.mDailyChartIndex = -1;
mBatteryChartPreferenceController.mHourlyChartIndex = -1;
mBatteryChartPreferenceController.mIsExpanded = false; mBatteryChartPreferenceController.mIsExpanded = false;
mBatteryChartPreferenceController.onCreate(bundle); mBatteryChartPreferenceController.onCreate(bundle);
mBatteryChartPreferenceController.setBatteryHistoryMap( mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25));
createBatteryHistoryMap());
assertThat(mBatteryChartPreferenceController.mTrapezoidIndex) assertThat(mBatteryChartPreferenceController.mDailyChartIndex)
.isEqualTo(expectedIndex); .isEqualTo(expectedDailyIndex);
assertThat(mBatteryChartPreferenceController.mHourlyChartIndex)
.isEqualTo(expectedHourlyIndex);
assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue(); assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue();
} }
@Test @Test
public void testIsValidToShowSummary_returnExpectedResult() { public void isValidToShowSummary_returnExpectedResult() {
assertThat(mBatteryChartPreferenceController assertThat(mBatteryChartPreferenceController
.isValidToShowSummary("com.google.android.apps.scone")) .isValidToShowSummary("com.google.android.apps.scone"))
.isTrue(); .isTrue();
@@ -652,31 +630,42 @@ public final class BatteryChartPreferenceControllerTest {
.isFalse(); .isFalse();
} }
@Test private static Long generateTimestamp(int index) {
public void testIsValidToShowEntry_returnExpectedResult() { // "2021-04-23 07:00:00 UTC" + index hours
assertThat(mBatteryChartPreferenceController return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS;
.isValidToShowEntry("com.google.android.apps.scone"))
.isTrue();
// Verifies the items which are defined in the array list.
assertThat(mBatteryChartPreferenceController
.isValidToShowEntry("com.android.gms.persistent"))
.isFalse();
} }
private static Map<Long, Map<String, BatteryHistEntry>> createBatteryHistoryMap() { private static Map<Long, Map<String, BatteryHistEntry>> createBatteryHistoryMap(
int numOfHours) {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>(); final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) { for (int index = 0; index < numOfHours; index++) {
final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();
values.put("batteryLevel", Integer.valueOf(100 - index)); values.put("batteryLevel", Integer.valueOf(100 - index));
values.put("consumePower", Integer.valueOf(100 - index));
final BatteryHistEntry entry = new BatteryHistEntry(values); final BatteryHistEntry entry = new BatteryHistEntry(values);
final Map<String, BatteryHistEntry> entryMap = new HashMap<>(); final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
entryMap.put("fake_entry_key" + index, entry); entryMap.put("fake_entry_key" + index, entry);
batteryHistoryMap.put(Long.valueOf(index + 1), entryMap); batteryHistoryMap.put(generateTimestamp(index), entryMap);
} }
return batteryHistoryMap; return batteryHistoryMap;
} }
private Map<Integer, Map<Integer, BatteryDiffData>> 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( private BatteryDiffEntry createBatteryDiffEntry(
long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) { long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
return new BatteryDiffEntry( return new BatteryDiffEntry(
@@ -684,13 +673,6 @@ public final class BatteryChartPreferenceControllerTest {
/*consumePower=*/ 0, mBatteryHistEntry); /*consumePower=*/ 0, mBatteryHistEntry);
} }
private void setUpBatteryHistoryKeys() {
mBatteryChartPreferenceController.mBatteryHistoryKeys =
new long[]{1619196786769L, 0L, 1619247636826L};
ConvertUtils.utcToLocalTimeHour(
mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false);
}
private BatteryChartPreferenceController createController() { private BatteryChartPreferenceController createController() {
final BatteryChartPreferenceController controller = final BatteryChartPreferenceController controller =
new BatteryChartPreferenceController( new BatteryChartPreferenceController(

View File

@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context; import android.content.Context;
import android.os.LocaleList; import android.os.LocaleList;
import android.view.View;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider; import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
@@ -41,6 +42,7 @@ import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Locale; import java.util.Locale;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@@ -55,6 +57,8 @@ public final class BatteryChartViewTest {
private AccessibilityServiceInfo mMockAccessibilityServiceInfo; private AccessibilityServiceInfo mMockAccessibilityServiceInfo;
@Mock @Mock
private AccessibilityManager mMockAccessibilityManager; private AccessibilityManager mMockAccessibilityManager;
@Mock
private View mMockView;
@Before @Before
public void setUp() { public void setUp() {
@@ -74,13 +78,13 @@ public final class BatteryChartViewTest {
} }
@Test @Test
public void testIsAccessibilityEnabled_disable_returnFalse() { public void isAccessibilityEnabled_disable_returnFalse() {
doReturn(false).when(mMockAccessibilityManager).isEnabled(); doReturn(false).when(mMockAccessibilityManager).isEnabled();
assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse(); assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse();
} }
@Test @Test
public void testIsAccessibilityEnabled_emptyInfo_returnFalse() { public void isAccessibilityEnabled_emptyInfo_returnFalse() {
doReturn(true).when(mMockAccessibilityManager).isEnabled(); doReturn(true).when(mMockAccessibilityManager).isEnabled();
doReturn(new ArrayList<AccessibilityServiceInfo>()) doReturn(new ArrayList<AccessibilityServiceInfo>())
.when(mMockAccessibilityManager) .when(mMockAccessibilityManager)
@@ -90,68 +94,70 @@ public final class BatteryChartViewTest {
} }
@Test @Test
public void testIsAccessibilityEnabled_validServiceId_returnTrue() { public void isAccessibilityEnabled_validServiceId_returnTrue() {
doReturn(true).when(mMockAccessibilityManager).isEnabled(); doReturn(true).when(mMockAccessibilityManager).isEnabled();
assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isTrue(); assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isTrue();
} }
@Test @Test
public void testSetSelectedIndex_invokesCallback() { public void onClick_invokesCallback() {
final int originalSelectedIndex = 2;
BatteryChartViewModel batteryChartViewModel = new BatteryChartViewModel(
List.of(90, 80, 70, 60), List.of("", "", "", ""),
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS);
batteryChartViewModel.setSelectedIndex(originalSelectedIndex);
mBatteryChartView.setViewModel(batteryChartViewModel);
for (int i = 0; i < mBatteryChartView.mTrapezoidSlots.length; i++) {
mBatteryChartView.mTrapezoidSlots[i] = new BatteryChartView.TrapezoidSlot();
mBatteryChartView.mTrapezoidSlots[i].mLeft = i;
mBatteryChartView.mTrapezoidSlots[i].mRight = i + 0.5f;
}
final int[] selectedIndex = new int[1]; final int[] selectedIndex = new int[1];
final int expectedIndex = 2;
mBatteryChartView.mSelectedIndex = 1;
mBatteryChartView.setOnSelectListener( mBatteryChartView.setOnSelectListener(
trapezoidIndex -> { trapezoidIndex -> {
selectedIndex[0] = trapezoidIndex; selectedIndex[0] = trapezoidIndex;
}); });
mBatteryChartView.setSelectedIndex(expectedIndex); // Verify onClick() a different index 1.
mBatteryChartView.mTouchUpEventX = 1;
selectedIndex[0] = Integer.MIN_VALUE;
mBatteryChartView.onClick(mMockView);
assertThat(selectedIndex[0]).isEqualTo(1);
assertThat(mBatteryChartView.mSelectedIndex) // Verify onClick() the same index 2.
.isEqualTo(expectedIndex); mBatteryChartView.mTouchUpEventX = 2;
assertThat(selectedIndex[0]).isEqualTo(expectedIndex); selectedIndex[0] = Integer.MIN_VALUE;
mBatteryChartView.onClick(mMockView);
assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewModel.SELECTED_INDEX_ALL);
} }
@Test @Test
public void testSetSelectedIndex_sameIndex_notInvokesCallback() { public void clickable_isChartGraphSlotsEnabledIsFalse_notClickable() {
final int[] selectedIndex = new int[1];
final int expectedIndex = 1;
mBatteryChartView.mSelectedIndex = expectedIndex;
mBatteryChartView.setOnSelectListener(
trapezoidIndex -> {
selectedIndex[0] = trapezoidIndex;
});
mBatteryChartView.setSelectedIndex(expectedIndex);
assertThat(selectedIndex[0]).isNotEqualTo(expectedIndex);
}
@Test
public void testClickable_isChartGraphSlotsEnabledIsFalse_notClickable() {
mBatteryChartView.setClickableForce(true); mBatteryChartView.setClickableForce(true);
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
.thenReturn(false); .thenReturn(false);
mBatteryChartView.onAttachedToWindow(); mBatteryChartView.onAttachedToWindow();
assertThat(mBatteryChartView.isClickable()).isFalse(); assertThat(mBatteryChartView.isClickable()).isFalse();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
} }
@Test @Test
public void testClickable_accessibilityIsDisabled_clickable() { public void clickable_accessibilityIsDisabled_clickable() {
mBatteryChartView.setClickableForce(true); mBatteryChartView.setClickableForce(true);
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
.thenReturn(true); .thenReturn(true);
doReturn(false).when(mMockAccessibilityManager).isEnabled(); doReturn(false).when(mMockAccessibilityManager).isEnabled();
mBatteryChartView.onAttachedToWindow(); mBatteryChartView.onAttachedToWindow();
assertThat(mBatteryChartView.isClickable()).isTrue(); assertThat(mBatteryChartView.isClickable()).isTrue();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
} }
@Test @Test
public void testClickable_accessibilityIsEnabledWithoutValidId_clickable() { public void clickable_accessibilityIsEnabledWithoutValidId_clickable() {
mBatteryChartView.setClickableForce(true); mBatteryChartView.setClickableForce(true);
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
.thenReturn(true); .thenReturn(true);
@@ -161,30 +167,34 @@ public final class BatteryChartViewTest {
.getEnabledAccessibilityServiceList(anyInt()); .getEnabledAccessibilityServiceList(anyInt());
mBatteryChartView.onAttachedToWindow(); mBatteryChartView.onAttachedToWindow();
assertThat(mBatteryChartView.isClickable()).isTrue(); assertThat(mBatteryChartView.isClickable()).isTrue();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull(); assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
} }
@Test @Test
public void testClickable_accessibilityIsEnabledWithValidId_notClickable() { public void clickable_accessibilityIsEnabledWithValidId_notClickable() {
mBatteryChartView.setClickableForce(true); mBatteryChartView.setClickableForce(true);
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
.thenReturn(true); .thenReturn(true);
doReturn(true).when(mMockAccessibilityManager).isEnabled(); doReturn(true).when(mMockAccessibilityManager).isEnabled();
mBatteryChartView.onAttachedToWindow(); mBatteryChartView.onAttachedToWindow();
assertThat(mBatteryChartView.isClickable()).isFalse(); assertThat(mBatteryChartView.isClickable()).isFalse();
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull(); assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
} }
@Test @Test
public void testClickable_restoreFromNonClickableState() { public void clickable_restoreFromNonClickableState() {
final int[] levels = new int[13]; final List<Integer> levels = new ArrayList<Integer>();
for (int index = 0; index < levels.length; index++) { final List<String> texts = new ArrayList<String>();
levels[index] = index + 1; for (int index = 0; index < 13; index++) {
levels.add(index + 1);
texts.add("");
} }
mBatteryChartView.setTrapezoidCount(12); mBatteryChartView.setViewModel(new BatteryChartViewModel(levels, texts,
mBatteryChartView.setLevels(levels); BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
mBatteryChartView.setClickableForce(true); mBatteryChartView.setClickableForce(true);
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext)) when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
.thenReturn(true); .thenReturn(true);
@@ -201,14 +211,14 @@ public final class BatteryChartViewTest {
} }
@Test @Test
public void testOnAttachedToWindow_addAccessibilityStateChangeListener() { public void onAttachedToWindow_addAccessibilityStateChangeListener() {
mBatteryChartView.onAttachedToWindow(); mBatteryChartView.onAttachedToWindow();
verify(mMockAccessibilityManager) verify(mMockAccessibilityManager)
.addAccessibilityStateChangeListener(mBatteryChartView); .addAccessibilityStateChangeListener(mBatteryChartView);
} }
@Test @Test
public void testOnDetachedFromWindow_removeAccessibilityStateChangeListener() { public void onDetachedFromWindow_removeAccessibilityStateChangeListener() {
mBatteryChartView.onAttachedToWindow(); mBatteryChartView.onAttachedToWindow();
mBatteryChartView.mHandler.postDelayed( mBatteryChartView.mHandler.postDelayed(
mBatteryChartView.mUpdateClickableStateRun, 1000); mBatteryChartView.mUpdateClickableStateRun, 1000);
@@ -223,7 +233,7 @@ public final class BatteryChartViewTest {
} }
@Test @Test
public void testOnAccessibilityStateChanged_postUpdateStateRunnable() { public void onAccessibilityStateChanged_postUpdateStateRunnable() {
mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler); mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler);
mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true); mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true);

View File

@@ -53,7 +53,7 @@ public final class BatteryHistoryLoaderTest {
public void testLoadIBackground_returnsMapFromPowerFeatureProvider() { public void testLoadIBackground_returnsMapFromPowerFeatureProvider() {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>(); final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
doReturn(batteryHistoryMap).when(mFeatureFactory.powerUsageFeatureProvider) doReturn(batteryHistoryMap).when(mFeatureFactory.powerUsageFeatureProvider)
.getBatteryHistory(mContext); .getBatteryHistorySinceLastFullCharge(mContext);
assertThat(mBatteryHistoryLoader.loadInBackground()) assertThat(mBatteryHistoryLoader.loadInBackground())
.isSameInstanceAs(batteryHistoryMap); .isSameInstanceAs(batteryHistoryMap);

View File

@@ -26,6 +26,7 @@ import android.os.BatteryManager;
import android.os.BatteryUsageStats; import android.os.BatteryUsageStats;
import android.os.LocaleList; import android.os.LocaleList;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.format.DateUtils;
import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider; import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
@@ -39,8 +40,8 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -173,7 +174,8 @@ public final class ConvertUtilsTest {
public void getIndexedUsageMap_returnsExpectedResult() { public void getIndexedUsageMap_returnsExpectedResult() {
// Creates the fake testing data. // Creates the fake testing data.
final int timeSlotSize = 2; final int timeSlotSize = 2;
final long[] batteryHistoryKeys = new long[]{101L, 102L, 103L, 104L, 105L}; final long[] batteryHistoryKeys = new long[]{generateTimestamp(0), generateTimestamp(1),
generateTimestamp(2), generateTimestamp(3), generateTimestamp(4)};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
new HashMap<>(); new HashMap<>();
final BatteryHistEntry fakeEntry = createBatteryHistEntry( final BatteryHistEntry fakeEntry = createBatteryHistEntry(
@@ -270,11 +272,11 @@ public final class ConvertUtilsTest {
for (int index = 0; index < remainingSize; index++) { for (int index = 0; index < remainingSize; index++) {
batteryHistoryMap.put(105L + index + 1, new HashMap<>()); batteryHistoryMap.put(105L + index + 1, new HashMap<>());
} }
when(mPowerUsageFeatureProvider.getBatteryHistory(mContext)) when(mPowerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext))
.thenReturn(batteryHistoryMap); .thenReturn(batteryHistoryMap);
final List<BatteryDiffEntry> batteryDiffEntryList = final List<BatteryDiffEntry> batteryDiffEntryList =
BatteryChartPreferenceController.getBatteryLast24HrUsageData(mContext); BatteryChartPreferenceController.getAppBatteryUsageData(mContext);
assertThat(batteryDiffEntryList).isNotEmpty(); assertThat(batteryDiffEntryList).isNotEmpty();
final BatteryDiffEntry resultEntry = batteryDiffEntryList.get(0); final BatteryDiffEntry resultEntry = batteryDiffEntryList.get(0);
@@ -472,4 +474,9 @@ public final class ConvertUtilsTest {
assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs); assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs);
assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs); assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs);
} }
private static Long generateTimestamp(int index) {
// "2021-04-23 07:00:00 UTC" + index hours
return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS;
}
} }

View File

@@ -0,0 +1,950 @@
/*
* 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 static org.mockito.Mockito.when;
import android.content.ContentValues;
import android.content.Context;
import android.text.format.DateUtils;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.testutils.FakeFeatureFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
@RunWith(RobolectricTestRunner.class)
public class DataProcessorTest {
private static final String FAKE_ENTRY_KEY = "fake_entry_key";
private Context mContext;
private FakeFeatureFactory mFeatureFactory;
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
mContext = spy(RuntimeEnvironment.application);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
}
@Test
public void getBatteryLevelData_emptyHistoryMap_returnNull() {
assertThat(DataProcessor.getBatteryLevelData(
mContext,
/*handler=*/ null,
/*batteryHistoryMap=*/ null,
/*asyncResponseDelegate=*/ null))
.isNull();
assertThat(DataProcessor.getBatteryLevelData(
mContext, /*handler=*/ null, new HashMap<>(), /*asyncResponseDelegate=*/ null))
.isNull();
}
@Test
public void getBatteryLevelData_notEnoughData_returnNull() {
// The timestamps are within 1 hour.
final long[] timestamps = {1000000L, 2000000L, 3000000L};
final int[] levels = {100, 99, 98};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
assertThat(DataProcessor.getBatteryLevelData(
mContext, /*handler=*/ null, batteryHistoryMap, /*asyncResponseDelegate=*/ null))
.isNull();
}
@Test
public void getBatteryLevelData_returnExpectedResult() {
// Timezone GMT+8: 2022-01-01 00:00:00, 2022-01-01 01:00:00, 2022-01-01 02:00:00
final long[] timestamps = {1640966400000L, 1640970000000L, 1640973600000L};
final int[] levels = {100, 99, 98};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
final BatteryLevelData resultData =
DataProcessor.getBatteryLevelData(
mContext,
/*handler=*/ null,
batteryHistoryMap,
/*asyncResponseDelegate=*/ null);
final List<Long> expectedDailyTimestamps = List.of(timestamps[0], timestamps[2]);
final List<Integer> expectedDailyLevels = List.of(levels[0], levels[2]);
final List<List<Long>> expectedHourlyTimestamps = List.of(expectedDailyTimestamps);
final List<List<Integer>> expectedHourlyLevels = List.of(expectedDailyLevels);
verifyExpectedBatteryLevelData(
resultData,
expectedDailyTimestamps,
expectedDailyLevels,
expectedHourlyTimestamps,
expectedHourlyLevels);
}
@Test
public void getHistoryMapWithExpectedTimestamps_emptyHistoryMap_returnEmptyMap() {
assertThat(DataProcessor
.getHistoryMapWithExpectedTimestamps(mContext, new HashMap<>()))
.isEmpty();
}
@Test
public void getHistoryMapWithExpectedTimestamps_returnExpectedMap() {
// Timezone GMT+8
final long[] timestamps = {
1640966700000L, // 2022-01-01 00:05:00
1640970180000L, // 2022-01-01 01:03:00
1640973840000L, // 2022-01-01 02:04:00
1640978100000L, // 2022-01-01 03:15:00
1640981400000L // 2022-01-01 04:10:00
};
final int[] levels = {100, 94, 90, 82, 50};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
final Map<Long, Map<String, BatteryHistEntry>> resultMap =
DataProcessor.getHistoryMapWithExpectedTimestamps(mContext, batteryHistoryMap);
// Timezone GMT+8
final long[] expectedTimestamps = {
1640966400000L, // 2022-01-01 00:00:00
1640970000000L, // 2022-01-01 01:00:00
1640973600000L, // 2022-01-01 02:00:00
1640977200000L, // 2022-01-01 03:00:00
1640980800000L // 2022-01-01 04:00:00
};
final int[] expectedLevels = {100, 94, 90, 84, 56};
assertThat(resultMap).hasSize(expectedLevels.length);
for (int index = 0; index < expectedLevels.length; index++) {
assertThat(resultMap.get(expectedTimestamps[index]).get(FAKE_ENTRY_KEY).mBatteryLevel)
.isEqualTo(expectedLevels[index]);
}
}
@Test
public void getLevelDataThroughProcessedHistoryMap_notEnoughData_returnNull() {
final long[] timestamps = {100L};
final int[] levels = {100};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
assertThat(
DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap))
.isNull();
}
@Test
public void getLevelDataThroughProcessedHistoryMap_OneDayData_returnExpectedResult() {
// Timezone GMT+8
final long[] timestamps = {
1640966400000L, // 2022-01-01 00:00:00
1640970000000L, // 2022-01-01 01:00:00
1640973600000L, // 2022-01-01 02:00:00
1640977200000L, // 2022-01-01 03:00:00
1640980800000L // 2022-01-01 04:00:00
};
final int[] levels = {100, 94, 90, 82, 50};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
final BatteryLevelData resultData =
DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap);
final List<Long> expectedDailyTimestamps = List.of(timestamps[0], timestamps[4]);
final List<Integer> expectedDailyLevels = List.of(levels[0], levels[4]);
final List<List<Long>> expectedHourlyTimestamps = List.of(
List.of(timestamps[0], timestamps[2], timestamps[4])
);
final List<List<Integer>> expectedHourlyLevels = List.of(
List.of(levels[0], levels[2], levels[4])
);
verifyExpectedBatteryLevelData(
resultData,
expectedDailyTimestamps,
expectedDailyLevels,
expectedHourlyTimestamps,
expectedHourlyLevels);
}
@Test
public void getLevelDataThroughProcessedHistoryMap_MultipleDaysData_returnExpectedResult() {
// Timezone GMT+8
final long[] timestamps = {
1641038400000L, // 2022-01-01 20:00:00
1641060000000L, // 2022-01-02 02:00:00
1641067200000L, // 2022-01-02 04:00:00
1641081600000L, // 2022-01-02 08:00:00
};
final int[] levels = {100, 94, 90, 82};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
createHistoryMap(timestamps, levels);
final BatteryLevelData resultData =
DataProcessor.getLevelDataThroughProcessedHistoryMap(mContext, batteryHistoryMap);
final List<Long> expectedDailyTimestamps = List.of(
1641038400000L, // 2022-01-01 20:00:00
1641052800000L, // 2022-01-02 00:00:00
1641081600000L // 2022-01-02 08:00:00
);
final List<Integer> expectedDailyLevels = new ArrayList<>();
expectedDailyLevels.add(100);
expectedDailyLevels.add(null);
expectedDailyLevels.add(82);
final List<List<Long>> expectedHourlyTimestamps = List.of(
List.of(
1641038400000L, // 2022-01-01 20:00:00
1641045600000L, // 2022-01-01 22:00:00
1641052800000L // 2022-01-02 00:00:00
),
List.of(
1641052800000L, // 2022-01-02 00:00:00
1641060000000L, // 2022-01-02 02:00:00
1641067200000L, // 2022-01-02 04:00:00
1641074400000L, // 2022-01-02 06:00:00
1641081600000L // 2022-01-02 08:00:00
)
);
final List<Integer> expectedHourlyLevels1 = new ArrayList<>();
expectedHourlyLevels1.add(100);
expectedHourlyLevels1.add(null);
expectedHourlyLevels1.add(null);
final List<Integer> expectedHourlyLevels2 = new ArrayList<>();
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(94);
expectedHourlyLevels2.add(90);
expectedHourlyLevels2.add(null);
expectedHourlyLevels2.add(82);
final List<List<Integer>> expectedHourlyLevels = List.of(
expectedHourlyLevels1,
expectedHourlyLevels2
);
verifyExpectedBatteryLevelData(
resultData,
expectedDailyTimestamps,
expectedDailyLevels,
expectedHourlyTimestamps,
expectedHourlyLevels);
}
@Test
public void getTimestampSlots_emptyRawList_returnEmptyList() {
final List<Long> resultList =
DataProcessor.getTimestampSlots(new ArrayList<>());
assertThat(resultList).isEmpty();
}
@Test
public void getTimestampSlots_startWithEvenHour_returnExpectedResult() {
final Calendar startCalendar = Calendar.getInstance();
startCalendar.set(2022, 6, 5, 6, 30, 50); // 2022-07-05 06:30:50
final Calendar endCalendar = Calendar.getInstance();
endCalendar.set(2022, 6, 5, 22, 30, 50); // 2022-07-05 22:30:50
final Calendar expectedStartCalendar = Calendar.getInstance();
expectedStartCalendar.set(2022, 6, 5, 6, 0, 0); // 2022-07-05 06:00:00
final Calendar expectedEndCalendar = Calendar.getInstance();
expectedEndCalendar.set(2022, 6, 5, 22, 0, 0); // 2022-07-05 22:00:00
verifyExpectedTimestampSlots(
startCalendar, endCalendar, expectedStartCalendar, expectedEndCalendar);
}
@Test
public void getTimestampSlots_startWithOddHour_returnExpectedResult() {
final Calendar startCalendar = Calendar.getInstance();
startCalendar.set(2022, 6, 5, 5, 0, 50); // 2022-07-05 05:00:50
final Calendar endCalendar = Calendar.getInstance();
endCalendar.set(2022, 6, 6, 21, 00, 50); // 2022-07-06 21:00:50
final Calendar expectedStartCalendar = Calendar.getInstance();
expectedStartCalendar.set(2022, 6, 5, 6, 00, 00); // 2022-07-05 06:00:00
final Calendar expectedEndCalendar = Calendar.getInstance();
expectedEndCalendar.set(2022, 6, 6, 20, 00, 00); // 2022-07-06 20:00:00
verifyExpectedTimestampSlots(
startCalendar, endCalendar, expectedStartCalendar, expectedEndCalendar);
}
@Test
public void getDailyTimestamps_notEnoughData_returnEmptyList() {
assertThat(DataProcessor.getDailyTimestamps(new ArrayList<>())).isEmpty();
assertThat(DataProcessor.getDailyTimestamps(List.of(100L))).isEmpty();
}
@Test
public void getDailyTimestamps_OneDayData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640966400000L, // 2022-01-01 00:00:00
1640970000000L, // 2022-01-01 01:00:00
1640973600000L, // 2022-01-01 02:00:00
1640977200000L, // 2022-01-01 03:00:00
1640980800000L // 2022-01-01 04:00:00
);
final List<Long> expectedTimestamps = List.of(
1640966400000L, // 2022-01-01 00:00:00
1640980800000L // 2022-01-01 04:00:00
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void getDailyTimestamps_MultipleDaysData_returnExpectedList() {
// Timezone GMT+8
final List<Long> timestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641060000000L, // 2022-01-02 02:00:00
1641160800000L, // 2022-01-03 06:00:00
1641254400000L // 2022-01-04 08:00:00
);
final List<Long> expectedTimestamps = List.of(
1640988000000L, // 2022-01-01 06:00:00
1641052800000L, // 2022-01-02 00:00:00
1641139200000L, // 2022-01-03 00:00:00
1641225600000L, // 2022-01-04 00:00:00
1641254400000L // 2022-01-04 08:00:00
);
assertThat(DataProcessor.getDailyTimestamps(timestamps)).isEqualTo(expectedTimestamps);
}
@Test
public void isFromFullCharge_emptyData_returnFalse() {
assertThat(DataProcessor.isFromFullCharge(null)).isFalse();
assertThat(DataProcessor.isFromFullCharge(new HashMap<>())).isFalse();
}
@Test
public void isFromFullCharge_notChargedData_returnFalse() {
final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
final ContentValues values = new ContentValues();
values.put("batteryLevel", 98);
final BatteryHistEntry entry = new BatteryHistEntry(values);
entryMap.put(FAKE_ENTRY_KEY, entry);
assertThat(DataProcessor.isFromFullCharge(entryMap)).isFalse();
}
@Test
public void isFromFullCharge_chargedData_returnTrue() {
final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
final ContentValues values = new ContentValues();
values.put("batteryLevel", 100);
final BatteryHistEntry entry = new BatteryHistEntry(values);
entryMap.put(FAKE_ENTRY_KEY, entry);
assertThat(DataProcessor.isFromFullCharge(entryMap)).isTrue();
}
@Test
public void findNearestTimestamp_returnExpectedResult() {
long[] results = DataProcessor.findNearestTimestamp(
Arrays.asList(10L, 20L, 30L, 40L), /*target=*/ 15L);
assertThat(results).isEqualTo(new long[] {10L, 20L});
results = DataProcessor.findNearestTimestamp(
Arrays.asList(10L, 20L, 30L, 40L), /*target=*/ 10L);
assertThat(results).isEqualTo(new long[] {10L, 10L});
results = DataProcessor.findNearestTimestamp(
Arrays.asList(10L, 20L, 30L, 40L), /*target=*/ 5L);
assertThat(results).isEqualTo(new long[] {0L, 10L});
results = DataProcessor.findNearestTimestamp(
Arrays.asList(10L, 20L, 30L, 40L), /*target=*/ 50L);
assertThat(results).isEqualTo(new long[] {40L, 0L});
}
@Test
public void getTimestampOfNextDay_returnExpectedResult() {
// 2021-02-28 06:00:00 => 2021-03-01 00:00:00
assertThat(DataProcessor.getTimestampOfNextDay(1614463200000L))
.isEqualTo(1614528000000L);
// 2021-12-31 16:00:00 => 2022-01-01 00:00:00
assertThat(DataProcessor.getTimestampOfNextDay(1640937600000L))
.isEqualTo(1640966400000L);
}
@Test
public void isForDailyChart_returnExpectedResult() {
assertThat(DataProcessor.isForDailyChart(/*isStartOrEnd=*/ true, 0L)).isTrue();
// 2022-01-01 00:00:00
assertThat(DataProcessor.isForDailyChart(/*isStartOrEnd=*/ false, 1640966400000L))
.isTrue();
// 2022-01-01 01:00:05
assertThat(DataProcessor.isForDailyChart(/*isStartOrEnd=*/ false, 1640970005000L))
.isFalse();
}
@Test
public void getBatteryUsageMap_emptyHistoryMap_returnNull() {
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(new ArrayList<>(), new ArrayList<>()));
assertThat(DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, new HashMap<>())).isNull();
}
@Test
public void getBatteryUsageMap_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641045600000L, // 2022-01-01 22:00:00
1641049200000L, // 2022-01-01 23:00:00
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L, // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
final BatteryHistEntry fakeEntry = createBatteryHistEntry(
ConvertUtils.FAKE_PACKAGE_NAME, "fake_label", /*consumePower=*/ 0, /*uid=*/ 0L,
currentUserId, ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
/*foregroundUsageTimeInMs=*/ 0L, /*backgroundUsageTimeInMs=*/ 0L);
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 5.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 20.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 15L,
25L);
entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
// Adds the index = 3 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 40.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 25L,
/*backgroundUsageTimeInMs=*/ 35L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 10.0, /*uid=*/ 3L, currentUserId,
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*foregroundUsageTimeInMs=*/ 40L,
/*backgroundUsageTimeInMs=*/ 50L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package3", "label3", /*consumePower=*/ 15.0, /*uid=*/ 4L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 5L,
/*backgroundUsageTimeInMs=*/ 5L);
entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[3], entryMap);
// Adds the index = 4 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 40.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 30L,
/*backgroundUsageTimeInMs=*/ 40L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 20.0, /*uid=*/ 3L, currentUserId,
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*foregroundUsageTimeInMs=*/ 50L,
/*backgroundUsageTimeInMs=*/ 60L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package3", "label3", /*consumePower=*/ 40.0, /*uid=*/ 4L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 5L,
/*backgroundUsageTimeInMs=*/ 5L);
entryMap.put(entry.getKey(), entry);
entryMap.put(fakeEntry.getKey(), fakeEntry);
batteryHistoryMap.put(batteryHistoryKeys[4], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
// Adds the day 1 data.
List<Long> timestamps =
List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
// Adds the day 2 data.
timestamps = List.of(batteryHistoryKeys[2], batteryHistoryKeys[4]);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 40.0,
/*foregroundUsageTimeInMs=*/ 30, /*backgroundUsageTimeInMs=*/ 40);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(1), currentUserId, /*uid=*/ 4L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 40.0,
/*foregroundUsageTimeInMs=*/ 5, /*backgroundUsageTimeInMs=*/ 5);
assertBatteryDiffEntry(
resultDiffData.getSystemDiffEntryList().get(0), currentUserId, /*uid=*/ 3L,
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*consumePercentage=*/ 20.0,
/*foregroundUsageTimeInMs=*/ 50, /*backgroundUsageTimeInMs=*/ 60);
resultDiffData = resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 100.0,
/*foregroundUsageTimeInMs=*/ 15, /*backgroundUsageTimeInMs=*/ 25);
resultDiffData = resultMap.get(1).get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 4L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 50.0,
/*foregroundUsageTimeInMs=*/ 5, /*backgroundUsageTimeInMs=*/ 5);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(1), currentUserId, /*uid=*/ 2L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 25.0,
/*foregroundUsageTimeInMs=*/ 15, /*backgroundUsageTimeInMs=*/ 15);
assertBatteryDiffEntry(
resultDiffData.getSystemDiffEntryList().get(0), currentUserId, /*uid=*/ 3L,
ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY, /*consumePercentage=*/ 25.0,
/*foregroundUsageTimeInMs=*/ 50, /*backgroundUsageTimeInMs=*/ 60);
}
@Test
public void getBatteryUsageMap_multipleUsers_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 5.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 10.0, /*uid=*/ 2L, currentUserId + 1,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 5.0, /*uid=*/ 3L, currentUserId + 2,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 15.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 30.0, /*uid=*/ 2L, currentUserId + 1,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 15.0, /*uid=*/ 3L, currentUserId + 2,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 30L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 25.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 50.0, /*uid=*/ 2L, currentUserId + 1,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 20L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 25.0, /*uid=*/ 3L, currentUserId + 2,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 30L,
/*backgroundUsageTimeInMs=*/ 30L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
final BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 1L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 25.0,
/*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 10);
assertBatteryDiffEntry(
resultDiffData.getSystemDiffEntryList().get(0), BatteryUtils.UID_OTHER_USERS,
/*uid=*/ BatteryUtils.UID_OTHER_USERS, ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
/*consumePercentage=*/ 75.0, /*foregroundUsageTimeInMs=*/ 0,
/*backgroundUsageTimeInMs=*/ 0);
assertThat(resultMap.get(0).get(0)).isNotNull();
assertThat(resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL)).isNotNull();
}
@Test
public void getBatteryUsageMap_usageTimeExceed_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 500.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 3600000L,
/*backgroundUsageTimeInMs=*/ 7200000L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
final BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
// Verifies the clipped usage time.
final float ratio = (float) (7200) / (float) (3600 + 7200);
final BatteryDiffEntry resultEntry = resultDiffData.getAppDiffEntryList().get(0);
assertThat(resultEntry.mForegroundUsageTimeInMs)
.isEqualTo(Math.round(entry.mForegroundUsageTimeInMs * ratio));
assertThat(resultEntry.mBackgroundUsageTimeInMs)
.isEqualTo(Math.round(entry.mBackgroundUsageTimeInMs * ratio));
assertThat(resultEntry.mConsumePower)
.isEqualTo(entry.mConsumePower * ratio);
assertThat(resultMap.get(0).get(0)).isNotNull();
assertThat(resultMap.get(0).get(DataProcessor.SELECTED_INDEX_ALL)).isNotNull();
}
@Test
public void getBatteryUsageMap_hideApplicationEntries_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 10.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 10.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
when(mPowerUsageFeatureProvider.getHideApplicationEntries(mContext))
.thenReturn(new CharSequence[]{"package1"});
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
final BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
assertBatteryDiffEntry(
resultDiffData.getAppDiffEntryList().get(0), currentUserId, /*uid=*/ 2L,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*consumePercentage=*/ 50.0,
/*foregroundUsageTimeInMs=*/ 10, /*backgroundUsageTimeInMs=*/ 20);
}
@Test
public void getBatteryUsageMap_hideBackgroundUsageTime_returnsExpectedResult() {
final long[] batteryHistoryKeys = new long[]{
1641052800000L, // 2022-01-02 00:00:00
1641056400000L, // 2022-01-02 01:00:00
1641060000000L // 2022-01-02 02:00:00
};
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
final int currentUserId = mContext.getUserId();
// Adds the index = 0 data.
Map<String, BatteryHistEntry> entryMap = new HashMap<>();
BatteryHistEntry entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[0], entryMap);
// Adds the index = 1 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 0L,
/*backgroundUsageTimeInMs=*/ 0L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[1], entryMap);
// Adds the index = 2 data.
entryMap = new HashMap<>();
entry = createBatteryHistEntry(
"package1", "label1", /*consumePower=*/ 10.0, /*uid=*/ 1L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
entry = createBatteryHistEntry(
"package2", "label2", /*consumePower=*/ 10.0, /*uid=*/ 2L, currentUserId,
ConvertUtils.CONSUMER_TYPE_UID_BATTERY, /*foregroundUsageTimeInMs=*/ 10L,
/*backgroundUsageTimeInMs=*/ 20L);
entryMap.put(entry.getKey(), entry);
batteryHistoryMap.put(batteryHistoryKeys[2], entryMap);
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay =
new ArrayList<>();
List<Long> timestamps = List.of(batteryHistoryKeys[0], batteryHistoryKeys[2]);
final List<Integer> levels = List.of(100, 100);
hourlyBatteryLevelsPerDay.add(
new BatteryLevelData.PeriodBatteryLevelData(timestamps, levels));
when(mPowerUsageFeatureProvider.getHideBackgroundUsageTimeSet(mContext))
.thenReturn(new HashSet(Arrays.asList((CharSequence) "package2")));
final Map<Integer, Map<Integer, BatteryDiffData>> resultMap =
DataProcessor.getBatteryUsageMap(
mContext, hourlyBatteryLevelsPerDay, batteryHistoryMap);
final BatteryDiffData resultDiffData =
resultMap
.get(DataProcessor.SELECTED_INDEX_ALL)
.get(DataProcessor.SELECTED_INDEX_ALL);
BatteryDiffEntry resultEntry = resultDiffData.getAppDiffEntryList().get(0);
assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(20);
resultEntry = resultDiffData.getAppDiffEntryList().get(1);
assertThat(resultEntry.mBackgroundUsageTimeInMs).isEqualTo(0);
}
private static Map<Long, Map<String, BatteryHistEntry>> createHistoryMap(
final long[] timestamps, final int[] levels) {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
for (int index = 0; index < timestamps.length; index++) {
final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
final ContentValues values = new ContentValues();
values.put(BatteryHistEntry.KEY_BATTERY_LEVEL, levels[index]);
final BatteryHistEntry entry = new BatteryHistEntry(values);
entryMap.put(FAKE_ENTRY_KEY, entry);
batteryHistoryMap.put(timestamps[index], entryMap);
}
return batteryHistoryMap;
}
private static BatteryHistEntry createBatteryHistEntry(
final String packageName, final String appLabel, final double consumePower,
final long uid, final long userId, final int consumerType,
final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs) {
// Only insert required fields.
final ContentValues values = new ContentValues();
values.put(BatteryHistEntry.KEY_PACKAGE_NAME, packageName);
values.put(BatteryHistEntry.KEY_APP_LABEL, appLabel);
values.put(BatteryHistEntry.KEY_UID, uid);
values.put(BatteryHistEntry.KEY_USER_ID, userId);
values.put(BatteryHistEntry.KEY_CONSUMER_TYPE, consumerType);
values.put(BatteryHistEntry.KEY_CONSUME_POWER, consumePower);
values.put(BatteryHistEntry.KEY_FOREGROUND_USAGE_TIME, foregroundUsageTimeInMs);
values.put(BatteryHistEntry.KEY_BACKGROUND_USAGE_TIME, backgroundUsageTimeInMs);
return new BatteryHistEntry(values);
}
private static void verifyExpectedBatteryLevelData(
final BatteryLevelData resultData,
final List<Long> expectedDailyTimestamps,
final List<Integer> expectedDailyLevels,
final List<List<Long>> expectedHourlyTimestamps,
final List<List<Integer>> expectedHourlyLevels) {
final BatteryLevelData.PeriodBatteryLevelData dailyResultData =
resultData.getDailyBatteryLevels();
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyResultData =
resultData.getHourlyBatteryLevelsPerDay();
verifyExpectedDailyBatteryLevelData(
dailyResultData, expectedDailyTimestamps, expectedDailyLevels);
verifyExpectedHourlyBatteryLevelData(
hourlyResultData, expectedHourlyTimestamps, expectedHourlyLevels);
}
private static void verifyExpectedDailyBatteryLevelData(
final BatteryLevelData.PeriodBatteryLevelData dailyResultData,
final List<Long> expectedDailyTimestamps,
final List<Integer> expectedDailyLevels) {
assertThat(dailyResultData.getTimestamps()).isEqualTo(expectedDailyTimestamps);
assertThat(dailyResultData.getLevels()).isEqualTo(expectedDailyLevels);
}
private static void verifyExpectedHourlyBatteryLevelData(
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyResultData,
final List<List<Long>> expectedHourlyTimestamps,
final List<List<Integer>> expectedHourlyLevels) {
final int expectedHourlySize = expectedHourlyTimestamps.size();
assertThat(hourlyResultData).hasSize(expectedHourlySize);
for (int dailyIndex = 0; dailyIndex < expectedHourlySize; dailyIndex++) {
assertThat(hourlyResultData.get(dailyIndex).getTimestamps())
.isEqualTo(expectedHourlyTimestamps.get(dailyIndex));
assertThat(hourlyResultData.get(dailyIndex).getLevels())
.isEqualTo(expectedHourlyLevels.get(dailyIndex));
}
}
private static void verifyExpectedTimestampSlots(
final Calendar start,
final Calendar end,
final Calendar expectedStart,
final Calendar expectedEnd) {
expectedStart.set(Calendar.MILLISECOND, 0);
expectedEnd.set(Calendar.MILLISECOND, 0);
final ArrayList<Long> timestampSlots = new ArrayList<>();
timestampSlots.add(start.getTimeInMillis());
timestampSlots.add(end.getTimeInMillis());
final List<Long> resultList =
DataProcessor.getTimestampSlots(timestampSlots);
for (int index = 0; index < resultList.size(); index++) {
final long expectedTimestamp =
expectedStart.getTimeInMillis() + index * DateUtils.HOUR_IN_MILLIS;
assertThat(resultList.get(index)).isEqualTo(expectedTimestamp);
}
assertThat(resultList.get(resultList.size() - 1))
.isEqualTo(expectedEnd.getTimeInMillis());
}
private static void assertBatteryDiffEntry(
final BatteryDiffEntry entry, final long userId, final long uid,
final int consumerType, final double consumePercentage,
final long foregroundUsageTimeInMs, final long backgroundUsageTimeInMs) {
assertThat(entry.mBatteryHistEntry.mUserId).isEqualTo(userId);
assertThat(entry.mBatteryHistEntry.mUid).isEqualTo(uid);
assertThat(entry.mBatteryHistEntry.mConsumerType).isEqualTo(consumerType);
assertThat(entry.getPercentOfTotal()).isEqualTo(consumePercentage);
assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs);
assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs);
}
}

View File

@@ -139,17 +139,7 @@ public class PowerUsageSummaryTest {
} }
@Test @Test
public void initPreference_chartGraphEnabled_hasCorrectSummary() { public void initPreference_hasCorrectSummary() {
mFragment.initPreference();
verify(mBatteryUsagePreference).setSummary("View usage for past 24 hours");
}
@Test
public void initPreference_chartGraphDisabled_hasCorrectSummary() {
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mRealContext))
.thenReturn(false);
mFragment.initPreference(); mFragment.initPreference();
verify(mBatteryUsagePreference).setSummary("View usage from last full charge"); verify(mBatteryUsagePreference).setSummary("View usage from last full charge");