Refactor BatteryChartView X-axis labels. Instead of

only timestamps, also support any string[] labels.

Bug: 236101166
Test: manual
Change-Id: I84763ccce0ee63da0b5b26e1416bf5bd5b58963d
This commit is contained in:
Zaiyue Xue
2022-07-15 16:11:53 +08:00
parent 661c035a1e
commit 24bc269ec1
3 changed files with 68 additions and 64 deletions

View File

@@ -588,9 +588,13 @@ public class BatteryChartPreferenceControllerV2 extends AbstractPreferenceContro
if (mBatteryChartView == null || mBatteryHistoryKeys == null) { if (mBatteryChartView == null || mBatteryHistoryKeys == null) {
return; return;
} }
final long latestTimestamp = final boolean is24HourFormat = DateFormat.is24HourFormat(mContext);
mBatteryHistoryKeys[mBatteryHistoryKeys.length - 1]; final String[] labels = new String[mBatteryHistoryKeys.length];
mBatteryChartView.setLatestTimestamp(latestTimestamp); for (int i = 0; i < mBatteryHistoryKeys.length; i++) {
labels[i] = ConvertUtils.utcToLocalTimeHour(mContext, mBatteryHistoryKeys[i],
is24HourFormat);
}
mBatteryChartView.setAxisLabels(labels);
} }
private void addFooterPreferenceIfNeeded(boolean containAppItems) { private void addFooterPreferenceIfNeeded(boolean containAppItems) {

View File

@@ -20,6 +20,7 @@ import static com.android.settings.Utils.formatPercentage;
import static java.lang.Math.round; import static java.lang.Math.round;
import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Canvas; import android.graphics.Canvas;
@@ -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;
@@ -46,7 +45,6 @@ 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.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -59,8 +57,8 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService"); Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService");
private static final int DEFAULT_TRAPEZOID_COUNT = 12; private static final int DEFAULT_TRAPEZOID_COUNT = 12;
private static final int DEFAULT_TIMESTAMP_COUNT = 4; private static final int DEFAULT_AXIS_LABEL_COUNT = 4;
private static final int TIMESTAMP_GAPS_COUNT = DEFAULT_TIMESTAMP_COUNT - 1; private static final int AXIS_LABEL_GAPS_COUNT = DEFAULT_AXIS_LABEL_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;
@@ -87,7 +85,7 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
@VisibleForTesting @VisibleForTesting
int mSelectedIndex = SELECTED_INDEX_INVALID; int mSelectedIndex = SELECTED_INDEX_INVALID;
@VisibleForTesting @VisibleForTesting
String[] mTimestamps; String[] mAxisLabels;
// Colors for drawing the trapezoid shape and dividers. // Colors for drawing the trapezoid shape and dividers.
private int mTrapezoidColor; private int mTrapezoidColor;
@@ -98,8 +96,8 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
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 Rect[] mAxisLabelsBounds =
new Rect[]{new Rect(), new Rect(), new Rect(), new Rect()}; new Rect[]{new Rect(), new Rect(), new Rect(), new Rect()};
@VisibleForTesting @VisibleForTesting
@@ -131,7 +129,6 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
setSelectedIndex(SELECTED_INDEX_ALL); setSelectedIndex(SELECTED_INDEX_ALL);
setTrapezoidCount(DEFAULT_TRAPEZOID_COUNT); setTrapezoidCount(DEFAULT_TRAPEZOID_COUNT);
setClickable(false); setClickable(false);
setLatestTimestamp(0);
} }
/** Sets the total trapezoid count for drawing. */ /** Sets the total trapezoid count for drawing. */
@@ -199,24 +196,22 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
requestLayout(); requestLayout();
} }
/** Sets the latest timestamp for drawing into x-axis information. */ /**
public void setLatestTimestamp(long latestTimestamp) { * Sets the X-axis labels list for each level. This class will choose some labels among the
if (latestTimestamp == 0) { * input list to show.
latestTimestamp = Clock.systemUTC().millis(); *
* @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];
} }
if (mTimestamps == null) { // Current logic is always showing {@code AXIS_LABEL_GAPS_COUNT} labels.
mTimestamps = new String[DEFAULT_TIMESTAMP_COUNT]; // TODO: Support different count of labels for different levels sizes.
} final int step = (labels.length - 1) / AXIS_LABEL_GAPS_COUNT;
final long timeSlotOffset = for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
DateUtils.HOUR_IN_MILLIS * (/*total 24 hours*/ 24 / TIMESTAMP_GAPS_COUNT); mAxisLabels[index] = labels[index * step];
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();
} }
@@ -235,13 +230,13 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
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 (mAxisLabels != null) {
int maxHeight = 0; int maxHeight = 0;
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
mTextPaint.getTextBounds( mTextPaint.getTextBounds(
mTimestamps[index], 0, mTimestamps[index].length(), mAxisLabels[index], 0, mAxisLabels[index].length(),
mTimestampsBounds[index]); mAxisLabelsBounds[index]);
maxHeight = Math.max(maxHeight, mTimestampsBounds[index].height()); maxHeight = Math.max(maxHeight, mAxisLabelsBounds[index].height());
} }
mIndent.bottom = maxHeight + round(mTextPadding * 1.5f); mIndent.bottom = maxHeight + round(mTextPadding * 1.5f);
} }
@@ -463,47 +458,50 @@ public class BatteryChartViewV2 extends AppCompatImageView implements View.OnCli
} }
startX = nextX; startX = nextX;
} }
// Draws the timestamp slot information. // Draws the axis label slot information.
if (mTimestamps != null) { if (mAxisLabels != null) {
final float[] xOffsets = new float[DEFAULT_TIMESTAMP_COUNT]; final float[] xOffsets = new float[DEFAULT_AXIS_LABEL_COUNT];
final float baselineX = mDividerWidth * .5f; final float baselineX = mDividerWidth * .5f;
final float offsetX = mDividerWidth + unitWidth; final float offsetX = mDividerWidth + unitWidth;
final int slotBarOffset = (/*total 12 bars*/ 12) / TIMESTAMP_GAPS_COUNT; // TODO: Support different count of labels for different levels sizes.
for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { final int slotBarOffset = (/*total 12 bars*/ 12) / AXIS_LABEL_GAPS_COUNT;
for (int index = 0; index < DEFAULT_AXIS_LABEL_COUNT; index++) {
xOffsets[index] = baselineX + index * offsetX * slotBarOffset; xOffsets[index] = baselineX + index * offsetX * slotBarOffset;
} }
drawTimestamp(canvas, xOffsets); drawAxisLabel(canvas, xOffsets);
} }
} }
private void drawTimestamp(Canvas canvas, float[] xOffsets) { private void drawAxisLabel(Canvas canvas, float[] xOffsets) {
// Draws the 1st timestamp info. // Draws the 1st axis label info.
canvas.drawText( canvas.drawText(
mTimestamps[0], mAxisLabels[0], xOffsets[0] - mAxisLabelsBounds[0].left, getAxisLabelY(0),
xOffsets[0] - mTimestampsBounds[0].left, mTextPaint);
getTimestampY(0), mTextPaint); final int latestIndex = DEFAULT_AXIS_LABEL_COUNT - 1;
final int latestIndex = DEFAULT_TIMESTAMP_COUNT - 1; // Draws the last axis label info.
// Draws the last timestamp info.
canvas.drawText( canvas.drawText(
mTimestamps[latestIndex], mAxisLabels[latestIndex],
xOffsets[latestIndex] - mTimestampsBounds[latestIndex].width() xOffsets[latestIndex]
- mTimestampsBounds[latestIndex].left, - mAxisLabelsBounds[latestIndex].width()
getTimestampY(latestIndex), mTextPaint); - mAxisLabelsBounds[latestIndex].left,
// Draws the rest of timestamp info since it is located in the center. getAxisLabelY(latestIndex),
for (int index = 1; index <= DEFAULT_TIMESTAMP_COUNT - 2; index++) { mTextPaint);
// Draws the rest of axis label info since it is located in the center.
for (int index = 1; index <= DEFAULT_AXIS_LABEL_COUNT - 2; index++) {
canvas.drawText( canvas.drawText(
mTimestamps[index], mAxisLabels[index],
xOffsets[index] xOffsets[index]
- (mTimestampsBounds[index].width() - mTimestampsBounds[index].left) - (mAxisLabelsBounds[index].width() - mAxisLabelsBounds[index].left)
* .5f, * .5f,
getTimestampY(index), mTextPaint); getAxisLabelY(index),
mTextPaint);
} }
} }
private int getTimestampY(int index) { private int getAxisLabelY(int index) {
return getHeight() - mTimestampsBounds[index].height() return getHeight()
+ (mTimestampsBounds[index].height() + mTimestampsBounds[index].top) - mAxisLabelsBounds[index].height()
+ (mAxisLabelsBounds[index].height() + mAxisLabelsBounds[index].top)
+ round(mTextPadding * 1.5f); + round(mTextPadding * 1.5f);
} }

