Add battery chart view model.
Test: manual Bug: 239491373 Bug: 236101166 Change-Id: I1ae0e5fcc006855ac552fbbdfb4cd73f3dec52e7
This commit is contained in:
@@ -29,6 +29,7 @@ import android.text.format.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceGroup;
|
||||
@@ -53,7 +54,6 @@ import com.android.settingslib.utils.StringUtil;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -98,13 +98,13 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
@VisibleForTesting
|
||||
boolean mIsExpanded = false;
|
||||
@VisibleForTesting
|
||||
int[] mBatteryHistoryLevels;
|
||||
@VisibleForTesting
|
||||
long[] mBatteryHistoryKeys;
|
||||
@VisibleForTesting
|
||||
int mTrapezoidIndex = BatteryChartViewV2.SELECTED_INDEX_INVALID;
|
||||
BatteryChartViewModel mViewModel;
|
||||
@VisibleForTesting
|
||||
int mTrapezoidIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
|
||||
private boolean mIs24HourFormat = false;
|
||||
private boolean mIs24HourFormat;
|
||||
private boolean mIsFooterPrefAdded = false;
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
private FooterPreference mFooterPreference;
|
||||
@@ -252,10 +252,11 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
@Override
|
||||
public void onSelect(int trapezoidIndex) {
|
||||
Log.d(TAG, "onChartSelect:" + trapezoidIndex);
|
||||
refreshUi(trapezoidIndex, /*isForce=*/ false);
|
||||
mTrapezoidIndex = trapezoidIndex;
|
||||
refreshUi();
|
||||
mMetricsFeatureProvider.action(
|
||||
mPrefContext,
|
||||
trapezoidIndex == BatteryChartViewV2.SELECTED_INDEX_ALL
|
||||
trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
|
||||
? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
|
||||
: SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT);
|
||||
}
|
||||
@@ -276,18 +277,19 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
|
||||
mBatteryIndexedMap = null;
|
||||
mBatteryHistoryKeys = null;
|
||||
mBatteryHistoryLevels = null;
|
||||
mViewModel = null;
|
||||
addFooterPreferenceIfNeeded(false);
|
||||
return;
|
||||
}
|
||||
mBatteryHistoryKeys = getBatteryHistoryKeys(batteryHistoryMap);
|
||||
mBatteryHistoryLevels = new int[CHART_LEVEL_ARRAY_SIZE];
|
||||
List<Integer> levels = new ArrayList<Integer>();
|
||||
for (int index = 0; index < CHART_LEVEL_ARRAY_SIZE; index++) {
|
||||
final long timestamp = mBatteryHistoryKeys[index * 2];
|
||||
final Map<String, BatteryHistEntry> entryMap = batteryHistoryMap.get(timestamp);
|
||||
if (entryMap == null || entryMap.isEmpty()) {
|
||||
Log.e(TAG, "abnormal entry list in the timestamp:"
|
||||
+ ConvertUtils.utcToLocalTime(mPrefContext, timestamp));
|
||||
levels.add(0);
|
||||
continue;
|
||||
}
|
||||
// Averages the battery level in each time slot to avoid corner conditions.
|
||||
@@ -295,16 +297,17 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
for (BatteryHistEntry entry : entryMap.values()) {
|
||||
batteryLevelCounter += entry.mBatteryLevel;
|
||||
}
|
||||
mBatteryHistoryLevels[index] =
|
||||
Math.round(batteryLevelCounter / entryMap.size());
|
||||
levels.add(Math.round(batteryLevelCounter / entryMap.size()));
|
||||
}
|
||||
forceRefreshUi();
|
||||
final List<String> texts = generateTimestampTexts(mBatteryHistoryKeys, mContext);
|
||||
mViewModel = new BatteryChartViewModel(levels, texts, mTrapezoidIndex);
|
||||
refreshUi();
|
||||
Log.d(TAG, String.format(
|
||||
"setBatteryHistoryMap() size=%d key=%s\nlevels=%s",
|
||||
"setBatteryHistoryMap() size=%d key=%s\nview model=%s",
|
||||
batteryHistoryMap.size(),
|
||||
ConvertUtils.utcToLocalTime(mPrefContext,
|
||||
mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1]),
|
||||
Arrays.toString(mBatteryHistoryLevels)));
|
||||
mViewModel));
|
||||
|
||||
// Loads item icon and label in the background.
|
||||
new LoadAllItemsInfoTask(batteryHistoryMap).execute();
|
||||
@@ -319,35 +322,20 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
private void setBatteryChartViewInner(final BatteryChartViewV2 batteryChartView) {
|
||||
mBatteryChartView = batteryChartView;
|
||||
mBatteryChartView.setOnSelectListener(this);
|
||||
forceRefreshUi();
|
||||
}
|
||||
|
||||
private void forceRefreshUi() {
|
||||
final int refreshIndex =
|
||||
mTrapezoidIndex == BatteryChartViewV2.SELECTED_INDEX_INVALID
|
||||
? BatteryChartViewV2.SELECTED_INDEX_ALL
|
||||
: mTrapezoidIndex;
|
||||
if (mBatteryChartView != null) {
|
||||
mBatteryChartView.setLevels(mBatteryHistoryLevels);
|
||||
mBatteryChartView.setSelectedIndex(refreshIndex);
|
||||
setTimestampLabel();
|
||||
}
|
||||
refreshUi(refreshIndex, /*isForce=*/ true);
|
||||
refreshUi();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
boolean refreshUi(int trapezoidIndex, boolean isForce) {
|
||||
boolean refreshUi() {
|
||||
// Invalid refresh condition.
|
||||
if (mBatteryIndexedMap == null
|
||||
|| mBatteryChartView == null
|
||||
|| (mTrapezoidIndex == trapezoidIndex && !isForce)) {
|
||||
if (mBatteryIndexedMap == null || mBatteryChartView == null) {
|
||||
return false;
|
||||
}
|
||||
Log.d(TAG, String.format("refreshUi: index=%d size=%d isForce:%b",
|
||||
trapezoidIndex, mBatteryIndexedMap.size(), isForce));
|
||||
if (mViewModel != null) {
|
||||
mViewModel.setSelectedIndex(mTrapezoidIndex);
|
||||
}
|
||||
mBatteryChartView.setViewModel(mViewModel);
|
||||
|
||||
mTrapezoidIndex = trapezoidIndex;
|
||||
mBatteryChartView.setSelectedIndex(mTrapezoidIndex);
|
||||
mHandler.post(() -> {
|
||||
final long start = System.currentTimeMillis();
|
||||
removeAndCacheAllPrefs();
|
||||
@@ -584,20 +572,6 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
return !contains(packageName, mNotAllowShowEntryPackages);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void setTimestampLabel() {
|
||||
if (mBatteryChartView == null || mBatteryHistoryKeys == null) {
|
||||
return;
|
||||
}
|
||||
final boolean is24HourFormat = DateFormat.is24HourFormat(mContext);
|
||||
final String[] labels = new String[mBatteryHistoryKeys.length];
|
||||
for (int i = 0; i < mBatteryHistoryKeys.length; i++) {
|
||||
labels[i] = ConvertUtils.utcToLocalTimeHour(mContext, mBatteryHistoryKeys[i],
|
||||
is24HourFormat);
|
||||
}
|
||||
mBatteryChartView.setAxisLabels(labels);
|
||||
}
|
||||
|
||||
private void addFooterPreferenceIfNeeded(boolean containAppItems) {
|
||||
if (mIsFooterPrefAdded || mFooterPreference == null) {
|
||||
return;
|
||||
@@ -610,6 +584,17 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference));
|
||||
}
|
||||
|
||||
private static List<String> generateTimestampTexts(
|
||||
@NonNull long[] timestamps, Context context) {
|
||||
final boolean is24HourFormat = DateFormat.is24HourFormat(context);
|
||||
final List<String> texts = new ArrayList<String>();
|
||||
for (int index = 0; index < CHART_LEVEL_ARRAY_SIZE; index++) {
|
||||
texts.add(ConvertUtils.utcToLocalTimeHour(context, timestamps[index * 2],
|
||||
is24HourFormat));
|
||||
}
|
||||
return texts;
|
||||
}
|
||||
|
||||
private static boolean contains(String target, CharSequence[] packageNames) {
|
||||
if (target != null && packageNames != null) {
|
||||
for (CharSequence packageName : packageNames) {
|
||||
@@ -654,7 +639,7 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
getBatteryHistoryKeys(batteryHistoryMap),
|
||||
batteryHistoryMap,
|
||||
/*purgeLowPercentageAndFakeData=*/ true);
|
||||
return batteryIndexedMap.get(BatteryChartViewV2.SELECTED_INDEX_ALL);
|
||||
return batteryIndexedMap.get(BatteryChartViewModel.SELECTED_INDEX_ALL);
|
||||
}
|
||||
|
||||
/** Used for {@link AppBatteryPreferenceController}. */
|
||||
@@ -735,7 +720,7 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
|
||||
// Posts results back to main thread to refresh UI.
|
||||
mHandler.post(() -> {
|
||||
mBatteryIndexedMap = indexedUsageMap;
|
||||
forceRefreshUi();
|
||||
refreshUi();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.settings.fuelgauge.batteryusage;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.util.Preconditions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
/** The view model of {@code BatteryChartViewV2} */
|
||||
class BatteryChartViewModel {
|
||||
private static final String TAG = "BatteryChartViewModel";
|
||||
|
||||
public static final int SELECTED_INDEX_ALL = -1;
|
||||
public static final int SELECTED_INDEX_INVALID = -2;
|
||||
|
||||
// We need at least 2 levels to draw a trapezoid.
|
||||
private static final int MIN_LEVELS_DATA_SIZE = 2;
|
||||
|
||||
private final List<Integer> mLevels;
|
||||
private final List<String> mTexts;
|
||||
private int mSelectedIndex;
|
||||
|
||||
BatteryChartViewModel(
|
||||
@NonNull List<Integer> levels, @NonNull List<String> texts, int selectedIndex) {
|
||||
Preconditions.checkArgument(
|
||||
levels.size() == texts.size()
|
||||
&& levels.size() >= MIN_LEVELS_DATA_SIZE
|
||||
&& selectedIndex >= SELECTED_INDEX_ALL
|
||||
&& selectedIndex < levels.size(),
|
||||
String.format(Locale.getDefault(), "Invalid BatteryChartViewModel"
|
||||
+ " levels.size: %d\ntexts.size: %d\nselectedIndex: %d.",
|
||||
levels.size(), texts.size(), selectedIndex));
|
||||
mLevels = levels;
|
||||
mTexts = texts;
|
||||
mSelectedIndex = selectedIndex;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return mLevels.size();
|
||||
}
|
||||
|
||||
public List<Integer> levels() {
|
||||
return mLevels;
|
||||
}
|
||||
|
||||
public List<String> texts() {
|
||||
return mTexts;
|
||||
}
|
||||
|
||||
public int selectedIndex() {
|
||||
return mSelectedIndex;
|
||||
}
|
||||
|
||||
public void setSelectedIndex(int index) {
|
||||
mSelectedIndex = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mLevels, mTexts, mSelectedIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
} else if (!(other instanceof BatteryChartViewModel)) {
|
||||
return false;
|
||||
}
|
||||
final BatteryChartViewModel batteryChartViewModel = (BatteryChartViewModel) other;
|
||||
return Objects.equals(mLevels, batteryChartViewModel.mLevels)
|
||||
&& Objects.equals(mTexts, batteryChartViewModel.mTexts)
|
||||
&& mSelectedIndex == batteryChartViewModel.mSelectedIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.getDefault(), "levels: %s\ntexts: %s\nselectedIndex: %d",
|
||||
Objects.toString(mLevels), Objects.toString(mTexts), mSelectedIndex);
|
||||
}
|
||||
}
|
@@ -20,7 +20,6 @@ import static com.android.settings.Utils.formatPercentage;
|
||||
import static java.lang.Math.round;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
@@ -38,6 +37,7 @@ import android.view.View;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
@@ -61,16 +61,14 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5");
|
||||
private static final long UPDATE_STATE_DELAYED_TIME = 500L;
|
||||
|
||||
/** Selects all trapezoid shapes. */
|
||||
public static final int SELECTED_INDEX_ALL = -1;
|
||||
public static final int SELECTED_INDEX_INVALID = -2;
|
||||
|
||||
/** A callback listener for selected group index is updated. */
|
||||
public interface OnSelectListener {
|
||||
/** The callback function for selected group index is updated. */
|
||||
void onSelect(int trapezoidIndex);
|
||||
}
|
||||
|
||||
private BatteryChartViewModel mViewModel;
|
||||
|
||||
private int mDividerWidth;
|
||||
private int mDividerHeight;
|
||||
private float mTrapezoidVOffset;
|
||||
@@ -79,9 +77,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
private String[] mPercentages = getPercentages();
|
||||
|
||||
@VisibleForTesting
|
||||
int mHoveredIndex = SELECTED_INDEX_INVALID;
|
||||
@VisibleForTesting
|
||||
int mSelectedIndex = SELECTED_INDEX_INVALID;
|
||||
int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
@VisibleForTesting
|
||||
String[] mAxisLabels;
|
||||
|
||||
@@ -103,7 +99,6 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
@VisibleForTesting
|
||||
final Runnable mUpdateClickableStateRun = () -> updateClickableState();
|
||||
|
||||
private int[] mLevels;
|
||||
private Paint mTextPaint;
|
||||
private Paint mDividerPaint;
|
||||
private Paint mTrapezoidPaint;
|
||||
@@ -126,44 +121,26 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
initializeColors(context);
|
||||
// Registers the click event listener.
|
||||
setOnClickListener(this);
|
||||
setSelectedIndex(SELECTED_INDEX_ALL);
|
||||
setClickable(false);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/** Sets all levels value to draw the trapezoid shape */
|
||||
public void setLevels(int[] levels) {
|
||||
Log.d(TAG, "setLevels() " + (levels == null ? "null" : levels.length));
|
||||
// At least 2 levels to draw a trapezoid.
|
||||
if (levels == null || levels.length < 2) {
|
||||
mLevels = null;
|
||||
/** Sets the data model of this view. */
|
||||
public void setViewModel(BatteryChartViewModel viewModel) {
|
||||
if (viewModel == null) {
|
||||
mViewModel = null;
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
mLevels = levels;
|
||||
|
||||
// Initialize trapezoid slots.
|
||||
mTrapezoidSlots = new TrapezoidSlot[mLevels.length - 1];
|
||||
for (int index = 0; index < mTrapezoidSlots.length; index++) {
|
||||
mTrapezoidSlots[index] = new TrapezoidSlot();
|
||||
}
|
||||
Log.d(TAG, String.format("setViewModel(): size: %d, selectedIndex: %d.",
|
||||
viewModel.size(), viewModel.selectedIndex()));
|
||||
mViewModel = viewModel;
|
||||
|
||||
setClickable(false);
|
||||
invalidate();
|
||||
// Sets the chart is clickable if there is at least one valid item in it.
|
||||
for (int index = 0; index < mLevels.length - 1; index++) {
|
||||
if (mLevels[index] != 0 && mLevels[index + 1] != 0) {
|
||||
setClickable(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the selected group index to draw highlight effect. */
|
||||
public void setSelectedIndex(int index) {
|
||||
if (mSelectedIndex != index) {
|
||||
mSelectedIndex = index;
|
||||
invalidate();
|
||||
}
|
||||
initializeTrapezoidSlots(viewModel.size() - 1);
|
||||
initializeAxisLabels(viewModel.texts());
|
||||
setClickable(hasNonZeroTrapezoid(viewModel.levels()));
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/** Sets the callback to monitor the selected group index. */
|
||||
@@ -184,26 +161,6 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the X-axis labels list for each level. This class will choose some labels among the
|
||||
* input list to show.
|
||||
*
|
||||
* @param labels The length of this parameter should be the same as the length of
|
||||
* {@code levels}.
|
||||
*/
|
||||
public void setAxisLabels(@NonNull String[] labels) {
|
||||
if (mAxisLabels == null) {
|
||||
mAxisLabels = new String[DEFAULT_AXIS_LABEL_COUNT];
|
||||
}
|
||||
// Current logic is always showing {@code AXIS_LABEL_GAPS_COUNT} labels.
|
||||
// TODO: Support different count of labels for different levels sizes.
|
||||
final int step = (labels.length - 1) / AXIS_LABEL_GAPS_COUNT;
|
||||
for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
|
||||
mAxisLabels[index] = labels[index * step];
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
@@ -240,7 +197,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
// Before mLevels initialized, the count of trapezoids is unknown. Only draws the
|
||||
// horizontal percentages and dividers.
|
||||
drawHorizontalDividers(canvas);
|
||||
if (mLevels == null) {
|
||||
if (mViewModel == null) {
|
||||
return;
|
||||
}
|
||||
drawVerticalDividers(canvas);
|
||||
@@ -282,7 +239,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
public void onHoverChanged(boolean hovered) {
|
||||
super.onHoverChanged(hovered);
|
||||
if (!hovered) {
|
||||
mHoveredIndex = SELECTED_INDEX_INVALID; // reset
|
||||
mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
@@ -295,13 +252,15 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
}
|
||||
final int trapezoidIndex = getTrapezoidIndex(mTouchUpEventX);
|
||||
// Ignores the click event if the level is zero.
|
||||
if (trapezoidIndex == SELECTED_INDEX_INVALID
|
||||
if (trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID
|
||||
|| !isValidToDraw(trapezoidIndex)) {
|
||||
return;
|
||||
}
|
||||
if (mOnSelectListener != null) {
|
||||
// Selects all if users click the same trapezoid item two times.
|
||||
mOnSelectListener.onSelect(
|
||||
trapezoidIndex == mSelectedIndex ? SELECTED_INDEX_ALL : trapezoidIndex);
|
||||
trapezoidIndex == mViewModel.selectedIndex()
|
||||
? BatteryChartViewModel.SELECTED_INDEX_ALL : trapezoidIndex);
|
||||
}
|
||||
view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||
}
|
||||
@@ -350,8 +309,8 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
mTrapezoidCurvePaint.setStrokeWidth(mDividerWidth * 2);
|
||||
} else if (mIsSlotsClickabled) {
|
||||
mTrapezoidCurvePaint = null;
|
||||
// Sets levels again to force update the click state.
|
||||
setLevels(mLevels);
|
||||
// Sets view model again to force update the click state.
|
||||
setViewModel(mViewModel);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
@@ -366,6 +325,28 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
super.setClickable(clickable);
|
||||
}
|
||||
|
||||
private void initializeTrapezoidSlots(int count) {
|
||||
mTrapezoidSlots = new TrapezoidSlot[count];
|
||||
for (int index = 0; index < mTrapezoidSlots.length; index++) {
|
||||
mTrapezoidSlots[index] = new TrapezoidSlot();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the displayed X-axis labels list selected from the model all texts list.
|
||||
*/
|
||||
private void initializeAxisLabels(@NonNull List<String> allTexts) {
|
||||
if (mAxisLabels == null) {
|
||||
mAxisLabels = new String[DEFAULT_AXIS_LABEL_COUNT];
|
||||
}
|
||||
// Current logic is always showing {@code AXIS_LABEL_GAPS_COUNT} labels.
|
||||
// TODO: Support different count of labels for different levels sizes.
|
||||
final int step = (allTexts.size() - 1) / AXIS_LABEL_GAPS_COUNT;
|
||||
for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
|
||||
mAxisLabels[index] = allTexts.get(index * step);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeColors(Context context) {
|
||||
setBackgroundColor(Color.TRANSPARENT);
|
||||
mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
|
||||
@@ -498,7 +479,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
|
||||
private void drawTrapezoids(Canvas canvas) {
|
||||
// Ignores invalid trapezoid data.
|
||||
if (mLevels == null) {
|
||||
if (mViewModel == null) {
|
||||
return;
|
||||
}
|
||||
final float trapezoidBottom =
|
||||
@@ -519,17 +500,17 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
continue;
|
||||
}
|
||||
// Configures the trapezoid paint color.
|
||||
final int trapezoidColor =
|
||||
!mIsSlotsClickabled
|
||||
? mTrapezoidColor
|
||||
: mSelectedIndex == index || mSelectedIndex == SELECTED_INDEX_ALL
|
||||
? mTrapezoidSolidColor : mTrapezoidColor;
|
||||
final int trapezoidColor = mIsSlotsClickabled && (mViewModel.selectedIndex() == index
|
||||
|| mViewModel.selectedIndex() == BatteryChartViewModel.SELECTED_INDEX_ALL)
|
||||
? mTrapezoidSolidColor : mTrapezoidColor;
|
||||
final boolean isHoverState =
|
||||
mIsSlotsClickabled && mHoveredIndex == index && isValidToDraw(mHoveredIndex);
|
||||
mTrapezoidPaint.setColor(isHoverState ? mTrapezoidHoverColor : trapezoidColor);
|
||||
|
||||
final float leftTop = round(trapezoidBottom - mLevels[index] * unitHeight);
|
||||
final float rightTop = round(trapezoidBottom - mLevels[index + 1] * unitHeight);
|
||||
final float leftTop = round(
|
||||
trapezoidBottom - mViewModel.levels().get(index) * unitHeight);
|
||||
final float rightTop = round(
|
||||
trapezoidBottom - mViewModel.levels().get(index + 1) * unitHeight);
|
||||
trapezoidPath.reset();
|
||||
trapezoidPath.moveTo(mTrapezoidSlots[index].mLeft, trapezoidBottom);
|
||||
trapezoidPath.lineTo(mTrapezoidSlots[index].mLeft, leftTop);
|
||||
@@ -568,15 +549,25 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return SELECTED_INDEX_INVALID;
|
||||
return BatteryChartViewModel.SELECTED_INDEX_INVALID;
|
||||
}
|
||||
|
||||
private boolean isValidToDraw(int trapezoidIndex) {
|
||||
return mLevels != null
|
||||
return mViewModel != null
|
||||
&& trapezoidIndex >= 0
|
||||
&& trapezoidIndex < mLevels.length - 1
|
||||
&& mLevels[trapezoidIndex] != 0
|
||||
&& mLevels[trapezoidIndex + 1] != 0;
|
||||
&& trapezoidIndex < mViewModel.size() - 1
|
||||
&& mViewModel.levels().get(trapezoidIndex) != 0
|
||||
&& mViewModel.levels().get(trapezoidIndex + 1) != 0;
|
||||
}
|
||||
|
||||
private static boolean hasNonZeroTrapezoid(List<Integer> levels) {
|
||||
// Sets the chart is clickable if there is at least one valid item in it.
|
||||
for (int index = 0; index < levels.size() - 1; index++) {
|
||||
if (levels.get(index) != 0 && levels.get(index + 1) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String[] getPercentages() {
|
||||
|
@@ -55,6 +55,7 @@ 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;
|
||||
@@ -173,11 +174,11 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
|
||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index])
|
||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
||||
.isEqualTo(index + 1);
|
||||
.isEqualTo(generateTimestamp(index));
|
||||
}
|
||||
// Verifies the created battery levels array.
|
||||
for (int index = 0; index < 13; index++) {
|
||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index])
|
||||
assertThat(mBatteryChartPreferenceController.mViewModel.levels().get(index))
|
||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
||||
.isEqualTo(100 - index * 2);
|
||||
}
|
||||
@@ -193,79 +194,63 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
|
||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryKeys[index])
|
||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
||||
.isEqualTo(index + 1);
|
||||
.isEqualTo(generateTimestamp(index));
|
||||
}
|
||||
// Verifies the created battery levels array.
|
||||
for (int index = 0; index < 13; index++) {
|
||||
assertThat(mBatteryChartPreferenceController.mBatteryHistoryLevels[index])
|
||||
assertThat(mBatteryChartPreferenceController.mViewModel.levels().get(index))
|
||||
// These values is are calculated by hand from createBatteryHistoryMap().
|
||||
.isEqualTo(100 - index * 2);
|
||||
}
|
||||
assertThat(mBatteryChartPreferenceController.mBatteryIndexedMap).hasSize(13);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetBatteryChartViewModel() {
|
||||
verify(mBatteryChartPreferenceController.mBatteryChartView)
|
||||
.setViewModel(new BatteryChartViewModel(
|
||||
List.of(100, 98, 96, 94, 92, 90, 88, 86, 84, 82, 80, 78, 76),
|
||||
List.of("7 am", "9 am", "11 am", "1 pm", "3 pm", "5 pm", "7 pm",
|
||||
"9 pm", "11 pm", "1 am", "3 am", "5 am", "7 am"),
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshUi_refresh() {
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshUi_batteryIndexedMapIsNull_ignoreRefresh() {
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(null);
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
||||
/*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshUi_batteryChartViewIsNull_ignoreRefresh() {
|
||||
mBatteryChartPreferenceController.mBatteryChartView = null;
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
||||
/*trapezoidIndex=*/ 1, /*isForce=*/ false)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshUi_trapezoidIndexIsNotChanged_ignoreRefresh() {
|
||||
final int trapezoidIndex = 1;
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex;
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
||||
trapezoidIndex, /*isForce=*/ false)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefreshUi_forceUpdate_refreshUi() {
|
||||
final int trapezoidIndex = 1;
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex = trapezoidIndex;
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi(
|
||||
trapezoidIndex, /*isForce=*/ true)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForceRefreshUi_updateTrapezoidIndexIntoSelectAll() {
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex =
|
||||
BatteryChartViewV2.SELECTED_INDEX_INVALID;
|
||||
mBatteryChartPreferenceController.setBatteryHistoryMap(
|
||||
createBatteryHistoryMap());
|
||||
|
||||
assertThat(mBatteryChartPreferenceController.mTrapezoidIndex)
|
||||
.isEqualTo(BatteryChartViewV2.SELECTED_INDEX_ALL);
|
||||
assertThat(mBatteryChartPreferenceController.refreshUi()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAndCacheAllPrefs_emptyContent_ignoreRemoveAll() {
|
||||
final int trapezoidIndex = 1;
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex = 1;
|
||||
doReturn(0).when(mAppListGroup).getPreferenceCount();
|
||||
|
||||
mBatteryChartPreferenceController.refreshUi(
|
||||
trapezoidIndex, /*isForce=*/ true);
|
||||
mBatteryChartPreferenceController.refreshUi();
|
||||
verify(mAppListGroup, never()).removeAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAndCacheAllPrefs_buildCacheAndRemoveAllPreference() {
|
||||
final int trapezoidIndex = 1;
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex = 1;
|
||||
doReturn(1).when(mAppListGroup).getPreferenceCount();
|
||||
doReturn(mPowerGaugePreference).when(mAppListGroup).getPreference(0);
|
||||
doReturn(PREF_KEY).when(mPowerGaugePreference).getKey();
|
||||
// Ensures the testing data is correct.
|
||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache).isEmpty();
|
||||
|
||||
mBatteryChartPreferenceController.refreshUi(
|
||||
trapezoidIndex, /*isForce=*/ true);
|
||||
mBatteryChartPreferenceController.refreshUi();
|
||||
|
||||
assertThat(mBatteryChartPreferenceController.mPreferenceCache.get(PREF_KEY))
|
||||
.isEqualTo(mPowerGaugePreference);
|
||||
@@ -522,7 +507,7 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
@Test
|
||||
public void testOnSelect_selectAll_logMetric() {
|
||||
mBatteryChartPreferenceController.onSelect(
|
||||
BatteryChartViewV2.SELECTED_INDEX_ALL /*slot index*/);
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL /*slot index*/);
|
||||
|
||||
verify(mMetricsFeatureProvider)
|
||||
.action(mContext, SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL);
|
||||
@@ -562,7 +547,7 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
spy(new ExpandDividerPreference(mContext));
|
||||
// Simulates select all condition.
|
||||
mBatteryChartPreferenceController.mTrapezoidIndex =
|
||||
BatteryChartViewV2.SELECTED_INDEX_ALL;
|
||||
BatteryChartViewModel.SELECTED_INDEX_ALL;
|
||||
|
||||
mBatteryChartPreferenceController.refreshCategoryTitle();
|
||||
|
||||
@@ -580,44 +565,6 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
.isEqualTo("System usage for past 24 hr");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTimestampLabel_nullBatteryHistoryKeys_ignore() {
|
||||
mBatteryChartPreferenceController = createController();
|
||||
mBatteryChartPreferenceController.mBatteryHistoryKeys = null;
|
||||
mBatteryChartPreferenceController.mBatteryChartView =
|
||||
spy(new BatteryChartViewV2(mContext));
|
||||
mBatteryChartPreferenceController.setTimestampLabel();
|
||||
|
||||
verify(mBatteryChartPreferenceController.mBatteryChartView, never())
|
||||
.setAxisLabels(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTimestampLabel_setExpectedTimestampData() {
|
||||
mBatteryChartPreferenceController = createController();
|
||||
mBatteryChartPreferenceController.mBatteryChartView =
|
||||
spy(new BatteryChartViewV2(mContext));
|
||||
setUpBatteryHistoryKeys();
|
||||
|
||||
mBatteryChartPreferenceController.setTimestampLabel();
|
||||
|
||||
verify(mBatteryChartPreferenceController.mBatteryChartView)
|
||||
.setAxisLabels(new String[] {"4 pm", "12 am", "7 am"});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetTimestampLabel_withoutValidTimestamp_setExpectedTimestampData() {
|
||||
mBatteryChartPreferenceController = createController();
|
||||
mBatteryChartPreferenceController.mBatteryChartView =
|
||||
spy(new BatteryChartViewV2(mContext));
|
||||
mBatteryChartPreferenceController.mBatteryHistoryKeys = new long[]{0L};
|
||||
|
||||
mBatteryChartPreferenceController.setTimestampLabel();
|
||||
|
||||
verify(mBatteryChartPreferenceController.mBatteryChartView)
|
||||
.setAxisLabels(new String[] {"12 am"});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnSaveInstanceState_restoreSelectedIndexAndExpandState() {
|
||||
final int expectedIndex = 1;
|
||||
@@ -663,6 +610,11 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
.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() {
|
||||
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
|
||||
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {
|
||||
@@ -671,7 +623,7 @@ public final class BatteryChartPreferenceControllerV2Test {
|
||||
final BatteryHistEntry entry = new BatteryHistEntry(values);
|
||||
final Map<String, BatteryHistEntry> entryMap = new HashMap<>();
|
||||
entryMap.put("fake_entry_key" + index, entry);
|
||||
batteryHistoryMap.put(Long.valueOf(index + 1), entryMap);
|
||||
batteryHistoryMap.put(generateTimestamp(index), entryMap);
|
||||
}
|
||||
return batteryHistoryMap;
|
||||
}
|
||||
|
@@ -42,6 +42,7 @@ import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@@ -100,13 +101,15 @@ public final class BatteryChartViewV2Test {
|
||||
|
||||
@Test
|
||||
public void onClick_invokesCallback() {
|
||||
mBatteryChartView.setLevels(new int[] {90, 80, 70, 60});
|
||||
final int originalSelectedIndex = 2;
|
||||
mBatteryChartView.setViewModel(
|
||||
new BatteryChartViewModel(List.of(90, 80, 70, 60), List.of("", "", "", ""),
|
||||
originalSelectedIndex));
|
||||
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;
|
||||
}
|
||||
mBatteryChartView.mSelectedIndex = 2;
|
||||
final int[] selectedIndex = new int[1];
|
||||
mBatteryChartView.setOnSelectListener(
|
||||
trapezoidIndex -> {
|
||||
@@ -123,7 +126,7 @@ public final class BatteryChartViewV2Test {
|
||||
mBatteryChartView.mTouchUpEventX = 2;
|
||||
selectedIndex[0] = Integer.MIN_VALUE;
|
||||
mBatteryChartView.onClick(mMockView);
|
||||
assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewV2.SELECTED_INDEX_ALL);
|
||||
assertThat(selectedIndex[0]).isEqualTo(BatteryChartViewModel.SELECTED_INDEX_ALL);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -178,11 +181,14 @@ public final class BatteryChartViewV2Test {
|
||||
|
||||
@Test
|
||||
public void clickable_restoreFromNonClickableState() {
|
||||
final int[] levels = new int[13];
|
||||
for (int index = 0; index < levels.length; index++) {
|
||||
levels[index] = index + 1;
|
||||
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.setLevels(levels);
|
||||
mBatteryChartView.setViewModel(new BatteryChartViewModel(
|
||||
levels, texts, BatteryChartViewModel.SELECTED_INDEX_ALL));
|
||||
mBatteryChartView.setClickableForce(true);
|
||||
when(mPowerUsageFeatureProvider.isChartGraphSlotsEnabled(mContext))
|
||||
.thenReturn(true);
|
||||
|
Reference in New Issue
Block a user