Switch Battery Usage Chart from V1 to V2.
Test: manual Bug: 236101166 Change-Id: I9142c0d4e00dea3771777ba9aedeab07b635fa1a
This commit is contained in:
@@ -32,10 +32,20 @@
|
|||||||
android:text="@string/battery_usage_chart_graph_hint_last_full_charge" />
|
android:text="@string/battery_usage_chart_graph_hint_last_full_charge" />
|
||||||
|
|
||||||
<com.android.settings.fuelgauge.batteryusage.BatteryChartView
|
<com.android.settings.fuelgauge.batteryusage.BatteryChartView
|
||||||
android:id="@+id/battery_chart"
|
android:id="@+id/daily_battery_chart"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="170dp"
|
android:layout_height="170dp"
|
||||||
android:layout_marginBottom="6dp"
|
android:layout_marginBottom="16dp"
|
||||||
|
android:visibility="invisible"
|
||||||
|
android: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="invisible"
|
android:visibility="invisible"
|
||||||
android:contentDescription="@string/battery_usage_chart"
|
android:contentDescription="@string/battery_usage_chart"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
@@ -473,7 +473,6 @@
|
|||||||
<dimen name="chartview_trapezoid_radius">5dp</dimen>
|
<dimen name="chartview_trapezoid_radius">5dp</dimen>
|
||||||
<dimen name="chartview_trapezoid_margin_start">1dp</dimen>
|
<dimen name="chartview_trapezoid_margin_start">1dp</dimen>
|
||||||
<dimen name="chartview_trapezoid_margin_bottom">2dp</dimen>
|
<dimen name="chartview_trapezoid_margin_bottom">2dp</dimen>
|
||||||
<dimen name="chartview_two_charts_margin">16dp</dimen>
|
|
||||||
|
|
||||||
<!-- Dimensions for Dream settings cards -->
|
<!-- Dimensions for Dream settings cards -->
|
||||||
<dimen name="dream_item_min_column_width">174dp</dimen>
|
<dimen name="dream_item_min_column_width">174dp</dimen>
|
||||||
|
@@ -40,7 +40,7 @@ import com.android.settings.core.BasePreferenceController;
|
|||||||
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
|
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
import com.android.settings.fuelgauge.BatteryUtils;
|
||||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||||
import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceControllerV2;
|
import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController;
|
||||||
import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry;
|
import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry;
|
||||||
import com.android.settings.fuelgauge.batteryusage.BatteryEntry;
|
import com.android.settings.fuelgauge.batteryusage.BatteryEntry;
|
||||||
import com.android.settings.fuelgauge.batteryusage.BatteryUsageStatsLoader;
|
import com.android.settings.fuelgauge.batteryusage.BatteryUsageStatsLoader;
|
||||||
@@ -179,7 +179,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final BatteryDiffEntry entry =
|
final BatteryDiffEntry entry =
|
||||||
BatteryChartPreferenceControllerV2.getAppBatteryUsageData(
|
BatteryChartPreferenceController.getAppBatteryUsageData(
|
||||||
mContext, mPackageName, mUserId);
|
mContext, mPackageName, mUserId);
|
||||||
Log.d(TAG, "loadBatteryDiffEntries():\n" + entry);
|
Log.d(TAG, "loadBatteryDiffEntries():\n" + entry);
|
||||||
return entry;
|
return entry;
|
||||||
|
@@ -20,7 +20,6 @@ import android.app.settings.SettingsEnums;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@@ -28,7 +27,9 @@ import android.text.TextUtils;
|
|||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceGroup;
|
import androidx.preference.PreferenceGroup;
|
||||||
@@ -53,8 +54,6 @@ import com.android.settingslib.utils.StringUtil;
|
|||||||
import com.android.settingslib.widget.FooterPreference;
|
import com.android.settingslib.widget.FooterPreference;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -62,27 +61,23 @@ import java.util.Map;
|
|||||||
/** Controls the update for chart graph and the list items. */
|
/** Controls the update for chart graph and the list items. */
|
||||||
public class BatteryChartPreferenceController extends AbstractPreferenceController
|
public class BatteryChartPreferenceController extends AbstractPreferenceController
|
||||||
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
|
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
|
||||||
OnSaveInstanceState, BatteryChartView.OnSelectListener, OnResume,
|
OnSaveInstanceState, OnResume, ExpandDividerPreference.OnExpandListener {
|
||||||
ExpandDividerPreference.OnExpandListener {
|
|
||||||
private static final String TAG = "BatteryChartPreferenceController";
|
private static final String TAG = "BatteryChartPreferenceController";
|
||||||
private static final String KEY_FOOTER_PREF = "battery_graph_footer";
|
private static final String KEY_FOOTER_PREF = "battery_graph_footer";
|
||||||
private static final String PACKAGE_NAME_NONE = "none";
|
private static final String PACKAGE_NAME_NONE = "none";
|
||||||
|
|
||||||
/** Desired battery history size for timestamp slots. */
|
|
||||||
public static final int DESIRED_HISTORY_SIZE = 25;
|
|
||||||
private static final int CHART_LEVEL_ARRAY_SIZE = 13;
|
|
||||||
private static final int CHART_KEY_ARRAY_SIZE = DESIRED_HISTORY_SIZE;
|
|
||||||
private static final long VALID_USAGE_TIME_DURATION = DateUtils.HOUR_IN_MILLIS * 2;
|
private static final long VALID_USAGE_TIME_DURATION = DateUtils.HOUR_IN_MILLIS * 2;
|
||||||
private static final long VALID_DIFF_DURATION = DateUtils.MINUTE_IN_MILLIS * 3;
|
private static final long VALID_DIFF_DURATION = DateUtils.MINUTE_IN_MILLIS * 3;
|
||||||
|
|
||||||
// Keys for bundle instance to restore configurations.
|
// Keys for bundle instance to restore configurations.
|
||||||
private static final String KEY_EXPAND_SYSTEM_INFO = "expand_system_info";
|
private static final String KEY_EXPAND_SYSTEM_INFO = "expand_system_info";
|
||||||
private static final String KEY_CURRENT_TIME_SLOT = "current_time_slot";
|
private static final String KEY_DAILY_CHART_INDEX = "daily_chart_index";
|
||||||
|
private static final String KEY_HOURLY_CHART_INDEX = "hourly_chart_index";
|
||||||
|
|
||||||
private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
|
private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Map<Integer, List<BatteryDiffEntry>> mBatteryIndexedMap;
|
Map<Integer, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Context mPrefContext;
|
Context mPrefContext;
|
||||||
@@ -91,28 +86,34 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
PreferenceGroup mAppListPrefGroup;
|
PreferenceGroup mAppListPrefGroup;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
BatteryChartView mBatteryChartView;
|
|
||||||
@VisibleForTesting
|
|
||||||
ExpandDividerPreference mExpandDividerPreference;
|
ExpandDividerPreference mExpandDividerPreference;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean mIsExpanded = false;
|
boolean mIsExpanded = false;
|
||||||
@VisibleForTesting
|
|
||||||
int[] mBatteryHistoryLevels;
|
|
||||||
@VisibleForTesting
|
|
||||||
long[] mBatteryHistoryKeys;
|
|
||||||
@VisibleForTesting
|
|
||||||
int mTrapezoidIndex = BatteryChartView.SELECTED_INDEX_INVALID;
|
|
||||||
|
|
||||||
private boolean mIs24HourFormat = false;
|
@VisibleForTesting
|
||||||
|
BatteryChartView mDailyChartView;
|
||||||
|
@VisibleForTesting
|
||||||
|
BatteryChartView mHourlyChartView;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
@VisibleForTesting
|
||||||
|
int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
|
||||||
|
private boolean mIs24HourFormat;
|
||||||
private boolean mIsFooterPrefAdded = false;
|
private boolean mIsFooterPrefAdded = false;
|
||||||
private PreferenceScreen mPreferenceScreen;
|
private PreferenceScreen mPreferenceScreen;
|
||||||
private FooterPreference mFooterPreference;
|
private FooterPreference mFooterPreference;
|
||||||
|
// Daily view model only saves abbreviated day of week texts (e.g. MON). This field saves the
|
||||||
|
// full day of week texts (e.g. Monday), which is used in category title and battery detail
|
||||||
|
// page.
|
||||||
|
private List<String> mDailyTimestampFullTexts;
|
||||||
|
private BatteryChartViewModel mDailyViewModel;
|
||||||
|
private List<BatteryChartViewModel> mHourlyViewModels;
|
||||||
|
|
||||||
private final String mPreferenceKey;
|
private final String mPreferenceKey;
|
||||||
private final SettingsActivity mActivity;
|
private final SettingsActivity mActivity;
|
||||||
private final InstrumentedPreferenceFragment mFragment;
|
private final InstrumentedPreferenceFragment mFragment;
|
||||||
private final CharSequence[] mNotAllowShowEntryPackages;
|
|
||||||
private final CharSequence[] mNotAllowShowSummaryPackages;
|
private final CharSequence[] mNotAllowShowSummaryPackages;
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
||||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
||||||
@@ -120,8 +121,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
// Preference cache to avoid create new instance each time.
|
// Preference cache to avoid create new instance each time.
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final Map<String, Preference> mPreferenceCache = new HashMap<>();
|
final Map<String, Preference> mPreferenceCache = new HashMap<>();
|
||||||
@VisibleForTesting
|
|
||||||
final List<BatteryDiffEntry> mSystemEntries = new ArrayList<>();
|
|
||||||
|
|
||||||
public BatteryChartPreferenceController(
|
public BatteryChartPreferenceController(
|
||||||
Context context, String preferenceKey,
|
Context context, String preferenceKey,
|
||||||
@@ -134,10 +133,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
mIs24HourFormat = DateFormat.is24HourFormat(context);
|
mIs24HourFormat = DateFormat.is24HourFormat(context);
|
||||||
mMetricsFeatureProvider =
|
mMetricsFeatureProvider =
|
||||||
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
||||||
mNotAllowShowEntryPackages =
|
|
||||||
FeatureFactory.getFactory(context)
|
|
||||||
.getPowerUsageFeatureProvider(context)
|
|
||||||
.getHideApplicationEntries(context);
|
|
||||||
mNotAllowShowSummaryPackages =
|
mNotAllowShowSummaryPackages =
|
||||||
FeatureFactory.getFactory(context)
|
FeatureFactory.getFactory(context)
|
||||||
.getPowerUsageFeatureProvider(context)
|
.getPowerUsageFeatureProvider(context)
|
||||||
@@ -152,12 +147,14 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mTrapezoidIndex =
|
mDailyChartIndex =
|
||||||
savedInstanceState.getInt(KEY_CURRENT_TIME_SLOT, mTrapezoidIndex);
|
savedInstanceState.getInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
|
||||||
|
mHourlyChartIndex =
|
||||||
|
savedInstanceState.getInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
|
||||||
mIsExpanded =
|
mIsExpanded =
|
||||||
savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
|
savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
|
||||||
Log.d(TAG, String.format("onCreate() slotIndex=%d isExpanded=%b",
|
Log.d(TAG, String.format("onCreate() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
|
||||||
mTrapezoidIndex, mIsExpanded));
|
mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -179,10 +176,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
if (savedInstance == null) {
|
if (savedInstance == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
savedInstance.putInt(KEY_CURRENT_TIME_SLOT, mTrapezoidIndex);
|
savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
|
||||||
|
savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
|
||||||
savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
|
savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
|
||||||
Log.d(TAG, String.format("onSaveInstanceState() slotIndex=%d isExpanded=%b",
|
Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
|
||||||
mTrapezoidIndex, mIsExpanded));
|
mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -204,8 +202,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
mPrefContext = screen.getContext();
|
mPrefContext = screen.getContext();
|
||||||
mAppListPrefGroup = screen.findPreference(mPreferenceKey);
|
mAppListPrefGroup = screen.findPreference(mPreferenceKey);
|
||||||
mAppListPrefGroup.setOrderingAsAdded(false);
|
mAppListPrefGroup.setOrderingAsAdded(false);
|
||||||
mAppListPrefGroup.setTitle(
|
mAppListPrefGroup.setTitle(mPrefContext.getString(R.string.battery_app_usage));
|
||||||
mPrefContext.getString(R.string.battery_app_usage_for_past_24));
|
|
||||||
mFooterPreference = screen.findPreference(KEY_FOOTER_PREF);
|
mFooterPreference = screen.findPreference(KEY_FOOTER_PREF);
|
||||||
// Removes footer first until usage data is loaded to avoid flashing.
|
// Removes footer first until usage data is loaded to avoid flashing.
|
||||||
if (mFooterPreference != null) {
|
if (mFooterPreference != null) {
|
||||||
@@ -249,17 +246,6 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSelect(int trapezoidIndex) {
|
|
||||||
Log.d(TAG, "onChartSelect:" + trapezoidIndex);
|
|
||||||
refreshUi(trapezoidIndex, /*isForce=*/ false);
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
mPrefContext,
|
|
||||||
trapezoidIndex == BatteryChartView.SELECTED_INDEX_ALL
|
|
||||||
? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
|
|
||||||
: SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onExpand(boolean isExpanded) {
|
public void onExpand(boolean isExpanded) {
|
||||||
mIsExpanded = isExpanded;
|
mIsExpanded = isExpanded;
|
||||||
@@ -272,81 +258,119 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
|
|
||||||
void setBatteryHistoryMap(
|
void setBatteryHistoryMap(
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
||||||
// Resets all battery history data relative variables.
|
Log.d(TAG, "setBatteryHistoryMap() " + (batteryHistoryMap == null ? "null"
|
||||||
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
: ("size=" + batteryHistoryMap.size())));
|
||||||
mBatteryIndexedMap = null;
|
final BatteryLevelData batteryLevelData =
|
||||||
mBatteryHistoryKeys = null;
|
DataProcessor.getBatteryLevelData(mContext, mHandler, batteryHistoryMap,
|
||||||
mBatteryHistoryLevels = null;
|
batteryUsageMap -> {
|
||||||
addFooterPreferenceIfNeeded(false);
|
mBatteryUsageMap = batteryUsageMap;
|
||||||
|
refreshUi();
|
||||||
|
});
|
||||||
|
Log.d(TAG, "getBatteryLevelData: " + batteryLevelData);
|
||||||
|
if (batteryLevelData == null) {
|
||||||
|
mDailyTimestampFullTexts = null;
|
||||||
|
mDailyViewModel = null;
|
||||||
|
mHourlyViewModels = null;
|
||||||
|
refreshUi();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mBatteryHistoryKeys = getBatteryHistoryKeys(batteryHistoryMap);
|
mDailyTimestampFullTexts = generateTimestampDayOfWeekTexts(
|
||||||
mBatteryHistoryLevels = new int[CHART_LEVEL_ARRAY_SIZE];
|
mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(),
|
||||||
for (int index = 0; index < CHART_LEVEL_ARRAY_SIZE; index++) {
|
/* isAbbreviation= */ false);
|
||||||
final long timestamp = mBatteryHistoryKeys[index * 2];
|
mDailyViewModel = new BatteryChartViewModel(
|
||||||
final Map<String, BatteryHistEntry> entryMap = batteryHistoryMap.get(timestamp);
|
batteryLevelData.getDailyBatteryLevels().getLevels(),
|
||||||
if (entryMap == null || entryMap.isEmpty()) {
|
generateTimestampDayOfWeekTexts(
|
||||||
Log.e(TAG, "abnormal entry list in the timestamp:"
|
mContext, batteryLevelData.getDailyBatteryLevels().getTimestamps(),
|
||||||
+ ConvertUtils.utcToLocalTime(mPrefContext, timestamp));
|
/* isAbbreviation= */ true),
|
||||||
continue;
|
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS);
|
||||||
|
mHourlyViewModels = new ArrayList<>();
|
||||||
|
for (BatteryLevelData.PeriodBatteryLevelData hourlyBatteryLevelsPerDay :
|
||||||
|
batteryLevelData.getHourlyBatteryLevelsPerDay()) {
|
||||||
|
mHourlyViewModels.add(new BatteryChartViewModel(
|
||||||
|
hourlyBatteryLevelsPerDay.getLevels(),
|
||||||
|
generateTimestampHourTexts(
|
||||||
|
mContext, hourlyBatteryLevelsPerDay.getTimestamps()),
|
||||||
|
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||||
}
|
}
|
||||||
// Averages the battery level in each time slot to avoid corner conditions.
|
refreshUi();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBatteryChartView(final BatteryChartView batteryChartView) {
|
void setBatteryChartView(@NonNull final BatteryChartView dailyChartView,
|
||||||
if (mBatteryChartView != batteryChartView) {
|
@NonNull final BatteryChartView hourlyChartView) {
|
||||||
mHandler.post(() -> setBatteryChartViewInner(batteryChartView));
|
if (mDailyChartView != dailyChartView || mHourlyChartView != hourlyChartView) {
|
||||||
|
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setBatteryChartViewInner(final BatteryChartView batteryChartView) {
|
private void setBatteryChartViewInner(@NonNull final BatteryChartView dailyChartView,
|
||||||
mBatteryChartView = batteryChartView;
|
@NonNull final BatteryChartView hourlyChartView) {
|
||||||
mBatteryChartView.setOnSelectListener(this);
|
mDailyChartView = dailyChartView;
|
||||||
forceRefreshUi();
|
mDailyChartView.setOnSelectListener(trapezoidIndex -> {
|
||||||
|
if (mDailyChartIndex == trapezoidIndex) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex);
|
||||||
private void forceRefreshUi() {
|
mDailyChartIndex = trapezoidIndex;
|
||||||
final int refreshIndex =
|
mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
mTrapezoidIndex == BatteryChartView.SELECTED_INDEX_INVALID
|
refreshUi();
|
||||||
? BatteryChartView.SELECTED_INDEX_ALL
|
// TODO: Change to log daily data.
|
||||||
: mTrapezoidIndex;
|
});
|
||||||
if (mBatteryChartView != null) {
|
mHourlyChartView = hourlyChartView;
|
||||||
mBatteryChartView.setLevels(mBatteryHistoryLevels);
|
mHourlyChartView.setOnSelectListener(trapezoidIndex -> {
|
||||||
mBatteryChartView.setSelectedIndex(refreshIndex);
|
if (mHourlyChartIndex == trapezoidIndex) {
|
||||||
setTimestampLabel();
|
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
|
@VisibleForTesting
|
||||||
boolean refreshUi(int trapezoidIndex, boolean isForce) {
|
boolean refreshUi() {
|
||||||
// Invalid refresh condition.
|
if (mDailyChartView == null || mHourlyChartView == null) {
|
||||||
if (mBatteryIndexedMap == null
|
// Chart views are not initialized.
|
||||||
|| mBatteryChartView == null
|
return false;
|
||||||
|| (mTrapezoidIndex == trapezoidIndex && !isForce)) {
|
}
|
||||||
|
if (mDailyViewModel == null || mHourlyViewModels == null) {
|
||||||
|
// Fail to get battery level data, show an empty hourly chart view.
|
||||||
|
mDailyChartView.setVisibility(View.GONE);
|
||||||
|
mHourlyChartView.setVisibility(View.VISIBLE);
|
||||||
|
mHourlyChartView.setViewModel(null);
|
||||||
|
addFooterPreferenceIfNeeded(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (mBatteryUsageMap == null) {
|
||||||
|
// Battery usage data is not ready, wait for data ready to refresh UI.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Log.d(TAG, String.format("refreshUi: index=%d size=%d isForce:%b",
|
|
||||||
trapezoidIndex, mBatteryIndexedMap.size(), isForce));
|
|
||||||
|
|
||||||
mTrapezoidIndex = trapezoidIndex;
|
if (isBatteryLevelDataInOneDay()) {
|
||||||
|
// Only 1 day data, hide the daily chart view.
|
||||||
|
mDailyChartView.setVisibility(View.GONE);
|
||||||
|
mDailyChartIndex = 0;
|
||||||
|
} else {
|
||||||
|
mDailyChartView.setVisibility(View.VISIBLE);
|
||||||
|
mDailyViewModel.setSelectedIndex(mDailyChartIndex);
|
||||||
|
mDailyChartView.setViewModel(mDailyViewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
|
||||||
|
// Multiple days are selected, hide the hourly chart view.
|
||||||
|
mHourlyChartView.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
mHourlyChartView.setVisibility(View.VISIBLE);
|
||||||
|
final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex);
|
||||||
|
hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
|
||||||
|
mHourlyChartView.setViewModel(hourlyViewModel);
|
||||||
|
}
|
||||||
|
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
final long start = System.currentTimeMillis();
|
final long start = System.currentTimeMillis();
|
||||||
removeAndCacheAllPrefs();
|
removeAndCacheAllPrefs();
|
||||||
@@ -359,43 +383,22 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addAllPreferences() {
|
private void addAllPreferences() {
|
||||||
final List<BatteryDiffEntry> entries =
|
final BatteryDiffData batteryDiffData =
|
||||||
mBatteryIndexedMap.get(Integer.valueOf(mTrapezoidIndex));
|
mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
|
||||||
addFooterPreferenceIfNeeded(entries != null && !entries.isEmpty());
|
addFooterPreferenceIfNeeded(batteryDiffData != null
|
||||||
if (entries == null) {
|
&& (!batteryDiffData.getAppDiffEntryList().isEmpty()
|
||||||
Log.w(TAG, "cannot find BatteryDiffEntry for:" + mTrapezoidIndex);
|
|| !batteryDiffData.getSystemDiffEntryList().isEmpty()));
|
||||||
|
if (batteryDiffData == null) {
|
||||||
|
Log.w(TAG, "cannot find BatteryDiffEntry for daily_index: " + mDailyChartIndex
|
||||||
|
+ " hourly_index: " + mHourlyChartIndex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Separates data into two groups and sort them individually.
|
|
||||||
final List<BatteryDiffEntry> appEntries = new ArrayList<>();
|
|
||||||
mSystemEntries.clear();
|
|
||||||
entries.forEach(entry -> {
|
|
||||||
final String packageName = entry.getPackageName();
|
|
||||||
if (!isValidToShowEntry(packageName)) {
|
|
||||||
Log.w(TAG, "ignore showing item:" + packageName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (entry.isSystemEntry()) {
|
|
||||||
mSystemEntries.add(entry);
|
|
||||||
} else {
|
|
||||||
appEntries.add(entry);
|
|
||||||
}
|
|
||||||
// Validates the usage time if users click a specific slot.
|
|
||||||
if (mTrapezoidIndex >= 0) {
|
|
||||||
validateUsageTime(entry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Collections.sort(appEntries, BatteryDiffEntry.COMPARATOR);
|
|
||||||
Collections.sort(mSystemEntries, BatteryDiffEntry.COMPARATOR);
|
|
||||||
Log.d(TAG, String.format("addAllPreferences() app=%d system=%d",
|
|
||||||
appEntries.size(), mSystemEntries.size()));
|
|
||||||
|
|
||||||
// Adds app entries to the list if it is not empty.
|
// Adds app entries to the list if it is not empty.
|
||||||
if (!appEntries.isEmpty()) {
|
if (!batteryDiffData.getAppDiffEntryList().isEmpty()) {
|
||||||
addPreferenceToScreen(appEntries);
|
addPreferenceToScreen(batteryDiffData.getAppDiffEntryList());
|
||||||
}
|
}
|
||||||
// Adds the expabable divider if we have system entries data.
|
// Adds the expabable divider if we have system entries data.
|
||||||
if (!mSystemEntries.isEmpty()) {
|
if (!batteryDiffData.getSystemDiffEntryList().isEmpty()) {
|
||||||
if (mExpandDividerPreference == null) {
|
if (mExpandDividerPreference == null) {
|
||||||
mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
|
mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
|
||||||
mExpandDividerPreference.setOnExpandListener(this);
|
mExpandDividerPreference.setOnExpandListener(this);
|
||||||
@@ -469,11 +472,13 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void refreshExpandUi() {
|
private void refreshExpandUi() {
|
||||||
|
final List<BatteryDiffEntry> systemEntries = mBatteryUsageMap.get(mDailyChartIndex).get(
|
||||||
|
mHourlyChartIndex).getSystemDiffEntryList();
|
||||||
if (mIsExpanded) {
|
if (mIsExpanded) {
|
||||||
addPreferenceToScreen(mSystemEntries);
|
addPreferenceToScreen(systemEntries);
|
||||||
} else {
|
} else {
|
||||||
// Removes and recycles all system entries to hide all of them.
|
// Removes and recycles all system entries to hide all of them.
|
||||||
for (BatteryDiffEntry entry : mSystemEntries) {
|
for (BatteryDiffEntry entry : systemEntries) {
|
||||||
final String prefKey = entry.mBatteryHistEntry.getKey();
|
final String prefKey = entry.mBatteryHistEntry.getKey();
|
||||||
final Preference pref = mAppListPrefGroup.findPreference(prefKey);
|
final Preference pref = mAppListPrefGroup.findPreference(prefKey);
|
||||||
if (pref != null) {
|
if (pref != null) {
|
||||||
@@ -499,11 +504,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getSlotInformation(boolean isApp, String slotInformation) {
|
private String getSlotInformation(boolean isApp, String slotInformation) {
|
||||||
|
// TODO: Updates the right slot information from daily and hourly chart selection.
|
||||||
// Null means we show all information without a specific time slot.
|
// Null means we show all information without a specific time slot.
|
||||||
if (slotInformation == null) {
|
if (slotInformation == null) {
|
||||||
return isApp
|
return isApp
|
||||||
? mPrefContext.getString(R.string.battery_app_usage_for_past_24)
|
? mPrefContext.getString(R.string.battery_app_usage)
|
||||||
: mPrefContext.getString(R.string.battery_system_usage_for_past_24);
|
: mPrefContext.getString(R.string.battery_system_usage);
|
||||||
} else {
|
} else {
|
||||||
return isApp
|
return isApp
|
||||||
? mPrefContext.getString(R.string.battery_app_usage_for, slotInformation)
|
? mPrefContext.getString(R.string.battery_app_usage_for, slotInformation)
|
||||||
@@ -511,17 +517,33 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSlotInformation() {
|
@VisibleForTesting
|
||||||
if (mTrapezoidIndex < 0) {
|
String getSlotInformation() {
|
||||||
|
if (mDailyTimestampFullTexts == null || mDailyViewModel == null
|
||||||
|
|| mHourlyViewModels == null) {
|
||||||
|
// No data
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final String fromHour = ConvertUtils.utcToLocalTimeHour(mPrefContext,
|
if (isAllSelected()) {
|
||||||
mBatteryHistoryKeys[mTrapezoidIndex * 2], mIs24HourFormat);
|
return null;
|
||||||
final String toHour = ConvertUtils.utcToLocalTimeHour(mPrefContext,
|
}
|
||||||
mBatteryHistoryKeys[(mTrapezoidIndex + 1) * 2], mIs24HourFormat);
|
|
||||||
return mIs24HourFormat
|
final String selectedDayText = mDailyTimestampFullTexts.get(mDailyChartIndex);
|
||||||
? String.format("%s–%s", fromHour, toHour)
|
if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
|
||||||
: String.format("%s – %s", fromHour, toHour);
|
return selectedDayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String fromHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get(
|
||||||
|
mHourlyChartIndex);
|
||||||
|
final String toHourText = mHourlyViewModels.get(mDailyChartIndex).texts().get(
|
||||||
|
mHourlyChartIndex + 1);
|
||||||
|
final String selectedHourText =
|
||||||
|
String.format("%s%s%s", fromHourText, mIs24HourFormat ? "-" : " - ", toHourText);
|
||||||
|
if (isBatteryLevelDataInOneDay()) {
|
||||||
|
return selectedHourText;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format("%s %s", selectedDayText, selectedHourText);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -575,22 +597,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
boolean isValidToShowSummary(String packageName) {
|
boolean isValidToShowSummary(String packageName) {
|
||||||
return !contains(packageName, mNotAllowShowSummaryPackages);
|
return !DataProcessor.contains(packageName, mNotAllowShowSummaryPackages);
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean isValidToShowEntry(String packageName) {
|
|
||||||
return !contains(packageName, mNotAllowShowEntryPackages);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void setTimestampLabel() {
|
|
||||||
if (mBatteryChartView == null || mBatteryHistoryKeys == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final long latestTimestamp =
|
|
||||||
mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1];
|
|
||||||
mBatteryChartView.setLatestTimestamp(latestTimestamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addFooterPreferenceIfNeeded(boolean containAppItems) {
|
private void addFooterPreferenceIfNeeded(boolean containAppItems) {
|
||||||
@@ -605,60 +612,65 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference));
|
mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean contains(String target, CharSequence[] packageNames) {
|
private boolean isBatteryLevelDataInOneDay() {
|
||||||
if (target != null && packageNames != null) {
|
return mHourlyViewModels != null && mHourlyViewModels.size() == 1;
|
||||||
for (CharSequence packageName : packageNames) {
|
|
||||||
if (TextUtils.equals(target, packageName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
private boolean isAllSelected() {
|
||||||
static boolean validateUsageTime(BatteryDiffEntry entry) {
|
return (isBatteryLevelDataInOneDay()
|
||||||
final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs;
|
|| mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||||
final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
|
&& mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
|
|
||||||
if (foregroundUsageTimeInMs > VALID_USAGE_TIME_DURATION
|
|
||||||
|| backgroundUsageTimeInMs > VALID_USAGE_TIME_DURATION
|
|
||||||
|| totalUsageTimeInMs > VALID_USAGE_TIME_DURATION) {
|
|
||||||
Log.e(TAG, "validateUsageTime() fail for\n" + entry);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
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}. */
|
/** Used for {@link AppBatteryPreferenceController}. */
|
||||||
public static List<BatteryDiffEntry> getBatteryLast24HrUsageData(Context context) {
|
public static List<BatteryDiffEntry> getAppBatteryUsageData(Context context) {
|
||||||
final long start = System.currentTimeMillis();
|
final long start = System.currentTimeMillis();
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
||||||
FeatureFactory.getFactory(context)
|
FeatureFactory.getFactory(context)
|
||||||
.getPowerUsageFeatureProvider(context)
|
.getPowerUsageFeatureProvider(context)
|
||||||
.getBatteryHistory(context);
|
.getBatteryHistorySinceLastFullCharge(context);
|
||||||
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Log.d(TAG, String.format("getBatteryLast24HrData() size=%d time=&d/ms",
|
Log.d(TAG, String.format("getBatterySinceLastFullChargeUsageData() size=%d time=%d/ms",
|
||||||
batteryHistoryMap.size(), (System.currentTimeMillis() - start)));
|
batteryHistoryMap.size(), (System.currentTimeMillis() - start)));
|
||||||
final Map<Integer, List<BatteryDiffEntry>> batteryIndexedMap =
|
|
||||||
ConvertUtils.getIndexedUsageMap(
|
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageData =
|
||||||
context,
|
DataProcessor.getBatteryUsageData(context, batteryHistoryMap);
|
||||||
/*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1,
|
return batteryUsageData == null
|
||||||
getBatteryHistoryKeys(batteryHistoryMap),
|
? null
|
||||||
batteryHistoryMap,
|
: batteryUsageData
|
||||||
/*purgeLowPercentageAndFakeData=*/ true);
|
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||||
return batteryIndexedMap.get(BatteryChartView.SELECTED_INDEX_ALL);
|
.get(BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||||
|
.getAppDiffEntryList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used for {@link AppBatteryPreferenceController}. */
|
/** Used for {@link AppBatteryPreferenceController}. */
|
||||||
public static BatteryDiffEntry getBatteryLast24HrUsageData(
|
public static BatteryDiffEntry getAppBatteryUsageData(
|
||||||
Context context, String packageName, int userId) {
|
Context context, String packageName, int userId) {
|
||||||
if (packageName == null) {
|
if (packageName == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final List<BatteryDiffEntry> entries = getBatteryLast24HrUsageData(context);
|
final List<BatteryDiffEntry> entries = getAppBatteryUsageData(context);
|
||||||
if (entries == null) {
|
if (entries == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -673,65 +685,4 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long[] getBatteryHistoryKeys(
|
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
|
||||||
final List<Long> batteryHistoryKeyList =
|
|
||||||
new ArrayList<>(batteryHistoryMap.keySet());
|
|
||||||
Collections.sort(batteryHistoryKeyList);
|
|
||||||
final long[] batteryHistoryKeys = new long[CHART_KEY_ARRAY_SIZE];
|
|
||||||
for (int index = 0; index < CHART_KEY_ARRAY_SIZE; index++) {
|
|
||||||
batteryHistoryKeys[index] = batteryHistoryKeyList.get(index);
|
|
||||||
}
|
|
||||||
return batteryHistoryKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loads all items icon and label in the background.
|
|
||||||
private final class LoadAllItemsInfoTask
|
|
||||||
extends AsyncTask<Void, Void, Map<Integer, List<BatteryDiffEntry>>> {
|
|
||||||
|
|
||||||
private long[] mBatteryHistoryKeysCache;
|
|
||||||
private Map<Long, Map<String, BatteryHistEntry>> mBatteryHistoryMap;
|
|
||||||
|
|
||||||
private LoadAllItemsInfoTask(
|
|
||||||
Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
|
||||||
this.mBatteryHistoryMap = batteryHistoryMap;
|
|
||||||
this.mBatteryHistoryKeysCache = mBatteryHistoryKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Map<Integer, List<BatteryDiffEntry>> doInBackground(Void... voids) {
|
|
||||||
if (mPrefContext == null || mBatteryHistoryKeysCache == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final long startTime = System.currentTimeMillis();
|
|
||||||
final Map<Integer, List<BatteryDiffEntry>> indexedUsageMap =
|
|
||||||
ConvertUtils.getIndexedUsageMap(
|
|
||||||
mPrefContext, /*timeSlotSize=*/ CHART_LEVEL_ARRAY_SIZE - 1,
|
|
||||||
mBatteryHistoryKeysCache, mBatteryHistoryMap,
|
|
||||||
/*purgeLowPercentageAndFakeData=*/ true);
|
|
||||||
// Pre-loads each BatteryDiffEntry relative icon and label for all slots.
|
|
||||||
for (List<BatteryDiffEntry> entries : indexedUsageMap.values()) {
|
|
||||||
entries.forEach(entry -> entry.loadLabelAndIcon());
|
|
||||||
}
|
|
||||||
Log.d(TAG, String.format("execute LoadAllItemsInfoTask in %d/ms",
|
|
||||||
(System.currentTimeMillis() - startTime)));
|
|
||||||
return indexedUsageMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(
|
|
||||||
Map<Integer, List<BatteryDiffEntry>> indexedUsageMap) {
|
|
||||||
mBatteryHistoryMap = null;
|
|
||||||
mBatteryHistoryKeysCache = null;
|
|
||||||
if (indexedUsageMap == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Posts results back to main thread to refresh UI.
|
|
||||||
mHandler.post(() -> {
|
|
||||||
mBatteryIndexedMap = indexedUsageMap;
|
|
||||||
forceRefreshUi();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,701 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.fuelgauge.batteryusage;
|
|
||||||
|
|
||||||
import android.app.settings.SettingsEnums;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.text.format.DateFormat;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.preference.Preference;
|
|
||||||
import androidx.preference.PreferenceGroup;
|
|
||||||
import androidx.preference.PreferenceScreen;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.SettingsActivity;
|
|
||||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
|
||||||
import com.android.settings.core.PreferenceControllerMixin;
|
|
||||||
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnCreate;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnResume;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
|
|
||||||
import com.android.settingslib.utils.StringUtil;
|
|
||||||
import com.android.settingslib.widget.FooterPreference;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/** Controls the update for chart graph and the list items. */
|
|
||||||
public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceController
|
|
||||||
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
|
|
||||||
OnSaveInstanceState, OnResume, ExpandDividerPreference.OnExpandListener {
|
|
||||||
private static final String TAG = "BatteryChartPreferenceControllerV2";
|
|
||||||
private static final String KEY_FOOTER_PREF = "battery_graph_footer";
|
|
||||||
private static final String PACKAGE_NAME_NONE = "none";
|
|
||||||
|
|
||||||
private static final int TWENTY_FOUR_HOURS_TIME_SLOT_SIZE = 12;
|
|
||||||
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_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, Map<Integer, BatteryDiffData>> mBatteryUsageMap;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
Context mPrefContext;
|
|
||||||
@VisibleForTesting
|
|
||||||
BatteryUtils mBatteryUtils;
|
|
||||||
@VisibleForTesting
|
|
||||||
PreferenceGroup mAppListPrefGroup;
|
|
||||||
@VisibleForTesting
|
|
||||||
ExpandDividerPreference mExpandDividerPreference;
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean mIsExpanded = false;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
BatteryChartViewV2 mDailyChartView;
|
|
||||||
@VisibleForTesting
|
|
||||||
BatteryChartViewV2 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[] mNotAllowShowSummaryPackages;
|
|
||||||
private final MetricsFeatureProvider mMetricsFeatureProvider;
|
|
||||||
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
|
||||||
|
|
||||||
// Preference cache to avoid create new instance each time.
|
|
||||||
@VisibleForTesting
|
|
||||||
final Map<String, Preference> mPreferenceCache = new HashMap<>();
|
|
||||||
|
|
||||||
public BatteryChartPreferenceControllerV2(
|
|
||||||
Context context, String preferenceKey,
|
|
||||||
Lifecycle lifecycle, SettingsActivity activity,
|
|
||||||
InstrumentedPreferenceFragment fragment) {
|
|
||||||
super(context);
|
|
||||||
mActivity = activity;
|
|
||||||
mFragment = fragment;
|
|
||||||
mPreferenceKey = preferenceKey;
|
|
||||||
mIs24HourFormat = DateFormat.is24HourFormat(context);
|
|
||||||
mMetricsFeatureProvider =
|
|
||||||
FeatureFactory.getFactory(mContext).getMetricsFeatureProvider();
|
|
||||||
mNotAllowShowSummaryPackages =
|
|
||||||
FeatureFactory.getFactory(context)
|
|
||||||
.getPowerUsageFeatureProvider(context)
|
|
||||||
.getHideApplicationSummary(context);
|
|
||||||
if (lifecycle != null) {
|
|
||||||
lifecycle.addObserver(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
|
|
||||||
mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
final int currentUiMode =
|
|
||||||
mContext.getResources().getConfiguration().uiMode
|
|
||||||
& Configuration.UI_MODE_NIGHT_MASK;
|
|
||||||
if (sUiMode != currentUiMode) {
|
|
||||||
sUiMode = currentUiMode;
|
|
||||||
BatteryDiffEntry.clearCache();
|
|
||||||
Log.d(TAG, "clear icon and label cache since uiMode is changed");
|
|
||||||
}
|
|
||||||
mIs24HourFormat = DateFormat.is24HourFormat(mContext);
|
|
||||||
mMetricsFeatureProvider.action(mPrefContext, SettingsEnums.OPEN_BATTERY_USAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSaveInstanceState(Bundle savedInstance) {
|
|
||||||
if (savedInstance == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
savedInstance.putInt(KEY_DAILY_CHART_INDEX, mDailyChartIndex);
|
|
||||||
savedInstance.putInt(KEY_HOURLY_CHART_INDEX, mHourlyChartIndex);
|
|
||||||
savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
|
|
||||||
Log.d(TAG, String.format("onSaveInstanceState() dailyIndex=%d hourlyIndex=%d isExpanded=%b",
|
|
||||||
mDailyChartIndex, mHourlyChartIndex, mIsExpanded));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
if (mActivity.isChangingConfigurations()) {
|
|
||||||
BatteryDiffEntry.clearCache();
|
|
||||||
}
|
|
||||||
mHandler.removeCallbacksAndMessages(/*token=*/ null);
|
|
||||||
mPreferenceCache.clear();
|
|
||||||
if (mAppListPrefGroup != null) {
|
|
||||||
mAppListPrefGroup.removeAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void displayPreference(PreferenceScreen screen) {
|
|
||||||
super.displayPreference(screen);
|
|
||||||
mPreferenceScreen = screen;
|
|
||||||
mPrefContext = screen.getContext();
|
|
||||||
mAppListPrefGroup = screen.findPreference(mPreferenceKey);
|
|
||||||
mAppListPrefGroup.setOrderingAsAdded(false);
|
|
||||||
mAppListPrefGroup.setTitle(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) {
|
|
||||||
screen.removePreference(mFooterPreference);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPreferenceKey() {
|
|
||||||
return mPreferenceKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
|
||||||
if (!(preference instanceof PowerGaugePreference)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final PowerGaugePreference powerPref = (PowerGaugePreference) preference;
|
|
||||||
final BatteryDiffEntry diffEntry = powerPref.getBatteryDiffEntry();
|
|
||||||
final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
|
|
||||||
final String packageName = histEntry.mPackageName;
|
|
||||||
final boolean isAppEntry = histEntry.isAppEntry();
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
/* attribution */ SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
/* action */ isAppEntry
|
|
||||||
? SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM
|
|
||||||
: SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
|
|
||||||
/* pageId */ SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
TextUtils.isEmpty(packageName) ? PACKAGE_NAME_NONE : packageName,
|
|
||||||
(int) Math.round(diffEntry.getPercentOfTotal()));
|
|
||||||
Log.d(TAG, String.format("handleClick() label=%s key=%s package=%s",
|
|
||||||
diffEntry.getAppLabel(), histEntry.getKey(), histEntry.mPackageName));
|
|
||||||
AdvancedPowerUsageDetail.startBatteryDetailPage(
|
|
||||||
mActivity, mFragment, diffEntry, powerPref.getPercent(),
|
|
||||||
isValidToShowSummary(packageName), getSlotInformation());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExpand(boolean isExpanded) {
|
|
||||||
mIsExpanded = isExpanded;
|
|
||||||
mMetricsFeatureProvider.action(
|
|
||||||
mPrefContext,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM,
|
|
||||||
isExpanded);
|
|
||||||
refreshExpandUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBatteryHistoryMap(
|
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
refreshUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBatteryChartView(@NonNull final BatteryChartViewV2 dailyChartView,
|
|
||||||
@NonNull final BatteryChartViewV2 hourlyChartView) {
|
|
||||||
if (mDailyChartView != dailyChartView || mHourlyChartView != hourlyChartView) {
|
|
||||||
mHandler.post(() -> setBatteryChartViewInner(dailyChartView, hourlyChartView));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setBatteryChartViewInner(@NonNull final BatteryChartViewV2 dailyChartView,
|
|
||||||
@NonNull final BatteryChartViewV2 hourlyChartView) {
|
|
||||||
mDailyChartView = dailyChartView;
|
|
||||||
mDailyChartView.setOnSelectListener(trapezoidIndex -> {
|
|
||||||
if (mDailyChartIndex == trapezoidIndex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex);
|
|
||||||
mDailyChartIndex = trapezoidIndex;
|
|
||||||
mHourlyChartIndex = BatteryChartView.SELECTED_INDEX_ALL;
|
|
||||||
refreshUi();
|
|
||||||
// TODO: Change to log daily data.
|
|
||||||
});
|
|
||||||
mHourlyChartView = hourlyChartView;
|
|
||||||
mHourlyChartView.setOnSelectListener(trapezoidIndex -> {
|
|
||||||
if (mHourlyChartIndex == trapezoidIndex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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() {
|
|
||||||
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);
|
|
||||||
addFooterPreferenceIfNeeded(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (mBatteryUsageMap == null) {
|
|
||||||
// Battery usage data is not ready, wait for data ready to refresh UI.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
updateMarginBetweenCharts(false);
|
|
||||||
} else {
|
|
||||||
mHourlyChartView.setVisibility(View.VISIBLE);
|
|
||||||
final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex);
|
|
||||||
hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
|
|
||||||
mHourlyChartView.setViewModel(hourlyViewModel);
|
|
||||||
|
|
||||||
updateMarginBetweenCharts(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
mHandler.post(() -> {
|
|
||||||
final long start = System.currentTimeMillis();
|
|
||||||
removeAndCacheAllPrefs();
|
|
||||||
addAllPreferences();
|
|
||||||
refreshCategoryTitle();
|
|
||||||
Log.d(TAG, String.format("refreshUi is finished in %d/ms",
|
|
||||||
(System.currentTimeMillis() - start)));
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateMarginBetweenCharts(boolean addMargin) {
|
|
||||||
final LinearLayout.LayoutParams layoutParams =
|
|
||||||
(LinearLayout.LayoutParams) mDailyChartView.getLayoutParams();
|
|
||||||
layoutParams.bottomMargin = addMargin ? Math.round(
|
|
||||||
mContext.getResources().getDimension(R.dimen.chartview_two_charts_margin)) : 0;
|
|
||||||
mDailyChartView.setLayoutParams(layoutParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAllPreferences() {
|
|
||||||
final BatteryDiffData batteryDiffData =
|
|
||||||
mBatteryUsageMap.get(mDailyChartIndex).get(mHourlyChartIndex);
|
|
||||||
addFooterPreferenceIfNeeded(batteryDiffData != null
|
|
||||||
&& (!batteryDiffData.getAppDiffEntryList().isEmpty()
|
|
||||||
|| !batteryDiffData.getSystemDiffEntryList().isEmpty()));
|
|
||||||
if (batteryDiffData == null) {
|
|
||||||
Log.w(TAG, "cannot find BatteryDiffEntry for daily_index: " + mDailyChartIndex
|
|
||||||
+ " hourly_index: " + mHourlyChartIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Adds app entries to the list if it is not empty.
|
|
||||||
if (!batteryDiffData.getAppDiffEntryList().isEmpty()) {
|
|
||||||
addPreferenceToScreen(batteryDiffData.getAppDiffEntryList());
|
|
||||||
}
|
|
||||||
// Adds the expabable divider if we have system entries data.
|
|
||||||
if (!batteryDiffData.getSystemDiffEntryList().isEmpty()) {
|
|
||||||
if (mExpandDividerPreference == null) {
|
|
||||||
mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
|
|
||||||
mExpandDividerPreference.setOnExpandListener(this);
|
|
||||||
mExpandDividerPreference.setIsExpanded(mIsExpanded);
|
|
||||||
}
|
|
||||||
mExpandDividerPreference.setOrder(
|
|
||||||
mAppListPrefGroup.getPreferenceCount());
|
|
||||||
mAppListPrefGroup.addPreference(mExpandDividerPreference);
|
|
||||||
}
|
|
||||||
refreshExpandUi();
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void addPreferenceToScreen(List<BatteryDiffEntry> entries) {
|
|
||||||
if (mAppListPrefGroup == null || entries.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int prefIndex = mAppListPrefGroup.getPreferenceCount();
|
|
||||||
for (BatteryDiffEntry entry : entries) {
|
|
||||||
boolean isAdded = false;
|
|
||||||
final String appLabel = entry.getAppLabel();
|
|
||||||
final Drawable appIcon = entry.getAppIcon();
|
|
||||||
if (TextUtils.isEmpty(appLabel) || appIcon == null) {
|
|
||||||
Log.w(TAG, "cannot find app resource for:" + entry.getPackageName());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final String prefKey = entry.mBatteryHistEntry.getKey();
|
|
||||||
PowerGaugePreference pref = mAppListPrefGroup.findPreference(prefKey);
|
|
||||||
if (pref != null) {
|
|
||||||
isAdded = true;
|
|
||||||
Log.w(TAG, "preference should be removed for:" + entry.getPackageName());
|
|
||||||
} else {
|
|
||||||
pref = (PowerGaugePreference) mPreferenceCache.get(prefKey);
|
|
||||||
}
|
|
||||||
// Creates new innstance if cached preference is not found.
|
|
||||||
if (pref == null) {
|
|
||||||
pref = new PowerGaugePreference(mPrefContext);
|
|
||||||
pref.setKey(prefKey);
|
|
||||||
mPreferenceCache.put(prefKey, pref);
|
|
||||||
}
|
|
||||||
pref.setIcon(appIcon);
|
|
||||||
pref.setTitle(appLabel);
|
|
||||||
pref.setOrder(prefIndex);
|
|
||||||
pref.setPercent(entry.getPercentOfTotal());
|
|
||||||
pref.setSingleLineTitle(true);
|
|
||||||
// Sets the BatteryDiffEntry to preference for launching detailed page.
|
|
||||||
pref.setBatteryDiffEntry(entry);
|
|
||||||
pref.setEnabled(entry.validForRestriction());
|
|
||||||
setPreferenceSummary(pref, entry);
|
|
||||||
if (!isAdded) {
|
|
||||||
mAppListPrefGroup.addPreference(pref);
|
|
||||||
}
|
|
||||||
prefIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeAndCacheAllPrefs() {
|
|
||||||
if (mAppListPrefGroup == null
|
|
||||||
|| mAppListPrefGroup.getPreferenceCount() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final int prefsCount = mAppListPrefGroup.getPreferenceCount();
|
|
||||||
for (int index = 0; index < prefsCount; index++) {
|
|
||||||
final Preference pref = mAppListPrefGroup.getPreference(index);
|
|
||||||
if (TextUtils.isEmpty(pref.getKey())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mPreferenceCache.put(pref.getKey(), pref);
|
|
||||||
}
|
|
||||||
mAppListPrefGroup.removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshExpandUi() {
|
|
||||||
final List<BatteryDiffEntry> systemEntries = mBatteryUsageMap.get(mDailyChartIndex).get(
|
|
||||||
mHourlyChartIndex).getSystemDiffEntryList();
|
|
||||||
if (mIsExpanded) {
|
|
||||||
addPreferenceToScreen(systemEntries);
|
|
||||||
} else {
|
|
||||||
// Removes and recycles all system entries to hide all of them.
|
|
||||||
for (BatteryDiffEntry entry : systemEntries) {
|
|
||||||
final String prefKey = entry.mBatteryHistEntry.getKey();
|
|
||||||
final Preference pref = mAppListPrefGroup.findPreference(prefKey);
|
|
||||||
if (pref != null) {
|
|
||||||
mAppListPrefGroup.removePreference(pref);
|
|
||||||
mPreferenceCache.put(pref.getKey(), pref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void refreshCategoryTitle() {
|
|
||||||
final String slotInformation = getSlotInformation();
|
|
||||||
Log.d(TAG, String.format("refreshCategoryTitle:%s", slotInformation));
|
|
||||||
if (mAppListPrefGroup != null) {
|
|
||||||
mAppListPrefGroup.setTitle(
|
|
||||||
getSlotInformation(/*isApp=*/ true, slotInformation));
|
|
||||||
}
|
|
||||||
if (mExpandDividerPreference != null) {
|
|
||||||
mExpandDividerPreference.setTitle(
|
|
||||||
getSlotInformation(/*isApp=*/ false, slotInformation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSlotInformation(boolean isApp, String slotInformation) {
|
|
||||||
// TODO: Updates the right slot information from daily and hourly chart selection.
|
|
||||||
// Null means we show all information without a specific time slot.
|
|
||||||
if (slotInformation == null) {
|
|
||||||
return isApp
|
|
||||||
? mPrefContext.getString(R.string.battery_app_usage)
|
|
||||||
: mPrefContext.getString(R.string.battery_system_usage);
|
|
||||||
} else {
|
|
||||||
return isApp
|
|
||||||
? mPrefContext.getString(R.string.battery_app_usage_for, slotInformation)
|
|
||||||
: mPrefContext.getString(R.string.battery_system_usage_for, slotInformation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
String getSlotInformation() {
|
|
||||||
if (mDailyTimestampFullTexts == null || mDailyViewModel == null
|
|
||||||
|| mHourlyViewModels == null) {
|
|
||||||
// No data
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
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
|
|
||||||
void setPreferenceSummary(
|
|
||||||
PowerGaugePreference preference, BatteryDiffEntry entry) {
|
|
||||||
final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs;
|
|
||||||
final long backgroundUsageTimeInMs = entry.mBackgroundUsageTimeInMs;
|
|
||||||
final long totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
|
|
||||||
// Checks whether the package is allowed to show summary or not.
|
|
||||||
if (!isValidToShowSummary(entry.getPackageName())) {
|
|
||||||
preference.setSummary(null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String usageTimeSummary = null;
|
|
||||||
// Not shows summary for some system components without usage time.
|
|
||||||
if (totalUsageTimeInMs == 0) {
|
|
||||||
preference.setSummary(null);
|
|
||||||
// Shows background summary only if we don't have foreground usage time.
|
|
||||||
} else if (foregroundUsageTimeInMs == 0 && backgroundUsageTimeInMs != 0) {
|
|
||||||
usageTimeSummary = buildUsageTimeInfo(backgroundUsageTimeInMs, true);
|
|
||||||
// Shows total usage summary only if total usage time is small.
|
|
||||||
} else if (totalUsageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
|
|
||||||
usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
|
|
||||||
} else {
|
|
||||||
usageTimeSummary = buildUsageTimeInfo(totalUsageTimeInMs, false);
|
|
||||||
// Shows background usage time if it is larger than a minute.
|
|
||||||
if (backgroundUsageTimeInMs > 0) {
|
|
||||||
usageTimeSummary +=
|
|
||||||
"\n" + buildUsageTimeInfo(backgroundUsageTimeInMs, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
preference.setSummary(usageTimeSummary);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildUsageTimeInfo(long usageTimeInMs, boolean isBackground) {
|
|
||||||
if (usageTimeInMs < DateUtils.MINUTE_IN_MILLIS) {
|
|
||||||
return mPrefContext.getString(
|
|
||||||
isBackground
|
|
||||||
? R.string.battery_usage_background_less_than_one_minute
|
|
||||||
: R.string.battery_usage_total_less_than_one_minute);
|
|
||||||
}
|
|
||||||
final CharSequence timeSequence =
|
|
||||||
StringUtil.formatElapsedTime(mPrefContext, usageTimeInMs,
|
|
||||||
/*withSeconds=*/ false, /*collapseTimeUnit=*/ false);
|
|
||||||
final int resourceId =
|
|
||||||
isBackground
|
|
||||||
? R.string.battery_usage_for_background_time
|
|
||||||
: R.string.battery_usage_for_total_time;
|
|
||||||
return mPrefContext.getString(resourceId, timeSequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
boolean isValidToShowSummary(String packageName) {
|
|
||||||
return !DataProcessor.contains(packageName, mNotAllowShowSummaryPackages);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addFooterPreferenceIfNeeded(boolean containAppItems) {
|
|
||||||
if (mIsFooterPrefAdded || mFooterPreference == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mIsFooterPrefAdded = true;
|
|
||||||
mFooterPreference.setTitle(mPrefContext.getString(
|
|
||||||
containAppItems
|
|
||||||
? R.string.battery_usage_screen_footer
|
|
||||||
: R.string.battery_usage_screen_footer_empty));
|
|
||||||
mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isBatteryLevelDataInOneDay() {
|
|
||||||
return mHourlyViewModels != null && mHourlyViewModels.size() == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isAllSelected() {
|
|
||||||
return (isBatteryLevelDataInOneDay()
|
|
||||||
|| mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL)
|
|
||||||
&& mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
}
|
|
||||||
|
|
||||||
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> getAppBatteryUsageData(Context context) {
|
|
||||||
final long start = System.currentTimeMillis();
|
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
|
||||||
FeatureFactory.getFactory(context)
|
|
||||||
.getPowerUsageFeatureProvider(context)
|
|
||||||
.getBatteryHistorySinceLastFullCharge(context);
|
|
||||||
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Log.d(TAG, String.format("getBatterySinceLastFullChargeUsageData() size=%d time=%d/ms",
|
|
||||||
batteryHistoryMap.size(), (System.currentTimeMillis() - start)));
|
|
||||||
|
|
||||||
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 getAppBatteryUsageData(
|
|
||||||
Context context, String packageName, int userId) {
|
|
||||||
if (packageName == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final List<BatteryDiffEntry> entries = getAppBatteryUsageData(context);
|
|
||||||
if (entries == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (BatteryDiffEntry entry : entries) {
|
|
||||||
final BatteryHistEntry batteryHistEntry = entry.mBatteryHistEntry;
|
|
||||||
if (batteryHistEntry != null
|
|
||||||
&& batteryHistEntry.mConsumerType == ConvertUtils.CONSUMER_TYPE_UID_BATTERY
|
|
||||||
&& batteryHistEntry.mUserId == userId
|
|
||||||
&& packageName.equals(entry.getPackageName())) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batteryusage;
|
|||||||
import static com.android.settings.Utils.formatPercentage;
|
import static com.android.settings.Utils.formatPercentage;
|
||||||
|
|
||||||
import static java.lang.Math.round;
|
import static java.lang.Math.round;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -29,8 +30,6 @@ import android.graphics.Paint;
|
|||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.text.format.DateFormat;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
@@ -39,6 +38,7 @@ import android.view.View;
|
|||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.appcompat.widget.AppCompatImageView;
|
import androidx.appcompat.widget.AppCompatImageView;
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ import com.android.settings.R;
|
|||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
import com.android.settingslib.Utils;
|
import com.android.settingslib.Utils;
|
||||||
|
|
||||||
import java.time.Clock;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -58,36 +58,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
|
private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
|
||||||
Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
|
Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
|
||||||
|
|
||||||
private static final int DEFAULT_TRAPEZOID_COUNT = 12;
|
|
||||||
private static final int DEFAULT_TIMESTAMP_COUNT = 4;
|
|
||||||
private static final int TIMESTAMP_GAPS_COUNT = DEFAULT_TIMESTAMP_COUNT - 1;
|
|
||||||
private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5");
|
private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5");
|
||||||
private static final long UPDATE_STATE_DELAYED_TIME = 500L;
|
private static final long UPDATE_STATE_DELAYED_TIME = 500L;
|
||||||
|
|
||||||
/** Selects all trapezoid shapes. */
|
|
||||||
public static final int SELECTED_INDEX_ALL = -1;
|
|
||||||
public static final int SELECTED_INDEX_INVALID = -2;
|
|
||||||
|
|
||||||
/** A callback listener for selected group index is updated. */
|
/** A callback listener for selected group index is updated. */
|
||||||
public interface OnSelectListener {
|
public interface OnSelectListener {
|
||||||
/** The callback function for selected group index is updated. */
|
/** The callback function for selected group index is updated. */
|
||||||
void onSelect(int trapezoidIndex);
|
void onSelect(int trapezoidIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private BatteryChartViewModel mViewModel;
|
||||||
|
|
||||||
private int mDividerWidth;
|
private int mDividerWidth;
|
||||||
private int mDividerHeight;
|
private int mDividerHeight;
|
||||||
private int mTrapezoidCount;
|
|
||||||
private float mTrapezoidVOffset;
|
private float mTrapezoidVOffset;
|
||||||
private float mTrapezoidHOffset;
|
private float mTrapezoidHOffset;
|
||||||
private boolean mIsSlotsClickabled;
|
private boolean mIsSlotsClickabled;
|
||||||
private String[] mPercentages = getPercentages();
|
private String[] mPercentages = getPercentages();
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
int mHoveredIndex = SELECTED_INDEX_INVALID;
|
int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||||
@VisibleForTesting
|
|
||||||
int mSelectedIndex = SELECTED_INDEX_INVALID;
|
|
||||||
@VisibleForTesting
|
|
||||||
String[] mTimestamps;
|
|
||||||
|
|
||||||
// Colors for drawing the trapezoid shape and dividers.
|
// Colors for drawing the trapezoid shape and dividers.
|
||||||
private int mTrapezoidColor;
|
private int mTrapezoidColor;
|
||||||
@@ -98,25 +88,26 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
private final Rect mIndent = new Rect();
|
private final Rect mIndent = new Rect();
|
||||||
private final Rect[] mPercentageBounds =
|
private final Rect[] mPercentageBounds =
|
||||||
new Rect[]{new Rect(), new Rect(), new Rect()};
|
new Rect[]{new Rect(), new Rect(), new Rect()};
|
||||||
// For drawing the timestamp information.
|
// For drawing the axis label information.
|
||||||
private final Rect[] mTimestampsBounds =
|
private final List<Rect> mAxisLabelsBounds = new ArrayList<>();
|
||||||
new Rect[]{new Rect(), new Rect(), new Rect(), new Rect()};
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Handler mHandler = new Handler();
|
Handler mHandler = new Handler();
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
final Runnable mUpdateClickableStateRun = () -> updateClickableState();
|
final Runnable mUpdateClickableStateRun = () -> updateClickableState();
|
||||||
|
|
||||||
private int[] mLevels;
|
|
||||||
private Paint mTextPaint;
|
private Paint mTextPaint;
|
||||||
private Paint mDividerPaint;
|
private Paint mDividerPaint;
|
||||||
private Paint mTrapezoidPaint;
|
private Paint mTrapezoidPaint;
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Paint mTrapezoidCurvePaint = null;
|
Paint mTrapezoidCurvePaint = null;
|
||||||
private TrapezoidSlot[] mTrapezoidSlots;
|
@VisibleForTesting
|
||||||
|
TrapezoidSlot[] mTrapezoidSlots;
|
||||||
// Records the location to calculate selected index.
|
// Records the location to calculate selected index.
|
||||||
private float mTouchUpEventX = Float.MIN_VALUE;
|
@VisibleForTesting
|
||||||
|
float mTouchUpEventX = Float.MIN_VALUE;
|
||||||
private BatteryChartView.OnSelectListener mOnSelectListener;
|
private BatteryChartView.OnSelectListener mOnSelectListener;
|
||||||
|
|
||||||
public BatteryChartView(Context context) {
|
public BatteryChartView(Context context) {
|
||||||
@@ -128,57 +119,25 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
initializeColors(context);
|
initializeColors(context);
|
||||||
// Registers the click event listener.
|
// Registers the click event listener.
|
||||||
setOnClickListener(this);
|
setOnClickListener(this);
|
||||||
setSelectedIndex(SELECTED_INDEX_ALL);
|
|
||||||
setTrapezoidCount(DEFAULT_TRAPEZOID_COUNT);
|
|
||||||
setClickable(false);
|
setClickable(false);
|
||||||
setLatestTimestamp(0);
|
requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the total trapezoid count for drawing. */
|
/** Sets the data model of this view. */
|
||||||
public void setTrapezoidCount(int trapezoidCount) {
|
public void setViewModel(BatteryChartViewModel viewModel) {
|
||||||
Log.i(TAG, "trapezoidCount:" + trapezoidCount);
|
if (viewModel == null) {
|
||||||
mTrapezoidCount = trapezoidCount;
|
mViewModel = null;
|
||||||
mTrapezoidSlots = new TrapezoidSlot[trapezoidCount];
|
|
||||||
// Allocates the trapezoid slot array.
|
|
||||||
for (int index = 0; index < trapezoidCount; index++) {
|
|
||||||
mTrapezoidSlots[index] = new TrapezoidSlot();
|
|
||||||
}
|
|
||||||
invalidate();
|
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;
|
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. */
|
Log.d(TAG, String.format("setViewModel(): size: %d, selectedIndex: %d.",
|
||||||
public void setSelectedIndex(int index) {
|
viewModel.size(), viewModel.selectedIndex()));
|
||||||
if (mSelectedIndex != index) {
|
mViewModel = viewModel;
|
||||||
mSelectedIndex = index;
|
initializeAxisLabelsBounds();
|
||||||
invalidate();
|
initializeTrapezoidSlots(viewModel.size() - 1);
|
||||||
// Callbacks to the listener if we have.
|
setClickable(hasAnyValidTrapezoid(viewModel));
|
||||||
if (mOnSelectListener != null) {
|
requestLayout();
|
||||||
mOnSelectListener.onSelect(mSelectedIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the callback to monitor the selected group index. */
|
/** Sets the callback to monitor the selected group index. */
|
||||||
@@ -195,29 +154,6 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
} else {
|
} else {
|
||||||
mTextPaint = null;
|
mTextPaint = null;
|
||||||
}
|
}
|
||||||
setVisibility(View.VISIBLE);
|
|
||||||
requestLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the latest timestamp for drawing into x-axis information. */
|
|
||||||
public void setLatestTimestamp(long latestTimestamp) {
|
|
||||||
if (latestTimestamp == 0) {
|
|
||||||
latestTimestamp = Clock.systemUTC().millis();
|
|
||||||
}
|
|
||||||
if (mTimestamps == null) {
|
|
||||||
mTimestamps = new String[DEFAULT_TIMESTAMP_COUNT];
|
|
||||||
}
|
|
||||||
final long timeSlotOffset =
|
|
||||||
DateUtils.HOUR_IN_MILLIS * (/*total 24 hours*/ 24 / TIMESTAMP_GAPS_COUNT);
|
|
||||||
final boolean is24HourFormat = DateFormat.is24HourFormat(getContext());
|
|
||||||
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) {
|
|
||||||
mTimestamps[index] =
|
|
||||||
ConvertUtils.utcToLocalTimeHour(
|
|
||||||
getContext(),
|
|
||||||
latestTimestamp - (TIMESTAMP_GAPS_COUNT - index)
|
|
||||||
* timeSlotOffset,
|
|
||||||
is24HourFormat);
|
|
||||||
}
|
|
||||||
requestLayout();
|
requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +162,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
// Measures text bounds and updates indent configuration.
|
// Measures text bounds and updates indent configuration.
|
||||||
if (mTextPaint != null) {
|
if (mTextPaint != null) {
|
||||||
|
mTextPaint.setTextAlign(Paint.Align.LEFT);
|
||||||
for (int index = 0; index < mPercentages.length; index++) {
|
for (int index = 0; index < mPercentages.length; index++) {
|
||||||
mTextPaint.getTextBounds(
|
mTextPaint.getTextBounds(
|
||||||
mPercentages[index], 0, mPercentages[index].length(),
|
mPercentages[index], 0, mPercentages[index].length(),
|
||||||
@@ -235,15 +172,14 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
mIndent.top = mPercentageBounds[0].height();
|
mIndent.top = mPercentageBounds[0].height();
|
||||||
mIndent.right = mPercentageBounds[0].width() + mTextPadding;
|
mIndent.right = mPercentageBounds[0].width() + mTextPadding;
|
||||||
|
|
||||||
if (mTimestamps != null) {
|
if (mViewModel != null) {
|
||||||
int maxHeight = 0;
|
int maxTop = 0;
|
||||||
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) {
|
for (int index = 0; index < mViewModel.size(); index++) {
|
||||||
mTextPaint.getTextBounds(
|
final String text = mViewModel.texts().get(index);
|
||||||
mTimestamps[index], 0, mTimestamps[index].length(),
|
mTextPaint.getTextBounds(text, 0, text.length(), mAxisLabelsBounds.get(index));
|
||||||
mTimestampsBounds[index]);
|
maxTop = Math.max(maxTop, -mAxisLabelsBounds.get(index).top);
|
||||||
maxHeight = Math.max(maxHeight, mTimestampsBounds[index].height());
|
|
||||||
}
|
}
|
||||||
mIndent.bottom = maxHeight + round(mTextPadding * 1.5f);
|
mIndent.bottom = maxTop + round(mTextPadding * 2f);
|
||||||
}
|
}
|
||||||
Log.d(TAG, "setIndent:" + mPercentageBounds[0]);
|
Log.d(TAG, "setIndent:" + mPercentageBounds[0]);
|
||||||
} else {
|
} else {
|
||||||
@@ -254,7 +190,12 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
@Override
|
@Override
|
||||||
public void draw(Canvas canvas) {
|
public void draw(Canvas canvas) {
|
||||||
super.draw(canvas);
|
super.draw(canvas);
|
||||||
|
// Before mLevels initialized, the count of trapezoids is unknown. Only draws the
|
||||||
|
// horizontal percentages and dividers.
|
||||||
drawHorizontalDividers(canvas);
|
drawHorizontalDividers(canvas);
|
||||||
|
if (mViewModel == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
drawVerticalDividers(canvas);
|
drawVerticalDividers(canvas);
|
||||||
drawTrapezoids(canvas);
|
drawTrapezoids(canvas);
|
||||||
}
|
}
|
||||||
@@ -294,7 +235,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
public void onHoverChanged(boolean hovered) {
|
public void onHoverChanged(boolean hovered) {
|
||||||
super.onHoverChanged(hovered);
|
super.onHoverChanged(hovered);
|
||||||
if (!hovered) {
|
if (!hovered) {
|
||||||
mHoveredIndex = SELECTED_INDEX_INVALID; // reset
|
mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,15 +248,15 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
}
|
}
|
||||||
final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX);
|
final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX);
|
||||||
// Ignores the click event if the level is zero.
|
// Ignores the click event if the level is zero.
|
||||||
if (trapezoidIndex == SELECTED_INDEX_INVALID
|
if (trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID
|
||||||
|| !isValidToDraw(trapezoidIndex)) {
|
|| !isValidToDraw(mViewModel, trapezoidIndex)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (mOnSelectListener != null) {
|
||||||
// Selects all if users click the same trapezoid item two times.
|
// Selects all if users click the same trapezoid item two times.
|
||||||
if (trapezoidIndex == mSelectedIndex) {
|
mOnSelectListener.onSelect(
|
||||||
setSelectedIndex(SELECTED_INDEX_ALL);
|
trapezoidIndex == mViewModel.selectedIndex()
|
||||||
} else {
|
? BatteryChartViewModel.SELECTED_INDEX_ALL : trapezoidIndex);
|
||||||
setSelectedIndex(trapezoidIndex);
|
|
||||||
}
|
}
|
||||||
view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||||
}
|
}
|
||||||
@@ -364,8 +305,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
|
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
|
||||||
} else if (mIsSlotsClickabled) {
|
} else if (mIsSlotsClickabled) {
|
||||||
mTrapezoidCurvePaint = null;
|
mTrapezoidCurvePaint = null;
|
||||||
// Sets levels again to force update the click state.
|
// Sets view model again to force update the click state.
|
||||||
setLevels(mLevels);
|
setViewModel(mViewModel);
|
||||||
}
|
}
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
@@ -380,6 +321,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
super.setClickable(clickable);
|
super.setClickable(clickable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initializeTrapezoidSlots(int count) {
|
||||||
|
mTrapezoidSlots = new TrapezoidSlot[count];
|
||||||
|
for (int index = 0; index < mTrapezoidSlots.length; index++) {
|
||||||
|
mTrapezoidSlots[index] = new TrapezoidSlot();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeColors(Context context) {
|
private void initializeColors(Context context) {
|
||||||
setBackgroundColor(Color.TRANSPARENT);
|
setBackgroundColor(Color.TRANSPARENT);
|
||||||
mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
|
mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
|
||||||
@@ -434,10 +382,10 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
|
|
||||||
private void drawPercentage(Canvas canvas, int index, float offsetY) {
|
private void drawPercentage(Canvas canvas, int index, float offsetY) {
|
||||||
if (mTextPaint != null) {
|
if (mTextPaint != null) {
|
||||||
|
mTextPaint.setTextAlign(Paint.Align.RIGHT);
|
||||||
canvas.drawText(
|
canvas.drawText(
|
||||||
mPercentages[index],
|
mPercentages[index],
|
||||||
getWidth() - mPercentageBounds[index].width()
|
getWidth(),
|
||||||
- mPercentageBounds[index].left,
|
|
||||||
offsetY + mPercentageBounds[index].height() * .5f,
|
offsetY + mPercentageBounds[index].height() * .5f,
|
||||||
mTextPaint);
|
mTextPaint);
|
||||||
}
|
}
|
||||||
@@ -445,9 +393,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
|
|
||||||
private void drawVerticalDividers(Canvas canvas) {
|
private void drawVerticalDividers(Canvas canvas) {
|
||||||
final int width = getWidth() - mIndent.right;
|
final int width = getWidth() - mIndent.right;
|
||||||
final int dividerCount = mTrapezoidCount + 1;
|
final int dividerCount = mTrapezoidSlots.length + 1;
|
||||||
final float dividerSpace = dividerCount * mDividerWidth;
|
final float dividerSpace = dividerCount * mDividerWidth;
|
||||||
final float unitWidth = (width - dividerSpace) / (float) mTrapezoidCount;
|
final float unitWidth = (width - dividerSpace) / (float) mTrapezoidSlots.length;
|
||||||
final float bottomY = getHeight() - mIndent.bottom;
|
final float bottomY = getHeight() - mIndent.bottom;
|
||||||
final float startY = bottomY - mDividerHeight;
|
final float startY = bottomY - mDividerHeight;
|
||||||
final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f;
|
final float trapezoidSlotOffset = mTrapezoidHOffset + mDividerWidth * .5f;
|
||||||
@@ -463,53 +411,119 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
}
|
}
|
||||||
startX = nextX;
|
startX = nextX;
|
||||||
}
|
}
|
||||||
// Draws the timestamp slot information.
|
// Draws the axis label slot information.
|
||||||
if (mTimestamps != null) {
|
if (mViewModel != null) {
|
||||||
final float[] xOffsets = new float[DEFAULT_TIMESTAMP_COUNT];
|
final float baselineY = getHeight() - mTextPadding * 1.5f;
|
||||||
final float baselineX = mDividerWidth * .5f;
|
Rect[] axisLabelDisplayAreas;
|
||||||
final float offsetX = mDividerWidth + unitWidth;
|
switch (mViewModel.axisLabelPosition()) {
|
||||||
final int slotBarOffset = (/*total 12 bars*/ 12) / TIMESTAMP_GAPS_COUNT;
|
case CENTER_OF_TRAPEZOIDS:
|
||||||
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) {
|
axisLabelDisplayAreas = getAxisLabelDisplayAreas(
|
||||||
xOffsets[index] = baselineX + index * offsetX * slotBarOffset;
|
/* size= */ mViewModel.size() - 1,
|
||||||
|
/* baselineX= */ mDividerWidth + unitWidth * .5f,
|
||||||
|
/* offsetX= */ mDividerWidth + unitWidth,
|
||||||
|
baselineY,
|
||||||
|
/* shiftFirstAndLast= */ false);
|
||||||
|
break;
|
||||||
|
case BETWEEN_TRAPEZOIDS:
|
||||||
|
default:
|
||||||
|
axisLabelDisplayAreas = getAxisLabelDisplayAreas(
|
||||||
|
/* size= */ mViewModel.size(),
|
||||||
|
/* baselineX= */ mDividerWidth * .5f,
|
||||||
|
/* offsetX= */ mDividerWidth + unitWidth,
|
||||||
|
baselineY,
|
||||||
|
/* shiftFirstAndLast= */ true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
drawTimestamp(canvas, xOffsets);
|
drawAxisLabels(canvas, axisLabelDisplayAreas, baselineY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawTimestamp(Canvas canvas, float[] xOffsets) {
|
/** Gets all the axis label texts displaying area positions if they are shown. */
|
||||||
// Draws the 1st timestamp info.
|
private Rect[] getAxisLabelDisplayAreas(final int size, final float baselineX,
|
||||||
canvas.drawText(
|
final float offsetX, final float baselineY, final boolean shiftFirstAndLast) {
|
||||||
mTimestamps[0],
|
final Rect[] result = new Rect[size];
|
||||||
xOffsets[0] - mTimestampsBounds[0].left,
|
for (int index = 0; index < result.length; index++) {
|
||||||
getTimestampY(0), mTextPaint);
|
final float width = mAxisLabelsBounds.get(index).width();
|
||||||
final int latestIndex = DEFAULT_TIMESTAMP_COUNT - 1;
|
float middle = baselineX + index * offsetX;
|
||||||
// Draws the last timestamp info.
|
if (shiftFirstAndLast) {
|
||||||
canvas.drawText(
|
if (index == 0) {
|
||||||
mTimestamps[latestIndex],
|
middle += width * .5f;
|
||||||
xOffsets[latestIndex] - mTimestampsBounds[latestIndex].width()
|
}
|
||||||
- mTimestampsBounds[latestIndex].left,
|
if (index == size - 1) {
|
||||||
getTimestampY(latestIndex), mTextPaint);
|
middle -= width * .5f;
|
||||||
// Draws the rest of timestamp info since it is located in the center.
|
}
|
||||||
for (int index = 1; index <= DEFAULT_TIMESTAMP_COUNT - 2; index++) {
|
}
|
||||||
canvas.drawText(
|
final float left = middle - width * .5f;
|
||||||
mTimestamps[index],
|
final float right = left + width;
|
||||||
xOffsets[index]
|
final float top = baselineY + mAxisLabelsBounds.get(index).top;
|
||||||
- (mTimestampsBounds[index].width() - mTimestampsBounds[index].left)
|
final float bottom = top + mAxisLabelsBounds.get(index).height();
|
||||||
* .5f,
|
result[index] = new Rect(round(left), round(top), round(right), round(bottom));
|
||||||
getTimestampY(index), mTextPaint);
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pairly draws axis labels from left and right side to middle. If the pair of labels have
|
||||||
|
* any overlap, skips that pair of labels.
|
||||||
|
*/
|
||||||
|
private void drawAxisLabels(Canvas canvas, final Rect[] displayAreas, final float baselineY) {
|
||||||
|
int forwardCheckLine = Integer.MIN_VALUE;
|
||||||
|
int backwardCheckLine = Integer.MAX_VALUE;
|
||||||
|
Rect middleDisplayArea = null;
|
||||||
|
for (int forwardIndex = 0, backwordIndex = displayAreas.length - 1;
|
||||||
|
forwardIndex <= backwordIndex; forwardIndex++, backwordIndex--) {
|
||||||
|
final Rect forwardDisplayArea = displayAreas[forwardIndex];
|
||||||
|
final Rect backwardDisplayArea = displayAreas[backwordIndex];
|
||||||
|
if (forwardDisplayArea.left < forwardCheckLine
|
||||||
|
|| backwardDisplayArea.right > backwardCheckLine) {
|
||||||
|
// Overlapped at left or right, skip the pair of labels
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (middleDisplayArea != null && (
|
||||||
|
forwardDisplayArea.right + mTextPadding > middleDisplayArea.left
|
||||||
|
|| backwardDisplayArea.left - mTextPadding < middleDisplayArea.right)) {
|
||||||
|
// Overlapped with the middle label.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (forwardIndex != backwordIndex
|
||||||
|
&& forwardDisplayArea.right + mTextPadding > backwardDisplayArea.left) {
|
||||||
|
// Overlapped in the middle, skip the pair of labels
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawAxisLabelText(canvas, forwardIndex, forwardDisplayArea, baselineY);
|
||||||
|
drawAxisLabelText(canvas, backwordIndex, backwardDisplayArea, baselineY);
|
||||||
|
|
||||||
|
forwardCheckLine = forwardDisplayArea.right + mTextPadding;
|
||||||
|
backwardCheckLine = backwardDisplayArea.left - mTextPadding;
|
||||||
|
|
||||||
|
// If the number of labels is odd, draw the middle label first
|
||||||
|
if (forwardIndex == 0 && backwordIndex % 2 == 0) {
|
||||||
|
final int middleIndex = backwordIndex / 2;
|
||||||
|
middleDisplayArea = displayAreas[middleIndex];
|
||||||
|
if (middleDisplayArea.left < forwardCheckLine
|
||||||
|
|| middleDisplayArea.right > backwardCheckLine) {
|
||||||
|
// Overlapped at left or right, skip the pair of labels
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
drawAxisLabelText(canvas, middleIndex, middleDisplayArea, baselineY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getTimestampY(int index) {
|
private void drawAxisLabelText(
|
||||||
return getHeight() - mTimestampsBounds[index].height()
|
Canvas canvas, final int index, final Rect displayArea, final float baselineY) {
|
||||||
+ (mTimestampsBounds[index].height() + mTimestampsBounds[index].top)
|
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
||||||
+ round(mTextPadding * 1.5f);
|
canvas.drawText(
|
||||||
|
mViewModel.texts().get(index),
|
||||||
|
displayArea.centerX(),
|
||||||
|
baselineY,
|
||||||
|
mTextPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawTrapezoids(Canvas canvas) {
|
private void drawTrapezoids(Canvas canvas) {
|
||||||
// Ignores invalid trapezoid data.
|
// Ignores invalid trapezoid data.
|
||||||
if (mLevels == null) {
|
if (mViewModel == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final float trapezoidBottom =
|
final float trapezoidBottom =
|
||||||
@@ -520,9 +534,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
// Draws all trapezoid shapes into the canvas.
|
// Draws all trapezoid shapes into the canvas.
|
||||||
final Path trapezoidPath = new Path();
|
final Path trapezoidPath = new Path();
|
||||||
Path trapezoidCurvePath = null;
|
Path trapezoidCurvePath = null;
|
||||||
for (int index = 0; index < mTrapezoidCount; index++) {
|
for (int index = 0; index < mTrapezoidSlots.length; index++) {
|
||||||
// Not draws the trapezoid for corner or not initialization cases.
|
// Not draws the trapezoid for corner or not initialization cases.
|
||||||
if (!isValidToDraw(index)) {
|
if (!isValidToDraw(mViewModel, index)) {
|
||||||
if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) {
|
if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) {
|
||||||
canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint);
|
canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint);
|
||||||
trapezoidCurvePath = null;
|
trapezoidCurvePath = null;
|
||||||
@@ -530,17 +544,18 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Configures the trapezoid paint color.
|
// Configures the trapezoid paint color.
|
||||||
final int trapezoidColor =
|
final int trapezoidColor = mIsSlotsClickabled && (mViewModel.selectedIndex() == index
|
||||||
!mIsSlotsClickabled
|
|| mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||||
? mTrapezoidColor
|
|
||||||
: mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
|
|
||||||
? mTrapezoidSolidColor : mTrapezoidColor;
|
? mTrapezoidSolidColor : mTrapezoidColor;
|
||||||
final boolean isHoverState =
|
final boolean isHoverState =
|
||||||
mIsSlotsClickabled && mHoveredIndex == index && isValidToDraw(mHoveredIndex);
|
mIsSlotsClickabled && mHoveredIndex == index
|
||||||
|
&& isValidToDraw(mViewModel, mHoveredIndex);
|
||||||
mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor);
|
mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor);
|
||||||
|
|
||||||
final float leftTop = round(trapezoidBottom - mLevels[index] * unitHeight);
|
final float leftTop = round(
|
||||||
final float rightTop = round(trapezoidBottom - mLevels[index + 1] * unitHeight);
|
trapezoidBottom - requireNonNull(mViewModel.levels().get(index)) * unitHeight);
|
||||||
|
final float rightTop = round(trapezoidBottom
|
||||||
|
- requireNonNull(mViewModel.levels().get(index + 1)) * unitHeight);
|
||||||
trapezoidPath.reset();
|
trapezoidPath.reset();
|
||||||
trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom);
|
trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom);
|
||||||
trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop);
|
trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop);
|
||||||
@@ -579,15 +594,37 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SELECTED_INDEX_INVALID;
|
return BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isValidToDraw(int trapezoidIndex) {
|
private void initializeAxisLabelsBounds() {
|
||||||
return mLevels != null
|
mAxisLabelsBounds.clear();
|
||||||
|
for (int i = 0; i < mViewModel.size(); i++) {
|
||||||
|
mAxisLabelsBounds.add(new Rect());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTrapezoidValid(
|
||||||
|
@NonNull BatteryChartViewModel viewModel, int trapezoidIndex) {
|
||||||
|
return viewModel.levels().get(trapezoidIndex) != null
|
||||||
|
&& viewModel.levels().get(trapezoidIndex + 1) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isValidToDraw(BatteryChartViewModel viewModel, int trapezoidIndex) {
|
||||||
|
return viewModel != null
|
||||||
&& trapezoidIndex >= 0
|
&& trapezoidIndex >= 0
|
||||||
&& trapezoidIndex < mLevels.length - 1
|
&& trapezoidIndex < viewModel.size() - 1
|
||||||
&& mLevels[trapezoidIndex] != 0
|
&& isTrapezoidValid(viewModel, trapezoidIndex);
|
||||||
&& mLevels[trapezoidIndex + 1] != 0;
|
}
|
||||||
|
|
||||||
|
private static boolean hasAnyValidTrapezoid(@NonNull BatteryChartViewModel viewModel) {
|
||||||
|
// Sets the chart is clickable if there is at least one valid item in it.
|
||||||
|
for (int trapezoidIndex = 0; trapezoidIndex < viewModel.size() - 1; trapezoidIndex++) {
|
||||||
|
if (isTrapezoidValid(viewModel, trapezoidIndex)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] getPercentages() {
|
private static String[] getPercentages() {
|
||||||
@@ -621,7 +658,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A container class for each trapezoid left and right location.
|
// A container class for each trapezoid left and right location.
|
||||||
private static final class TrapezoidSlot {
|
@VisibleForTesting
|
||||||
|
static final class TrapezoidSlot {
|
||||||
public float mLeft;
|
public float mLeft;
|
||||||
public float mRight;
|
public float mRight;
|
||||||
|
|
||||||
|
@@ -23,7 +23,7 @@ import java.util.List;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/** The view model of {@code BatteryChartViewV2} */
|
/** The view model of {@code BatteryChartView} */
|
||||||
class BatteryChartViewModel {
|
class BatteryChartViewModel {
|
||||||
private static final String TAG = "BatteryChartViewModel";
|
private static final String TAG = "BatteryChartViewModel";
|
||||||
|
|
||||||
|
@@ -1,671 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.fuelgauge.batteryusage;
|
|
||||||
|
|
||||||
import static com.android.settings.Utils.formatPercentage;
|
|
||||||
|
|
||||||
import static java.lang.Math.round;
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.CornerPathEffect;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Path;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
|
||||||
import com.android.settingslib.Utils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/** A widget component to draw chart graph. */
|
|
||||||
public class BatteryChartViewV2 extends AppCompatImageView implements View.OnClickListener,
|
|
||||||
AccessibilityManager.AccessibilityStateChangeListener {
|
|
||||||
private static final String TAG = "BatteryChartViewV2";
|
|
||||||
private static final List<String> ACCESSIBILITY_SERVICE_NAMES =
|
|
||||||
Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
|
|
||||||
|
|
||||||
private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5");
|
|
||||||
private static final long UPDATE_STATE_DELAYED_TIME = 500L;
|
|
||||||
|
|
||||||
/** 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 float mTrapezoidVOffset;
|
|
||||||
private float mTrapezoidHOffset;
|
|
||||||
private boolean mIsSlotsClickabled;
|
|
||||||
private String[] mPercentages = getPercentages();
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
|
||||||
|
|
||||||
// Colors for drawing the trapezoid shape and dividers.
|
|
||||||
private int mTrapezoidColor;
|
|
||||||
private int mTrapezoidSolidColor;
|
|
||||||
private int mTrapezoidHoverColor;
|
|
||||||
// For drawing the percentage information.
|
|
||||||
private int mTextPadding;
|
|
||||||
private final Rect mIndent = new Rect();
|
|
||||||
private final Rect[] mPercentageBounds =
|
|
||||||
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 Paint mTextPaint;
|
|
||||||
private Paint mDividerPaint;
|
|
||||||
private Paint mTrapezoidPaint;
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
Paint mTrapezoidCurvePaint = null;
|
|
||||||
@VisibleForTesting
|
|
||||||
TrapezoidSlot[] mTrapezoidSlots;
|
|
||||||
// Records the location to calculate selected index.
|
|
||||||
@VisibleForTesting
|
|
||||||
float mTouchUpEventX = Float.MIN_VALUE;
|
|
||||||
private BatteryChartViewV2.OnSelectListener mOnSelectListener;
|
|
||||||
|
|
||||||
public BatteryChartViewV2(Context context) {
|
|
||||||
super(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BatteryChartViewV2(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
initializeColors(context);
|
|
||||||
// Registers the click event listener.
|
|
||||||
setOnClickListener(this);
|
|
||||||
setClickable(false);
|
|
||||||
requestLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the data model of this view. */
|
|
||||||
public void setViewModel(BatteryChartViewModel viewModel) {
|
|
||||||
if (viewModel == null) {
|
|
||||||
mViewModel = null;
|
|
||||||
invalidate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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. */
|
|
||||||
public void setOnSelectListener(BatteryChartViewV2.OnSelectListener listener) {
|
|
||||||
mOnSelectListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets the companion {@link TextView} for percentage information. */
|
|
||||||
public void setCompanionTextView(TextView textView) {
|
|
||||||
if (textView != null) {
|
|
||||||
// Pre-draws the view first to load style atttributions into paint.
|
|
||||||
textView.draw(new Canvas());
|
|
||||||
mTextPaint = textView.getPaint();
|
|
||||||
} else {
|
|
||||||
mTextPaint = null;
|
|
||||||
}
|
|
||||||
requestLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
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(),
|
|
||||||
mPercentageBounds[index]);
|
|
||||||
}
|
|
||||||
// Updates the indent configurations.
|
|
||||||
mIndent.top = mPercentageBounds[0].height();
|
|
||||||
mIndent.right = mPercentageBounds[0].width() + mTextPadding;
|
|
||||||
|
|
||||||
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 = maxTop + round(mTextPadding * 2f);
|
|
||||||
}
|
|
||||||
Log.d(TAG, "setIndent:" + mPercentageBounds[0]);
|
|
||||||
} else {
|
|
||||||
mIndent.set(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
// Caches the location to calculate selected trapezoid index.
|
|
||||||
final int action = event.getAction();
|
|
||||||
switch (action) {
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
mTouchUpEventX = event.getX();
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_CANCEL:
|
|
||||||
mTouchUpEventX = Float.MIN_VALUE; // reset
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return super.onTouchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onHoverEvent(MotionEvent event) {
|
|
||||||
final int action = event.getAction();
|
|
||||||
switch (action) {
|
|
||||||
case MotionEvent.ACTION_HOVER_ENTER:
|
|
||||||
case MotionEvent.ACTION_HOVER_MOVE:
|
|
||||||
final int trapezoidIndex = getTrapezoidIndex(event.getX());
|
|
||||||
if (mHoveredIndex != trapezoidIndex) {
|
|
||||||
mHoveredIndex = trapezoidIndex;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return super.onHoverEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHoverChanged(boolean hovered) {
|
|
||||||
super.onHoverChanged(hovered);
|
|
||||||
if (!hovered) {
|
|
||||||
mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
if (mTouchUpEventX == Float.MIN_VALUE) {
|
|
||||||
Log.w(TAG, "invalid motion event for onClick() callback");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX);
|
|
||||||
// Ignores the click event if the level is zero.
|
|
||||||
if (trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID
|
|
||||||
|| !isValidToDraw(mViewModel, trapezoidIndex)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mOnSelectListener != null) {
|
|
||||||
// Selects all if users click the same trapezoid item two times.
|
|
||||||
mOnSelectListener.onSelect(
|
|
||||||
trapezoidIndex == mViewModel.selectedIndex()
|
|
||||||
? BatteryChartViewModel.SELECTED_INDEX_ALL : trapezoidIndex);
|
|
||||||
}
|
|
||||||
view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow();
|
|
||||||
updateClickableState();
|
|
||||||
mContext.getSystemService(AccessibilityManager.class)
|
|
||||||
.addAccessibilityStateChangeListener(/*listener=*/ this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDetachedFromWindow() {
|
|
||||||
super.onDetachedFromWindow();
|
|
||||||
mContext.getSystemService(AccessibilityManager.class)
|
|
||||||
.removeAccessibilityStateChangeListener(/*listener=*/ this);
|
|
||||||
mHandler.removeCallbacks(mUpdateClickableStateRun);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAccessibilityStateChanged(boolean enabled) {
|
|
||||||
Log.d(TAG, "onAccessibilityStateChanged:" + enabled);
|
|
||||||
mHandler.removeCallbacks(mUpdateClickableStateRun);
|
|
||||||
// We should delay it a while since accessibility manager will spend
|
|
||||||
// some times to bind with new enabled accessibility services.
|
|
||||||
mHandler.postDelayed(
|
|
||||||
mUpdateClickableStateRun, UPDATE_STATE_DELAYED_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateClickableState() {
|
|
||||||
final Context context = mContext;
|
|
||||||
mIsSlotsClickabled =
|
|
||||||
FeatureFactory.getFactory(context)
|
|
||||||
.getPowerUsageFeatureProvider(context)
|
|
||||||
.isChartGraphSlotsEnabled(context)
|
|
||||||
&& !isAccessibilityEnabled(context);
|
|
||||||
Log.d(TAG, "isChartGraphSlotsEnabled:" + mIsSlotsClickabled);
|
|
||||||
setClickable(isClickable());
|
|
||||||
// Initializes the trapezoid curve paint for non-clickable case.
|
|
||||||
if (!mIsSlotsClickabled && mTrapezoidCurvePaint == null) {
|
|
||||||
mTrapezoidCurvePaint = new Paint();
|
|
||||||
mTrapezoidCurvePaint.setAntiAlias(true);
|
|
||||||
mTrapezoidCurvePaint.setColor(mTrapezoidSolidColor);
|
|
||||||
mTrapezoidCurvePaint.setStyle(Paint.Style.STROKE);
|
|
||||||
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
|
|
||||||
} else if (mIsSlotsClickabled) {
|
|
||||||
mTrapezoidCurvePaint = null;
|
|
||||||
// Sets view model again to force update the click state.
|
|
||||||
setViewModel(mViewModel);
|
|
||||||
}
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setClickable(boolean clickable) {
|
|
||||||
super.setClickable(mIsSlotsClickabled && clickable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void setClickableForce(boolean clickable) {
|
|
||||||
super.setClickable(clickable);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeTrapezoidSlots(int count) {
|
|
||||||
mTrapezoidSlots = new TrapezoidSlot[count];
|
|
||||||
for (int index = 0; index < mTrapezoidSlots.length; index++) {
|
|
||||||
mTrapezoidSlots[index] = new TrapezoidSlot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeColors(Context context) {
|
|
||||||
setBackgroundColor(Color.TRANSPARENT);
|
|
||||||
mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
|
|
||||||
mTrapezoidColor = Utils.getDisabled(context, mTrapezoidSolidColor);
|
|
||||||
mTrapezoidHoverColor = Utils.getColorAttrDefaultColor(context,
|
|
||||||
com.android.internal.R.attr.colorAccentSecondaryVariant);
|
|
||||||
// Initializes the divider line paint.
|
|
||||||
final Resources resources = getContext().getResources();
|
|
||||||
mDividerWidth = resources.getDimensionPixelSize(R.dimen.chartview_divider_width);
|
|
||||||
mDividerHeight = resources.getDimensionPixelSize(R.dimen.chartview_divider_height);
|
|
||||||
mDividerPaint = new Paint();
|
|
||||||
mDividerPaint.setAntiAlias(true);
|
|
||||||
mDividerPaint.setColor(DIVIDER_COLOR);
|
|
||||||
mDividerPaint.setStyle(Paint.Style.STROKE);
|
|
||||||
mDividerPaint.setStrokeWidth(mDividerWidth);
|
|
||||||
Log.i(TAG, "mDividerWidth:" + mDividerWidth);
|
|
||||||
Log.i(TAG, "mDividerHeight:" + mDividerHeight);
|
|
||||||
// Initializes the trapezoid paint.
|
|
||||||
mTrapezoidHOffset = resources.getDimension(R.dimen.chartview_trapezoid_margin_start);
|
|
||||||
mTrapezoidVOffset = resources.getDimension(R.dimen.chartview_trapezoid_margin_bottom);
|
|
||||||
mTrapezoidPaint = new Paint();
|
|
||||||
mTrapezoidPaint.setAntiAlias(true);
|
|
||||||
mTrapezoidPaint.setColor(mTrapezoidSolidColor);
|
|
||||||
mTrapezoidPaint.setStyle(Paint.Style.FILL);
|
|
||||||
mTrapezoidPaint.setPathEffect(
|
|
||||||
new CornerPathEffect(
|
|
||||||
resources.getDimensionPixelSize(R.dimen.chartview_trapezoid_radius)));
|
|
||||||
// Initializes for drawing text information.
|
|
||||||
mTextPadding = resources.getDimensionPixelSize(R.dimen.chartview_text_padding);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawHorizontalDividers(Canvas canvas) {
|
|
||||||
final int width = getWidth() - mIndent.right;
|
|
||||||
final int height = getHeight() - mIndent.top - mIndent.bottom;
|
|
||||||
// Draws the top divider line for 100% curve.
|
|
||||||
float offsetY = mIndent.top + mDividerWidth * .5f;
|
|
||||||
canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
|
|
||||||
drawPercentage(canvas, /*index=*/ 0, offsetY);
|
|
||||||
|
|
||||||
// Draws the center divider line for 50% curve.
|
|
||||||
final float availableSpace =
|
|
||||||
height - mDividerWidth * 2 - mTrapezoidVOffset - mDividerHeight;
|
|
||||||
offsetY = mIndent.top + mDividerWidth + availableSpace * .5f;
|
|
||||||
canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
|
|
||||||
drawPercentage(canvas, /*index=*/ 1, offsetY);
|
|
||||||
|
|
||||||
// Draws the bottom divider line for 0% curve.
|
|
||||||
offsetY = mIndent.top + (height - mDividerHeight - mDividerWidth * .5f);
|
|
||||||
canvas.drawLine(0, offsetY, width, offsetY, mDividerPaint);
|
|
||||||
drawPercentage(canvas, /*index=*/ 2, offsetY);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawPercentage(Canvas canvas, int index, float offsetY) {
|
|
||||||
if (mTextPaint != null) {
|
|
||||||
mTextPaint.setTextAlign(Paint.Align.RIGHT);
|
|
||||||
canvas.drawText(
|
|
||||||
mPercentages[index],
|
|
||||||
getWidth(),
|
|
||||||
offsetY + mPercentageBounds[index].height() * .5f,
|
|
||||||
mTextPaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawVerticalDividers(Canvas canvas) {
|
|
||||||
final int width = getWidth() - mIndent.right;
|
|
||||||
final int dividerCount = mTrapezoidSlots.length + 1;
|
|
||||||
final float dividerSpace = dividerCount * mDividerWidth;
|
|
||||||
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;
|
|
||||||
// Draws each vertical dividers.
|
|
||||||
float startX = mDividerWidth * .5f;
|
|
||||||
for (int index = 0; index < dividerCount; index++) {
|
|
||||||
canvas.drawLine(startX, startY, startX, bottomY, mDividerPaint);
|
|
||||||
final float nextX = startX + mDividerWidth + unitWidth;
|
|
||||||
// Updates the trapezoid slots for drawing.
|
|
||||||
if (index < mTrapezoidSlots.length) {
|
|
||||||
mTrapezoidSlots[index].mLeft = round(startX + trapezoidSlotOffset);
|
|
||||||
mTrapezoidSlots[index].mRight = round(nextX - trapezoidSlotOffset);
|
|
||||||
}
|
|
||||||
startX = nextX;
|
|
||||||
}
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
drawAxisLabels(canvas, axisLabelDisplayAreas, baselineY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pairly draws axis labels from left and right side to middle. If the pair of labels have
|
|
||||||
* any overlap, skips that pair of labels.
|
|
||||||
*/
|
|
||||||
private void drawAxisLabels(Canvas canvas, final Rect[] displayAreas, final float baselineY) {
|
|
||||||
int forwardCheckLine = Integer.MIN_VALUE;
|
|
||||||
int backwardCheckLine = Integer.MAX_VALUE;
|
|
||||||
Rect middleDisplayArea = null;
|
|
||||||
for (int forwardIndex = 0, backwordIndex = displayAreas.length - 1;
|
|
||||||
forwardIndex <= backwordIndex; forwardIndex++, backwordIndex--) {
|
|
||||||
final Rect forwardDisplayArea = displayAreas[forwardIndex];
|
|
||||||
final Rect backwardDisplayArea = displayAreas[backwordIndex];
|
|
||||||
if (forwardDisplayArea.left < forwardCheckLine
|
|
||||||
|| backwardDisplayArea.right > backwardCheckLine) {
|
|
||||||
// Overlapped at left or right, skip the pair of labels
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (middleDisplayArea != null && (
|
|
||||||
forwardDisplayArea.right + mTextPadding > middleDisplayArea.left
|
|
||||||
|| backwardDisplayArea.left - mTextPadding < middleDisplayArea.right)) {
|
|
||||||
// Overlapped with the middle label.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (forwardIndex != backwordIndex
|
|
||||||
&& forwardDisplayArea.right + mTextPadding > backwardDisplayArea.left) {
|
|
||||||
// Overlapped in the middle, skip the pair of labels
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
drawAxisLabelText(canvas, forwardIndex, forwardDisplayArea, baselineY);
|
|
||||||
drawAxisLabelText(canvas, backwordIndex, backwardDisplayArea, baselineY);
|
|
||||||
|
|
||||||
forwardCheckLine = forwardDisplayArea.right + mTextPadding;
|
|
||||||
backwardCheckLine = backwardDisplayArea.left - mTextPadding;
|
|
||||||
|
|
||||||
// If the number of labels is odd, draw the middle label first
|
|
||||||
if (forwardIndex == 0 && backwordIndex % 2 == 0) {
|
|
||||||
final int middleIndex = backwordIndex / 2;
|
|
||||||
middleDisplayArea = displayAreas[middleIndex];
|
|
||||||
if (middleDisplayArea.left < forwardCheckLine
|
|
||||||
|| middleDisplayArea.right > backwardCheckLine) {
|
|
||||||
// Overlapped at left or right, skip the pair of labels
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
drawAxisLabelText(canvas, middleIndex, middleDisplayArea, baselineY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (mViewModel == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final float trapezoidBottom =
|
|
||||||
getHeight() - mIndent.bottom - mDividerHeight - mDividerWidth
|
|
||||||
- mTrapezoidVOffset;
|
|
||||||
final float availableSpace = trapezoidBottom - mDividerWidth * .5f - mIndent.top;
|
|
||||||
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 < mTrapezoidSlots.length; index++) {
|
|
||||||
// Not draws the trapezoid for corner or not initialization cases.
|
|
||||||
if (!isValidToDraw(mViewModel, index)) {
|
|
||||||
if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) {
|
|
||||||
canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint);
|
|
||||||
trapezoidCurvePath = null;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Configures the trapezoid paint color.
|
|
||||||
final int trapezoidColor = mIsSlotsClickabled && (mViewModel.selectedIndex() == index
|
|
||||||
|| mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL)
|
|
||||||
? mTrapezoidSolidColor : mTrapezoidColor;
|
|
||||||
final boolean isHoverState =
|
|
||||||
mIsSlotsClickabled && mHoveredIndex == index
|
|
||||||
&& isValidToDraw(mViewModel, mHoveredIndex);
|
|
||||||
mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor);
|
|
||||||
|
|
||||||
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);
|
|
||||||
trapezoidPath.lineTo(mTrapezoidSlots[index].mRight, rightTop);
|
|
||||||
trapezoidPath.lineTo(mTrapezoidSlots[index].mRight, trapezoidBottom);
|
|
||||||
// A tricky way to make the trapezoid shape drawing the rounded corner.
|
|
||||||
trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, trapezoidBottom);
|
|
||||||
trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop);
|
|
||||||
// Draws the trapezoid shape into canvas.
|
|
||||||
canvas.drawPath(trapezoidPath, mTrapezoidPaint);
|
|
||||||
|
|
||||||
// Generates path for non-clickable trapezoid curve.
|
|
||||||
if (mTrapezoidCurvePaint != null) {
|
|
||||||
if (trapezoidCurvePath == null) {
|
|
||||||
trapezoidCurvePath = new Path();
|
|
||||||
trapezoidCurvePath.moveTo(mTrapezoidSlots[index].mLeft, leftTop);
|
|
||||||
} else {
|
|
||||||
trapezoidCurvePath.lineTo(mTrapezoidSlots[index].mLeft, leftTop);
|
|
||||||
}
|
|
||||||
trapezoidCurvePath.lineTo(mTrapezoidSlots[index].mRight, rightTop);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Draws the trapezoid curve for non-clickable case.
|
|
||||||
if (mTrapezoidCurvePaint != null && trapezoidCurvePath != null) {
|
|
||||||
canvas.drawPath(trapezoidCurvePath, mTrapezoidCurvePaint);
|
|
||||||
trapezoidCurvePath = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Searches the corresponding trapezoid index from x location.
|
|
||||||
private int getTrapezoidIndex(float x) {
|
|
||||||
for (int index = 0; index < mTrapezoidSlots.length; index++) {
|
|
||||||
final TrapezoidSlot slot = mTrapezoidSlots[index];
|
|
||||||
if (x >= slot.mLeft - mTrapezoidHOffset
|
|
||||||
&& x <= slot.mRight + mTrapezoidHOffset) {
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 < 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() {
|
|
||||||
return new String[]{
|
|
||||||
formatPercentage(/*percentage=*/ 100, /*round=*/ true),
|
|
||||||
formatPercentage(/*percentage=*/ 50, /*round=*/ true),
|
|
||||||
formatPercentage(/*percentage=*/ 0, /*round=*/ true)};
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static boolean isAccessibilityEnabled(Context context) {
|
|
||||||
final AccessibilityManager accessibilityManager =
|
|
||||||
context.getSystemService(AccessibilityManager.class);
|
|
||||||
if (!accessibilityManager.isEnabled()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final List<AccessibilityServiceInfo> serviceInfoList =
|
|
||||||
accessibilityManager.getEnabledAccessibilityServiceList(
|
|
||||||
AccessibilityServiceInfo.FEEDBACK_SPOKEN
|
|
||||||
| AccessibilityServiceInfo.FEEDBACK_GENERIC);
|
|
||||||
for (AccessibilityServiceInfo info : serviceInfoList) {
|
|
||||||
for (String serviceName : ACCESSIBILITY_SERVICE_NAMES) {
|
|
||||||
final String serviceId = info.getId();
|
|
||||||
if (serviceId != null && serviceId.contains(serviceName)) {
|
|
||||||
Log.d(TAG, "acccessibilityEnabled:" + serviceId);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A container class for each trapezoid left and right location.
|
|
||||||
@VisibleForTesting
|
|
||||||
static final class TrapezoidSlot {
|
|
||||||
public float mLeft;
|
|
||||||
public float mRight;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format(Locale.US, "TrapezoidSlot[%f,%f]", mLeft, mRight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -43,6 +43,6 @@ public class BatteryHistoryLoader
|
|||||||
public Map<Long, Map<String, BatteryHistEntry>> loadInBackground() {
|
public Map<Long, Map<String, BatteryHistEntry>> loadInBackground() {
|
||||||
final PowerUsageFeatureProvider powerUsageFeatureProvider =
|
final PowerUsageFeatureProvider powerUsageFeatureProvider =
|
||||||
FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(mContext);
|
FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(mContext);
|
||||||
return powerUsageFeatureProvider.getBatteryHistory(mContext);
|
return powerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -49,7 +49,8 @@ public class BatteryHistoryPreference extends Preference {
|
|||||||
|
|
||||||
private TextView mSummaryView;
|
private TextView mSummaryView;
|
||||||
private CharSequence mSummaryContent;
|
private CharSequence mSummaryContent;
|
||||||
private BatteryChartView mBatteryChartView;
|
private BatteryChartView mDailyChartView;
|
||||||
|
private BatteryChartView mHourlyChartView;
|
||||||
private BatteryChartPreferenceController mChartPreferenceController;
|
private BatteryChartPreferenceController mChartPreferenceController;
|
||||||
|
|
||||||
public BatteryHistoryPreference(Context context, AttributeSet attrs) {
|
public BatteryHistoryPreference(Context context, AttributeSet attrs) {
|
||||||
@@ -92,8 +93,8 @@ public class BatteryHistoryPreference extends Preference {
|
|||||||
|
|
||||||
void setChartPreferenceController(BatteryChartPreferenceController controller) {
|
void setChartPreferenceController(BatteryChartPreferenceController controller) {
|
||||||
mChartPreferenceController = controller;
|
mChartPreferenceController = controller;
|
||||||
if (mBatteryChartView != null) {
|
if (mDailyChartView != null && mHourlyChartView != null) {
|
||||||
mChartPreferenceController.setBatteryChartView(mBatteryChartView);
|
mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,11 +106,14 @@ public class BatteryHistoryPreference extends Preference {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mIsChartGraphEnabled) {
|
if (mIsChartGraphEnabled) {
|
||||||
mBatteryChartView = (BatteryChartView) view.findViewById(R.id.battery_chart);
|
mDailyChartView = (BatteryChartView) view.findViewById(R.id.daily_battery_chart);
|
||||||
mBatteryChartView.setCompanionTextView(
|
mDailyChartView.setCompanionTextView(
|
||||||
|
(TextView) view.findViewById(R.id.companion_text));
|
||||||
|
mHourlyChartView = (BatteryChartView) view.findViewById(R.id.hourly_battery_chart);
|
||||||
|
mHourlyChartView.setCompanionTextView(
|
||||||
(TextView) view.findViewById(R.id.companion_text));
|
(TextView) view.findViewById(R.id.companion_text));
|
||||||
if (mChartPreferenceController != null) {
|
if (mChartPreferenceController != null) {
|
||||||
mChartPreferenceController.setBatteryChartView(mBatteryChartView);
|
mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final TextView chargeView = (TextView) view.findViewById(R.id.charge);
|
final TextView chargeView = (TextView) view.findViewById(R.id.charge);
|
||||||
|
@@ -276,7 +276,7 @@ public final class ConvertUtils {
|
|||||||
diffEntry.setTotalConsumePower(totalConsumePower);
|
diffEntry.setTotalConsumePower(totalConsumePower);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
insert24HoursData(BatteryChartView.SELECTED_INDEX_ALL, resultMap);
|
insert24HoursData(BatteryChartViewModel.SELECTED_INDEX_ALL, resultMap);
|
||||||
resolveMultiUsersData(context, resultMap);
|
resolveMultiUsersData(context, resultMap);
|
||||||
if (purgeLowPercentageAndFakeData) {
|
if (purgeLowPercentageAndFakeData) {
|
||||||
purgeLowPercentageAndFakeData(context, resultMap);
|
purgeLowPercentageAndFakeData(context, resultMap);
|
||||||
|
@@ -19,9 +19,10 @@ package com.android.settings.fuelgauge.batteryusage;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
import static org.mockito.Mockito.anyLong;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.reset;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
@@ -33,6 +34,8 @@ import android.graphics.drawable.Drawable;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.LocaleList;
|
import android.os.LocaleList;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceCategory;
|
import androidx.preference.PreferenceCategory;
|
||||||
@@ -56,15 +59,15 @@ import org.robolectric.RuntimeEnvironment;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
public final class BatteryChartPreferenceControllerTest {
|
public final class BatteryChartPreferenceControllerTest {
|
||||||
private static final String PREF_KEY = "pref_key";
|
private static final String PREF_KEY = "pref_key";
|
||||||
private static final String PREF_SUMMARY = "fake preference summary";
|
private static final String PREF_SUMMARY = "fake preference summary";
|
||||||
private static final int DESIRED_HISTORY_SIZE =
|
|
||||||
BatteryChartPreferenceController.DESIRED_HISTORY_SIZE;
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private InstrumentedPreferenceFragment mFragment;
|
private InstrumentedPreferenceFragment mFragment;
|
||||||
@@ -77,11 +80,15 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private BatteryHistEntry mBatteryHistEntry;
|
private BatteryHistEntry mBatteryHistEntry;
|
||||||
@Mock
|
@Mock
|
||||||
private BatteryChartView mBatteryChartView;
|
private BatteryChartView mDailyChartView;
|
||||||
|
@Mock
|
||||||
|
private BatteryChartView mHourlyChartView;
|
||||||
@Mock
|
@Mock
|
||||||
private PowerGaugePreference mPowerGaugePreference;
|
private PowerGaugePreference mPowerGaugePreference;
|
||||||
@Mock
|
@Mock
|
||||||
private BatteryUtils mBatteryUtils;
|
private BatteryUtils mBatteryUtils;
|
||||||
|
@Mock
|
||||||
|
private LinearLayout.LayoutParams mLayoutParams;
|
||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private FakeFeatureFactory mFeatureFactory;
|
private FakeFeatureFactory mFeatureFactory;
|
||||||
@@ -94,6 +101,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
Locale.setDefault(new Locale("en_US"));
|
Locale.setDefault(new Locale("en_US"));
|
||||||
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
|
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
|
||||||
|
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
||||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||||
mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
|
mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
@@ -106,10 +114,12 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
doReturn(new String[]{"com.android.gms.persistent"})
|
doReturn(new String[]{"com.android.gms.persistent"})
|
||||||
.when(mFeatureFactory.powerUsageFeatureProvider)
|
.when(mFeatureFactory.powerUsageFeatureProvider)
|
||||||
.getHideApplicationEntries(mContext);
|
.getHideApplicationEntries(mContext);
|
||||||
|
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
|
||||||
mBatteryChartPreferenceController = createController();
|
mBatteryChartPreferenceController = createController();
|
||||||
mBatteryChartPreferenceController.mPrefContext = mContext;
|
mBatteryChartPreferenceController.mPrefContext = mContext;
|
||||||
mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup;
|
mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup;
|
||||||
mBatteryChartPreferenceController.mBatteryChartView = mBatteryChartView;
|
mBatteryChartPreferenceController.mDailyChartView = mDailyChartView;
|
||||||
|
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
|
||||||
mBatteryDiffEntry = new BatteryDiffEntry(
|
mBatteryDiffEntry = new BatteryDiffEntry(
|
||||||
mContext,
|
mContext,
|
||||||
/*foregroundUsageTimeInMs=*/ 1,
|
/*foregroundUsageTimeInMs=*/ 1,
|
||||||
@@ -121,12 +131,10 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
BatteryDiffEntry.sResourceCache.put(
|
BatteryDiffEntry.sResourceCache.put(
|
||||||
"fakeBatteryDiffEntryKey",
|
"fakeBatteryDiffEntryKey",
|
||||||
new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
|
new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
|
||||||
createBatteryHistoryMap());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnDestroy_activityIsChanging_clearBatteryEntryCache() {
|
public void onDestroy_activityIsChanging_clearBatteryEntryCache() {
|
||||||
doReturn(true).when(mSettingsActivity).isChangingConfigurations();
|
doReturn(true).when(mSettingsActivity).isChangingConfigurations();
|
||||||
// Ensures the testing environment is correct.
|
// Ensures the testing environment is correct.
|
||||||
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
|
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
|
||||||
@@ -136,7 +144,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnDestroy_activityIsNotChanging_notClearBatteryEntryCache() {
|
public void onDestroy_activityIsNotChanging_notClearBatteryEntryCache() {
|
||||||
doReturn(false).when(mSettingsActivity).isChangingConfigurations();
|
doReturn(false).when(mSettingsActivity).isChangingConfigurations();
|
||||||
// Ensures the testing environment is correct.
|
// Ensures the testing environment is correct.
|
||||||
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
|
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
|
||||||
@@ -146,7 +154,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnDestroy_clearPreferenceCache() {
|
public void onDestroy_clearPreferenceCache() {
|
||||||
// Ensures the testing environment is correct.
|
// Ensures the testing environment is correct.
|
||||||
mBatteryChartPreferenceController.mPreferenceCache.put(
|
mBatteryChartPreferenceController.mPreferenceCache.put(
|
||||||
PREF_KEY, mPowerGaugePreference);
|
PREF_KEY, mPowerGaugePreference);
|
||||||
@@ -158,113 +166,135 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnDestroy_removeAllPreferenceFromPreferenceGroup() {
|
public void onDestroy_removeAllPreferenceFromPreferenceGroup() {
|
||||||
mBatteryChartPreferenceController.onDestroy();
|
mBatteryChartPreferenceController.onDestroy();
|
||||||
verify(mAppListGroup).removeAll();
|
verify(mAppListGroup).removeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetBatteryHistoryMap_createExpectedKeysAndLevels() {
|
public void setBatteryChartViewModel_6Hours() {
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||||
createBatteryHistoryMap());
|
|
||||||
|
|
||||||
// Verifies the created battery keys array.
|
verify(mDailyChartView, atLeastOnce()).setVisibility(View.GONE);
|
||||||
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
|
verify(mHourlyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
|
||||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index])
|
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
|
||||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
List.of(100, 97, 95),
|
||||||
.isEqualTo(index + 1);
|
List.of("8 am", "10 am", "12 pm"),
|
||||||
}
|
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||||
// Verifies the created battery levels array.
|
|
||||||
for (int index = 0; index < 13; index++) {
|
|
||||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index])
|
|
||||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
|
||||||
.isEqualTo(100 - index * 2);
|
|
||||||
}
|
|
||||||
assertThat(mBatteryChartPreferenceController.mBatteryIndexedMap).hasSize(13);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetBatteryHistoryMap_largeSize_createExpectedKeysAndLevels() {
|
public void setBatteryChartViewModel_60Hours() {
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel(
|
||||||
createBatteryHistoryMap());
|
List.of(100, 83, 59, 41),
|
||||||
|
List.of("Sat", "Sun", "Mon", "Mon"),
|
||||||
|
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS);
|
||||||
|
|
||||||
// Verifies the created battery keys array.
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
||||||
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
|
|
||||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index])
|
verify(mDailyChartView, atLeastOnce()).setVisibility(View.VISIBLE);
|
||||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
verify(mHourlyChartView, atLeastOnce()).setVisibility(View.GONE);
|
||||||
.isEqualTo(index + 1);
|
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
|
||||||
}
|
|
||||||
// Verifies the created battery levels array.
|
reset(mDailyChartView);
|
||||||
for (int index = 0; index < 13; index++) {
|
reset(mHourlyChartView);
|
||||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index])
|
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
|
||||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
mBatteryChartPreferenceController.mDailyChartIndex = 0;
|
||||||
.isEqualTo(100 - index * 2);
|
mBatteryChartPreferenceController.refreshUi();
|
||||||
}
|
verify(mDailyChartView).setVisibility(View.VISIBLE);
|
||||||
assertThat(mBatteryChartPreferenceController.mBatteryIndexedMap).hasSize(13);
|
verify(mHourlyChartView).setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
expectedDailyViewModel.setSelectedIndex(0);
|
||||||
|
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
|
||||||
|
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
|
||||||
|
List.of(100, 97, 95, 93, 91, 89, 87, 85, 83),
|
||||||
|
List.of("8 am", "10 am", "12 pm", "2 pm", "4 pm", "6 pm", "8 pm", "10 pm",
|
||||||
|
"12 am"),
|
||||||
|
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||||
|
|
||||||
|
reset(mDailyChartView);
|
||||||
|
reset(mHourlyChartView);
|
||||||
|
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
|
||||||
|
mBatteryChartPreferenceController.mDailyChartIndex = 1;
|
||||||
|
mBatteryChartPreferenceController.mHourlyChartIndex = 6;
|
||||||
|
mBatteryChartPreferenceController.refreshUi();
|
||||||
|
verify(mDailyChartView).setVisibility(View.VISIBLE);
|
||||||
|
verify(mHourlyChartView).setVisibility(View.VISIBLE);
|
||||||
|
expectedDailyViewModel.setSelectedIndex(1);
|
||||||
|
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
|
||||||
|
BatteryChartViewModel expectedHourlyViewModel = new BatteryChartViewModel(
|
||||||
|
List.of(83, 81, 79, 77, 75, 73, 71, 69, 67, 65, 63, 61, 59),
|
||||||
|
List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm",
|
||||||
|
"4 pm", "6 pm", "8 pm", "10 pm", "12 am"),
|
||||||
|
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS);
|
||||||
|
expectedHourlyViewModel.setSelectedIndex(6);
|
||||||
|
verify(mHourlyChartView).setViewModel(expectedHourlyViewModel);
|
||||||
|
|
||||||
|
reset(mDailyChartView);
|
||||||
|
reset(mHourlyChartView);
|
||||||
|
doReturn(mLayoutParams).when(mDailyChartView).getLayoutParams();
|
||||||
|
mBatteryChartPreferenceController.mDailyChartIndex = 2;
|
||||||
|
mBatteryChartPreferenceController.mHourlyChartIndex =
|
||||||
|
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
mBatteryChartPreferenceController.refreshUi();
|
||||||
|
verify(mDailyChartView).setVisibility(View.VISIBLE);
|
||||||
|
verify(mHourlyChartView).setVisibility(View.VISIBLE);
|
||||||
|
expectedDailyViewModel.setSelectedIndex(2);
|
||||||
|
verify(mDailyChartView).setViewModel(expectedDailyViewModel);
|
||||||
|
verify(mHourlyChartView).setViewModel(new BatteryChartViewModel(
|
||||||
|
List.of(59, 57, 55, 53, 51, 49, 47, 45, 43, 41),
|
||||||
|
List.of("12 am", "2 am", "4 am", "6 am", "8 am", "10 am", "12 pm", "2 pm",
|
||||||
|
"4 pm", "6 pm"),
|
||||||
|
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRefreshUi_batteryIndexedMapIsNull_ignoreRefresh() {
|
public void refreshUi_normalCase_returnTrue() {
|
||||||
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||||
|
assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void refreshUi_batteryIndexedMapIsNull_ignoreRefresh() {
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(null);
|
mBatteryChartPreferenceController.setBatteryHistoryMap(null);
|
||||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||||
/*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRefreshUi_batteryChartViewIsNull_ignoreRefresh() {
|
public void refreshUi_dailyChartViewIsNull_ignoreRefresh() {
|
||||||
mBatteryChartPreferenceController.mBatteryChartView = null;
|
mBatteryChartPreferenceController.mDailyChartView = null;
|
||||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||||
/*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRefreshUi_trapezoidIndexIsNotChanged_ignoreRefresh() {
|
public void refreshUi_hourlyChartViewIsNull_ignoreRefresh() {
|
||||||
final int trapezoidIndex = 1;
|
mBatteryChartPreferenceController.mHourlyChartView = null;
|
||||||
mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex;
|
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
|
||||||
trapezoidIndex, /*isForce=*/ false)).isFalse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRefreshUi_forceUpdate_refreshUi() {
|
public void removeAndCacheAllPrefs_emptyContent_ignoreRemoveAll() {
|
||||||
final int trapezoidIndex = 1;
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||||
mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex;
|
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
||||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
|
||||||
trapezoidIndex, /*isForce=*/ true)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testForceRefreshUi_updateTrapezoidIndexIntoSelectAll() {
|
|
||||||
mBatteryChartPreferenceController.mTrapezoidIndex =
|
|
||||||
BatteryChartView.SELECTED_INDEX_INVALID;
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
|
||||||
createBatteryHistoryMap());
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.mTrapezoidIndex)
|
|
||||||
.isEqualTo(BatteryChartView.SELECTED_INDEX_ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRemoveAndCacheAllPrefs_emptyContent_ignoreRemoveAll() {
|
|
||||||
final int trapezoidIndex = 1;
|
|
||||||
doReturn(0).when(mAppListGroup).getPreferenceCount();
|
doReturn(0).when(mAppListGroup).getPreferenceCount();
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshUi(
|
mBatteryChartPreferenceController.refreshUi();
|
||||||
trapezoidIndex, /*isForce=*/ true);
|
|
||||||
verify(mAppListGroup, never()).removeAll();
|
verify(mAppListGroup, never()).removeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveAndCacheAllPrefs_buildCacheAndRemoveAllPreference() {
|
public void removeAndCacheAllPrefs_buildCacheAndRemoveAllPreference() {
|
||||||
final int trapezoidIndex = 1;
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||||
|
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0);
|
doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0);
|
||||||
|
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
||||||
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
|
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
|
||||||
|
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
||||||
// Ensures the testing data is correct.
|
// Ensures the testing data is correct.
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshUi(
|
mBatteryChartPreferenceController.refreshUi();
|
||||||
trapezoidIndex, /*isForce=*/ true);
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY))
|
assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY))
|
||||||
.isEqualTo(mPowerGaugePreference);
|
.isEqualTo(mPowerGaugePreference);
|
||||||
@@ -272,14 +302,14 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddPreferenceToScreen_emptyContent_ignoreAddPreference() {
|
public void addPreferenceToScreen_emptyContent_ignoreAddPreference() {
|
||||||
mBatteryChartPreferenceController.addPreferenceToScreen(
|
mBatteryChartPreferenceController.addPreferenceToScreen(
|
||||||
new ArrayList<BatteryDiffEntry>());
|
new ArrayList<BatteryDiffEntry>());
|
||||||
verify(mAppListGroup, never()).addPreference(any());
|
verify(mAppListGroup, never()).addPreference(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddPreferenceToScreen_addPreferenceIntoScreen() {
|
public void addPreferenceToScreen_addPreferenceIntoScreen() {
|
||||||
final String appLabel = "fake app label";
|
final String appLabel = "fake app label";
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
||||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
||||||
@@ -308,7 +338,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
|
public void addPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
|
||||||
final String appLabel = "fake app label";
|
final String appLabel = "fake app label";
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
||||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
||||||
@@ -323,7 +353,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHandlePreferenceTreeiClick_notPowerGaugePreference_returnFalse() {
|
public void handlePreferenceTreeClick_notPowerGaugePreference_returnFalse() {
|
||||||
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(mAppListGroup))
|
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(mAppListGroup))
|
||||||
.isFalse();
|
.isFalse();
|
||||||
|
|
||||||
@@ -334,7 +364,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHandlePreferenceTreeClick_forAppEntry_returnTrue() {
|
public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
|
||||||
doReturn(false).when(mBatteryHistEntry).isAppEntry();
|
doReturn(false).when(mBatteryHistEntry).isAppEntry();
|
||||||
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
||||||
|
|
||||||
@@ -350,7 +380,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHandlePreferenceTreeClick_forSystemEntry_returnTrue() {
|
public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
|
||||||
mBatteryChartPreferenceController.mBatteryUtils = mBatteryUtils;
|
mBatteryChartPreferenceController.mBatteryUtils = mBatteryUtils;
|
||||||
doReturn(true).when(mBatteryHistEntry).isAppEntry();
|
doReturn(true).when(mBatteryHistEntry).isAppEntry();
|
||||||
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
||||||
@@ -367,7 +397,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
|
public void setPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
pref.setSummary(PREF_SUMMARY);
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
@@ -379,7 +409,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetPreferenceSummary_setBackgroundUsageTimeOnly() {
|
public void setPreferenceSummary_setBackgroundUsageTimeOnly() {
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
pref.setSummary(PREF_SUMMARY);
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
@@ -391,7 +421,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
|
public void setPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
pref.setSummary(PREF_SUMMARY);
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
@@ -403,7 +433,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
|
public void setPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
pref.setSummary(PREF_SUMMARY);
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
@@ -416,7 +446,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetPreferenceSummary_setTotalAndBackgroundUsageTime() {
|
public void setPreferenceSummary_setTotalAndBackgroundUsageTime() {
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
pref.setSummary(PREF_SUMMARY);
|
pref.setSummary(PREF_SUMMARY);
|
||||||
|
|
||||||
@@ -428,7 +458,7 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetPreferenceSummary_notAllowShownPackage_setSummayAsNull() {
|
public void setPreferenceSummary_notAllowShownPackage_setSummayAsNull() {
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
||||||
pref.setSummary(PREF_SUMMARY);
|
pref.setSummary(PREF_SUMMARY);
|
||||||
final BatteryDiffEntry batteryDiffEntry =
|
final BatteryDiffEntry batteryDiffEntry =
|
||||||
@@ -443,36 +473,9 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateUsageTime_returnTrueIfBatteryDiffEntryIsValid() {
|
public void onExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() {
|
||||||
assertThat(BatteryChartPreferenceController.validateUsageTime(
|
|
||||||
createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS)))
|
|
||||||
.isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateUsageTime_foregroundTimeExceedThreshold_returnFalse() {
|
|
||||||
assertThat(BatteryChartPreferenceController.validateUsageTime(
|
|
||||||
createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ DateUtils.HOUR_IN_MILLIS * 3,
|
|
||||||
/*backgroundUsageTimeInMs=*/ 0)))
|
|
||||||
.isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateUsageTime_backgroundTimeExceedThreshold_returnFalse() {
|
|
||||||
assertThat(BatteryChartPreferenceController.validateUsageTime(
|
|
||||||
createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ 0,
|
|
||||||
/*backgroundUsageTimeInMs=*/ DateUtils.HOUR_IN_MILLIS * 3)))
|
|
||||||
.isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() {
|
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
||||||
mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry);
|
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
||||||
doReturn("label").when(mBatteryDiffEntry).getAppLabel();
|
doReturn("label").when(mBatteryDiffEntry).getAppLabel();
|
||||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
||||||
@@ -491,10 +494,10 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() {
|
public void onExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() {
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
||||||
mBatteryChartPreferenceController.mSystemEntries.add(mBatteryDiffEntry);
|
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
||||||
// Verifies the cache is empty first.
|
// Verifies the cache is empty first.
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
||||||
|
|
||||||
@@ -511,57 +514,17 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnSelect_selectSpecificTimeSlot_logMetric() {
|
public void refreshCategoryTitle_setLastFullChargeIntoBothTitleTextView() {
|
||||||
mBatteryChartPreferenceController.onSelect(1 /*slot index*/);
|
|
||||||
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnSelect_selectAll_logMetric() {
|
|
||||||
mBatteryChartPreferenceController.onSelect(
|
|
||||||
BatteryChartView.SELECTED_INDEX_ALL /*slot index*/);
|
|
||||||
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRefreshCategoryTitle_setHourIntoBothTitleTextView() {
|
|
||||||
mBatteryChartPreferenceController = createController();
|
|
||||||
setUpBatteryHistoryKeys();
|
|
||||||
mBatteryChartPreferenceController.mAppListPrefGroup =
|
|
||||||
spy(new PreferenceCategory(mContext));
|
|
||||||
mBatteryChartPreferenceController.mExpandDividerPreference =
|
|
||||||
spy(new ExpandDividerPreference(mContext));
|
|
||||||
// Simulates select the first slot.
|
|
||||||
mBatteryChartPreferenceController.mTrapezoidIndex = 0;
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshCategoryTitle();
|
|
||||||
|
|
||||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
|
||||||
// Verifies the title in the preference group.
|
|
||||||
verify(mBatteryChartPreferenceController.mAppListPrefGroup)
|
|
||||||
.setTitle(captor.capture());
|
|
||||||
assertThat(captor.getValue()).isNotEqualTo("App usage for past 24 hr");
|
|
||||||
// Verifies the title in the expandable divider.
|
|
||||||
captor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(mBatteryChartPreferenceController.mExpandDividerPreference)
|
|
||||||
.setTitle(captor.capture());
|
|
||||||
assertThat(captor.getValue()).isNotEqualTo("System usage for past 24 hr");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRefreshCategoryTitle_setLast24HrIntoBothTitleTextView() {
|
|
||||||
mBatteryChartPreferenceController = createController();
|
mBatteryChartPreferenceController = createController();
|
||||||
mBatteryChartPreferenceController.mAppListPrefGroup =
|
mBatteryChartPreferenceController.mAppListPrefGroup =
|
||||||
spy(new PreferenceCategory(mContext));
|
spy(new PreferenceCategory(mContext));
|
||||||
mBatteryChartPreferenceController.mExpandDividerPreference =
|
mBatteryChartPreferenceController.mExpandDividerPreference =
|
||||||
spy(new ExpandDividerPreference(mContext));
|
spy(new ExpandDividerPreference(mContext));
|
||||||
// Simulates select all condition.
|
// Simulates select all condition.
|
||||||
mBatteryChartPreferenceController.mTrapezoidIndex =
|
mBatteryChartPreferenceController.mDailyChartIndex =
|
||||||
BatteryChartView.SELECTED_INDEX_ALL;
|
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
mBatteryChartPreferenceController.mHourlyChartIndex =
|
||||||
|
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshCategoryTitle();
|
mBatteryChartPreferenceController.refreshCategoryTitle();
|
||||||
|
|
||||||
@@ -570,76 +533,93 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
verify(mBatteryChartPreferenceController.mAppListPrefGroup)
|
verify(mBatteryChartPreferenceController.mAppListPrefGroup)
|
||||||
.setTitle(captor.capture());
|
.setTitle(captor.capture());
|
||||||
assertThat(captor.getValue())
|
assertThat(captor.getValue())
|
||||||
.isEqualTo("App usage for past 24 hr");
|
.isEqualTo("App usage since last full charge");
|
||||||
// Verifies the title in the expandable divider.
|
// Verifies the title in the expandable divider.
|
||||||
captor = ArgumentCaptor.forClass(String.class);
|
captor = ArgumentCaptor.forClass(String.class);
|
||||||
verify(mBatteryChartPreferenceController.mExpandDividerPreference)
|
verify(mBatteryChartPreferenceController.mExpandDividerPreference)
|
||||||
.setTitle(captor.capture());
|
.setTitle(captor.capture());
|
||||||
assertThat(captor.getValue())
|
assertThat(captor.getValue())
|
||||||
.isEqualTo("System usage for past 24 hr");
|
.isEqualTo("System usage since last full charge");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetTimestampLabel_nullBatteryHistoryKeys_ignore() {
|
public void selectedSlotText_selectAllDaysAllHours_returnNull() {
|
||||||
mBatteryChartPreferenceController = createController();
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
||||||
mBatteryChartPreferenceController.mBatteryHistoryKeys = null;
|
mBatteryChartPreferenceController.mDailyChartIndex =
|
||||||
mBatteryChartPreferenceController.mBatteryChartView =
|
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
spy(new BatteryChartView(mContext));
|
mBatteryChartPreferenceController.mHourlyChartIndex =
|
||||||
mBatteryChartPreferenceController.setTimestampLabel();
|
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
|
||||||
verify(mBatteryChartPreferenceController.mBatteryChartView, never())
|
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
|
||||||
.setLatestTimestamp(anyLong());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetTimestampLabel_setExpectedTimestampData() {
|
public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() {
|
||||||
mBatteryChartPreferenceController = createController();
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||||
mBatteryChartPreferenceController.mBatteryChartView =
|
mBatteryChartPreferenceController.mDailyChartIndex = 0;
|
||||||
spy(new BatteryChartView(mContext));
|
mBatteryChartPreferenceController.mHourlyChartIndex =
|
||||||
setUpBatteryHistoryKeys();
|
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setTimestampLabel();
|
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
|
||||||
|
|
||||||
verify(mBatteryChartPreferenceController.mBatteryChartView)
|
|
||||||
.setLatestTimestamp(1619247636826L);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetTimestampLabel_withoutValidTimestamp_setExpectedTimestampData() {
|
public void selectedSlotText_selectADayAllHours_onlyDayText() {
|
||||||
mBatteryChartPreferenceController = createController();
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
||||||
mBatteryChartPreferenceController.mBatteryChartView =
|
mBatteryChartPreferenceController.mDailyChartIndex = 1;
|
||||||
spy(new BatteryChartView(mContext));
|
mBatteryChartPreferenceController.mHourlyChartIndex =
|
||||||
mBatteryChartPreferenceController.mBatteryHistoryKeys = new long[]{0L};
|
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setTimestampLabel();
|
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo("Sunday");
|
||||||
|
|
||||||
verify(mBatteryChartPreferenceController.mBatteryChartView)
|
|
||||||
.setLatestTimestamp(anyLong());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnSaveInstanceState_restoreSelectedIndexAndExpandState() {
|
public void selectedSlotText_onlyOneDayDataSelectAnHour_onlyHourText() {
|
||||||
final int expectedIndex = 1;
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
||||||
|
mBatteryChartPreferenceController.mDailyChartIndex = 0;
|
||||||
|
mBatteryChartPreferenceController.mHourlyChartIndex = 1;
|
||||||
|
|
||||||
|
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(
|
||||||
|
"10 am - 12 pm");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void selectedSlotText_SelectADayAnHour_dayAndHourText() {
|
||||||
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
||||||
|
mBatteryChartPreferenceController.mDailyChartIndex = 1;
|
||||||
|
mBatteryChartPreferenceController.mHourlyChartIndex = 8;
|
||||||
|
|
||||||
|
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(
|
||||||
|
"Sunday 4 pm - 6 pm");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void onSaveInstanceState_restoreSelectedIndexAndExpandState() {
|
||||||
|
final int expectedDailyIndex = 1;
|
||||||
|
final int expectedHourlyIndex = 2;
|
||||||
final boolean isExpanded = true;
|
final boolean isExpanded = true;
|
||||||
final Bundle bundle = new Bundle();
|
final Bundle bundle = new Bundle();
|
||||||
mBatteryChartPreferenceController.mTrapezoidIndex = expectedIndex;
|
mBatteryChartPreferenceController.mDailyChartIndex = expectedDailyIndex;
|
||||||
|
mBatteryChartPreferenceController.mHourlyChartIndex = expectedHourlyIndex;
|
||||||
mBatteryChartPreferenceController.mIsExpanded = isExpanded;
|
mBatteryChartPreferenceController.mIsExpanded = isExpanded;
|
||||||
mBatteryChartPreferenceController.onSaveInstanceState(bundle);
|
mBatteryChartPreferenceController.onSaveInstanceState(bundle);
|
||||||
// Replaces the original controller with other values.
|
// Replaces the original controller with other values.
|
||||||
mBatteryChartPreferenceController.mTrapezoidIndex = -1;
|
mBatteryChartPreferenceController.mDailyChartIndex = -1;
|
||||||
|
mBatteryChartPreferenceController.mHourlyChartIndex = -1;
|
||||||
mBatteryChartPreferenceController.mIsExpanded = false;
|
mBatteryChartPreferenceController.mIsExpanded = false;
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onCreate(bundle);
|
mBatteryChartPreferenceController.onCreate(bundle);
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25));
|
||||||
createBatteryHistoryMap());
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.mTrapezoidIndex)
|
assertThat(mBatteryChartPreferenceController.mDailyChartIndex)
|
||||||
.isEqualTo(expectedIndex);
|
.isEqualTo(expectedDailyIndex);
|
||||||
|
assertThat(mBatteryChartPreferenceController.mHourlyChartIndex)
|
||||||
|
.isEqualTo(expectedHourlyIndex);
|
||||||
assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue();
|
assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsValidToShowSummary_returnExpectedResult() {
|
public void isValidToShowSummary_returnExpectedResult() {
|
||||||
assertThat(mBatteryChartPreferenceController
|
assertThat(mBatteryChartPreferenceController
|
||||||
.isValidToShowSummary("com.google.android.apps.scone"))
|
.isValidToShowSummary("com.google.android.apps.scone"))
|
||||||
.isTrue();
|
.isTrue();
|
||||||
@@ -650,31 +630,42 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
.isFalse();
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
private static Long generateTimestamp(int index) {
|
||||||
public void testIsValidToShowEntry_returnExpectedResult() {
|
// "2021-04-23 07:00:00 UTC" + index hours
|
||||||
assertThat(mBatteryChartPreferenceController
|
return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS;
|
||||||
.isValidToShowEntry("com.google.android.apps.scone"))
|
|
||||||
.isTrue();
|
|
||||||
|
|
||||||
// Verifies the items which are defined in the array list.
|
|
||||||
assertThat(mBatteryChartPreferenceController
|
|
||||||
.isValidToShowEntry("com.android.gms.persistent"))
|
|
||||||
.isFalse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<Long, Map<String, BatteryHistEntry>> createBatteryHistoryMap() {
|
private static Map<Long, Map<String, BatteryHistEntry>> createBatteryHistoryMap(
|
||||||
|
int numOfHours) {
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||||
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
|
for (int index = 0; index < numOfHours; index++) {
|
||||||
final ContentValues values = new ContentValues();
|
final ContentValues values = new ContentValues();
|
||||||
values.put("batteryLevel", Integer.valueOf(100 - index));
|
values.put("batteryLevel", Integer.valueOf(100 - index));
|
||||||
|
values.put("consumePower", Integer.valueOf(100 - index));
|
||||||
final BatteryHistEntry entry = new BatteryHistEntry(values);
|
final BatteryHistEntry entry = new BatteryHistEntry(values);
|
||||||
final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
|
final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
|
||||||
entryMap.put("fake_entry_key" + index, entry);
|
entryMap.put("fake_entry_key" + index, entry);
|
||||||
batteryHistoryMap.put(Long.valueOf(index + 1), entryMap);
|
batteryHistoryMap.put(generateTimestamp(index), entryMap);
|
||||||
}
|
}
|
||||||
return batteryHistoryMap;
|
return batteryHistoryMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<Integer, Map<Integer, BatteryDiffData>> createBatteryUsageMap() {
|
||||||
|
final int selectedAll = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||||
|
return Map.of(
|
||||||
|
selectedAll, Map.of(
|
||||||
|
selectedAll, new BatteryDiffData(
|
||||||
|
Arrays.asList(mBatteryDiffEntry),
|
||||||
|
Arrays.asList(mBatteryDiffEntry))),
|
||||||
|
0, Map.of(
|
||||||
|
selectedAll, new BatteryDiffData(
|
||||||
|
Arrays.asList(mBatteryDiffEntry),
|
||||||
|
Arrays.asList(mBatteryDiffEntry)),
|
||||||
|
0, new BatteryDiffData(
|
||||||
|
Arrays.asList(mBatteryDiffEntry),
|
||||||
|
Arrays.asList(mBatteryDiffEntry))));
|
||||||
|
}
|
||||||
|
|
||||||
private BatteryDiffEntry createBatteryDiffEntry(
|
private BatteryDiffEntry createBatteryDiffEntry(
|
||||||
long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
|
long foregroundUsageTimeInMs, long backgroundUsageTimeInMs) {
|
||||||
return new BatteryDiffEntry(
|
return new BatteryDiffEntry(
|
||||||
@@ -682,13 +673,6 @@ public final class BatteryChartPreferenceControllerTest {
|
|||||||
/*consumePower=*/ 0, mBatteryHistEntry);
|
/*consumePower=*/ 0, mBatteryHistEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpBatteryHistoryKeys() {
|
|
||||||
mBatteryChartPreferenceController.mBatteryHistoryKeys =
|
|
||||||
new long[]{1619196786769L, 0L, 1619247636826L};
|
|
||||||
ConvertUtils.utcToLocalTimeHour(
|
|
||||||
mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BatteryChartPreferenceController createController() {
|
private BatteryChartPreferenceController createController() {
|
||||||
final BatteryChartPreferenceController controller =
|
final BatteryChartPreferenceController controller =
|
||||||
new BatteryChartPreferenceController(
|
new BatteryChartPreferenceController(
|
||||||
|
@@ -1,684 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.settings.fuelgauge.batteryusage;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.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.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;
|
|
||||||
import androidx.preference.PreferenceGroup;
|
|
||||||
|
|
||||||
import com.android.settings.SettingsActivity;
|
|
||||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
|
||||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
|
||||||
public final class BatteryChartPreferenceControllerV2Test {
|
|
||||||
private static final String PREF_KEY = "pref_key";
|
|
||||||
private static final String PREF_SUMMARY = "fake preference summary";
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private InstrumentedPreferenceFragment mFragment;
|
|
||||||
@Mock
|
|
||||||
private SettingsActivity mSettingsActivity;
|
|
||||||
@Mock
|
|
||||||
private PreferenceGroup mAppListGroup;
|
|
||||||
@Mock
|
|
||||||
private Drawable mDrawable;
|
|
||||||
@Mock
|
|
||||||
private BatteryHistEntry mBatteryHistEntry;
|
|
||||||
@Mock
|
|
||||||
private BatteryChartViewV2 mDailyChartView;
|
|
||||||
@Mock
|
|
||||||
private BatteryChartViewV2 mHourlyChartView;
|
|
||||||
@Mock
|
|
||||||
private PowerGaugePreference mPowerGaugePreference;
|
|
||||||
@Mock
|
|
||||||
private BatteryUtils mBatteryUtils;
|
|
||||||
@Mock
|
|
||||||
private LinearLayout.LayoutParams mLayoutParams;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private FakeFeatureFactory mFeatureFactory;
|
|
||||||
private BatteryDiffEntry mBatteryDiffEntry;
|
|
||||||
private MetricsFeatureProvider mMetricsFeatureProvider;
|
|
||||||
private BatteryChartPreferenceControllerV2 mBatteryChartPreferenceController;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
Locale.setDefault(new Locale("en_US"));
|
|
||||||
org.robolectric.shadows.ShadowSettings.set24HourTimeFormat(false);
|
|
||||||
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
|
|
||||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
|
||||||
mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
|
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
|
||||||
final Resources resources = spy(mContext.getResources());
|
|
||||||
resources.getConfiguration().setLocales(new LocaleList(new Locale("en_US")));
|
|
||||||
doReturn(resources).when(mContext).getResources();
|
|
||||||
doReturn(new String[]{"com.android.googlequicksearchbox"})
|
|
||||||
.when(mFeatureFactory.powerUsageFeatureProvider)
|
|
||||||
.getHideApplicationSummary(mContext);
|
|
||||||
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.mDailyChartView = mDailyChartView;
|
|
||||||
mBatteryChartPreferenceController.mHourlyChartView = mHourlyChartView;
|
|
||||||
mBatteryDiffEntry = new BatteryDiffEntry(
|
|
||||||
mContext,
|
|
||||||
/*foregroundUsageTimeInMs=*/ 1,
|
|
||||||
/*backgroundUsageTimeInMs=*/ 2,
|
|
||||||
/*consumePower=*/ 3,
|
|
||||||
mBatteryHistEntry);
|
|
||||||
mBatteryDiffEntry = spy(mBatteryDiffEntry);
|
|
||||||
// Adds fake testing data.
|
|
||||||
BatteryDiffEntry.sResourceCache.put(
|
|
||||||
"fakeBatteryDiffEntryKey",
|
|
||||||
new BatteryEntry.NameAndIcon("fakeName", /*icon=*/ null, /*iconId=*/ 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onDestroy_activityIsChanging_clearBatteryEntryCache() {
|
|
||||||
doReturn(true).when(mSettingsActivity).isChangingConfigurations();
|
|
||||||
// Ensures the testing environment is correct.
|
|
||||||
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onDestroy();
|
|
||||||
assertThat(BatteryDiffEntry.sResourceCache).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onDestroy_activityIsNotChanging_notClearBatteryEntryCache() {
|
|
||||||
doReturn(false).when(mSettingsActivity).isChangingConfigurations();
|
|
||||||
// Ensures the testing environment is correct.
|
|
||||||
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onDestroy();
|
|
||||||
assertThat(BatteryDiffEntry.sResourceCache).isNotEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onDestroy_clearPreferenceCache() {
|
|
||||||
// Ensures the testing environment is correct.
|
|
||||||
mBatteryChartPreferenceController.mPreferenceCache.put(
|
|
||||||
PREF_KEY, mPowerGaugePreference);
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).hasSize(1);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onDestroy();
|
|
||||||
// Verifies the result after onDestroy.
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onDestroy_removeAllPreferenceFromPreferenceGroup() {
|
|
||||||
mBatteryChartPreferenceController.onDestroy();
|
|
||||||
verify(mAppListGroup).removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setBatteryChartViewModel_6Hours() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
|
||||||
|
|
||||||
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 setBatteryChartViewModel_60Hours() {
|
|
||||||
BatteryChartViewModel expectedDailyViewModel = new BatteryChartViewModel(
|
|
||||||
List.of(100, 83, 59, 41),
|
|
||||||
List.of("Sat", "Sun", "Mon", "Mon"),
|
|
||||||
BatteryChartViewModel.AxisLabelPosition.CENTER_OF_TRAPEZOIDS);
|
|
||||||
|
|
||||||
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 refreshUi_normalCase_returnTrue() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
|
||||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void refreshUi_batteryIndexedMapIsNull_ignoreRefresh() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(null);
|
|
||||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void refreshUi_dailyChartViewIsNull_ignoreRefresh() {
|
|
||||||
mBatteryChartPreferenceController.mDailyChartView = null;
|
|
||||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void refreshUi_hourlyChartViewIsNull_ignoreRefresh() {
|
|
||||||
mBatteryChartPreferenceController.mHourlyChartView = null;
|
|
||||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removeAndCacheAllPrefs_emptyContent_ignoreRemoveAll() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
|
||||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
|
||||||
doReturn(0).when(mAppListGroup).getPreferenceCount();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshUi();
|
|
||||||
verify(mAppListGroup, never()).removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void removeAndCacheAllPrefs_buildCacheAndRemoveAllPreference() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
|
||||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0);
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
|
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
// Ensures the testing data is correct.
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshUi();
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY))
|
|
||||||
.isEqualTo(mPowerGaugePreference);
|
|
||||||
verify(mAppListGroup).removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addPreferenceToScreen_emptyContent_ignoreAddPreference() {
|
|
||||||
mBatteryChartPreferenceController.addPreferenceToScreen(
|
|
||||||
new ArrayList<BatteryDiffEntry>());
|
|
||||||
verify(mAppListGroup, never()).addPreference(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addPreferenceToScreen_addPreferenceIntoScreen() {
|
|
||||||
final String appLabel = "fake app label";
|
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
|
||||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
|
||||||
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
doReturn(null).when(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
doReturn(false).when(mBatteryDiffEntry).validForRestriction();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.addPreferenceToScreen(
|
|
||||||
Arrays.asList(mBatteryDiffEntry));
|
|
||||||
|
|
||||||
// Verifies the preference cache.
|
|
||||||
final PowerGaugePreference pref =
|
|
||||||
(PowerGaugePreference) mBatteryChartPreferenceController.mPreferenceCache
|
|
||||||
.get(PREF_KEY);
|
|
||||||
assertThat(pref).isNotNull();
|
|
||||||
// Verifies the added preference configuration.
|
|
||||||
verify(mAppListGroup).addPreference(pref);
|
|
||||||
assertThat(pref.getKey()).isEqualTo(PREF_KEY);
|
|
||||||
assertThat(pref.getTitle()).isEqualTo(appLabel);
|
|
||||||
assertThat(pref.getIcon()).isEqualTo(mDrawable);
|
|
||||||
assertThat(pref.getOrder()).isEqualTo(1);
|
|
||||||
assertThat(pref.getBatteryDiffEntry()).isSameInstanceAs(mBatteryDiffEntry);
|
|
||||||
assertThat(pref.isSingleLineTitle()).isTrue();
|
|
||||||
assertThat(pref.isEnabled()).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void addPreferenceToScreen_alreadyInScreen_notAddPreferenceAgain() {
|
|
||||||
final String appLabel = "fake app label";
|
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
|
||||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
|
||||||
doReturn(appLabel).when(mBatteryDiffEntry).getAppLabel();
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.addPreferenceToScreen(
|
|
||||||
Arrays.asList(mBatteryDiffEntry));
|
|
||||||
|
|
||||||
verify(mAppListGroup, never()).addPreference(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void handlePreferenceTreeClick_notPowerGaugePreference_returnFalse() {
|
|
||||||
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(mAppListGroup))
|
|
||||||
.isFalse();
|
|
||||||
|
|
||||||
verify(mMetricsFeatureProvider, never())
|
|
||||||
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM);
|
|
||||||
verify(mMetricsFeatureProvider, never())
|
|
||||||
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void handlePreferenceTreeClick_forAppEntry_returnTrue() {
|
|
||||||
doReturn(false).when(mBatteryHistEntry).isAppEntry();
|
|
||||||
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(
|
|
||||||
mPowerGaugePreference)).isTrue();
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(
|
|
||||||
SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_SYSTEM_ITEM,
|
|
||||||
SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
/* package name */ "none",
|
|
||||||
/* percentage of total */ 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void handlePreferenceTreeClick_forSystemEntry_returnTrue() {
|
|
||||||
mBatteryChartPreferenceController.mBatteryUtils = mBatteryUtils;
|
|
||||||
doReturn(true).when(mBatteryHistEntry).isAppEntry();
|
|
||||||
doReturn(mBatteryDiffEntry).when(mPowerGaugePreference).getBatteryDiffEntry();
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.handlePreferenceTreeClick(
|
|
||||||
mPowerGaugePreference)).isTrue();
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(
|
|
||||||
SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_APP_ITEM,
|
|
||||||
SettingsEnums.OPEN_BATTERY_USAGE,
|
|
||||||
/* package name */ "none",
|
|
||||||
/* percentage of total */ 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setNullContentIfTotalUsageTimeIsZero() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ 0,
|
|
||||||
/*backgroundUsageTimeInMs=*/ 0));
|
|
||||||
assertThat(pref.getSummary()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setBackgroundUsageTimeOnly() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ 0,
|
|
||||||
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
|
|
||||||
assertThat(pref.getSummary()).isEqualTo("Background: 1 min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setTotalUsageTimeLessThanAMinute() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ 100,
|
|
||||||
/*backgroundUsageTimeInMs=*/ 200));
|
|
||||||
assertThat(pref.getSummary()).isEqualTo("Total: less than a min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setTotalTimeIfBackgroundTimeLessThanAMinute() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
/*backgroundUsageTimeInMs=*/ 200));
|
|
||||||
assertThat(pref.getSummary())
|
|
||||||
.isEqualTo("Total: 1 min\nBackground: less than a min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_setTotalAndBackgroundUsageTime() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(
|
|
||||||
pref, createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
|
|
||||||
assertThat(pref.getSummary()).isEqualTo("Total: 2 min\nBackground: 1 min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void setPreferenceSummary_notAllowShownPackage_setSummayAsNull() {
|
|
||||||
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
|
|
||||||
pref.setSummary(PREF_SUMMARY);
|
|
||||||
final BatteryDiffEntry batteryDiffEntry =
|
|
||||||
spy(createBatteryDiffEntry(
|
|
||||||
/*foregroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
/*backgroundUsageTimeInMs=*/ DateUtils.MINUTE_IN_MILLIS));
|
|
||||||
doReturn("com.android.googlequicksearchbox").when(batteryDiffEntry)
|
|
||||||
.getPackageName();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.setPreferenceSummary(pref, batteryDiffEntry);
|
|
||||||
assertThat(pref.getSummary()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onExpand_expandedIsTrue_addSystemEntriesToPreferenceGroup() {
|
|
||||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
|
||||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
|
||||||
doReturn("label").when(mBatteryDiffEntry).getAppLabel();
|
|
||||||
doReturn(mDrawable).when(mBatteryDiffEntry).getAppIcon();
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onExpand(/*isExpanded=*/ true);
|
|
||||||
|
|
||||||
final ArgumentCaptor<Preference> captor = ArgumentCaptor.forClass(Preference.class);
|
|
||||||
verify(mAppListGroup).addPreference(captor.capture());
|
|
||||||
// Verifies the added preference.
|
|
||||||
assertThat(captor.getValue().getKey()).isEqualTo(PREF_KEY);
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(
|
|
||||||
mContext,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM,
|
|
||||||
true /*isExpanded*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onExpand_expandedIsFalse_removeSystemEntriesFromPreferenceGroup() {
|
|
||||||
doReturn(PREF_KEY).when(mBatteryHistEntry).getKey();
|
|
||||||
doReturn(mPowerGaugePreference).when(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
mBatteryChartPreferenceController.mBatteryUsageMap = createBatteryUsageMap();
|
|
||||||
// Verifies the cache is empty first.
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onExpand(/*isExpanded=*/ false);
|
|
||||||
|
|
||||||
verify(mAppListGroup).findPreference(PREF_KEY);
|
|
||||||
verify(mAppListGroup).removePreference(mPowerGaugePreference);
|
|
||||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).hasSize(1);
|
|
||||||
verify(mMetricsFeatureProvider)
|
|
||||||
.action(
|
|
||||||
mContext,
|
|
||||||
SettingsEnums.ACTION_BATTERY_USAGE_EXPAND_ITEM,
|
|
||||||
false /*isExpanded*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void refreshCategoryTitle_setLastFullChargeIntoBothTitleTextView() {
|
|
||||||
mBatteryChartPreferenceController = createController();
|
|
||||||
mBatteryChartPreferenceController.mAppListPrefGroup =
|
|
||||||
spy(new PreferenceCategory(mContext));
|
|
||||||
mBatteryChartPreferenceController.mExpandDividerPreference =
|
|
||||||
spy(new ExpandDividerPreference(mContext));
|
|
||||||
// Simulates select all condition.
|
|
||||||
mBatteryChartPreferenceController.mDailyChartIndex =
|
|
||||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
mBatteryChartPreferenceController.mHourlyChartIndex =
|
|
||||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.refreshCategoryTitle();
|
|
||||||
|
|
||||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
|
||||||
// Verifies the title in the preference group.
|
|
||||||
verify(mBatteryChartPreferenceController.mAppListPrefGroup)
|
|
||||||
.setTitle(captor.capture());
|
|
||||||
assertThat(captor.getValue())
|
|
||||||
.isEqualTo("App usage since last full charge");
|
|
||||||
// Verifies the title in the expandable divider.
|
|
||||||
captor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(mBatteryChartPreferenceController.mExpandDividerPreference)
|
|
||||||
.setTitle(captor.capture());
|
|
||||||
assertThat(captor.getValue())
|
|
||||||
.isEqualTo("System usage since last full charge");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void selectedSlotText_selectAllDaysAllHours_returnNull() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
|
||||||
mBatteryChartPreferenceController.mDailyChartIndex =
|
|
||||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
mBatteryChartPreferenceController.mHourlyChartIndex =
|
|
||||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void selectedSlotText_onlyOneDayDataSelectAllHours_returnNull() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(6));
|
|
||||||
mBatteryChartPreferenceController.mDailyChartIndex = 0;
|
|
||||||
mBatteryChartPreferenceController.mHourlyChartIndex =
|
|
||||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void selectedSlotText_selectADayAllHours_onlyDayText() {
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(60));
|
|
||||||
mBatteryChartPreferenceController.mDailyChartIndex = 1;
|
|
||||||
mBatteryChartPreferenceController.mHourlyChartIndex =
|
|
||||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo("Sunday");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
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.mDailyChartIndex = expectedDailyIndex;
|
|
||||||
mBatteryChartPreferenceController.mHourlyChartIndex = expectedHourlyIndex;
|
|
||||||
mBatteryChartPreferenceController.mIsExpanded = isExpanded;
|
|
||||||
mBatteryChartPreferenceController.onSaveInstanceState(bundle);
|
|
||||||
// Replaces the original controller with other values.
|
|
||||||
mBatteryChartPreferenceController.mDailyChartIndex = -1;
|
|
||||||
mBatteryChartPreferenceController.mHourlyChartIndex = -1;
|
|
||||||
mBatteryChartPreferenceController.mIsExpanded = false;
|
|
||||||
|
|
||||||
mBatteryChartPreferenceController.onCreate(bundle);
|
|
||||||
mBatteryChartPreferenceController.setBatteryHistoryMap(createBatteryHistoryMap(25));
|
|
||||||
|
|
||||||
assertThat(mBatteryChartPreferenceController.mDailyChartIndex)
|
|
||||||
.isEqualTo(expectedDailyIndex);
|
|
||||||
assertThat(mBatteryChartPreferenceController.mHourlyChartIndex)
|
|
||||||
.isEqualTo(expectedHourlyIndex);
|
|
||||||
assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isValidToShowSummary_returnExpectedResult() {
|
|
||||||
assertThat(mBatteryChartPreferenceController
|
|
||||||
.isValidToShowSummary("com.google.android.apps.scone"))
|
|
||||||
.isTrue();
|
|
||||||
|
|
||||||
// Verifies the item which is defined in the array list.
|
|
||||||
assertThat(mBatteryChartPreferenceController
|
|
||||||
.isValidToShowSummary("com.android.googlequicksearchbox"))
|
|
||||||
.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(
|
|
||||||
int numOfHours) {
|
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
|
||||||
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(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(
|
|
||||||
mContext, foregroundUsageTimeInMs, backgroundUsageTimeInMs,
|
|
||||||
/*consumePower=*/ 0, mBatteryHistEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BatteryChartPreferenceControllerV2 createController() {
|
|
||||||
final BatteryChartPreferenceControllerV2 controller =
|
|
||||||
new BatteryChartPreferenceControllerV2(
|
|
||||||
mContext, "app_list", /*lifecycle=*/ null,
|
|
||||||
mSettingsActivity, mFragment);
|
|
||||||
controller.mPrefContext = mContext;
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.LocaleList;
|
import android.os.LocaleList;
|
||||||
|
import android.view.View;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||||
@@ -41,6 +42,7 @@ import org.robolectric.RuntimeEnvironment;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
@@ -55,6 +57,8 @@ public final class BatteryChartViewTest {
|
|||||||
private AccessibilityServiceInfo mMockAccessibilityServiceInfo;
|
private AccessibilityServiceInfo mMockAccessibilityServiceInfo;
|
||||||
@Mock
|
@Mock
|
||||||
private AccessibilityManager mMockAccessibilityManager;
|
private AccessibilityManager mMockAccessibilityManager;
|
||||||
|
@Mock
|
||||||
|
private View mMockView;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
@@ -74,13 +78,13 @@ public final class BatteryChartViewTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsAccessibilityEnabled_disable_returnFalse() {
|
public void isAccessibilityEnabled_disable_returnFalse() {
|
||||||
doReturn(false).when(mMockAccessibilityManager).isEnabled();
|
doReturn(false).when(mMockAccessibilityManager).isEnabled();
|
||||||
assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse();
|
assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsAccessibilityEnabled_emptyInfo_returnFalse() {
|
public void isAccessibilityEnabled_emptyInfo_returnFalse() {
|
||||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
||||||
doReturn(new ArrayList<AccessibilityServiceInfo>())
|
doReturn(new ArrayList<AccessibilityServiceInfo>())
|
||||||
.when(mMockAccessibilityManager)
|
.when(mMockAccessibilityManager)
|
||||||
@@ -90,68 +94,70 @@ public final class BatteryChartViewTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsAccessibilityEnabled_validServiceId_returnTrue() {
|
public void isAccessibilityEnabled_validServiceId_returnTrue() {
|
||||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
||||||
assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isTrue();
|
assertThat(BatteryChartView.isAccessibilityEnabled(mContext)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetSelectedIndex_invokesCallback() {
|
public void onClick_invokesCallback() {
|
||||||
|
final int originalSelectedIndex = 2;
|
||||||
|
BatteryChartViewModel batteryChartViewModel = new BatteryChartViewModel(
|
||||||
|
List.of(90, 80, 70, 60), List.of("", "", "", ""),
|
||||||
|
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS);
|
||||||
|
batteryChartViewModel.setSelectedIndex(originalSelectedIndex);
|
||||||
|
mBatteryChartView.setViewModel(batteryChartViewModel);
|
||||||
|
for (int i = 0; i < mBatteryChartView.mTrapezoidSlots.length; i++) {
|
||||||
|
mBatteryChartView.mTrapezoidSlots[i] = new BatteryChartView.TrapezoidSlot();
|
||||||
|
mBatteryChartView.mTrapezoidSlots[i].mLeft = i;
|
||||||
|
mBatteryChartView.mTrapezoidSlots[i].mRight = i + 0.5f;
|
||||||
|
}
|
||||||
final int[] selectedIndex = new int[1];
|
final int[] selectedIndex = new int[1];
|
||||||
final int expectedIndex = 2;
|
|
||||||
mBatteryChartView.mSelectedIndex = 1;
|
|
||||||
mBatteryChartView.setOnSelectListener(
|
mBatteryChartView.setOnSelectListener(
|
||||||
trapezoidIndex -> {
|
trapezoidIndex -> {
|
||||||
selectedIndex[0] = trapezoidIndex;
|
selectedIndex[0] = trapezoidIndex;
|
||||||
});
|
});
|
||||||
|
|
||||||
mBatteryChartView.setSelectedIndex(expectedIndex);
|
// Verify onClick() a different index 1.
|
||||||
|
mBatteryChartView.mTouchUpEventX = 1;
|
||||||
|
selectedIndex[0] = Integer.MIN_VALUE;
|
||||||
|
mBatteryChartView.onClick(mMockView);
|
||||||
|
assertThat(selectedIndex[0]).isEqualTo(1);
|
||||||
|
|
||||||
assertThat(mBatteryChartView.mSelectedIndex)
|
// Verify onClick() the same index 2.
|
||||||
.isEqualTo(expectedIndex);
|
mBatteryChartView.mTouchUpEventX = 2;
|
||||||
assertThat(selectedIndex[0]).isEqualTo(expectedIndex);
|
selectedIndex[0] = Integer.MIN_VALUE;
|
||||||
|
mBatteryChartView.onClick(mMockView);
|
||||||
|
assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewModel.SELECTED_INDEX_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetSelectedIndex_sameIndex_notInvokesCallback() {
|
public void clickable_isChartGraphSlotsEnabledIsFalse_notClickable() {
|
||||||
final int[] selectedIndex = new int[1];
|
|
||||||
final int expectedIndex = 1;
|
|
||||||
mBatteryChartView.mSelectedIndex = expectedIndex;
|
|
||||||
mBatteryChartView.setOnSelectListener(
|
|
||||||
trapezoidIndex -> {
|
|
||||||
selectedIndex[0] = trapezoidIndex;
|
|
||||||
});
|
|
||||||
|
|
||||||
mBatteryChartView.setSelectedIndex(expectedIndex);
|
|
||||||
|
|
||||||
assertThat(selectedIndex[0]).isNotEqualTo(expectedIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClickable_isChartGraphSlotsEnabledIsFalse_notClickable() {
|
|
||||||
mBatteryChartView.setClickableForce(true);
|
mBatteryChartView.setClickableForce(true);
|
||||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||||
.thenReturn(false);
|
.thenReturn(false);
|
||||||
|
|
||||||
mBatteryChartView.onAttachedToWindow();
|
mBatteryChartView.onAttachedToWindow();
|
||||||
|
|
||||||
assertThat(mBatteryChartView.isClickable()).isFalse();
|
assertThat(mBatteryChartView.isClickable()).isFalse();
|
||||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
|
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClickable_accessibilityIsDisabled_clickable() {
|
public void clickable_accessibilityIsDisabled_clickable() {
|
||||||
mBatteryChartView.setClickableForce(true);
|
mBatteryChartView.setClickableForce(true);
|
||||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||||
.thenReturn(true);
|
.thenReturn(true);
|
||||||
doReturn(false).when(mMockAccessibilityManager).isEnabled();
|
doReturn(false).when(mMockAccessibilityManager).isEnabled();
|
||||||
|
|
||||||
mBatteryChartView.onAttachedToWindow();
|
mBatteryChartView.onAttachedToWindow();
|
||||||
|
|
||||||
assertThat(mBatteryChartView.isClickable()).isTrue();
|
assertThat(mBatteryChartView.isClickable()).isTrue();
|
||||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
|
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClickable_accessibilityIsEnabledWithoutValidId_clickable() {
|
public void clickable_accessibilityIsEnabledWithoutValidId_clickable() {
|
||||||
mBatteryChartView.setClickableForce(true);
|
mBatteryChartView.setClickableForce(true);
|
||||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||||
.thenReturn(true);
|
.thenReturn(true);
|
||||||
@@ -161,30 +167,34 @@ public final class BatteryChartViewTest {
|
|||||||
.getEnabledAccessibilityServiceList(anyInt());
|
.getEnabledAccessibilityServiceList(anyInt());
|
||||||
|
|
||||||
mBatteryChartView.onAttachedToWindow();
|
mBatteryChartView.onAttachedToWindow();
|
||||||
|
|
||||||
assertThat(mBatteryChartView.isClickable()).isTrue();
|
assertThat(mBatteryChartView.isClickable()).isTrue();
|
||||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
|
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClickable_accessibilityIsEnabledWithValidId_notClickable() {
|
public void clickable_accessibilityIsEnabledWithValidId_notClickable() {
|
||||||
mBatteryChartView.setClickableForce(true);
|
mBatteryChartView.setClickableForce(true);
|
||||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||||
.thenReturn(true);
|
.thenReturn(true);
|
||||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
||||||
|
|
||||||
mBatteryChartView.onAttachedToWindow();
|
mBatteryChartView.onAttachedToWindow();
|
||||||
|
|
||||||
assertThat(mBatteryChartView.isClickable()).isFalse();
|
assertThat(mBatteryChartView.isClickable()).isFalse();
|
||||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
|
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClickable_restoreFromNonClickableState() {
|
public void clickable_restoreFromNonClickableState() {
|
||||||
final int[] levels = new int[13];
|
final List<Integer> levels = new ArrayList<Integer>();
|
||||||
for (int index = 0; index < levels.length; index++) {
|
final List<String> texts = new ArrayList<String>();
|
||||||
levels[index] = index + 1;
|
for (int index = 0; index < 13; index++) {
|
||||||
|
levels.add(index + 1);
|
||||||
|
texts.add("");
|
||||||
}
|
}
|
||||||
mBatteryChartView.setTrapezoidCount(12);
|
mBatteryChartView.setViewModel(new BatteryChartViewModel(levels, texts,
|
||||||
mBatteryChartView.setLevels(levels);
|
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
||||||
mBatteryChartView.setClickableForce(true);
|
mBatteryChartView.setClickableForce(true);
|
||||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||||
.thenReturn(true);
|
.thenReturn(true);
|
||||||
@@ -201,14 +211,14 @@ public final class BatteryChartViewTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnAttachedToWindow_addAccessibilityStateChangeListener() {
|
public void onAttachedToWindow_addAccessibilityStateChangeListener() {
|
||||||
mBatteryChartView.onAttachedToWindow();
|
mBatteryChartView.onAttachedToWindow();
|
||||||
verify(mMockAccessibilityManager)
|
verify(mMockAccessibilityManager)
|
||||||
.addAccessibilityStateChangeListener(mBatteryChartView);
|
.addAccessibilityStateChangeListener(mBatteryChartView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnDetachedFromWindow_removeAccessibilityStateChangeListener() {
|
public void onDetachedFromWindow_removeAccessibilityStateChangeListener() {
|
||||||
mBatteryChartView.onAttachedToWindow();
|
mBatteryChartView.onAttachedToWindow();
|
||||||
mBatteryChartView.mHandler.postDelayed(
|
mBatteryChartView.mHandler.postDelayed(
|
||||||
mBatteryChartView.mUpdateClickableStateRun, 1000);
|
mBatteryChartView.mUpdateClickableStateRun, 1000);
|
||||||
@@ -223,7 +233,7 @@ public final class BatteryChartViewTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOnAccessibilityStateChanged_postUpdateStateRunnable() {
|
public void onAccessibilityStateChanged_postUpdateStateRunnable() {
|
||||||
mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler);
|
mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler);
|
||||||
mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true);
|
mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true);
|
||||||
|
|
||||||
|
@@ -1,245 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.android.settings.fuelgauge.batteryusage;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
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;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
|
||||||
public final class BatteryChartViewV2Test {
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private BatteryChartViewV2 mBatteryChartView;
|
|
||||||
private FakeFeatureFactory mFeatureFactory;
|
|
||||||
private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private AccessibilityServiceInfo mMockAccessibilityServiceInfo;
|
|
||||||
@Mock
|
|
||||||
private AccessibilityManager mMockAccessibilityManager;
|
|
||||||
@Mock
|
|
||||||
private View mMockView;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
|
||||||
mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider;
|
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
|
||||||
mContext.getResources().getConfiguration().setLocales(
|
|
||||||
new LocaleList(new Locale("en_US")));
|
|
||||||
mBatteryChartView = new BatteryChartViewV2(mContext);
|
|
||||||
doReturn(mMockAccessibilityManager).when(mContext)
|
|
||||||
.getSystemService(AccessibilityManager.class);
|
|
||||||
doReturn("TalkBackService").when(mMockAccessibilityServiceInfo).getId();
|
|
||||||
doReturn(Arrays.asList(mMockAccessibilityServiceInfo))
|
|
||||||
.when(mMockAccessibilityManager)
|
|
||||||
.getEnabledAccessibilityServiceList(anyInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isAccessibilityEnabled_disable_returnFalse() {
|
|
||||||
doReturn(false).when(mMockAccessibilityManager).isEnabled();
|
|
||||||
assertThat(BatteryChartViewV2.isAccessibilityEnabled(mContext)).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isAccessibilityEnabled_emptyInfo_returnFalse() {
|
|
||||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
|
||||||
doReturn(new ArrayList<AccessibilityServiceInfo>())
|
|
||||||
.when(mMockAccessibilityManager)
|
|
||||||
.getEnabledAccessibilityServiceList(anyInt());
|
|
||||||
|
|
||||||
assertThat(BatteryChartViewV2.isAccessibilityEnabled(mContext)).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void isAccessibilityEnabled_validServiceId_returnTrue() {
|
|
||||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
|
||||||
assertThat(BatteryChartViewV2.isAccessibilityEnabled(mContext)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
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 BatteryChartViewV2.TrapezoidSlot();
|
|
||||||
mBatteryChartView.mTrapezoidSlots[i].mLeft = i;
|
|
||||||
mBatteryChartView.mTrapezoidSlots[i].mRight = i + 0.5f;
|
|
||||||
}
|
|
||||||
final int[] selectedIndex = new int[1];
|
|
||||||
mBatteryChartView.setOnSelectListener(
|
|
||||||
trapezoidIndex -> {
|
|
||||||
selectedIndex[0] = trapezoidIndex;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Verify onClick() a different index 1.
|
|
||||||
mBatteryChartView.mTouchUpEventX = 1;
|
|
||||||
selectedIndex[0] = Integer.MIN_VALUE;
|
|
||||||
mBatteryChartView.onClick(mMockView);
|
|
||||||
assertThat(selectedIndex[0]).isEqualTo(1);
|
|
||||||
|
|
||||||
// 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 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 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 clickable_accessibilityIsEnabledWithoutValidId_clickable() {
|
|
||||||
mBatteryChartView.setClickableForce(true);
|
|
||||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
|
||||||
.thenReturn(true);
|
|
||||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
|
||||||
doReturn(new ArrayList<AccessibilityServiceInfo>())
|
|
||||||
.when(mMockAccessibilityManager)
|
|
||||||
.getEnabledAccessibilityServiceList(anyInt());
|
|
||||||
|
|
||||||
mBatteryChartView.onAttachedToWindow();
|
|
||||||
|
|
||||||
assertThat(mBatteryChartView.isClickable()).isTrue();
|
|
||||||
assertThat(mBatteryChartView.mTrapezoidCurvePaint).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
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 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.setViewModel(new BatteryChartViewModel(levels, texts,
|
|
||||||
BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS));
|
|
||||||
mBatteryChartView.setClickableForce(true);
|
|
||||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
|
||||||
.thenReturn(true);
|
|
||||||
doReturn(true).when(mMockAccessibilityManager).isEnabled();
|
|
||||||
mBatteryChartView.onAttachedToWindow();
|
|
||||||
// Ensures the testing environment is correct.
|
|
||||||
assertThat(mBatteryChartView.isClickable()).isFalse();
|
|
||||||
// Turns off accessibility service.
|
|
||||||
doReturn(false).when(mMockAccessibilityManager).isEnabled();
|
|
||||||
|
|
||||||
mBatteryChartView.onAttachedToWindow();
|
|
||||||
|
|
||||||
assertThat(mBatteryChartView.isClickable()).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onAttachedToWindow_addAccessibilityStateChangeListener() {
|
|
||||||
mBatteryChartView.onAttachedToWindow();
|
|
||||||
verify(mMockAccessibilityManager)
|
|
||||||
.addAccessibilityStateChangeListener(mBatteryChartView);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onDetachedFromWindow_removeAccessibilityStateChangeListener() {
|
|
||||||
mBatteryChartView.onAttachedToWindow();
|
|
||||||
mBatteryChartView.mHandler.postDelayed(
|
|
||||||
mBatteryChartView.mUpdateClickableStateRun, 1000);
|
|
||||||
|
|
||||||
mBatteryChartView.onDetachedFromWindow();
|
|
||||||
|
|
||||||
verify(mMockAccessibilityManager)
|
|
||||||
.removeAccessibilityStateChangeListener(mBatteryChartView);
|
|
||||||
assertThat(mBatteryChartView.mHandler.hasCallbacks(
|
|
||||||
mBatteryChartView.mUpdateClickableStateRun))
|
|
||||||
.isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void onAccessibilityStateChanged_postUpdateStateRunnable() {
|
|
||||||
mBatteryChartView.mHandler = spy(mBatteryChartView.mHandler);
|
|
||||||
mBatteryChartView.onAccessibilityStateChanged(/*enabled=*/ true);
|
|
||||||
|
|
||||||
verify(mBatteryChartView.mHandler)
|
|
||||||
.removeCallbacks(mBatteryChartView.mUpdateClickableStateRun);
|
|
||||||
verify(mBatteryChartView.mHandler)
|
|
||||||
.postDelayed(mBatteryChartView.mUpdateClickableStateRun, 500L);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -53,7 +53,7 @@ public final class BatteryHistoryLoaderTest {
|
|||||||
public void testLoadIBackground_returnsMapFromPowerFeatureProvider() {
|
public void testLoadIBackground_returnsMapFromPowerFeatureProvider() {
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||||
doReturn(batteryHistoryMap).when(mFeatureFactory.powerUsageFeatureProvider)
|
doReturn(batteryHistoryMap).when(mFeatureFactory.powerUsageFeatureProvider)
|
||||||
.getBatteryHistory(mContext);
|
.getBatteryHistorySinceLastFullCharge(mContext);
|
||||||
|
|
||||||
assertThat(mBatteryHistoryLoader.loadInBackground())
|
assertThat(mBatteryHistoryLoader.loadInBackground())
|
||||||
.isSameInstanceAs(batteryHistoryMap);
|
.isSameInstanceAs(batteryHistoryMap);
|
||||||
|
@@ -26,6 +26,7 @@ import android.os.BatteryManager;
|
|||||||
import android.os.BatteryUsageStats;
|
import android.os.BatteryUsageStats;
|
||||||
import android.os.LocaleList;
|
import android.os.LocaleList;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
import com.android.settings.fuelgauge.BatteryUtils;
|
||||||
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
|
||||||
@@ -39,8 +40,8 @@ import org.mockito.MockitoAnnotations;
|
|||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.RuntimeEnvironment;
|
import org.robolectric.RuntimeEnvironment;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -173,7 +174,8 @@ public final class ConvertUtilsTest {
|
|||||||
public void getIndexedUsageMap_returnsExpectedResult() {
|
public void getIndexedUsageMap_returnsExpectedResult() {
|
||||||
// Creates the fake testing data.
|
// Creates the fake testing data.
|
||||||
final int timeSlotSize = 2;
|
final int timeSlotSize = 2;
|
||||||
final long[] batteryHistoryKeys = new long[]{101L, 102L, 103L, 104L, 105L};
|
final long[] batteryHistoryKeys = new long[]{generateTimestamp(0), generateTimestamp(1),
|
||||||
|
generateTimestamp(2), generateTimestamp(3), generateTimestamp(4)};
|
||||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
|
||||||
new HashMap<>();
|
new HashMap<>();
|
||||||
final BatteryHistEntry fakeEntry = createBatteryHistEntry(
|
final BatteryHistEntry fakeEntry = createBatteryHistEntry(
|
||||||
@@ -270,11 +272,11 @@ public final class ConvertUtilsTest {
|
|||||||
for (int index = 0; index < remainingSize; index++) {
|
for (int index = 0; index < remainingSize; index++) {
|
||||||
batteryHistoryMap.put(105L + index + 1, new HashMap<>());
|
batteryHistoryMap.put(105L + index + 1, new HashMap<>());
|
||||||
}
|
}
|
||||||
when(mPowerUsageFeatureProvider.getBatteryHistory(mContext))
|
when(mPowerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext))
|
||||||
.thenReturn(batteryHistoryMap);
|
.thenReturn(batteryHistoryMap);
|
||||||
|
|
||||||
final List<BatteryDiffEntry> batteryDiffEntryList =
|
final List<BatteryDiffEntry> batteryDiffEntryList =
|
||||||
BatteryChartPreferenceController.getBatteryLast24HrUsageData(mContext);
|
BatteryChartPreferenceController.getAppBatteryUsageData(mContext);
|
||||||
|
|
||||||
assertThat(batteryDiffEntryList).isNotEmpty();
|
assertThat(batteryDiffEntryList).isNotEmpty();
|
||||||
final BatteryDiffEntry resultEntry = batteryDiffEntryList.get(0);
|
final BatteryDiffEntry resultEntry = batteryDiffEntryList.get(0);
|
||||||
@@ -472,4 +474,9 @@ public final class ConvertUtilsTest {
|
|||||||
assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs);
|
assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs);
|
||||||
assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs);
|
assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Long generateTimestamp(int index) {
|
||||||
|
// "2021-04-23 07:00:00 UTC" + index hours
|
||||||
|
return 1619247600000L + index * DateUtils.HOUR_IN_MILLIS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user