Use BytesFormatter for data usage

Change "480 B" to "480 byte".

And no longer use FormattedDataUsage.

Bug: 321861088
Flag: EXEMPT bug fix
Test: manual - App data usage
Test: unit test
Change-Id: I9ed220e2d5b8fc512d7d28f6fa7faebb37beab83
This commit is contained in:
Chaohui Wang
2025-01-02 10:34:30 +08:00
parent 2d2770b835
commit 611c62294a
9 changed files with 30 additions and 66 deletions

View File

@@ -37,8 +37,7 @@ class AppDataUsageSummaryController(context: Context, preferenceKey: String) :
private val dataFlow = MutableStateFlow(NetworkUsageDetailsData.AllZero) private val dataFlow = MutableStateFlow(NetworkUsageDetailsData.AllZero)
private val dataUsageFormatter = DataUsageFormatter(context) private val dataUsageFormatter = DataUsageFormatter(context)
private val emptyDataUsage = private val emptyDataUsage = context.getPlaceholder()
DataUsageFormatter.FormattedDataUsage(context.getPlaceholder(), context.getPlaceholder())
private val totalUsageFlow = dataFlow.map { private val totalUsageFlow = dataFlow.map {
dataUsageFormatter.formatDataUsage(it.totalUsage) dataUsageFormatter.formatDataUsage(it.totalUsage)
@@ -66,18 +65,15 @@ class AppDataUsageSummaryController(context: Context, preferenceKey: String) :
val backgroundUsage by backgroundUsageFlow.collectAsStateWithLifecycle(emptyDataUsage) val backgroundUsage by backgroundUsageFlow.collectAsStateWithLifecycle(emptyDataUsage)
Preference(object : PreferenceModel { Preference(object : PreferenceModel {
override val title = stringResource(R.string.total_size_label) override val title = stringResource(R.string.total_size_label)
override val summary = { totalUsage.displayText } override val summary = { totalUsage }
override val summaryContentDescription = { totalUsage.contentDescription }
}) })
Preference(object : PreferenceModel { Preference(object : PreferenceModel {
override val title = stringResource(R.string.data_usage_label_foreground) override val title = stringResource(R.string.data_usage_label_foreground)
override val summary = { foregroundUsage.displayText } override val summary = { foregroundUsage }
override val summaryContentDescription = { foregroundUsage.contentDescription }
}) })
Preference(object : PreferenceModel { Preference(object : PreferenceModel {
override val title = stringResource(R.string.data_usage_label_background) override val title = stringResource(R.string.data_usage_label_background)
override val summary = { backgroundUsage.displayText } override val summary = { backgroundUsage }
override val summaryContentDescription = { backgroundUsage.contentDescription }
}) })
} }
} }

View File