View File

@@ -19,7 +19,6 @@ 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.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
@@ -58,6 +57,7 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
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 BatteryChartPreferenceControllerV2Test { public final class BatteryChartPreferenceControllerV2Test {
@@ -94,6 +94,7 @@ public final class BatteryChartPreferenceControllerV2Test {
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);
@@ -588,7 +589,7 @@ public final class BatteryChartPreferenceControllerV2Test {
mBatteryChartPreferenceController.setTimestampLabel(); mBatteryChartPreferenceController.setTimestampLabel();
verify(mBatteryChartPreferenceController.mBatteryChartView, never()) verify(mBatteryChartPreferenceController.mBatteryChartView, never())
.setLatestTimestamp(anyLong()); .setAxisLabels(any());
} }
@Test @Test
@@ -601,7 +602,7 @@ public final class BatteryChartPreferenceControllerV2Test {
mBatteryChartPreferenceController.setTimestampLabel(); mBatteryChartPreferenceController.setTimestampLabel();
verify(mBatteryChartPreferenceController.mBatteryChartView) verify(mBatteryChartPreferenceController.mBatteryChartView)
.setLatestTimestamp(1619247636826L); .setAxisLabels(new String[] {"4 pm", "12 am", "7 am"});
} }
@Test @Test
@@ -614,7 +615,7 @@ public final class BatteryChartPreferenceControllerV2Test {
mBatteryChartPreferenceController.setTimestampLabel(); mBatteryChartPreferenceController.setTimestampLabel();
verify(mBatteryChartPreferenceController.mBatteryChartView) verify(mBatteryChartPreferenceController.mBatteryChartView)
.setLatestTimestamp(anyLong()); .setAxisLabels(new String[] {"12 am"});
} }
@Test @Test
@@ -684,6 +685,7 @@ public final class BatteryChartPreferenceControllerV2Test {
private void setUpBatteryHistoryKeys() { private void setUpBatteryHistoryKeys() {
mBatteryChartPreferenceController.mBatteryHistoryKeys = mBatteryChartPreferenceController.mBatteryHistoryKeys =
// "2021-04-23 16:53:06 UTC", "1970-01-01 00:00:00 UTC", "2021-04-23 07:00:36 UTC"
new long[]{1619196786769L, 0L, 1619247636826L}; new long[]{1619196786769L, 0L, 1619247636826L};
ConvertUtils.utcToLocalTimeHour( ConvertUtils.utcToLocalTimeHour(
mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false);