Port new version battery usage chart implementation from master to tm-qpr-dev.
This cl is a merge of the following 36 cls: ag/19250259 Duplicate BatteryChartPreferenceController and BatteryChartView into new files for better diff review purpose ag/19279660 Use Mockito 4.6.1 API for BatteryChartPreferenceControllerV2Test ag/19267975 Add class BatteryLevelData used to parcel the battery timestamps and levels. It behaves as an interface between UI and data. ag/19289086 Refactor BatteryChartView X-axis labels. Instead of only timestamps, also support any string[] labels. ag/19238586 Add interpolation for the history data since last full charge loaded from database. ag/19331746 Return raw history map in function getHistoryMapSinceLastFullCharge. ag/19308838 In BatteryChartViewV2, use levels.length-1 to replace mTrapezoidCount. So the chartview could show any number of slots as the given levels length-1. ag/19332266 Add class BatteryDiffData used to parcel battery usage data ag/19331467 Refactor Battery Chart View State Controll ag/19358207 Add DataProcessor to process raw history map for UI. ag/19332276 Add battery chart view model. ag/19394744 Update trapezoid validation in battery chart view. ag/19379730 Support daily and hourly battery chartview. ag/19428426 Improve X axis labels in battery chart (1) ag/19446215 Improve X axis labels in battery chart (2) ag/19394745 Add the async task to compute diff usage data and load labels and icons. ag/19447624 Support showing app usage list for two battery charts ag/19500907 Updates battery usage messages from last 24hr to last full charge. (Part1: V2 files) ag/19505324 Update the selected period message in battery chart ag/19500905 Updates battery usage messages from last 24hr to last full charge. (Part2: non-V2 files) ag/19510363 Update usage data for EBS app usage list and App usage detail from 24 hours to last full charge. ag/19523184 Update usage data for EBS app usage list and App usage detail from 24 hours to last full charge. ag/19534864 Add margin between battery daily and hourly charts ag/19491093 Always do interpolation for battery level data in daily chart. ag/19565630 Avoid NullPointerException when batteryLevelData is null. ag/19561239 Fix b/241872474 Battery usage page will crash when selecting the last hour chart bar, going to app detail page, and going back ag/19565633 Fix b/241885070: Unexpected texts moving when going back to battery usage page ag/19534850 New way to draw battery chart axis labels ag/19561240 Switch Battery Usage Chart from V1 to V2. ag/19561338 Switch Battery Usage Chart from V1 to V2. ag/19600174 Fix b/242254055 Battery usage initial screen improvements (long data loading time) ag/19600284 Fix b/242252080: Add padding space on the top of the battery chart ag/19647338 Consider usage map valid even if [all][all] is null. ag/19634227 Use new content uri everytime to avoid cache ag/19600177 Fix b/242009481: Refine the battery usage chart timestamp label rule ag/19647337 Fix b/242809981 Charge battery to 100% when battery usage page opened, the chart will refresh, but the app list isn't refreshed in that case. Test: manual Bug: 239491373 Bug: 236101166 Bug: 236101687 Fix: 236101166 Change-Id: I7de8d9dcee14627da10752534991f1ec9f616020 Merged-In: I9142c0d4e00dea3771777ba9aedeab07b635fa1a
This commit is contained in:
@@ -29,14 +29,24 @@
|
||||
android:layout_marginVertical="16dp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
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
|
||||
android:id="@+id/battery_chart"
|
||||
android:id="@+id/daily_battery_chart"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="170dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:visibility="invisible"
|
||||
android:layout_marginBottom="16dp"
|
||||
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:textAppearance="?android:attr/textAppearanceSmall"
|
||||
settings:textColor="?android:attr/textColorSecondary" />
|
||||
|
@@ -6744,10 +6744,16 @@
|
||||
<!-- [CHAR_LIMIT=NONE] Battery percentage: Description for preference -->
|
||||
<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 -->
|
||||
<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 -->
|
||||
<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 -->
|
||||
<string name="battery_system_usage_for_past_24">System usage for past 24 hr</string>
|
||||
<!-- [CHAR_LIMIT=NONE] Battery system usage section header -->
|
||||
|
@@ -179,7 +179,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController
|
||||
return null;
|
||||
}
|
||||
final BatteryDiffEntry entry =
|
||||
BatteryChartPreferenceController.getBatteryLast24HrUsageData(
|
||||
BatteryChartPreferenceController.getAppBatteryUsageData(
|
||||
mContext, mPackageName, mUserId);
|
||||
Log.d(TAG, "loadBatteryDiffEntries():\n" + entry);
|
||||
return entry;
|
||||
@@ -200,10 +200,10 @@ public class AppBatteryPreferenceController extends BasePreferenceController
|
||||
mBatteryPercent = Utils.formatPercentage(
|
||||
mBatteryDiffEntry.getPercentOfTotal(), /* round */ true);
|
||||
mPreference.setSummary(mContext.getString(
|
||||
R.string.battery_summary_24hr, mBatteryPercent));
|
||||
R.string.battery_summary, mBatteryPercent));
|
||||
} else {
|
||||
mPreference.setSummary(
|
||||
mContext.getString(R.string.no_battery_summary_24hr));
|
||||
mContext.getString(R.string.no_battery_summary));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -539,15 +539,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
||||
return null;
|
||||
}
|
||||
if (totalTimeMs == 0) {
|
||||
final int batteryWithoutUsageTime = consumedPower > 0
|
||||
? R.string.battery_usage_without_time : R.string.battery_not_usage_24hr;
|
||||
usageTimeSummary = getText(isChartGraphEnabled
|
||||
? batteryWithoutUsageTime : R.string.battery_not_usage);
|
||||
usageTimeSummary = getText(
|
||||
isChartGraphEnabled && consumedPower > 0 ? R.string.battery_usage_without_time
|
||||
: R.string.battery_not_usage);
|
||||
} else if (slotTime == null) {
|
||||
// Shows summary text with past 24 hr or full charge if slot time is null.
|
||||
usageTimeSummary = isChartGraphEnabled
|
||||
? getAppPast24HrActiveSummary(foregroundTimeMs, backgroundTimeMs, totalTimeMs)
|
||||
: getAppFullChargeActiveSummary(
|
||||
// Shows summary text with last full charge if slot time is null.
|
||||
usageTimeSummary = getAppFullChargeActiveSummary(
|
||||
foregroundTimeMs, backgroundTimeMs, totalTimeMs);
|
||||
} else {
|
||||
// Shows summary text with slot time.
|
||||
|
@@ -20,7 +20,6 @@ import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@@ -28,7 +27,9 @@ import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
@@ -53,8 +54,6 @@ import com.android.settingslib.utils.StringUtil;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -62,27 +61,23 @@ import java.util.Map;
|
||||
/** Controls the update for chart graph and the list items. */
|
||||
public class BatteryChartPreferenceController extends AbstractPreferenceController
|
||||
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
|
||||
OnSaveInstanceState, BatteryChartView.OnSelectListener, OnResume,
|
||||
ExpandDividerPreference.OnExpandListener {
|
||||
OnSaveInstanceState, OnResume, ExpandDividerPreference.OnExpandListener {
|
||||
private static final String TAG = "BatteryChartPreferenceController";
|
||||
private static final String KEY_FOOTER_PREF = "battery_graph_footer";
|
||||
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_DIFF_DURATION = DateUtils.MINUTE_IN_MILLIS * 3;
|
||||
|
||||
// Keys for bundle instance to restore configurations.
|
||||
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;
|
||||
|
||||
@VisibleForTesting
|
||||
Map<Integer, List<BatteryDiffEntry>> mBatteryIndexedMap;
|
||||
Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
|
||||
|
||||
@VisibleForTesting
|
||||
Context mPrefContext;
|
||||
@@ -91,28 +86,34 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
@VisibleForTesting
|
||||
PreferenceGroup mAppListPrefGroup;
|
||||
@VisibleForTesting
|
||||
BatteryChartView mBatteryChartView;
|
||||
@VisibleForTesting
|
||||
ExpandDividerPreference mExpandDividerPreference;
|
||||
|
||||
@VisibleForTesting
|
||||
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 PreferenceScreen mPreferenceScreen;
|
||||
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 SettingsActivity mActivity;
|
||||
private final InstrumentedPreferenceFragment mFragment;
|
||||
private final CharSequence[] mNotAllowShowEntryPackages;
|
||||
private final CharSequence[] mNotAllowShowSummaryPackages;
|
||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
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.
|
||||
@VisibleForTesting
|
||||
final Map<String, Preference> mPreferenceCache = new HashMap<>();
|
||||
@VisibleForTesting
|
||||
final List<BatteryDiffEntry> mSystemEntries = new ArrayList<>();
|
||||
|
||||
public BatteryChartPreferenceController(
|
||||
Context context, String preferenceKey,
|
||||
@@ -134,10 +133,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
mIs24HourFormat = DateFormat.is24HourFormat(context);
|
||||
mMetricsFeatureProvider =
|
||||
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||
mNotAllowShowEntryPackages =
|
||||
FeatureFactory.getFactory(context)
|
||||
.getPowerUsageFeatureProvider(context)
|
||||
.getHideApplicationEntries(context);
|
||||
mNotAllowShowSummaryPackages =
|
||||
FeatureFactory.getFactory(context)
|
||||
.getPowerUsageFeatureProvider(context)
|
||||
@@ -152,12 +147,14 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
if (savedInstanceState == null) {
|
||||
return;
|
||||
}
|
||||
mTrapezoidIndex =
|
||||
savedInstanceState.getInt(KEY_CURRENT_TIME_SLOT, mTrapezoidIndex);
|
||||
mDailyChartIndex =
|
||||
savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
|
||||
mHourlyChartIndex =
|
||||
savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
|
||||
mIsExpanded =
|
||||
savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
|
||||
Log.d(TAG, String.format("onCreate() slotIndex=%d isExpanded=%b",
|
||||
mTrapezoidIndex, mIsExpanded));
|
||||
Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
|
||||
mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -179,10 +176,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
if (savedInstance == null) {
|
||||
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);
|
||||
Log.d(TAG, String.format("onSaveInstanceState() slotIndex=%d isExpanded=%b",
|
||||
mTrapezoidIndex, mIsExpanded));
|
||||
Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
|
||||
mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -204,8 +202,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
mPrefContext = screen.getContext();
|
||||
mAppListPrefGroup = screen.findPreference(mPreferenceKey);
|
||||
mAppListPrefGroup.setOrderingAsAdded(false);
|
||||
mAppListPrefGroup.setTitle(
|
||||
mPrefContext.getString(R.string.battery_app_usage_for_past_24));
|
||||
mAppListPrefGroup.setTitle(mPrefContext.getString(R.string.battery_app_usage));
|
||||
mFooterPreference = screen.findPreference(KEY_FOOTER_PREF);
|
||||
// Removes footer first until usage data is loaded to avoid flashing.
|
||||
if (mFooterPreference != null) {
|
||||
@@ -249,17 +246,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
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
|
||||
public void onExpand(boolean isExpanded) {
|
||||
mIsExpanded = isExpanded;
|
||||
@@ -272,81 +258,120 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
|
||||
void setBatteryHistoryMap(
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
||||
// Resets all battery history data relative variables.
|
||||
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
||||
mBatteryIndexedMap = null;
|
||||
mBatteryHistoryKeys = null;
|
||||
mBatteryHistoryLevels = null;
|
||||
addFooterPreferenceIfNeeded(false);
|
||||
Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null"
|
||||
: ("size=" + batteryHistoryMap.size())));
|
||||
final BatteryLevelData batteryLevelData =
|
||||
DataProcessor.getBatteryLevelData(mContext, mHandler, batteryHistoryMap,
|
||||
batteryUsageMap -> {
|
||||
mBatteryUsageMap = batteryUsageMap;
|
||||
refreshUi();
|
||||
});
|
||||
Log.d(TAG, "getBatteryLevelData: " + batteryLevelData);
|
||||
if (batteryLevelData == null) {
|
||||
mDailyTimestampFullTexts = null;
|
||||
mDailyViewModel = null;
|
||||
mHourlyViewModels = null;
|
||||
refreshUi();
|
||||
return;
|
||||
}
|
||||
mBatteryHistoryKeys = getBatteryHistoryKeys(batteryHistoryMap);
|
||||
mBatteryHistoryLevels = new int[CHART_LEVEL_ARRAY_SIZE];
|
||||
for (int index = 0; index < CHART_LEVEL_ARRAY_SIZE; index++) {
|
||||
final long timestamp = mBatteryHistoryKeys[index * 2];
|
||||
final Map<String, BatteryHistEntry> entryMap = batteryHistoryMap.get(timestamp);
|
||||
if (entryMap == null || entryMap.isEmpty()) {
|
||||
Log.e(TAG, "abnormal entry list in the timestamp:"
|
||||
+ ConvertUtils.utcToLocalTime(mPrefContext, timestamp));
|
||||
continue;
|
||||
mDailyTimestampFullTexts = generateTimestampDayOfWeekTexts(
|
||||
mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(),
|
||||
/* isAbbreviation= */ false);
|
||||
mDailyViewModel = new BatteryChartViewModel(
|
||||
batteryLevelData.getDailyBatteryLevels().getLevels(),
|
||||
generateTimestampDayOfWeekTexts(
|
||||
mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(),
|
||||
/* isAbbreviation= */ true),
|
||||
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));
|
||||
}
|
||||
// Averages the battery level in each time slot to avoid corner conditions.
|
||||
float batteryLevelCounter = 0;
|
||||
for (BatteryHistEntry entry : entryMap.values()) {
|
||||
batteryLevelCounter += entry.mBatteryLevel;
|
||||
}
|
||||
mBatteryHistoryLevels[index] =
|
||||
Math.round(batteryLevelCounter / entryMap.size());
|
||||
}
|
||||
forceRefreshUi();
|
||||
Log.d(TAG, String.format(
|
||||
"setBatteryHistoryMap() size=%d key=%s\nlevels=%s",
|
||||
batteryHistoryMap.size(),
|
||||
ConvertUtils.utcToLocalTime(mPrefContext,
|
||||
mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1]),
|
||||
Arrays.toString(mBatteryHistoryLevels)));
|
||||
|
||||
// Loads item icon and label in the background.
|
||||
new LoadAllItemsInfoTask(batteryHistoryMap).execute();
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
void setBatteryChartView(final BatteryChartView batteryChartView) {
|
||||
if (mBatteryChartView != batteryChartView) {
|
||||
mHandler.post(() -> setBatteryChartViewInner(batteryChartView));
|
||||
void setBatteryChartView(@NonNull final BatteryChartView dailyChartView,
|
||||
@NonNull final BatteryChartView hourlyChartView) {
|
||||
if (mDailyChartView != dailyChartView || mHourlyChartView != hourlyChartView) {
|
||||
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
|
||||
}
|
||||
}
|
||||
|
||||
private void setBatteryChartViewInner(final BatteryChartView batteryChartView) {
|
||||
mBatteryChartView = batteryChartView;
|
||||
mBatteryChartView.setOnSelectListener(this);
|
||||
forceRefreshUi();
|
||||
private void setBatteryChartViewInner(@NonNull final BatteryChartView dailyChartView,
|
||||
@NonNull final BatteryChartView hourlyChartView) {
|
||||
mDailyChartView = dailyChartView;
|
||||
mDailyChartView.setOnSelectListener(trapezoidIndex -> {
|
||||
if (mDailyChartIndex == trapezoidIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex);
|
||||
mDailyChartIndex = trapezoidIndex;
|
||||
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
refreshUi();
|
||||
// TODO: Change to log daily data.
|
||||
});
|
||||
mHourlyChartView = hourlyChartView;
|
||||
mHourlyChartView.setOnSelectListener(trapezoidIndex -> {
|
||||
if (mHourlyChartIndex == trapezoidIndex) {
|
||||
return;
|
||||
}
|
||||
refreshUi(refreshIndex, /*isForce=*/ true);
|
||||
Log.d(TAG, "onHourlyChartSelect:" + trapezoidIndex);
|
||||
mHourlyChartIndex = trapezoidIndex;
|
||||
refreshUi();
|
||||
mMetricsFeatureProvider.action(
|
||||
mPrefContext,
|
||||
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
|
||||
? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
|
||||
: SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT);
|
||||
});
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean refreshUi(int trapezoidIndex, boolean isForce) {
|
||||
// Invalid refresh condition.
|
||||
if (mBatteryIndexedMap == null
|
||||
|| mBatteryChartView == null
|
||||
|| (mTrapezoidIndex == trapezoidIndex && !isForce)) {
|
||||
boolean refreshUi() {
|
||||
if (mDailyChartView == null || mHourlyChartView == null) {
|
||||
// Chart views are not initialized.
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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(() -> {
|
||||
final long start = System.currentTimeMillis();
|
||||
removeAndCacheAllPrefs();
|
||||
@@ -359,43 +384,22 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
}
|
||||
|
||||
private void addAllPreferences() {
|
||||
final List<BatteryDiffEntry> entries =
|
||||
mBatteryIndexedMap.get(Integer.valueOf(mTrapezoidIndex));
|
||||
addFooterPreferenceIfNeeded(entries != null && !entries.isEmpty());
|
||||
if (entries == null) {
|
||||
Log.w(TAG, "cannot find BatteryDiffEntry for:" + mTrapezoidIndex);
|
||||
final BatteryDiffData batteryDiffData =
|
||||
mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
|
||||
addFooterPreferenceIfNeeded(batteryDiffData != null
|
||||
&& (!batteryDiffData.getAppDiffEntryList().isEmpty()
|
||||
|| !batteryDiffData.getSystemDiffEntryList().isEmpty()));
|
||||
if (batteryDiffData == null) {
|
||||
Log.w(TAG, "cannot find BatteryDiffEntry for daily_index: " + mDailyChartIndex
|
||||
+ " hourly_index: " + mHourlyChartIndex);
|
||||
return;
|
||||
}
|
||||
// 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.
|
||||
if (!appEntries.isEmpty()) {
|
||||
addPreferenceToScreen(appEntries);
|
||||
if (!batteryDiffData.getAppDiffEntryList().isEmpty()) {
|
||||
addPreferenceToScreen(batteryDiffData.getAppDiffEntryList());
|
||||
}
|
||||
// Adds the expabable divider if we have system entries data.
|
||||
if (!mSystemEntries.isEmpty()) {
|
||||
if (!batteryDiffData.getSystemDiffEntryList().isEmpty()) {
|
||||
if (mExpandDividerPreference == null) {
|
||||
mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
|
||||
mExpandDividerPreference.setOnExpandListener(this);
|
||||
@@ -469,11 +473,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
}
|
||||
|
||||
private void refreshExpandUi() {
|
||||
final List<BatteryDiffEntry> systemEntries = mBatteryUsageMap.get(mDailyChartIndex).get(
|
||||
mHourlyChartIndex).getSystemDiffEntryList();
|
||||
if (mIsExpanded) {
|
||||
addPreferenceToScreen(mSystemEntries);
|
||||
addPreferenceToScreen(systemEntries);
|
||||
} else {
|
||||
// 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 Preference pref = mAppListPrefGroup.findPreference(prefKey);
|
||||
if (pref != null) {
|
||||
@@ -499,11 +505,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
}
|
||||
|
||||
private String getSlotInformation(boolean isApp, String slotInformation) {
|
||||
// TODO: Updates the right slot information from daily and hourly chart selection.
|
||||
// Null means we show all information without a specific time slot.
|
||||
if (slotInformation == null) {
|
||||
return isApp
|
||||
? mPrefContext.getString(R.string.battery_app_usage_for_past_24)
|
||||
: mPrefContext.getString(R.string.battery_system_usage_for_past_24);
|
||||
? mPrefContext.getString(R.string.battery_app_usage)
|
||||
: mPrefContext.getString(R.string.battery_system_usage);
|
||||
} else {
|
||||
return isApp
|
||||
? mPrefContext.getString(R.string.battery_app_usage_for, slotInformation)
|
||||
@@ -511,17 +518,33 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
}
|
||||
}
|
||||
|
||||
private String getSlotInformation() {
|
||||
if (mTrapezoidIndex < 0) {
|
||||
@VisibleForTesting
|
||||
String getSlotInformation() {
|
||||
if (mDailyTimestampFullTexts == null || mDailyViewModel == null
|
||||
|| mHourlyViewModels == null) {
|
||||
// No data
|
||||
return null;
|
||||
}
|
||||
final String fromHour = ConvertUtils.utcToLocalTimeHour(mPrefContext,
|
||||
mBatteryHistoryKeys[mTrapezoidIndex * 2], mIs24HourFormat);
|
||||
final String toHour = ConvertUtils.utcToLocalTimeHour(mPrefContext,
|
||||
mBatteryHistoryKeys[(mTrapezoidIndex + 1) * 2], mIs24HourFormat);
|
||||
return mIs24HourFormat
|
||||
? String.format("%s–%s", fromHour, toHour)
|
||||
: String.format("%s – %s", fromHour, toHour);
|
||||
if (isAllSelected()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String selectedDayText = mDailyTimestampFullTexts.get(mDailyChartIndex);
|
||||
if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
|
||||
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
|
||||
@@ -575,22 +598,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isValidToShowSummary(String packageName) {
|
||||
return !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);
|
||||
return !DataProcessor.contains(packageName, mNotAllowShowSummaryPackages);
|
||||
}
|
||||
|
||||
private void addFooterPreferenceIfNeeded(boolean containAppItems) {
|
||||
@@ -605,60 +613,65 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference));
|
||||
}
|
||||
|
||||
private static boolean contains(String target, CharSequence[] packageNames) {
|
||||
if (target != null && packageNames != null) {
|
||||
for (CharSequence packageName : packageNames) {
|
||||
if (TextUtils.equals(target, packageName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
private boolean isBatteryLevelDataInOneDay() {
|
||||
return mHourlyViewModels != null && mHourlyViewModels.size() == 1;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean validateUsageTime(BatteryDiffEntry entry) {
|
||||
final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs;
|
||||
final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
|
||||
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
|
||||
if (foregroundUsageTimeInMs > VALID_USAGE_TIME_DURATION
|
||||
|| backgroundUsageTimeInMs > VALID_USAGE_TIME_DURATION
|
||||
|| totalUsageTimeInMs > VALID_USAGE_TIME_DURATION) {
|
||||
Log.e(TAG, "validateUsageTime() fail for\n" + entry);
|
||||
return false;
|
||||
private boolean isAllSelected() {
|
||||
return (isBatteryLevelDataInOneDay()
|
||||
|| mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||
&& mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
}
|
||||
return true;
|
||||
|
||||
private static List<String> generateTimestampDayOfWeekTexts(@NonNull final Context context,
|
||||
@NonNull final List<Long> timestamps, final boolean isAbbreviation) {
|
||||
final ArrayList<String> texts = new ArrayList<>();
|
||||
for (Long timestamp : timestamps) {
|
||||
texts.add(ConvertUtils.utcToLocalTimeDayOfWeek(context, timestamp, isAbbreviation));
|
||||
}
|
||||
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}. */
|
||||
public static List<BatteryDiffEntry> getBatteryLast24HrUsageData(Context context) {
|
||||
public static List<BatteryDiffEntry> getAppBatteryUsageData(Context context) {
|
||||
final long start = System.currentTimeMillis();
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
||||
FeatureFactory.getFactory(context)
|
||||
.getPowerUsageFeatureProvider(context)
|
||||
.getBatteryHistory(context);
|
||||
.getBatteryHistorySinceLastFullCharge(context);
|
||||
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
||||
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)));
|
||||
final Map<Integer, List<BatteryDiffEntry>> batteryIndexedMap =
|
||||
ConvertUtils.getIndexedUsageMap(
|
||||
context,
|
||||
/*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1,
|
||||
getBatteryHistoryKeys(batteryHistoryMap),
|
||||
batteryHistoryMap,
|
||||
/*purgeLowPercentageAndFakeData=*/ true);
|
||||
return batteryIndexedMap.get(BatteryChartView.SELECTED_INDEX_ALL);
|
||||
|
||||
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageData =
|
||||
DataProcessor.getBatteryUsageData(context, batteryHistoryMap);
|
||||
return batteryUsageData == null
|
||||
? null
|
||||
: batteryUsageData
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||
.getAppDiffEntryList();
|
||||
}
|
||||
|
||||
/** Used for {@link AppBatteryPreferenceController}. */
|
||||
public static BatteryDiffEntry getBatteryLast24HrUsageData(
|
||||
public static BatteryDiffEntry getAppBatteryUsageData(
|
||||
Context context, String packageName, int userId) {
|
||||
if (packageName == null) {
|
||||
return null;
|
||||
}
|
||||
final List<BatteryDiffEntry> entries = getBatteryLast24HrUsageData(context);
|
||||
final List<BatteryDiffEntry> entries = getAppBatteryUsageData(context);
|
||||
if (entries == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -673,65 +686,4 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
||||
}
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
import static com.android.settings.Utils.formatPercentage;
|
||||
|
||||
import static java.lang.Math.round;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.Context;
|
||||
@@ -29,8 +30,6 @@ import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
@@ -39,6 +38,7 @@ import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
@@ -46,7 +46,7 @@ import com.android.settings.R;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settingslib.Utils;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -58,36 +58,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
|
||||
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 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. */
|
||||
public interface OnSelectListener {
|
||||
/** The callback function for selected group index is updated. */
|
||||
void onSelect(int trapezoidIndex);
|
||||
}
|
||||
|
||||
private BatteryChartViewModel mViewModel;
|
||||
|
||||
private int mDividerWidth;
|
||||
private int mDividerHeight;
|
||||
private int mTrapezoidCount;
|
||||
private float mTrapezoidVOffset;
|
||||
private float mTrapezoidHOffset;
|
||||
private boolean mIsSlotsClickabled;
|
||||
private String[] mPercentages = getPercentages();
|
||||
|
||||
@VisibleForTesting
|
||||
int mHoveredIndex = SELECTED_INDEX_INVALID;
|
||||
@VisibleForTesting
|
||||
int mSelectedIndex = SELECTED_INDEX_INVALID;
|
||||
@VisibleForTesting
|
||||
String[] mTimestamps;
|
||||
int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
|
||||
// Colors for drawing the trapezoid shape and dividers.
|
||||
private int mTrapezoidColor;
|
||||
@@ -98,25 +88,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
private final Rect mIndent = new Rect();
|
||||
private final Rect[] mPercentageBounds =
|
||||
new Rect[]{new Rect(), new Rect(), new Rect()};
|
||||
// For drawing the timestamp information.
|
||||
private final Rect[] mTimestampsBounds =
|
||||
new Rect[]{new Rect(), new Rect(), new Rect(), new Rect()};
|
||||
// For drawing the axis label information.
|
||||
private final List<Rect> mAxisLabelsBounds = new ArrayList<>();
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
Handler mHandler = new Handler();
|
||||
@VisibleForTesting
|
||||
final Runnable mUpdateClickableStateRun = () -> updateClickableState();
|
||||
|
||||
private int[] mLevels;
|
||||
private Paint mTextPaint;
|
||||
private Paint mDividerPaint;
|
||||
private Paint mTrapezoidPaint;
|
||||
|
||||
@VisibleForTesting
|
||||
Paint mTrapezoidCurvePaint = null;
|
||||
private TrapezoidSlot[] mTrapezoidSlots;
|
||||
@VisibleForTesting
|
||||
TrapezoidSlot[] mTrapezoidSlots;
|
||||
// Records the location to calculate selected index.
|
||||
private float mTouchUpEventX = Float.MIN_VALUE;
|
||||
@VisibleForTesting
|
||||
float mTouchUpEventX = Float.MIN_VALUE;
|
||||
private BatteryChartView.OnSelectListener mOnSelectListener;
|
||||
|
||||
public BatteryChartView(Context context) {
|
||||
@@ -128,57 +119,25 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
initializeColors(context);
|
||||
// Registers the click event listener.
|
||||
setOnClickListener(this);
|
||||
setSelectedIndex(SELECTED_INDEX_ALL);
|
||||
setTrapezoidCount(DEFAULT_TRAPEZOID_COUNT);
|
||||
setClickable(false);
|
||||
setLatestTimestamp(0);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/** Sets the total trapezoid count for drawing. */
|
||||
public void setTrapezoidCount(int trapezoidCount) {
|
||||
Log.i(TAG, "trapezoidCount:" + trapezoidCount);
|
||||
mTrapezoidCount = trapezoidCount;
|
||||
mTrapezoidSlots = new TrapezoidSlot[trapezoidCount];
|
||||
// Allocates the trapezoid slot array.
|
||||
for (int index = 0; index < trapezoidCount; index++) {
|
||||
mTrapezoidSlots[index] = new TrapezoidSlot();
|
||||
}
|
||||
/** Sets the data model of this view. */
|
||||
public void setViewModel(BatteryChartViewModel viewModel) {
|
||||
if (viewModel == null) {
|
||||
mViewModel = null;
|
||||
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();
|
||||
// Callbacks to the listener if we have.
|
||||
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. */
|
||||
@@ -195,29 +154,6 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -226,6 +162,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
// Measures text bounds and updates indent configuration.
|
||||
if (mTextPaint != null) {
|
||||
mTextPaint.setTextAlign(Paint.Align.LEFT);
|
||||
for (int index = 0; index < mPercentages.length; index++) {
|
||||
mTextPaint.getTextBounds(
|
||||
mPercentages[index], 0, mPercentages[index].length(),
|
||||
@@ -235,15 +172,14 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
mIndent.top = mPercentageBounds[0].height();
|
||||
mIndent.right = mPercentageBounds[0].width() + mTextPadding;
|
||||
|
||||
if (mTimestamps != null) {
|
||||
int maxHeight = 0;
|
||||
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) {
|
||||
mTextPaint.getTextBounds(
|
||||
mTimestamps[index], 0, mTimestamps[index].length(),
|
||||
mTimestampsBounds[index]);
|
||||
maxHeight = Math.max(maxHeight, mTimestampsBounds[index].height());
|
||||
if (mViewModel != null) {
|
||||
int maxTop = 0;
|
||||
for (int index = 0; index < mViewModel.size(); index++) {
|
||||
final String text = mViewModel.texts().get(index);
|
||||
mTextPaint.getTextBounds(text, 0, text.length(), mAxisLabelsBounds.get(index));
|
||||
maxTop = Math.max(maxTop, -mAxisLabelsBounds.get(index).top);
|
||||
}
|
||||
mIndent.bottom = maxHeight + round(mTextPadding * 1.5f);
|
||||
mIndent.bottom = maxTop + round(mTextPadding * 2f);
|
||||
}
|
||||
Log.d(TAG, "setIndent:" + mPercentageBounds[0]);
|
||||
} else {
|
||||
@@ -254,7 +190,12 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
super.draw(canvas);
|
||||
// Before mLevels initialized, the count of trapezoids is unknown. Only draws the
|
||||
// horizontal percentages and dividers.
|
||||
drawHorizontalDividers(canvas);
|
||||
if (mViewModel == null) {
|
||||
return;
|
||||
}
|
||||
drawVerticalDividers(canvas);
|
||||
drawTrapezoids(canvas);
|
||||
}
|
||||
@@ -294,7 +235,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
public void onHoverChanged(boolean hovered) {
|
||||
super.onHoverChanged(hovered);
|
||||
if (!hovered) {
|
||||
mHoveredIndex = SELECTED_INDEX_INVALID; // reset
|
||||
mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@@ -307,15 +248,15 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
}
|
||||
final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX);
|
||||
// Ignores the click event if the level is zero.
|
||||
if (trapezoidIndex == SELECTED_INDEX_INVALID
|
||||
|| !isValidToDraw(trapezoidIndex)) {
|
||||
if (trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID
|
||||
|| !isValidToDraw(mViewModel, trapezoidIndex)) {
|
||||
return;
|
||||
}
|
||||
if (mOnSelectListener != null) {
|
||||
// Selects all if users click the same trapezoid item two times.
|
||||
if (trapezoidIndex == mSelectedIndex) {
|
||||
setSelectedIndex(SELECTED_INDEX_ALL);
|
||||
} else {
|
||||
setSelectedIndex(trapezoidIndex);
|
||||
mOnSelectListener.onSelect(
|
||||
trapezoidIndex == mViewModel.selectedIndex()
|
||||
? BatteryChartViewModel.SELECTED_INDEX_ALL : trapezoidIndex);
|
||||
}
|
||||
view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||
}
|
||||
@@ -364,8 +305,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
|
||||
} else if (mIsSlotsClickabled) {
|
||||
mTrapezoidCurvePaint = null;
|
||||
// Sets levels again to force update the click state.
|
||||
setLevels(mLevels);
|
||||
// Sets view model again to force update the click state.
|
||||
setViewModel(mViewModel);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
@@ -380,6 +321,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
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) {
|
||||
setBackgroundColor(Color.TRANSPARENT);
|
||||
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) {
|
||||
if (mTextPaint != null) {
|
||||
mTextPaint.setTextAlign(Paint.Align.RIGHT);
|
||||
canvas.drawText(
|
||||
mPercentages[index],
|
||||
getWidth() - mPercentageBounds[index].width()
|
||||
- mPercentageBounds[index].left,
|
||||
getWidth(),
|
||||
offsetY + mPercentageBounds[index].height() * .5f,
|
||||
mTextPaint);
|
||||
}
|
||||
@@ -445,9 +393,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
|
||||
private void drawVerticalDividers(Canvas canvas) {
|
||||
final int width = getWidth() - mIndent.right;
|
||||
final int dividerCount = mTrapezoidCount + 1;
|
||||
final int dividerCount = mTrapezoidSlots.length + 1;
|
||||
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 startY = bottomY - mDividerHeight;
|
||||
final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f;
|
||||
@@ -463,66 +411,140 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
}
|
||||
startX = nextX;
|
||||
}
|
||||
// Draws the timestamp slot information.
|
||||
if (mTimestamps != null) {
|
||||
final float[] xOffsets = new float[DEFAULT_TIMESTAMP_COUNT];
|
||||
final float baselineX = mDividerWidth * .5f;
|
||||
final float offsetX = mDividerWidth + unitWidth;
|
||||
final int slotBarOffset = (/*total 12 bars*/ 12) / TIMESTAMP_GAPS_COUNT;
|
||||
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) {
|
||||
xOffsets[index] = baselineX + index * offsetX * slotBarOffset;
|
||||
// Draws the axis label slot information.
|
||||
if (mViewModel != null) {
|
||||
final float baselineY = getHeight() - mTextPadding * 1.5f;
|
||||
Rect[] axisLabelDisplayAreas;
|
||||
switch (mViewModel.axisLabelPosition()) {
|
||||
case CENTER_OF_TRAPEZOIDS:
|
||||
axisLabelDisplayAreas = getAxisLabelDisplayAreas(
|
||||
/* 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) {
|
||||
// Draws the 1st timestamp info.
|
||||
canvas.drawText(
|
||||
mTimestamps[0],
|
||||
xOffsets[0] - mTimestampsBounds[0].left,
|
||||
getTimestampY(0), mTextPaint);
|
||||
final int latestIndex = DEFAULT_TIMESTAMP_COUNT - 1;
|
||||
// Draws the last timestamp info.
|
||||
canvas.drawText(
|
||||
mTimestamps[latestIndex],
|
||||
xOffsets[latestIndex] - mTimestampsBounds[latestIndex].width()
|
||||
- mTimestampsBounds[latestIndex].left,
|
||||
getTimestampY(latestIndex), mTextPaint);
|
||||
// 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(
|
||||
mTimestamps[index],
|
||||
xOffsets[index]
|
||||
- (mTimestampsBounds[index].width() - mTimestampsBounds[index].left)
|
||||
* .5f,
|
||||
getTimestampY(index), mTextPaint);
|
||||
/** Gets all the axis label texts displaying area positions if they are shown. */
|
||||
private Rect[] getAxisLabelDisplayAreas(final int size, final float baselineX,
|
||||
final float offsetX, final float baselineY, final boolean shiftFirstAndLast) {
|
||||
final Rect[] result = new Rect[size];
|
||||
for (int index = 0; index < result.length; index++) {
|
||||
final float width = mAxisLabelsBounds.get(index).width();
|
||||
float middle = baselineX + index * offsetX;
|
||||
if (shiftFirstAndLast) {
|
||||
if (index == 0) {
|
||||
middle += width * .5f;
|
||||
}
|
||||
if (index == size - 1) {
|
||||
middle -= width * .5f;
|
||||
}
|
||||
}
|
||||
final float left = middle - width * .5f;
|
||||
final float right = left + width;
|
||||
final float top = baselineY + mAxisLabelsBounds.get(index).top;
|
||||
final float bottom = top + mAxisLabelsBounds.get(index).height();
|
||||
result[index] = new Rect(round(left), round(top), round(right), round(bottom));
|
||||
}
|
||||
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) {
|
||||
return getHeight() - mTimestampsBounds[index].height()
|
||||
+ (mTimestampsBounds[index].height() + mTimestampsBounds[index].top)
|
||||
+ round(mTextPadding * 1.5f);
|
||||
private boolean hasOverlap(
|
||||
final Rect[] displayAreas, final int leftIndex, final int rightIndex) {
|
||||
return displayAreas[leftIndex].right + mTextPadding * 2f > displayAreas[rightIndex].left;
|
||||
}
|
||||
|
||||
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) {
|
||||
// Ignores invalid trapezoid data.
|
||||
if (mLevels == null) {
|
||||
if (mViewModel == null) {
|
||||
return;
|
||||
}
|
||||
final float trapezoidBottom =
|
||||
getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth
|
||||
- mTrapezoidVOffset;
|
||||
final float availableSpace = trapezoidBottom - mDividerWidth * .5f - mIndent.top;
|
||||
final float availableSpace =
|
||||
trapezoidBottom - mDividerWidth * .5f - mIndent.top - mTrapezoidVOffset;
|
||||
final float unitHeight = availableSpace / 100f;
|
||||
// Draws all trapezoid shapes into the canvas.
|
||||
final Path trapezoidPath = new Path();
|
||||
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.
|
||||
if (!isValidToDraw(index)) {
|
||||
if (!isValidToDraw(mViewModel, index)) {
|
||||
if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) {
|
||||
canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint);
|
||||
trapezoidCurvePath = null;
|
||||
@@ -530,17 +552,18 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
continue;
|
||||
}
|
||||
// Configures the trapezoid paint color.
|
||||
final int trapezoidColor =
|
||||
!mIsSlotsClickabled
|
||||
? mTrapezoidColor
|
||||
: mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
|
||||
final int trapezoidColor = mIsSlotsClickabled && (mViewModel.selectedIndex() == index
|
||||
|| mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||
? mTrapezoidSolidColor : mTrapezoidColor;
|
||||
final boolean isHoverState =
|
||||
mIsSlotsClickabled && mHoveredIndex == index && isValidToDraw(mHoveredIndex);
|
||||
mIsSlotsClickabled && mHoveredIndex == index
|
||||
&& isValidToDraw(mViewModel, mHoveredIndex);
|
||||
mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor);
|
||||
|
||||
final float leftTop = round(trapezoidBottom - mLevels[index] * unitHeight);
|
||||
final float rightTop = round(trapezoidBottom - mLevels[index + 1] * unitHeight);
|
||||
final float leftTop = round(
|
||||
trapezoidBottom - requireNonNull(mViewModel.levels().get(index)) * unitHeight);
|
||||
final float rightTop = round(trapezoidBottom
|
||||
- requireNonNull(mViewModel.levels().get(index + 1)) * unitHeight);
|
||||
trapezoidPath.reset();
|
||||
trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom);
|
||||
trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop);
|
||||
@@ -579,15 +602,37 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return SELECTED_INDEX_INVALID;
|
||||
return BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
}
|
||||
|
||||
private boolean isValidToDraw(int trapezoidIndex) {
|
||||
return mLevels != null
|
||||
private void initializeAxisLabelsBounds() {
|
||||
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 < mLevels.length - 1
|
||||
&& mLevels[trapezoidIndex] != 0
|
||||
&& mLevels[trapezoidIndex + 1] != 0;
|
||||
&& trapezoidIndex < viewModel.size() - 1
|
||||
&& isTrapezoidValid(viewModel, trapezoidIndex);
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -621,7 +666,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
||||
}
|
||||
|
||||
// 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 mRight;
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -43,6 +43,6 @@ public class BatteryHistoryLoader
|
||||
public Map<Long, Map<String, BatteryHistEntry>> loadInBackground() {
|
||||
final PowerUsageFeatureProvider powerUsageFeatureProvider =
|
||||
FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(mContext);
|
||||
return powerUsageFeatureProvider.getBatteryHistory(mContext);
|
||||
return powerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext);
|
||||
}
|
||||
}
|
||||
|
@@ -49,7 +49,8 @@ public class BatteryHistoryPreference extends Preference {
|
||||
|
||||
private TextView mSummaryView;
|
||||
private CharSequence mSummaryContent;
|
||||
private BatteryChartView mBatteryChartView;
|
||||
private BatteryChartView mDailyChartView;
|
||||
private BatteryChartView mHourlyChartView;
|
||||
private BatteryChartPreferenceController mChartPreferenceController;
|
||||
|
||||
public BatteryHistoryPreference(Context context, AttributeSet attrs) {
|
||||
@@ -92,8 +93,8 @@ public class BatteryHistoryPreference extends Preference {
|
||||
|
||||
void setChartPreferenceController(BatteryChartPreferenceController controller) {
|
||||
mChartPreferenceController = controller;
|
||||
if (mBatteryChartView != null) {
|
||||
mChartPreferenceController.setBatteryChartView(mBatteryChartView);
|
||||
if (mDailyChartView != null && mHourlyChartView != null) {
|
||||
mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,11 +106,14 @@ public class BatteryHistoryPreference extends Preference {
|
||||
return;
|
||||
}
|
||||
if (mIsChartGraphEnabled) {
|
||||
mBatteryChartView = (BatteryChartView) view.findViewById(R.id.battery_chart);
|
||||
mBatteryChartView.setCompanionTextView(
|
||||
mDailyChartView = (BatteryChartView) view.findViewById(R.id.daily_battery_chart);
|
||||
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));
|
||||
if (mChartPreferenceController != null) {
|
||||
mChartPreferenceController.setBatteryChartView(mBatteryChartView);
|
||||
mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView);
|
||||
}
|
||||
} else {
|
||||
final TextView chargeView = (TextView) view.findViewById(R.id.charge);
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
|
@@ -140,7 +140,7 @@ public final class ConvertUtils {
|
||||
|
||||
/** Converts UTC timestamp to local time hour data. */
|
||||
public static String utcToLocalTimeHour(
|
||||
Context context, long timestamp, boolean is24HourFormat) {
|
||||
final Context context, final long timestamp, final boolean is24HourFormat) {
|
||||
final Locale locale = getLocale(context);
|
||||
// e.g. for 12-hour format: 9 pm
|
||||
// e.g. for 24-hour format: 09:00
|
||||
@@ -149,6 +149,15 @@ public final class ConvertUtils {
|
||||
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. */
|
||||
public static Map<Integer, List<BatteryDiffEntry>> getIndexedUsageMap(
|
||||
final Context context,
|
||||
@@ -267,7 +276,7 @@ public final class ConvertUtils {
|
||||
diffEntry.setTotalConsumePower(totalConsumePower);
|
||||
}
|
||||
}
|
||||
insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap);
|
||||
insert24HoursData(BatteryChartViewModel.SELECTED_INDEX_ALL, resultMap);
|
||||
resolveMultiUsersData(context, resultMap);
|
||||
if (purgeLowPercentageAndFakeData) {
|
||||
purgeLowPercentageAndFakeData(context, resultMap);
|
||||
|
1058
src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
Normal file
1058
src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -259,10 +259,7 @@ public class PowerUsageSummary extends PowerUsageBase implements
|
||||
@VisibleForTesting
|
||||
void initPreference() {
|
||||
mBatteryUsagePreference = findPreference(KEY_BATTERY_USAGE);
|
||||
mBatteryUsagePreference.setSummary(
|
||||
mPowerFeatureProvider.isChartGraphEnabled(getContext())
|
||||
? getString(R.string.advanced_battery_preference_summary_with_hours)
|
||||
: getString(R.string.advanced_battery_preference_summary));
|
||||
mBatteryUsagePreference.setSummary(getString(R.string.advanced_battery_preference_summary));
|
||||
|
||||
mHelpPreference = findPreference(KEY_BATTERY_ERROR);
|
||||
mHelpPreference.setVisible(false);
|
||||
|
@@ -162,7 +162,8 @@ public class AppBatteryPreferenceControllerTest {
|
||||
|
||||
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
|
||||
@@ -175,7 +176,8 @@ public class AppBatteryPreferenceControllerTest {
|
||||
|
||||
mController.updateBatteryWithDiffEntry();
|
||||
|
||||
assertThat(mBatteryPreference.getSummary()).isEqualTo("60% use for past 24 hours");
|
||||
assertThat(mBatteryPreference.getSummary().toString()).isEqualTo(
|
||||
"60% use since last full charge");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -232,7 +232,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPreferenceScreenResId_returnNewLayout() {
|
||||
public void setPreferenceScreenResId_returnNewLayout() {
|
||||
assertThat(mFragment.getPreferenceScreenResId()).isEqualTo(R.xml.power_usage_detail);
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_HasAppEntry_BuildByAppEntry() {
|
||||
public void initHeader_HasAppEntry_BuildByAppEntry() {
|
||||
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
|
||||
new InstantAppDataProvider() {
|
||||
@Override
|
||||
@@ -269,7 +269,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_HasAppEntry_InstantApp() {
|
||||
public void initHeader_HasAppEntry_InstantApp() {
|
||||
ReflectionHelpers.setStaticField(AppUtils.class, "sInstantAppDataProvider",
|
||||
new InstantAppDataProvider() {
|
||||
@Override
|
||||
@@ -286,7 +286,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_noUsageTimeAndGraphDisabled_hasCorrectSummary() {
|
||||
public void initHeader_noUsageTimeAndGraphDisabled_hasCorrectSummary() {
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
|
||||
.thenReturn(false);
|
||||
|
||||
@@ -304,7 +304,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_bgTwoMinFgZeroAndGraphDisabled_hasCorrectSummary() {
|
||||
public void initHeader_bgTwoMinFgZeroAndGraphDisabled_hasCorrectSummary() {
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
|
||||
.thenReturn(false);
|
||||
|
||||
@@ -324,7 +324,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_bgLessThanAMinFgZeroAndGraphDisabled_hasCorrectSummary() {
|
||||
public void initHeader_bgLessThanAMinFgZeroAndGraphDisabled_hasCorrectSummary() {
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
|
||||
.thenReturn(false);
|
||||
|
||||
@@ -345,7 +345,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_totalUsageLessThanAMinAndGraphDisabled_hasCorrectSummary() {
|
||||
public void initHeader_totalUsageLessThanAMinAndGraphDisabled_hasCorrectSummary() {
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
|
||||
.thenReturn(false);
|
||||
|
||||
@@ -367,7 +367,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_TotalAMinutesBgLessThanAMinAndGraphDisabled_hasCorrectSummary() {
|
||||
public void initHeader_TotalAMinutesBgLessThanAMinAndGraphDisabled_hasCorrectSummary() {
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
|
||||
.thenReturn(false);
|
||||
|
||||
@@ -387,7 +387,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_TotalAMinBackgroundZeroAndGraphDisabled_hasCorrectSummary() {
|
||||
public void initHeader_TotalAMinBackgroundZeroAndGraphDisabled_hasCorrectSummary() {
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
|
||||
.thenReturn(false);
|
||||
final long backgroundTimeZero = 0;
|
||||
@@ -406,7 +406,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_fgTwoMinBgFourMinAndGraphDisabled_hasCorrectSummary() {
|
||||
public void initHeader_fgTwoMinBgFourMinAndGraphDisabled_hasCorrectSummary() {
|
||||
when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
|
||||
.thenReturn(false);
|
||||
final long backgroundTimeFourMinute = 240000;
|
||||
@@ -424,7 +424,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_noUsageTime_hasCorrectSummary() {
|
||||
public void initHeader_noUsageTime_hasCorrectSummary() {
|
||||
Bundle bundle = new Bundle(2);
|
||||
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_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);
|
||||
verify(mEntityHeaderController).setSummary(captor.capture());
|
||||
assertThat(captor.getValue().toString())
|
||||
.isEqualTo("No usage for past 24 hr");
|
||||
.isEqualTo("No usage from last full charge");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_noUsageTimeButConsumedPower_hasEmptySummary() {
|
||||
public void initHeader_noUsageTimeButConsumedPower_hasEmptySummary() {
|
||||
Bundle bundle = new Bundle(3);
|
||||
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, /* value */ 0);
|
||||
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, /* value */ 0);
|
||||
@@ -454,7 +454,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_backgroundTwoMinForegroundZero_hasCorrectSummary() {
|
||||
public void initHeader_backgroundTwoMinForegroundZero_hasCorrectSummary() {
|
||||
final long backgroundTimeTwoMinutes = 120000;
|
||||
final long foregroundTimeZero = 0;
|
||||
Bundle bundle = new Bundle(2);
|
||||
@@ -467,11 +467,11 @@ public class AdvancedPowerUsageDetailTest {
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mEntityHeaderController).setSummary(captor.capture());
|
||||
assertThat(captor.getValue().toString())
|
||||
.isEqualTo("2 min background for past 24 hr");
|
||||
.isEqualTo("2 min background from last full charge");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_backgroundLessThanAMinForegroundZero_hasCorrectSummary() {
|
||||
public void initHeader_backgroundLessThanAMinForegroundZero_hasCorrectSummary() {
|
||||
final long backgroundTimeLessThanAMinute = 59999;
|
||||
final long foregroundTimeZero = 0;
|
||||
Bundle bundle = new Bundle(2);
|
||||
@@ -485,11 +485,11 @@ public class AdvancedPowerUsageDetailTest {
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mEntityHeaderController).setSummary(captor.capture());
|
||||
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
|
||||
public void testInitHeader_totalUsageLessThanAMin_hasCorrectSummary() {
|
||||
public void initHeader_totalUsageLessThanAMin_hasCorrectSummary() {
|
||||
final long backgroundTimeLessThanHalfMinute = 20000;
|
||||
final long foregroundTimeLessThanHalfMinute = 20000;
|
||||
Bundle bundle = new Bundle(2);
|
||||
@@ -504,11 +504,11 @@ public class AdvancedPowerUsageDetailTest {
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mEntityHeaderController).setSummary(captor.capture());
|
||||
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
|
||||
public void testInitHeader_TotalAMinutesBackgroundLessThanAMin_hasCorrectSummary() {
|
||||
public void initHeader_TotalAMinutesBackgroundLessThanAMin_hasCorrectSummary() {
|
||||
final long backgroundTimeZero = 59999;
|
||||
final long foregroundTimeTwoMinutes = 1;
|
||||
Bundle bundle = new Bundle(2);
|
||||
@@ -521,11 +521,11 @@ public class AdvancedPowerUsageDetailTest {
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mEntityHeaderController).setSummary(captor.capture());
|
||||
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
|
||||
public void testInitHeader_TotalAMinBackgroundZero_hasCorrectSummary() {
|
||||
public void initHeader_TotalAMinBackgroundZero_hasCorrectSummary() {
|
||||
final long backgroundTimeZero = 0;
|
||||
final long foregroundTimeAMinutes = 60000;
|
||||
Bundle bundle = new Bundle(2);
|
||||
@@ -538,11 +538,11 @@ public class AdvancedPowerUsageDetailTest {
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mEntityHeaderController).setSummary(captor.capture());
|
||||
assertThat(captor.getValue().toString())
|
||||
.isEqualTo("1 min total for past 24 hr");
|
||||
.isEqualTo("1 min total from last full charge");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_foregroundTwoMinBackgroundFourMin_hasCorrectSummary() {
|
||||
public void initHeader_foregroundTwoMinBackgroundFourMin_hasCorrectSummary() {
|
||||
final long backgroundTimeFourMinute = 240000;
|
||||
final long foregroundTimeTwoMinutes = 120000;
|
||||
Bundle bundle = new Bundle(2);
|
||||
@@ -555,11 +555,11 @@ public class AdvancedPowerUsageDetailTest {
|
||||
ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
|
||||
verify(mEntityHeaderController).setSummary(captor.capture());
|
||||
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
|
||||
public void testInitHeader_totalUsageLessThanAMinWithSlotTime_hasCorrectSummary() {
|
||||
public void initHeader_totalUsageLessThanAMinWithSlotTime_hasCorrectSummary() {
|
||||
final long backgroundTimeLessThanHalfMinute = 20000;
|
||||
final long foregroundTimeLessThanHalfMinute = 20000;
|
||||
Bundle bundle = new Bundle(3);
|
||||
@@ -579,7 +579,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_TotalAMinBackgroundLessThanAMinWithSlotTime_hasCorrectSummary() {
|
||||
public void initHeader_TotalAMinBackgroundLessThanAMinWithSlotTime_hasCorrectSummary() {
|
||||
final long backgroundTimeZero = 59999;
|
||||
final long foregroundTimeTwoMinutes = 1;
|
||||
Bundle bundle = new Bundle(3);
|
||||
@@ -597,7 +597,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_TotalAMinBackgroundZeroWithSlotTime_hasCorrectSummary() {
|
||||
public void initHeader_TotalAMinBackgroundZeroWithSlotTime_hasCorrectSummary() {
|
||||
final long backgroundTimeZero = 0;
|
||||
final long foregroundTimeAMinutes = 60000;
|
||||
Bundle bundle = new Bundle(3);
|
||||
@@ -615,7 +615,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_foregroundTwoMinBackgroundFourMinWithSlotTime_hasCorrectSummary() {
|
||||
public void initHeader_foregroundTwoMinBackgroundFourMinWithSlotTime_hasCorrectSummary() {
|
||||
final long backgroundTimeFourMinute = 240000;
|
||||
final long foregroundTimeTwoMinutes = 120000;
|
||||
Bundle bundle = new Bundle(3);
|
||||
@@ -633,7 +633,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_systemUidWithChartIsDisabled_nullSummary() {
|
||||
public void initHeader_systemUidWithChartIsDisabled_nullSummary() {
|
||||
Bundle bundle = new Bundle(3);
|
||||
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, 240000);
|
||||
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, 120000);
|
||||
@@ -650,7 +650,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitHeader_systemUidWithChartIsEnabled_notNullSummary() {
|
||||
public void initHeader_systemUidWithChartIsEnabled_notNullSummary() {
|
||||
Bundle bundle = new Bundle(3);
|
||||
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, 240000);
|
||||
bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, 120000);
|
||||
@@ -665,7 +665,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBatteryDetailPage_hasBasicData() {
|
||||
public void startBatteryDetailPage_hasBasicData() {
|
||||
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
|
||||
mBatteryEntry, USAGE_PERCENT, /*isValidToShowSummary=*/ true);
|
||||
|
||||
@@ -679,7 +679,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBatteryDetailPage_invalidToShowSummary_noFGBDData() {
|
||||
public void startBatteryDetailPage_invalidToShowSummary_noFGBDData() {
|
||||
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
|
||||
mBatteryEntry, USAGE_PERCENT, /*isValidToShowSummary=*/ false);
|
||||
|
||||
@@ -693,7 +693,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBatteryDetailPage_NormalApp() {
|
||||
public void startBatteryDetailPage_NormalApp() {
|
||||
when(mBatteryEntry.getDefaultPackageName()).thenReturn(PACKAGE_NAME[0]);
|
||||
|
||||
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
|
||||
@@ -704,7 +704,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBatteryDetailPage_SystemApp() {
|
||||
public void startBatteryDetailPage_SystemApp() {
|
||||
when(mBatteryEntry.getDefaultPackageName()).thenReturn(null);
|
||||
|
||||
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment,
|
||||
@@ -716,7 +716,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBatteryDetailPage_WorkApp() {
|
||||
public void startBatteryDetailPage_WorkApp() {
|
||||
final int appUid = 1010019;
|
||||
doReturn(appUid).when(mBatteryEntry).getUid();
|
||||
|
||||
@@ -727,7 +727,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBatteryDetailPage_typeUser_startByCurrentUser() {
|
||||
public void startBatteryDetailPage_typeUser_startByCurrentUser() {
|
||||
when(mBatteryEntry.isUserEntry()).thenReturn(true);
|
||||
|
||||
final int currentUser = 20;
|
||||
@@ -739,7 +739,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBatteryDetailPage_noBatteryUsage_hasBasicData() {
|
||||
public void startBatteryDetailPage_noBatteryUsage_hasBasicData() {
|
||||
final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
|
||||
|
||||
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity, mFragment, PACKAGE_NAME[0]);
|
||||
@@ -756,7 +756,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartBatteryDetailPage_batteryEntryNotExisted_extractUidFromPackageName() throws
|
||||
public void startBatteryDetailPage_batteryEntryNotExisted_extractUidFromPackageName() throws
|
||||
PackageManager.NameNotFoundException {
|
||||
doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME[0], 0 /* no flag */);
|
||||
|
||||
@@ -796,7 +796,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitPreferenceForTriState_isSystemOrDefaultApp_hasCorrectString() {
|
||||
public void initPreferenceForTriState_isSystemOrDefaultApp_hasCorrectString() {
|
||||
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
|
||||
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true);
|
||||
|
||||
@@ -807,7 +807,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitPreferenceForTriState_hasCorrectString() {
|
||||
public void initPreferenceForTriState_hasCorrectString() {
|
||||
when(mBatteryOptimizeUtils.isValidPackageName()).thenReturn(true);
|
||||
when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false);
|
||||
|
||||
@@ -818,7 +818,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnRadioButtonClicked_clickOptimizePref_optimizePreferenceChecked() {
|
||||
public void onRadioButtonClicked_clickOptimizePref_optimizePreferenceChecked() {
|
||||
mOptimizePreference.setKey(KEY_PREF_OPTIMIZED);
|
||||
mRestrictedPreference.setKey(KEY_PREF_RESTRICTED);
|
||||
mUnrestrictedPreference.setKey(KEY_PREF_UNRESTRICTED);
|
||||
@@ -830,7 +830,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPause_optimizationModeChanged_logPreference() {
|
||||
public void onPause_optimizationModeChanged_logPreference() {
|
||||
final int mode = BatteryOptimizeUtils.MODE_RESTRICTED;
|
||||
mFragment.mOptimizationMode = mode;
|
||||
when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode);
|
||||
@@ -849,7 +849,7 @@ public class AdvancedPowerUsageDetailTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnPause_optimizationModeIsNotChanged_notInvokeLogging() {
|
||||
public void onPause_optimizationModeIsNotChanged_notInvokeLogging() {
|
||||
final int mode = BatteryOptimizeUtils.MODE_OPTIMIZED;
|
||||
mFragment.mOptimizationMode = mode;
|
||||
when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(mode);
|
||||
|
@@ -18,23 +18,24 @@ package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.anyLong;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.LocaleList;
|
||||
import android.text.format.DateUtils;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
@@ -58,15 +59,15 @@ import org.robolectric.RuntimeEnvironment;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public final class BatteryChartPreferenceControllerTest {
|
||||
private static final String PREF_KEY = "pref_key";
|
||||
private static final String PREF_SUMMARY = "fake preference summary";
|
||||
private static final int DESIRED_HISTORY_SIZE =
|
||||
BatteryChartPreferenceController.DESIRED_HISTORY_SIZE;
|
||||
|
||||
@Mock
|
||||
private InstrumentedPreferenceFragment mFragment;
|
||||
@@ -79,11 +80,15 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
@Mock
|
||||
private BatteryHistEntry mBatteryHistEntry;
|
||||
@Mock
|
||||
private BatteryChartView mBatteryChartView;
|
||||
private BatteryChartView mDailyChartView;
|
||||
@Mock
|
||||
private BatteryChartView mHourlyChartView;
|
||||
@Mock
|
||||
private PowerGaugePreference mPowerGaugePreference;
|
||||
@Mock
|
||||
private BatteryUtils mBatteryUtils;
|
||||
@Mock
|
||||
private LinearLayout.LayoutParams mLayoutParams;
|
||||
|
||||
private Context mContext;
|
||||
private FakeFeatureFactory mFeatureFactory;
|
||||
@@ -96,6 +101,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
Locale.setDefault(new Locale("en_US"));
|
||||
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
@@ -108,10 +114,12 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
doReturn(new String[]{"com.android.gms.persistent"})
|
||||
.when(mFeatureFactory.powerUsageFeatureProvider)
|
||||
.getHideApplicationEntries(mContext);
|
||||
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
|
||||
mBatteryChartPreferenceController = createController();
|
||||
mBatteryChartPreferenceController.mPrefContext = mContext;
|
||||
mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup;
|
||||
mBatteryChartPreferenceController.mBatteryChartView = mBatteryChartView;
|
||||
mBatteryChartPreferenceController.mDailyChartView = mDailyChartView;
|
||||
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
|
||||
mBatteryDiffEntry = new BatteryDiffEntry(
|
||||
mContext,
|
||||
/*foregroundUsageTimeInMs=*/ 1,
|
||||
@@ -123,12 +131,10 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
BatteryDiffEntry.sResourceCache.put(
|
||||
"fakeBatteryDiffEntryKey",
|
||||
new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
||||
createBatteryHistoryMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDestroy_activityIsChanging_clearBatteryEntryCache() {
|
||||
public void onDestroy_activityIsChanging_clearBatteryEntryCache() {
|
||||
doReturn(true).when(mSettingsActivity).isChangingConfigurations();
|
||||
// Ensures the testing environment is correct.
|
||||
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
|
||||
@@ -138,7 +144,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDestroy_activityIsNotChanging_notClearBatteryEntryCache() {
|
||||
public void onDestroy_activityIsNotChanging_notClearBatteryEntryCache() {
|
||||
doReturn(false).when(mSettingsActivity).isChangingConfigurations();
|
||||
// Ensures the testing environment is correct.
|
||||
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
|
||||
@@ -148,7 +154,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDestroy_clearPreferenceCache() {
|
||||
public void onDestroy_clearPreferenceCache() {
|
||||
// Ensures the testing environment is correct.
|
||||
mBatteryChartPreferenceController.mPreferenceCache.put(
|
||||
PREF_KEY, mPowerGaugePreference);
|
||||
@@ -160,113 +166,135 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDestroy_removeAllPreferenceFromPreferenceGroup() {
|
||||
public void onDestroy_removeAllPreferenceFromPreferenceGroup() {
|
||||
mBatteryChartPreferenceController.onDestroy();
|
||||
verify(mAppListGroup).removeAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBatteryHistoryMap_createExpectedKeysAndLevels() {
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
||||
createBatteryHistoryMap());
|
||||
public void setBatteryChartViewModel_6Hours() {
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||
|
||||
// Verifies the created battery keys array.
|
||||
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
|
||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index])
|
||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
||||
.isEqualTo(index + 1);
|
||||
}
|
||||
// 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);
|
||||
verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE);
|
||||
verify(mHourlyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
|
||||
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
|
||||
List.of(100, 97, 95),
|
||||
List.of("8 am", "10 am", "12 pm"),
|
||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBatteryHistoryMap_largeSize_createExpectedKeysAndLevels() {
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
||||
createBatteryHistoryMap());
|
||||
public void setBatteryChartViewModel_60Hours() {
|
||||
BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel(
|
||||
List.of(100, 83, 59, 41),
|
||||
List.of("Sat", "Sun", "Mon", "Mon"),
|
||||
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS);
|
||||
|
||||
// Verifies the created battery keys array.
|
||||
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
|
||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index])
|
||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
||||
.isEqualTo(index + 1);
|
||||
}
|
||||
// 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);
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
||||
|
||||
verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
|
||||
verify(mHourlyChartView, atLeastOnce()).setVisibility(View.GONE);
|
||||
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
|
||||
|
||||
reset(mDailyChartView);
|
||||
reset(mHourlyChartView);
|
||||
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
|
||||
mBatteryChartPreferenceController.mDailyChartIndex = 0;
|
||||
mBatteryChartPreferenceController.refreshUi();
|
||||
verify(mDailyChartView).setVisibility(View.VISIBLE);
|
||||
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
|
||||
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);
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
||||
/*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshUi_batteryChartViewIsNull_ignoreRefresh() {
|
||||
mBatteryChartPreferenceController.mBatteryChartView = null;
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
||||
/*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
|
||||
public void refreshUi_dailyChartViewIsNull_ignoreRefresh() {
|
||||
mBatteryChartPreferenceController.mDailyChartView = null;
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshUi_trapezoidIndexIsNotChanged_ignoreRefresh() {
|
||||
final int trapezoidIndex = 1;
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex;
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
||||
trapezoidIndex, /*isForce=*/ false)).isFalse();
|
||||
public void refreshUi_hourlyChartViewIsNull_ignoreRefresh() {
|
||||
mBatteryChartPreferenceController.mHourlyChartView = null;
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshUi_forceUpdate_refreshUi() {
|
||||
final int trapezoidIndex = 1;
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex;
|
||||
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;
|
||||
public void removeAndCacheAllPrefs_emptyContent_ignoreRemoveAll() {
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
||||
doReturn(0).when(mAppListGroup).getPreferenceCount();
|
||||
|
||||
mBatteryChartPreferenceController.refreshUi(
|
||||
trapezoidIndex, /*isForce=*/ true);
|
||||
mBatteryChartPreferenceController.refreshUi();
|
||||
verify(mAppListGroup, never()).removeAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAndCacheAllPrefs_buildCacheAndRemoveAllPreference() {
|
||||
final int trapezoidIndex = 1;
|
||||
public void removeAndCacheAllPrefs_buildCacheAndRemoveAllPreference() {
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
||||
doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0);
|
||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
||||
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
|
||||
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
||||
// Ensures the testing data is correct.
|
||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
||||
|
||||
mBatteryChartPreferenceController.refreshUi(
|
||||
trapezoidIndex, /*isForce=*/ true);
|
||||
mBatteryChartPreferenceController.refreshUi();
|
||||
|
||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY))
|
||||
.isEqualTo(mPowerGaugePreference);
|
||||
@@ -274,14 +302,14 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddPreferenceToScreen_emptyContent_ignoreAddPreference() {
|
||||
public void addPreferenceToScreen_emptyContent_ignoreAddPreference() {
|
||||
mBatteryChartPreferenceController.addPreferenceToScreen(
|
||||
new ArrayList<BatteryDiffEntry>());
|
||||
verify(mAppListGroup, never()).addPreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddPreferenceToScreen_addPreferenceIntoScreen() {
|
||||
public void addPreferenceToScreen_addPreferenceIntoScreen() {
|
||||
final String appLabel = "fake app label";
|
||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
||||
@@ -310,7 +338,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
|
||||
public void addPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
|
||||
final String appLabel = "fake app label";
|
||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
||||
@@ -325,7 +353,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePreferenceTreeiClick_notPowerGaugePreference_returnFalse() {
|
||||
public void handlePreferenceTreeClick_notPowerGaugePreference_returnFalse() {
|
||||
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(mAppListGroup))
|
||||
.isFalse();
|
||||
|
||||
@@ -336,7 +364,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePreferenceTreeClick_forAppEntry_returnTrue() {
|
||||
public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
|
||||
doReturn(false).when(mBatteryHistEntry).isAppEntry();
|
||||
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
||||
|
||||
@@ -352,7 +380,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePreferenceTreeClick_forSystemEntry_returnTrue() {
|
||||
public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
|
||||
mBatteryChartPreferenceController.mBatteryUtils = mBatteryUtils;
|
||||
doReturn(true).when(mBatteryHistEntry).isAppEntry();
|
||||
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
||||
@@ -369,7 +397,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
|
||||
public void setPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
|
||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||
pref.setSummary(PREF_SUMMARY);
|
||||
|
||||
@@ -381,7 +409,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPreferenceSummary_setBackgroundUsageTimeOnly() {
|
||||
public void setPreferenceSummary_setBackgroundUsageTimeOnly() {
|
||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||
pref.setSummary(PREF_SUMMARY);
|
||||
|
||||
@@ -393,7 +421,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
|
||||
public void setPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
|
||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||
pref.setSummary(PREF_SUMMARY);
|
||||
|
||||
@@ -405,7 +433,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
|
||||
public void setPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
|
||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||
pref.setSummary(PREF_SUMMARY);
|
||||
|
||||
@@ -418,7 +446,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPreferenceSummary_setTotalAndBackgroundUsageTime() {
|
||||
public void setPreferenceSummary_setTotalAndBackgroundUsageTime() {
|
||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||
pref.setSummary(PREF_SUMMARY);
|
||||
|
||||
@@ -430,7 +458,7 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPreferenceSummary_notAllowShownPackage_setSummayAsNull() {
|
||||
public void setPreferenceSummary_notAllowShownPackage_setSummayAsNull() {
|
||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||
pref.setSummary(PREF_SUMMARY);
|
||||
final BatteryDiffEntry batteryDiffEntry =
|
||||
@@ -445,36 +473,9 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateUsageTime_returnTrueIfBatteryDiffEntryIsValid() {
|
||||
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() {
|
||||
public void onExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() {
|
||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
||||
mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry);
|
||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
||||
doReturn("label").when(mBatteryDiffEntry).getAppLabel();
|
||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
||||
@@ -493,10 +494,10 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() {
|
||||
public void onExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() {
|
||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
||||
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
||||
mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry);
|
||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
||||
// Verifies the cache is empty first.
|
||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
||||
|
||||
@@ -513,57 +514,17 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnSelect_selectSpecificTimeSlot_logMetric() {
|
||||
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() {
|
||||
public void refreshCategoryTitle_setLastFullChargeIntoBothTitleTextView() {
|
||||
mBatteryChartPreferenceController = createController();
|
||||
mBatteryChartPreferenceController.mAppListPrefGroup =
|
||||
spy(new PreferenceCategory(mContext));
|
||||
mBatteryChartPreferenceController.mExpandDividerPreference =
|
||||
spy(new ExpandDividerPreference(mContext));
|
||||
// Simulates select all condition.
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex =
|
||||
BatteryChartView.SELECTED_INDEX_ALL;
|
||||
mBatteryChartPreferenceController.mDailyChartIndex =
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
mBatteryChartPreferenceController.mHourlyChartIndex =
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
|
||||
mBatteryChartPreferenceController.refreshCategoryTitle();
|
||||
|
||||
@@ -572,76 +533,93 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
verify(mBatteryChartPreferenceController.mAppListPrefGroup)
|
||||
.setTitle(captor.capture());
|
||||
assertThat(captor.getValue())
|
||||
.isEqualTo("App usage for past 24 hr");
|
||||
.isEqualTo("App usage since last full charge");
|
||||
// Verifies the title in the expandable divider.
|
||||
captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(mBatteryChartPreferenceController.mExpandDividerPreference)
|
||||
.setTitle(captor.capture());
|
||||
assertThat(captor.getValue())
|
||||
.isEqualTo("System usage for past 24 hr");
|
||||
.isEqualTo("System usage since last full charge");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTimestampLabel_nullBatteryHistoryKeys_ignore() {
|
||||
mBatteryChartPreferenceController = createController();
|
||||
mBatteryChartPreferenceController.mBatteryHistoryKeys = null;
|
||||
mBatteryChartPreferenceController.mBatteryChartView =
|
||||
spy(new BatteryChartView(mContext));
|
||||
mBatteryChartPreferenceController.setTimestampLabel();
|
||||
public void selectedSlotText_selectAllDaysAllHours_returnNull() {
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
||||
mBatteryChartPreferenceController.mDailyChartIndex =
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
mBatteryChartPreferenceController.mHourlyChartIndex =
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
|
||||
verify(mBatteryChartPreferenceController.mBatteryChartView, never())
|
||||
.setLatestTimestamp(anyLong());
|
||||
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTimestampLabel_setExpectedTimestampData() {
|
||||
mBatteryChartPreferenceController = createController();
|
||||
mBatteryChartPreferenceController.mBatteryChartView =
|
||||
spy(new BatteryChartView(mContext));
|
||||
setUpBatteryHistoryKeys();
|
||||
public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() {
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||
mBatteryChartPreferenceController.mDailyChartIndex = 0;
|
||||
mBatteryChartPreferenceController.mHourlyChartIndex =
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
|
||||
mBatteryChartPreferenceController.setTimestampLabel();
|
||||
|
||||
verify(mBatteryChartPreferenceController.mBatteryChartView)
|
||||
.setLatestTimestamp(1619247636826L);
|
||||
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTimestampLabel_withoutValidTimestamp_setExpectedTimestampData() {
|
||||
mBatteryChartPreferenceController = createController();
|
||||
mBatteryChartPreferenceController.mBatteryChartView =
|
||||
spy(new BatteryChartView(mContext));
|
||||
mBatteryChartPreferenceController.mBatteryHistoryKeys = new long[]{0L};
|
||||
public void selectedSlotText_selectADayAllHours_onlyDayText() {
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
||||
mBatteryChartPreferenceController.mDailyChartIndex = 1;
|
||||
mBatteryChartPreferenceController.mHourlyChartIndex =
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
|
||||
mBatteryChartPreferenceController.setTimestampLabel();
|
||||
|
||||
verify(mBatteryChartPreferenceController.mBatteryChartView)
|
||||
.setLatestTimestamp(anyLong());
|
||||
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo("Sunday");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnSaveInstanceState_restoreSelectedIndexAndExpandState() {
|
||||
final int expectedIndex = 1;
|
||||
public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() {
|
||||
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 Bundle bundle = new Bundle();
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex = expectedIndex;
|
||||
mBatteryChartPreferenceController.mDailyChartIndex = expectedDailyIndex;
|
||||
mBatteryChartPreferenceController.mHourlyChartIndex = expectedHourlyIndex;
|
||||
mBatteryChartPreferenceController.mIsExpanded = isExpanded;
|
||||
mBatteryChartPreferenceController.onSaveInstanceState(bundle);
|
||||
// Replaces the original controller with other values.
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex = -1;
|
||||
mBatteryChartPreferenceController.mDailyChartIndex = -1;
|
||||
mBatteryChartPreferenceController.mHourlyChartIndex = -1;
|
||||
mBatteryChartPreferenceController.mIsExpanded = false;
|
||||
|
||||
mBatteryChartPreferenceController.onCreate(bundle);
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
||||
createBatteryHistoryMap());
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25));
|
||||
|
||||
assertThat(mBatteryChartPreferenceController.mTrapezoidIndex)
|
||||
.isEqualTo(expectedIndex);
|
||||
assertThat(mBatteryChartPreferenceController.mDailyChartIndex)
|
||||
.isEqualTo(expectedDailyIndex);
|
||||
assertThat(mBatteryChartPreferenceController.mHourlyChartIndex)
|
||||
.isEqualTo(expectedHourlyIndex);
|
||||
assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidToShowSummary_returnExpectedResult() {
|
||||
public void isValidToShowSummary_returnExpectedResult() {
|
||||
assertThat(mBatteryChartPreferenceController
|
||||
.isValidToShowSummary("com.google.android.apps.scone"))
|
||||
.isTrue();
|
||||
@@ -652,31 +630,42 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidToShowEntry_returnExpectedResult() {
|
||||
assertThat(mBatteryChartPreferenceController
|
||||
.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 Long generateTimestamp(int index) {
|
||||
// "2021-04-23 07:00:00 UTC" + index hours
|
||||
return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS;
|
||||
}
|
||||
|
||||
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<>();
|
||||
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
|
||||
for (int index = 0; index < numOfHours; index++) {
|
||||
final ContentValues values = new ContentValues();
|
||||
values.put("batteryLevel", Integer.valueOf(100 - index));
|
||||
values.put("consumePower", Integer.valueOf(100 - index));
|
||||
final BatteryHistEntry entry = new BatteryHistEntry(values);
|
||||
final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
|
||||
entryMap.put("fake_entry_key" + index, entry);
|
||||
batteryHistoryMap.put(Long.valueOf(index + 1), entryMap);
|
||||
batteryHistoryMap.put(generateTimestamp(index), entryMap);
|
||||
}
|
||||
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(
|
||||
long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
|
||||
return new BatteryDiffEntry(
|
||||
@@ -684,13 +673,6 @@ public final class BatteryChartPreferenceControllerTest {
|
||||
/*consumePower=*/ 0, mBatteryHistEntry);
|
||||
}
|
||||
|
||||
private void setUpBatteryHistoryKeys() {
|
||||
mBatteryChartPreferenceController.mBatteryHistoryKeys =
|
||||
new long[]{1619196786769L, 0L, 1619247636826L};
|
||||
ConvertUtils.utcToLocalTimeHour(
|
||||
mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false);
|
||||
}
|
||||
|
||||
private BatteryChartPreferenceController createController() {
|
||||
final BatteryChartPreferenceController controller =
|
||||
new BatteryChartPreferenceController(
|
||||
|
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.Context;
|
||||
import android.os.LocaleList;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
@@ -41,6 +42,7 @@ import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@@ -55,6 +57,8 @@ public final class BatteryChartViewTest {
|
||||
private AccessibilityServiceInfo mMockAccessibilityServiceInfo;
|
||||
@Mock
|
||||
private AccessibilityManager mMockAccessibilityManager;
|
||||
@Mock
|
||||
private View mMockView;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -74,13 +78,13 @@ public final class BatteryChartViewTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAccessibilityEnabled_disable_returnFalse() {
|
||||
public void isAccessibilityEnabled_disable_returnFalse() {
|
||||
doReturn(false).when(mMockAccessibilityManager).isEnabled();
|
||||
assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAccessibilityEnabled_emptyInfo_returnFalse() {
|
||||
public void isAccessibilityEnabled_emptyInfo_returnFalse() {
|
||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
||||
doReturn(new ArrayList<AccessibilityServiceInfo>())
|
||||
.when(mMockAccessibilityManager)
|
||||
@@ -90,68 +94,70 @@ public final class BatteryChartViewTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAccessibilityEnabled_validServiceId_returnTrue() {
|
||||
public void isAccessibilityEnabled_validServiceId_returnTrue() {
|
||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
||||
assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isTrue();
|
||||
}
|
||||
|
||||
@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 expectedIndex = 2;
|
||||
mBatteryChartView.mSelectedIndex = 1;
|
||||
mBatteryChartView.setOnSelectListener(
|
||||
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)
|
||||
.isEqualTo(expectedIndex);
|
||||
assertThat(selectedIndex[0]).isEqualTo(expectedIndex);
|
||||
// Verify onClick() the same index 2.
|
||||
mBatteryChartView.mTouchUpEventX = 2;
|
||||
selectedIndex[0] = Integer.MIN_VALUE;
|
||||
mBatteryChartView.onClick(mMockView);
|
||||
assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewModel.SELECTED_INDEX_ALL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSelectedIndex_sameIndex_notInvokesCallback() {
|
||||
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() {
|
||||
public void clickable_isChartGraphSlotsEnabledIsFalse_notClickable() {
|
||||
mBatteryChartView.setClickableForce(true);
|
||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||
.thenReturn(false);
|
||||
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
|
||||
assertThat(mBatteryChartView.isClickable()).isFalse();
|
||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClickable_accessibilityIsDisabled_clickable() {
|
||||
public void clickable_accessibilityIsDisabled_clickable() {
|
||||
mBatteryChartView.setClickableForce(true);
|
||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||
.thenReturn(true);
|
||||
doReturn(false).when(mMockAccessibilityManager).isEnabled();
|
||||
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
|
||||
assertThat(mBatteryChartView.isClickable()).isTrue();
|
||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClickable_accessibilityIsEnabledWithoutValidId_clickable() {
|
||||
public void clickable_accessibilityIsEnabledWithoutValidId_clickable() {
|
||||
mBatteryChartView.setClickableForce(true);
|
||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||
.thenReturn(true);
|
||||
@@ -161,30 +167,34 @@ public final class BatteryChartViewTest {
|
||||
.getEnabledAccessibilityServiceList(anyInt());
|
||||
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
|
||||
assertThat(mBatteryChartView.isClickable()).isTrue();
|
||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClickable_accessibilityIsEnabledWithValidId_notClickable() {
|
||||
public void clickable_accessibilityIsEnabledWithValidId_notClickable() {
|
||||
mBatteryChartView.setClickableForce(true);
|
||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||
.thenReturn(true);
|
||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
||||
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
|
||||
assertThat(mBatteryChartView.isClickable()).isFalse();
|
||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClickable_restoreFromNonClickableState() {
|
||||
final int[] levels = new int[13];
|
||||
for (int index = 0; index < levels.length; index++) {
|
||||
levels[index] = index + 1;
|
||||
public void clickable_restoreFromNonClickableState() {
|
||||
final List<Integer> levels = new ArrayList<Integer>();
|
||||
final List<String> texts = new ArrayList<String>();
|
||||
for (int index = 0; index < 13; index++) {
|
||||
levels.add(index + 1);
|
||||
texts.add("");
|
||||
}
|
||||
mBatteryChartView.setTrapezoidCount(12);
|
||||
mBatteryChartView.setLevels(levels);
|
||||
mBatteryChartView.setViewModel(new BatteryChartViewModel(levels, texts,
|
||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||
mBatteryChartView.setClickableForce(true);
|
||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||
.thenReturn(true);
|
||||
@@ -201,14 +211,14 @@ public final class BatteryChartViewTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnAttachedToWindow_addAccessibilityStateChangeListener() {
|
||||
public void onAttachedToWindow_addAccessibilityStateChangeListener() {
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
verify(mMockAccessibilityManager)
|
||||
.addAccessibilityStateChangeListener(mBatteryChartView);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDetachedFromWindow_removeAccessibilityStateChangeListener() {
|
||||
public void onDetachedFromWindow_removeAccessibilityStateChangeListener() {
|
||||
mBatteryChartView.onAttachedToWindow();
|
||||
mBatteryChartView.mHandler.postDelayed(
|
||||
mBatteryChartView.mUpdateClickableStateRun, 1000);
|
||||
@@ -223,7 +233,7 @@ public final class BatteryChartViewTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnAccessibilityStateChanged_postUpdateStateRunnable() {
|
||||
public void onAccessibilityStateChanged_postUpdateStateRunnable() {
|
||||
mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler);
|
||||
mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true);
|
||||
|
||||
|
@@ -53,7 +53,7 @@ public final class BatteryHistoryLoaderTest {
|
||||
public void testLoadIBackground_returnsMapFromPowerFeatureProvider() {
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||
doReturn(batteryHistoryMap).when(mFeatureFactory.powerUsageFeatureProvider)
|
||||
.getBatteryHistory(mContext);
|
||||
.getBatteryHistorySinceLastFullCharge(mContext);
|
||||
|
||||
assertThat(mBatteryHistoryLoader.loadInBackground())
|
||||
.isSameInstanceAs(batteryHistoryMap);
|
||||
|
@@ -26,6 +26,7 @@ import android.os.BatteryManager;
|
||||
import android.os.BatteryUsageStats;
|
||||
import android.os.LocaleList;
|
||||
import android.os.UserHandle;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
import com.android.settings.fuelgauge.BatteryUtils;
|
||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||
@@ -39,8 +40,8 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -173,7 +174,8 @@ public final class ConvertUtilsTest {
|
||||
public void getIndexedUsageMap_returnsExpectedResult() {
|
||||
// Creates the fake testing data.
|
||||
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 =
|
||||
new HashMap<>();
|
||||
final BatteryHistEntry fakeEntry = createBatteryHistEntry(
|
||||
@@ -270,11 +272,11 @@ public final class ConvertUtilsTest {
|
||||
for (int index = 0; index < remainingSize; index++) {
|
||||
batteryHistoryMap.put(105L + index + 1, new HashMap<>());
|
||||
}
|
||||
when(mPowerUsageFeatureProvider.getBatteryHistory(mContext))
|
||||
when(mPowerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext))
|
||||
.thenReturn(batteryHistoryMap);
|
||||
|
||||
final List<BatteryDiffEntry> batteryDiffEntryList =
|
||||
BatteryChartPreferenceController.getBatteryLast24HrUsageData(mContext);
|
||||
BatteryChartPreferenceController.getAppBatteryUsageData(mContext);
|
||||
|
||||
assertThat(batteryDiffEntryList).isNotEmpty();
|
||||
final BatteryDiffEntry resultEntry = batteryDiffEntryList.get(0);
|
||||
@@ -472,4 +474,9 @@ public final class ConvertUtilsTest {
|
||||
assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -139,17 +139,7 @@ public class PowerUsageSummaryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initPreference_chartGraphEnabled_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);
|
||||
|
||||
public void initPreference_hasCorrectSummary() {
|
||||
mFragment.initPreference();
|
||||
|
||||
verify(mBatteryUsagePreference).setSummary("View usage from last full charge");
|
||||
|
Reference in New Issue
Block a user