diff --git a/res/values/strings.xml b/res/values/strings.xml index c58f6bb3b30..51305b52c9b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3633,8 +3633,6 @@ Recently opened apps See all %1$d apps - - ^1 ago @@ -4644,12 +4642,10 @@ Mobile network scanning - - ^1 ago - App usage since full charge (^1 ago) + App usage since full charge (^1) - Device usage since full charge (^1 ago) + Device usage since full charge (^1) Amount of time screen has been on since full charge diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 417ac0faeb5..fa61cecdf2f 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -52,8 +52,11 @@ import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.hardware.fingerprint.FingerprintManager; import android.icu.text.MeasureFormat; +import android.icu.text.RelativeDateTimeFormatter; +import android.icu.text.RelativeDateTimeFormatter.RelativeUnit; import android.icu.util.Measure; import android.icu.util.MeasureUnit; +import android.icu.util.ULocale; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.Network; @@ -860,6 +863,48 @@ public final class Utils extends com.android.settingslib.Utils { return sb; } + /** + * Returns relative time for the given millis in the past, in a short format such as "2 days + * ago", "5 hr. ago", "40 min. ago", or "29 sec. ago". + * + *

The unit is chosen to have good information value while only using one unit. So 27 hours + * and 50 minutes would be formatted as "28 hr. ago", while 50 hours would be formatted as + * "2 days ago". + * + * @param context the application context + * @param millis the elapsed time in milli seconds + * @param withSeconds include seconds? + * @return the formatted elapsed time + */ + public static CharSequence formatRelativeTime(Context context, double millis, + boolean withSeconds) { + final int seconds = (int) Math.floor(millis / 1000); + final RelativeUnit unit; + final int value; + if (withSeconds && seconds < 2 * SECONDS_PER_MINUTE) { + unit = RelativeUnit.SECONDS; + value = seconds; + } else if (seconds < 2 * SECONDS_PER_HOUR) { + unit = RelativeUnit.MINUTES; + value = (seconds + SECONDS_PER_MINUTE / 2) / SECONDS_PER_MINUTE; + } else if (seconds < 2 * SECONDS_PER_DAY) { + unit = RelativeUnit.HOURS; + value = (seconds + SECONDS_PER_HOUR / 2) / SECONDS_PER_HOUR; + } else { + unit = RelativeUnit.DAYS; + value = (seconds + SECONDS_PER_DAY / 2) / SECONDS_PER_DAY; + } + + final Locale locale = context.getResources().getConfiguration().locale; + final RelativeDateTimeFormatter formatter = RelativeDateTimeFormatter.getInstance( + ULocale.forLocale(locale), + null /* default NumberFormat */, + RelativeDateTimeFormatter.Style.SHORT, + android.icu.text.DisplayContext.CAPITALIZATION_FOR_MIDDLE_OF_SENTENCE); + + return formatter.format(value, RelativeDateTimeFormatter.Direction.LAST, unit); + } + /** * Queries for the UserInfo of a user. Returns null if the user doesn't exist (was removed). * @param userManager Instance of UserManager diff --git a/src/com/android/settings/applications/RecentAppsPreferenceController.java b/src/com/android/settings/applications/RecentAppsPreferenceController.java index d0f7584eaed..69a36f67a53 100644 --- a/src/com/android/settings/applications/RecentAppsPreferenceController.java +++ b/src/com/android/settings/applications/RecentAppsPreferenceController.java @@ -235,10 +235,8 @@ public class RecentAppsPreferenceController extends AbstractPreferenceController pref.setKey(pkgName); pref.setTitle(appEntry.label); pref.setIcon(mIconDrawableFactory.getBadgedIcon(appEntry.info)); - pref.setSummary(TextUtils.expandTemplate( - mContext.getResources().getText(R.string.recent_app_summary), - Utils.formatElapsedTime(mContext, - System.currentTimeMillis() - stat.getLastTimeUsed(), false))); + pref.setSummary(Utils.formatRelativeTime(mContext, + System.currentTimeMillis() - stat.getLastTimeUsed(), false)); pref.setOrder(i); pref.setOnPreferenceClickListener(preference -> { AppInfoBase.startAppInfoFragment(InstalledAppDetails.class, diff --git a/src/com/android/settings/fuelgauge/PowerUsageSummary.java b/src/com/android/settings/fuelgauge/PowerUsageSummary.java index 1596aca70b8..9798749ebad 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageSummary.java +++ b/src/com/android/settings/fuelgauge/PowerUsageSummary.java @@ -533,7 +533,7 @@ public class PowerUsageSummary extends PowerUsageBase implements updateScreenPreference(); updateLastFullChargePreference(lastFullChargeTime); - final CharSequence timeSequence = Utils.formatElapsedTime(context, lastFullChargeTime, + final CharSequence timeSequence = Utils.formatRelativeTime(context, lastFullChargeTime, false); final int resId = mShowAllApps ? R.string.power_usage_list_summary_device : R.string.power_usage_list_summary; @@ -682,10 +682,8 @@ public class PowerUsageSummary extends PowerUsageBase implements @VisibleForTesting void updateLastFullChargePreference(long timeMs) { - final CharSequence timeSequence = Utils.formatElapsedTime(getContext(), timeMs, false); - mLastFullChargePref.setSubtitle( - TextUtils.expandTemplate(getText(R.string.power_last_full_charge_summary), - timeSequence)); + final CharSequence timeSequence = Utils.formatRelativeTime(getContext(), timeMs, false); + mLastFullChargePref.setSubtitle(timeSequence); } @VisibleForTesting diff --git a/tests/robotests/src/com/android/settings/UtilsTest.java b/tests/robotests/src/com/android/settings/UtilsTest.java index 33ead1fd4d0..19b87a1b1fa 100644 --- a/tests/robotests/src/com/android/settings/UtilsTest.java +++ b/tests/robotests/src/com/android/settings/UtilsTest.java @@ -173,6 +173,105 @@ public class UtilsTest { assertThat(ttsSpans[0].getType()).isEqualTo(TtsSpan.TYPE_MEASURE); } + @Test + public void testFormatRelativeTime_WithSeconds_ShowSeconds() { + final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS; + final String expectedTime = "40 sec. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_NoSeconds_DoNotShowSeconds() { + final double testMillis = 40 * DateUtils.SECOND_IN_MILLIS; + final String expectedTime = "1 min. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_LessThanTwoMinutes_withSeconds() { + final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS; + final String expectedTime = "119 sec. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_LessThanTwoMinutes_NoSeconds() { + final double testMillis = 119 * DateUtils.SECOND_IN_MILLIS; + final String expectedTime = "2 min. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_TwoMinutes_withSeconds() { + final double testMillis = 2 * DateUtils.MINUTE_IN_MILLIS; + final String expectedTime = "2 min. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_LessThanTwoHours_withSeconds() { + final double testMillis = 119 * DateUtils.MINUTE_IN_MILLIS; + final String expectedTime = "119 min. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_TwoHours_withSeconds() { + final double testMillis = 2 * DateUtils.HOUR_IN_MILLIS; + final String expectedTime = "2 hr. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_LessThanTwoDays_withSeconds() { + final double testMillis = 47 * DateUtils.HOUR_IN_MILLIS; + final String expectedTime = "47 hr. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_TwoDays_withSeconds() { + final double testMillis = 2 * DateUtils.DAY_IN_MILLIS; + final String expectedTime = "2 days ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_FormatZero_WithSeconds() { + final double testMillis = 0; + final String expectedTime = "0 sec. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, true).toString()).isEqualTo( + expectedTime); + } + + @Test + public void testFormatRelativeTime_FormatZero_NoSeconds() { + final double testMillis = 0; + final String expectedTime = "0 min. ago"; + + assertThat(Utils.formatRelativeTime(mContext, testMillis, false).toString()).isEqualTo( + expectedTime); + } + @Test public void testInitializeVolumeDoesntBreakOnNullVolume() { VolumeInfo info = new VolumeInfo("id", 0, new DiskInfo("id", 0), ""); diff --git a/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java index 2510f201faa..5e85f9b7e48 100644 --- a/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/RecentAppsPreferenceControllerTest.java @@ -249,8 +249,6 @@ public class RecentAppsPreferenceControllerTest { when(mUsageStatsManager.queryUsageStats(anyInt(), anyLong(), anyLong())) .thenReturn(stats); - when(mMockContext.getResources().getText(eq(R.string.recent_app_summary))) - .thenReturn(mContext.getResources().getText(R.string.recent_app_summary)); final Configuration configuration = new Configuration(); configuration.locale = Locale.US; when(mMockContext.getResources().getConfiguration()).thenReturn(configuration); @@ -258,7 +256,7 @@ public class RecentAppsPreferenceControllerTest { mController = new RecentAppsPreferenceController(mMockContext, mAppState, null); mController.displayPreference(mScreen); - verify(mCategory).addPreference(argThat(summaryMatches("0m ago"))); + verify(mCategory).addPreference(argThat(summaryMatches("0 min. ago"))); } private static ArgumentMatcher summaryMatches(String expected) { diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java index db4fb6daa82..89a4208d84e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerUsageSummaryTest.java @@ -411,18 +411,11 @@ public class PowerUsageSummaryTest { @Test public void testUpdateLastFullChargePreference_showCorrectSummary() { - final CharSequence formattedString = mRealContext.getText( - R.string.power_last_full_charge_summary); - final CharSequence timeSequence = Utils.formatElapsedTime(mRealContext, - TIME_SINCE_LAST_FULL_CHARGE_MS, false); - final CharSequence expectedSummary = TextUtils.expandTemplate( - formattedString, timeSequence); - doReturn(formattedString).when(mFragment).getText(R.string.power_last_full_charge_summary); doReturn(mRealContext).when(mFragment).getContext(); mFragment.updateLastFullChargePreference(TIME_SINCE_LAST_FULL_CHARGE_MS); - assertThat(mLastFullChargePref.getSubtitle()).isEqualTo(expectedSummary); + assertThat(mLastFullChargePref.getSubtitle()).isEqualTo("2 hr. ago"); } @Test