From 2a75186e4b7d5b8d3f7f2b8fd43ac26c0500e1b7 Mon Sep 17 00:00:00 2001 From: ykhung Date: Wed, 9 Jun 2021 11:41:33 +0800 Subject: [PATCH] Resolve locale not update issues in the chart view - read locale from configuration rather than Locale.getDefault - refine 12-24 format to align the current status bar short style - resolve locale change not update chart percentage label - extend timestamp label in the chart graph from 4 to 5 labels Bug: 190150515 Bug: 190422902 Bug: 190226837 Test: make SettingsRoboTests Change-Id: I5347964900123a6d112dbc37c2af87eb7d73f1d2 --- .../BatteryChartPreferenceController.java | 12 ++--- .../settings/fuelgauge/BatteryChartView.java | 40 +++++++------- .../settings/fuelgauge/BatteryHistEntry.java | 3 +- .../settings/fuelgauge/ConvertUtils.java | 23 ++++++-- .../BatteryChartPreferenceControllerTest.java | 12 +++-- .../fuelgauge/BatteryChartViewTest.java | 10 ++-- .../settings/fuelgauge/ConvertUtilsTest.java | 54 ++++++++++++++----- 7 files changed, 104 insertions(+), 50 deletions(-) diff --git a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java index ffbd2d90e4e..3c9cbaa8e89 100644 --- a/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryChartPreferenceController.java @@ -272,7 +272,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll final Map entryMap = batteryHistoryMap.get(timestamp); if (entryMap == null || entryMap.isEmpty()) { Log.e(TAG, "abnormal entry list in the timestamp:" - + ConvertUtils.utcToLocalTime(timestamp)); + + ConvertUtils.utcToLocalTime(mPrefContext, timestamp)); continue; } // Averages the battery level in each time slot to avoid corner conditions. @@ -287,7 +287,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll Log.d(TAG, String.format( "setBatteryHistoryMap() size=%d\nkeys=%s\nlevels=%s", batteryHistoryMap.size(), - utcToLocalTime(mBatteryHistoryKeys), + utcToLocalTime(mPrefContext, mBatteryHistoryKeys), Arrays.toString(mBatteryHistoryLevels))); // Loads item icon and label in the background. @@ -496,9 +496,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll if (mTrapezoidIndex < 0) { return null; } - final String fromHour = ConvertUtils.utcToLocalTimeHour( + final String fromHour = ConvertUtils.utcToLocalTimeHour(mPrefContext, mBatteryHistoryKeys[mTrapezoidIndex * 2], mIs24HourFormat); - final String toHour = ConvertUtils.utcToLocalTimeHour( + final String toHour = ConvertUtils.utcToLocalTimeHour(mPrefContext, mBatteryHistoryKeys[(mTrapezoidIndex + 1) * 2], mIs24HourFormat); return String.format("%s - %s", fromHour, toHour); } @@ -584,11 +584,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll mHandler.post(() -> mPreferenceScreen.addPreference(mFooterPreference)); } - private static String utcToLocalTime(long[] timestamps) { + private static String utcToLocalTime(Context context, long[] timestamps) { final StringBuilder builder = new StringBuilder(); for (int index = 0; index < timestamps.length; index++) { builder.append(String.format("%s| ", - ConvertUtils.utcToLocalTime(timestamps[index]))); + ConvertUtils.utcToLocalTime(context, timestamps[index]))); } return builder.toString(); } diff --git a/src/com/android/settings/fuelgauge/BatteryChartView.java b/src/com/android/settings/fuelgauge/BatteryChartView.java index ed6417704f7..b721f14be71 100644 --- a/src/com/android/settings/fuelgauge/BatteryChartView.java +++ b/src/com/android/settings/fuelgauge/BatteryChartView.java @@ -56,14 +56,8 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private static final List ACCESSIBILITY_SERVICE_NAMES = Arrays.asList("SwitchAccessService", "TalkBackService", "JustSpeakService"); - // For drawing the percentage information. - private static final String[] PERCENTAGES = new String[] { - formatPercentage(/*percentage=*/ 100, /*round=*/ true), - formatPercentage(/*percentage=*/ 50, /*round=*/ true), - formatPercentage(/*percentage=*/ 0, /*round=*/ true)}; - private static final int DEFAULT_TRAPEZOID_COUNT = 12; - private static final int DEFAULT_TIMESTAMP_COUNT = 4; + private static final int DEFAULT_TIMESTAMP_COUNT = 5; private static final int DIVIDER_COLOR = Color.parseColor("#CDCCC5"); private static final long UPDATE_STATE_DELAYED_TIME = 500L; @@ -82,6 +76,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private float mTrapezoidVOffset; private float mTrapezoidHOffset; private boolean mIsSlotsClickabled; + private String[] mPercentages = getPercentages(); @VisibleForTesting int mSelectedIndex; @VisibleForTesting String[] mTimestamps; @@ -96,7 +91,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick new Rect[] {new Rect(), new Rect(), new Rect()}; // For drawing the timestamp information. private final Rect[] mTimestampsBounds = - new Rect[] {new Rect(), new Rect(), new Rect(), new Rect()}; + new Rect[] {new Rect(), new Rect(), new Rect(), new Rect(), new Rect()}; @VisibleForTesting Handler mHandler = new Handler(); @@ -107,6 +102,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private Paint mTextPaint; private Paint mDividerPaint; private Paint mTrapezoidPaint; + @VisibleForTesting Paint mTrapezoidCurvePaint = null; private TrapezoidSlot[] mTrapezoidSlots; @@ -201,12 +197,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick if (mTimestamps == null) { mTimestamps = new String[DEFAULT_TIMESTAMP_COUNT]; } - final long timeSlotOffset = DateUtils.HOUR_IN_MILLIS * 8; + final long timeSlotOffset = DateUtils.HOUR_IN_MILLIS * 6; final boolean is24HourFormat = DateFormat.is24HourFormat(getContext()); for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { mTimestamps[index] = ConvertUtils.utcToLocalTimeHour( - latestTimestamp - (3 - index) * timeSlotOffset, + getContext(), + latestTimestamp - (4 - index) * timeSlotOffset, is24HourFormat); } requestLayout(); @@ -217,9 +214,9 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick super.onMeasure(widthMeasureSpec, heightMeasureSpec); // Measures text bounds and updates indent configuration. if (mTextPaint != null) { - for (int index = 0; index < PERCENTAGES.length; index++) { + for (int index = 0; index < mPercentages.length; index++) { mTextPaint.getTextBounds( - PERCENTAGES[index], 0, PERCENTAGES[index].length(), + mPercentages[index], 0, mPercentages[index].length(), mPercentageBounds[index]); } // Updates the indent configurations. @@ -396,7 +393,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick private void drawPercentage(Canvas canvas, int index, float offsetY) { if (mTextPaint != null) { canvas.drawText( - PERCENTAGES[index], + mPercentages[index], getWidth() - mPercentageBounds[index].width() - mPercentageBounds[index].left, offsetY + mPercentageBounds[index].height() *.5f, mTextPaint); @@ -429,7 +426,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick final float baselineX = mDividerWidth * .5f; final float offsetX = mDividerWidth + unitWidth; for (int index = 0; index < DEFAULT_TIMESTAMP_COUNT; index++) { - xOffsets[index] = baselineX + index * offsetX * 4; + xOffsets[index] = baselineX + index * offsetX * 3; } drawTimestamp(canvas, xOffsets); } @@ -443,11 +440,11 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick getTimestampY(0), mTextPaint); // Draws the last timestamp info. canvas.drawText( - mTimestamps[3], - xOffsets[3] - mTimestampsBounds[3].width() - mTimestampsBounds[3].left, - getTimestampY(3), mTextPaint); + mTimestamps[4], + xOffsets[4] - mTimestampsBounds[4].width() - mTimestampsBounds[4].left, + getTimestampY(4), mTextPaint); // Draws the rest of timestamp info since it is located in the center. - for (int index = 1; index <= 2; index++) { + for (int index = 1; index <= 3; index++) { canvas.drawText( mTimestamps[index], xOffsets[index] - @@ -544,6 +541,13 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick && mLevels[trapezoidIndex + 1] != 0; } + 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 = diff --git a/src/com/android/settings/fuelgauge/BatteryHistEntry.java b/src/com/android/settings/fuelgauge/BatteryHistEntry.java index d83d8149691..4c8ecee6c8e 100644 --- a/src/com/android/settings/fuelgauge/BatteryHistEntry.java +++ b/src/com/android/settings/fuelgauge/BatteryHistEntry.java @@ -184,7 +184,8 @@ public class BatteryHistEntry { @Override public String toString() { - final String recordAtDateTime = ConvertUtils.utcToLocalTime(mTimestamp); + final String recordAtDateTime = + ConvertUtils.utcToLocalTime(/*context=*/ null, mTimestamp); final StringBuilder builder = new StringBuilder() .append("\nBatteryHistEntry{") .append(String.format("\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b", diff --git a/src/com/android/settings/fuelgauge/ConvertUtils.java b/src/com/android/settings/fuelgauge/ConvertUtils.java index 19805648f3a..01f510eca6d 100644 --- a/src/com/android/settings/fuelgauge/ConvertUtils.java +++ b/src/com/android/settings/fuelgauge/ConvertUtils.java @@ -17,6 +17,7 @@ import android.annotation.IntDef; import android.content.ContentValues; import android.content.Context; import android.os.BatteryUsageStats; +import android.os.LocaleList; import android.os.UserHandle; import android.text.format.DateUtils; import android.util.Log; @@ -133,8 +134,8 @@ public final class ConvertUtils { } /** Converts UTC timestamp to human readable local time string. */ - public static String utcToLocalTime(long timestamp) { - final Locale currentLocale = Locale.getDefault(); + public static String utcToLocalTime(Context context, long timestamp) { + final Locale currentLocale = getLocale(context); final String currentZoneId = TimeZone.getDefault().getID(); if (!currentZoneId.equals(sZoneId) || !currentLocale.equals(sLocale) @@ -148,8 +149,9 @@ public final class ConvertUtils { } /** Converts UTC timestamp to local time hour data. */ - public static String utcToLocalTimeHour(long timestamp, boolean is24HourFormat) { - final Locale currentLocale = Locale.getDefault(); + public static String utcToLocalTimeHour( + Context context, long timestamp, boolean is24HourFormat) { + final Locale currentLocale = getLocale(context); final String currentZoneId = TimeZone.getDefault().getID(); if (!currentZoneId.equals(sZoneIdForHour) || !currentLocale.equals(sLocaleForHour) @@ -159,7 +161,7 @@ public final class ConvertUtils { sZoneIdForHour = currentZoneId; sIs24HourFormat = is24HourFormat; sSimpleDateFormatForHour = new SimpleDateFormat( - sIs24HourFormat ? "HH" : "h aa", currentLocale); + sIs24HourFormat ? "HH" : "h", currentLocale); } return sSimpleDateFormatForHour.format(new Date(timestamp)) .toLowerCase(currentLocale); @@ -356,4 +358,15 @@ public final class ConvertUtils { ? entry3 : null; } } + + @VisibleForTesting + static Locale getLocale(Context context) { + if (context == null) { + return Locale.getDefault(); + } + final LocaleList locales = + context.getResources().getConfiguration().getLocales(); + return locales != null && !locales.isEmpty() ? locales.get(0) + : Locale.getDefault(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java index 7894c3f0c60..ef76eeeaa67 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartPreferenceControllerTest.java @@ -35,6 +35,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.LocaleList; import android.text.format.DateUtils; import android.util.Pair; @@ -100,6 +101,8 @@ public final class BatteryChartPreferenceControllerTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; mContext = spy(RuntimeEnvironment.application); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); mBatteryChartPreferenceController = createController(); mBatteryChartPreferenceController.mPrefContext = mContext; mBatteryChartPreferenceController.mAppListPrefGroup = mAppListGroup; @@ -573,14 +576,12 @@ public final class BatteryChartPreferenceControllerTest { // Verifies the title in the preference group. verify(mBatteryChartPreferenceController.mAppListPrefGroup) .setTitle(captor.capture()); - assertThat(captor.getValue()) - .isEqualTo("App usage for 4 pm - 7 am"); + assertThat(captor.getValue()).isEqualTo("App usage for 4 - 7"); // Verifies the title in the expandable divider. captor = ArgumentCaptor.forClass(String.class); verify(mBatteryChartPreferenceController.mExpandDividerPreference) .setTitle(captor.capture()); - assertThat(captor.getValue()) - .isEqualTo("System usage for 4 pm - 7 am"); + assertThat(captor.getValue()).isEqualTo("System usage for 4 - 7"); } @Test @@ -716,7 +717,8 @@ public final class BatteryChartPreferenceControllerTest { private void setUpBatteryHistoryKeys() { mBatteryChartPreferenceController.mBatteryHistoryKeys = new long[] {1619196786769L, 0L, 1619247636826L}; - ConvertUtils.utcToLocalTimeHour(/*timestamp=*/ 0, /*is24HourFormat=*/ false); + ConvertUtils.utcToLocalTimeHour( + mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); // Simulates the locale in GMT. ConvertUtils.sSimpleDateFormatForHour .setTimeZone(TimeZone.getTimeZone("GMT")); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java index 3f94456f8e1..ec77f4c3b02 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryChartViewTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; +import android.os.LocaleList; import android.view.accessibility.AccessibilityManager; import com.android.settings.testutils.FakeFeatureFactory; @@ -41,6 +42,7 @@ import org.robolectric.RuntimeEnvironment; import java.util.Arrays; import java.util.ArrayList; +import java.util.Locale; import java.util.TimeZone; @RunWith(RobolectricTestRunner.class) @@ -60,6 +62,8 @@ public final class BatteryChartViewTest { mFeatureFactory = FakeFeatureFactory.setupForTest(); mPowerUsageFeatureProvider = mFeatureFactory.powerUsageFeatureProvider; mContext = spy(RuntimeEnvironment.application); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); mBatteryChartView = new BatteryChartView(mContext); doReturn(mockAccessibilityManager).when(mContext) .getSystemService(AccessibilityManager.class); @@ -234,11 +238,11 @@ public final class BatteryChartViewTest { final long timestamp = 1619196786769L; ConvertUtils.sSimpleDateFormatForHour = null; // Invokes the method first to create the SimpleDateFormat. - ConvertUtils.utcToLocalTimeHour(/*timestamp=*/ 0, /*is24HourFormat=*/ false); + ConvertUtils.utcToLocalTimeHour( + mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); ConvertUtils.sSimpleDateFormatForHour .setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); - final String[] expectedTimestamps = - new String[] {"9 am", "5 pm", "1 am", "9 am"}; + final String[] expectedTimestamps = new String[] {"00", "06", "12", "18", "00"}; mBatteryChartView.setLatestTimestamp(timestamp); diff --git a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java index efabe44ed0d..67a60af4c74 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/ConvertUtilsTest.java @@ -24,6 +24,7 @@ import android.content.ContentValues; import android.content.Context; import android.os.BatteryManager; import android.os.BatteryUsageStats; +import android.os.LocaleList; import android.os.UserHandle; import com.android.settings.testutils.FakeFeatureFactory; @@ -315,6 +316,7 @@ public final class ConvertUtilsTest { .isEqualTo(entry.mConsumePower * ratio); } + @Test public void testUtcToLocalTime_returnExpectedResult() { ConvertUtils.sZoneId = null; ConvertUtils.sLocale = null; @@ -322,48 +324,76 @@ public final class ConvertUtilsTest { final String expectedZoneId = "America/Los_Angeles"; ConvertUtils.sSimpleDateFormat = null; // Invokes the method first to create the SimpleDateFormat. - ConvertUtils.utcToLocalTime(/*timestamp=*/ 0); + ConvertUtils.utcToLocalTime(mContext, /*timestamp=*/ 0); ConvertUtils.sSimpleDateFormat .setTimeZone(TimeZone.getTimeZone(expectedZoneId)); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); - assertThat(ConvertUtils.utcToLocalTime(timestamp)) - .isEqualTo("Apr 23,2021 09:53:06"); + assertThat(ConvertUtils.utcToLocalTime(mContext, timestamp)) + .isEqualTo("Apr 24,2021 00:53:06"); assertThat(ConvertUtils.sZoneId).isNotEqualTo(expectedZoneId); - assertThat(ConvertUtils.sLocale).isEqualTo(Locale.getDefault()); + assertThat(ConvertUtils.sLocale).isEqualTo(new Locale("en_US")); } + @Test public void testUtcToLocalTimeHour_12HourFormat_returnExpectedResult() { ConvertUtils.sZoneIdForHour = null; ConvertUtils.sLocaleForHour = null; - final long timestamp = 1619196786769L; + final long timestamp = 1619000086769L; final String expectedZoneId = "America/Los_Angeles"; ConvertUtils.sSimpleDateFormatForHour = null; // Invokes the method first to create the SimpleDateFormat. - ConvertUtils.utcToLocalTimeHour(/*timestamp=*/ 0, /*is24HourFormat=*/ false); + ConvertUtils.utcToLocalTimeHour( + mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); ConvertUtils.sSimpleDateFormatForHour .setTimeZone(TimeZone.getTimeZone(expectedZoneId)); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); assertThat(ConvertUtils.utcToLocalTimeHour( - timestamp, /*is24HourFormat=*/ false)).isEqualTo("9 am"); + mContext, timestamp, /*is24HourFormat=*/ false)).isEqualTo("6"); assertThat(ConvertUtils.sZoneIdForHour).isNotEqualTo(expectedZoneId); - assertThat(ConvertUtils.sLocaleForHour).isEqualTo(Locale.getDefault()); + assertThat(ConvertUtils.sLocaleForHour).isEqualTo(new Locale("en_US")); } + @Test public void testUtcToLocalTimeHour_24HourFormat_returnExpectedResult() { ConvertUtils.sZoneIdForHour = null; ConvertUtils.sLocaleForHour = null; - final long timestamp = 1619196786769L; + final long timestamp = 1619000086769L; final String expectedZoneId = "America/Los_Angeles"; ConvertUtils.sSimpleDateFormatForHour = null; // Invokes the method first to create the SimpleDateFormat. - ConvertUtils.utcToLocalTimeHour(/*timestamp=*/ 0, /*is24HourFormat=*/ true); + ConvertUtils.utcToLocalTimeHour( + mContext, /*timestamp=*/ 0, /*is24HourFormat=*/ false); ConvertUtils.sSimpleDateFormatForHour .setTimeZone(TimeZone.getTimeZone(expectedZoneId)); + mContext.getResources().getConfiguration().setLocales( + new LocaleList(new Locale("en_US"))); assertThat(ConvertUtils.utcToLocalTimeHour( - timestamp, /*is24HourFormat=*/ true)).isEqualTo("09"); + mContext, timestamp, /*is24HourFormat=*/ true)).isEqualTo("18"); assertThat(ConvertUtils.sZoneIdForHour).isNotEqualTo(expectedZoneId); - assertThat(ConvertUtils.sLocaleForHour).isEqualTo(Locale.getDefault()); + assertThat(ConvertUtils.sLocaleForHour).isEqualTo(new Locale("en_US")); + } + + @Test + public void getLocale_nullContext_returnDefaultLocale() { + assertThat(ConvertUtils.getLocale(/*context=*/ null)) + .isEqualTo(Locale.getDefault()); + } + + @Test + public void getLocale_nullLocaleList_returnDefaultLocale() { + mContext.getResources().getConfiguration().setLocales(null); + assertThat(ConvertUtils.getLocale(mContext)).isEqualTo(Locale.getDefault()); + } + + @Test + public void getLocale_emptyLocaleList_returnDefaultLocale() { + mContext.getResources().getConfiguration().setLocales(new LocaleList()); + assertThat(ConvertUtils.getLocale(mContext)).isEqualTo(Locale.getDefault()); } private static BatteryHistEntry createBatteryHistEntry(