@@ -172,7 +172,7 @@ open class DataUsageList : DashboardFragment() {
private fun updateSelectedCycle(usageData: NetworkUsageData) { private fun updateSelectedCycle(usageData: NetworkUsageData) {
Log.d(TAG, "showing cycle $usageData") Log.d(TAG, "showing cycle $usageData")
usageAmount?.title = usageData.getDataUsedString(requireContext()).displayText usageAmount?.title = usageData.getDataUsedString(requireContext())
viewModel.selectedCycleFlow.value = usageData viewModel.selectedCycleFlow.value = usageData
updateApps(usageData) updateApps(usageData)

View File

@@ -16,40 +16,16 @@
package com.android.settings.datausage.lib package com.android.settings.datausage.lib
import android.annotation.StringRes
import android.content.Context import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.icu.text.UnicodeSet
import android.icu.text.UnicodeSetSpanner
import android.text.BidiFormatter
import android.text.format.Formatter import android.text.format.Formatter
import com.android.internal.R import com.android.settingslib.spaprivileged.framework.common.BytesFormatter
class DataUsageFormatter(private val context: Context) { class DataUsageFormatter(private val context: Context) {
data class FormattedDataUsage(
val displayText: String,
val contentDescription: String,
) {
fun format(context: Context, @StringRes resId: Int, vararg formatArgs: Any?) =
FormattedDataUsage(
displayText = context.getString(resId, displayText, *formatArgs),
contentDescription = context.getString(resId, contentDescription, *formatArgs),
)
}
/** Formats the data usage. */ /** Formats the data usage. */
fun formatDataUsage(sizeBytes: Long): FormattedDataUsage { fun formatDataUsage(sizeBytes: Long): String =
val result = Formatter.formatBytes(context.resources, sizeBytes, Formatter.FLAG_IEC_UNITS) BytesFormatter(context).format(sizeBytes, BytesFormatter.UseCase.DataUsage)
return FormattedDataUsage(
displayText = BidiFormatter.getInstance().unicodeWrap(
context.getString(R.string.fileSizeSuffix, result.value, result.units)
),
contentDescription = context.getString(
R.string.fileSizeSuffix, result.value, result.unitsContentDescription
),
)
}
companion object { companion object {
/** /**

View File

@@ -20,7 +20,6 @@ import android.content.Context
import android.text.format.DateUtils import android.text.format.DateUtils
import android.util.Range import android.util.Range
import com.android.settings.R import com.android.settings.R
import com.android.settings.datausage.lib.DataUsageFormatter.FormattedDataUsage
/** /**
* Base data structure representing usage data in a period. * Base data structure representing usage data in a period.
@@ -38,11 +37,11 @@ data class NetworkUsageData(
fun formatDateRange(context: Context): String = fun formatDateRange(context: Context): String =
DateUtils.formatDateRange(context, startTime, endTime, DATE_FORMAT) DateUtils.formatDateRange(context, startTime, endTime, DATE_FORMAT)
fun formatUsage(context: Context): FormattedDataUsage = fun formatUsage(context: Context): String =
DataUsageFormatter(context).formatDataUsage(usage) DataUsageFormatter(context).formatDataUsage(usage)
fun getDataUsedString(context: Context): FormattedDataUsage = fun getDataUsedString(context: Context): String =
formatUsage(context).format(context, R.string.data_used_template) context.getString(R.string.data_used_template, formatUsage(context))
companion object { companion object {
val AllZero = NetworkUsageData( val AllZero = NetworkUsageData(

View File

@@ -31,7 +31,6 @@ import androidx.preference.PreferenceScreen
import com.android.settings.R import com.android.settings.R
import com.android.settings.core.BasePreferenceController import com.android.settings.core.BasePreferenceController
import com.android.settings.datausage.DataUsageUtils import com.android.settings.datausage.DataUsageUtils
import com.android.settings.datausage.lib.DataUsageFormatter.FormattedDataUsage
import com.android.settings.datausage.lib.DataUsageLib import com.android.settings.datausage.lib.DataUsageLib
import com.android.settings.datausage.lib.NetworkCycleDataRepository import com.android.settings.datausage.lib.NetworkCycleDataRepository
import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.AllTimeRange import com.android.settings.datausage.lib.NetworkStatsRepository.Companion.AllTimeRange
@@ -88,7 +87,7 @@ class DataUsagePreferenceController(context: Context, key: String) :
getDataUsageSummaryAndEnabled() getDataUsageSummaryAndEnabled()
} }
preference.isEnabled = enabled preference.isEnabled = enabled
preference.summary = summary?.displayText preference.summary = summary
} }
private fun getNetworkTemplate(): NetworkTemplate? = private fun getNetworkTemplate(): NetworkTemplate? =
@@ -100,12 +99,16 @@ class DataUsagePreferenceController(context: Context, key: String) :
fun createNetworkCycleDataRepository(): NetworkCycleDataRepository? = fun createNetworkCycleDataRepository(): NetworkCycleDataRepository? =
networkTemplate?.let { NetworkCycleDataRepository(mContext, it) } networkTemplate?.let { NetworkCycleDataRepository(mContext, it) }
private fun getDataUsageSummaryAndEnabled(): Pair<FormattedDataUsage?, Boolean> { private fun getDataUsageSummaryAndEnabled(): Pair<String?, Boolean> {
val repository = createNetworkCycleDataRepository() ?: return null to false val repository = createNetworkCycleDataRepository() ?: return null to false
repository.loadFirstCycle()?.let { usageData -> repository.loadFirstCycle()?.let { usageData ->
val formattedDataUsage = usageData.formatUsage(mContext) val formattedDataUsage =
.format(mContext, R.string.data_usage_template, usageData.formatDateRange(mContext)) mContext.getString(
R.string.data_usage_template,
usageData.formatUsage(mContext),
usageData.formatDateRange(mContext),
)
val hasUsage = usageData.usage > 0 || repository.queryUsage(AllTimeRange).usage > 0 val hasUsage = usageData.usage > 0 || repository.queryUsage(AllTimeRange).usage > 0
return formattedDataUsage to hasUsage return formattedDataUsage to hasUsage
} }

View File

@@ -113,7 +113,7 @@ private class AppDataUsagePresenter(
} else { } else {
context.getString( context.getString(
R.string.data_summary_format, R.string.data_summary_format,
appUsageData.formatUsage(context).displayText, appUsageData.formatUsage(context),
appUsageData.formatStartDate(context), appUsageData.formatStartDate(context),
) )
} }

View File

@@ -56,13 +56,10 @@ class AppDataUsageSummaryControllerTest {
composeTestRule.onNode(hasTextExactly("Total", "6.75 kB")).assertIsDisplayed() composeTestRule.onNode(hasTextExactly("Total", "6.75 kB")).assertIsDisplayed()
composeTestRule.onNode(hasTextExactly("Foreground", "5.54 kB")).assertIsDisplayed() composeTestRule.onNode(hasTextExactly("Foreground", "5.54 kB")).assertIsDisplayed()
composeTestRule.onNode(hasTextExactly("Background", "1.21 kB")).assertIsDisplayed() composeTestRule.onNode(hasTextExactly("Background", "1.21 kB")).assertIsDisplayed()
composeTestRule.onNodeWithContentDescription("6.75 kB").assertIsDisplayed()
composeTestRule.onNodeWithContentDescription("5.54 kB").assertIsDisplayed()
composeTestRule.onNodeWithContentDescription("1.21 kB").assertIsDisplayed()
} }
@Test @Test
fun summary_zero() { fun summary_smallByte() {
val appUsage = NetworkUsageDetailsData( val appUsage = NetworkUsageDetailsData(
range = Range(1L, 2L), range = Range(1L, 2L),
totalUsage = 3, totalUsage = 3,
@@ -75,12 +72,9 @@ class AppDataUsageSummaryControllerTest {
controller.Content() controller.Content()
} }
composeTestRule.onNode(hasTextExactly("Total", "3 B")).assertIsDisplayed() composeTestRule.onNode(hasTextExactly("Total", "3 byte")).assertIsDisplayed()
composeTestRule.onNode(hasTextExactly("Foreground", "1 B")).assertIsDisplayed() composeTestRule.onNode(hasTextExactly("Foreground", "1 byte")).assertIsDisplayed()
composeTestRule.onNode(hasTextExactly("Background", "2 B")).assertIsDisplayed() composeTestRule.onNode(hasTextExactly("Background", "2 byte")).assertIsDisplayed()
composeTestRule.onNodeWithContentDescription("3 byte").assertIsDisplayed()
composeTestRule.onNodeWithContentDescription("1 byte").assertIsDisplayed()
composeTestRule.onNodeWithContentDescription("2 byte").assertIsDisplayed()
} }
private companion object { private companion object {

View File

@@ -21,7 +21,6 @@ import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.datausage.lib.DataUsageFormatter.Companion.getBytesDisplayUnit import com.android.settings.datausage.lib.DataUsageFormatter.Companion.getBytesDisplayUnit
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -33,26 +32,23 @@ class DataUsageFormatterTest {
@Test @Test
fun formatDataUsage_0() { fun formatDataUsage_0() {
val (displayText, contentDescription) = dataUsageFormatter.formatDataUsage(0) val displayText = dataUsageFormatter.formatDataUsage(0)
assertThat(displayText).isEqualTo("0 B") assertThat(displayText).isEqualTo("0 byte")
assertThat(contentDescription).isEqualTo("0 byte")
} }
@Test @Test
fun formatDataUsage_1000() { fun formatDataUsage_1000() {
val (displayText, contentDescription) = dataUsageFormatter.formatDataUsage(1000) val displayText = dataUsageFormatter.formatDataUsage(1000)
assertThat(displayText).isEqualTo("0.98 kB") assertThat(displayText).isEqualTo("0.98 kB")
assertThat(contentDescription).isEqualTo("0.98 kB")
} }
@Test @Test
fun formatDataUsage_2000000() { fun formatDataUsage_2000000() {
val (displayText, contentDescription) = dataUsageFormatter.formatDataUsage(2000000) val displayText = dataUsageFormatter.formatDataUsage(2000000)
assertThat(displayText).isEqualTo("1.91 MB") assertThat(displayText).isEqualTo("1.91 MB")
assertThat(contentDescription).isEqualTo("1.91 MB")
} }
@Test @Test

View File

@@ -143,7 +143,7 @@ class DataUsagePreferenceControllerTest {
controller.onViewCreated(TestLifecycleOwner()) controller.onViewCreated(TestLifecycleOwner())
waitUntil { preference.isEnabled } waitUntil { preference.isEnabled }
waitUntil { preference.summary?.contains("0 B used") == true } waitUntil { preference.summary?.contains("0 byte used") == true }
} }
@Test @Test
@@ -159,7 +159,7 @@ class DataUsagePreferenceControllerTest {
controller.onViewCreated(TestLifecycleOwner()) controller.onViewCreated(TestLifecycleOwner())
waitUntil { !preference.isEnabled } waitUntil { !preference.isEnabled }
waitUntil { preference.summary?.contains("0 B used") == true } waitUntil { preference.summary?.contains("0 byte used") == true }
} }
@Test @Test