Properly format numbers in weather widget
This commit is contained in:
@@ -5,7 +5,6 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.text.format.DateFormat
|
||||
import android.text.format.DateUtils
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
@@ -81,6 +80,10 @@ import de.mm20.launcher2.ui.component.Tooltip
|
||||
import de.mm20.launcher2.ui.component.weather.AnimatedWeatherIcon
|
||||
import de.mm20.launcher2.ui.component.weather.WeatherIcon
|
||||
import de.mm20.launcher2.ui.theme.transparency.transparency
|
||||
import de.mm20.launcher2.ui.utils.formatPercent
|
||||
import de.mm20.launcher2.ui.utils.formatPrecipitation
|
||||
import de.mm20.launcher2.ui.utils.formatSpeed
|
||||
import de.mm20.launcher2.ui.utils.formatTemperature
|
||||
import de.mm20.launcher2.weather.DailyForecast
|
||||
import de.mm20.launcher2.weather.Forecast
|
||||
import de.mm20.launcher2.widgets.WeatherWidget
|
||||
@@ -88,7 +91,6 @@ import kotlinx.coroutines.flow.map
|
||||
import java.text.DecimalFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun WeatherWidget(widget: WeatherWidget) {
|
||||
@@ -337,10 +339,11 @@ fun CurrentWeather(
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = convertTemperature(
|
||||
useFahrenheit = useFahrenheit,
|
||||
temp = forecast.temperature
|
||||
).toString() + "°",
|
||||
text = formatTemperature(
|
||||
context,
|
||||
forecast.temperature.toFloat(),
|
||||
measurementSystem
|
||||
),
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
)
|
||||
Text(
|
||||
@@ -379,7 +382,7 @@ fun CurrentWeather(
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(3.dp))
|
||||
Text(
|
||||
text = "${forecast.humidity!!.roundToInt()} %",
|
||||
text = formatPercent(forecast.humidity!!.toFloat()),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
}
|
||||
@@ -415,7 +418,11 @@ fun CurrentWeather(
|
||||
Spacer(modifier = Modifier.padding(3.dp))
|
||||
Text(
|
||||
text = if (forecast.windSpeed != null) {
|
||||
formatWindSpeed(useMph, forecast)
|
||||
formatSpeed(
|
||||
context,
|
||||
forecast.windSpeed!!.toFloat(),
|
||||
measurementSystem
|
||||
)
|
||||
} else {
|
||||
windDirectionAsWord(forecast.windDirection!!)
|
||||
},
|
||||
@@ -439,7 +446,15 @@ fun CurrentWeather(
|
||||
)
|
||||
Spacer(modifier = Modifier.padding(3.dp))
|
||||
Text(
|
||||
text = formatPrecipitation(useInches, forecast),
|
||||
text = if (forecast.precipProbability != null) {
|
||||
formatPercent(forecast.precipProbability!!.toFloat())
|
||||
} else {
|
||||
formatPrecipitation(
|
||||
context,
|
||||
forecast.precipitation?.toFloat() ?: 0f,
|
||||
measurementSystem
|
||||
)
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
}
|
||||
@@ -523,7 +538,11 @@ fun WeatherTimeSelector(
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.alpha(alpha),
|
||||
text = "${convertTemperature(useFahrenheit, fc.temperature)}°",
|
||||
text = formatTemperature(
|
||||
context,
|
||||
fc.temperature.toFloat(),
|
||||
measurementSystem
|
||||
),
|
||||
softWrap = false,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
@@ -550,7 +569,7 @@ fun WeatherDaySelector(
|
||||
measurementSystem: MeasurementSystem
|
||||
) {
|
||||
val dateFormat = SimpleDateFormat("EEE")
|
||||
val useFahrenheit = measurementSystem == MeasurementSystem.UnitedStates
|
||||
val context = LocalContext.current
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
LazyRow(
|
||||
@@ -592,8 +611,18 @@ fun WeatherDaySelector(
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 8.dp),
|
||||
text = "${dateFormat.format(day.timestamp)} " +
|
||||
"${convertTemperature(useFahrenheit, day.minTemp)}° / " +
|
||||
"${convertTemperature(useFahrenheit, day.maxTemp)}°",
|
||||
"${
|
||||
formatTemperature(
|
||||
context,
|
||||
day.minTemp.toFloat(),
|
||||
measurementSystem
|
||||
)
|
||||
} / " +
|
||||
formatTemperature(
|
||||
context,
|
||||
day.maxTemp.toFloat(),
|
||||
measurementSystem
|
||||
),
|
||||
softWrap = false,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
@@ -619,36 +648,13 @@ fun WeatherDaySelector(
|
||||
|
||||
private fun formatTime(context: Context, timestamp: Long, timeFormat: TimeFormat): String {
|
||||
val skeleton = if (timeFormat == TimeFormat.TwelveHour) "h:mm a" else "H:mm"
|
||||
val dateFormat = SimpleDateFormat(DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton), Locale.getDefault())
|
||||
val dateFormat = SimpleDateFormat(
|
||||
DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton),
|
||||
Locale.getDefault()
|
||||
)
|
||||
return dateFormat.format(timestamp)
|
||||
}
|
||||
|
||||
private fun convertTemperature(useFahrenheit: Boolean, temp: Double): Int {
|
||||
return (if (useFahrenheit) temp * 9.0 / 5.0 - 459.67 else temp + -273.15).roundToInt()
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun formatWindSpeed(imperialUnits: Boolean, forecast: Forecast): String {
|
||||
if (forecast.windSpeed == null) return ""
|
||||
val formatter = DecimalFormat("0.#")
|
||||
val speedValue = formatter.format(forecast.windSpeed!! * if (imperialUnits) 2.2369 else 1.0)
|
||||
val speedUnit =
|
||||
stringResource(id = if (imperialUnits) R.string.unit_mile_per_hour_symbol else R.string.unit_meter_per_second_symbol)
|
||||
return "$speedValue $speedUnit"
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun formatPrecipitation(useInches: Boolean, forecast: Forecast): String {
|
||||
if (forecast.precipProbability != null) {
|
||||
return "${forecast.precipProbability} %"
|
||||
}
|
||||
val formatter = if (useInches) DecimalFormat("#.##") else DecimalFormat("#.#")
|
||||
val precipUnit =
|
||||
if (useInches) stringResource(id = R.string.unit_inch_symbol) else stringResource(id = R.string.unit_millimeter_symbol)
|
||||
|
||||
return "${formatter.format(forecast.precipitation)} $precipUnit"
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun windDirectionAsWord(direction: Double): String {
|
||||
return stringResource(
|
||||
|
||||
293
app/ui/src/main/java/de/mm20/launcher2/ui/utils/Units.kt
Normal file
293
app/ui/src/main/java/de/mm20/launcher2/ui/utils/Units.kt
Normal file
@@ -0,0 +1,293 @@
|
||||
package de.mm20.launcher2.ui.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.icu.number.NumberFormatter
|
||||
import android.icu.number.Precision
|
||||
import android.icu.util.Measure
|
||||
import android.icu.util.MeasureUnit
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.MeasurementSystem
|
||||
import de.mm20.launcher2.ui.R
|
||||
import java.text.NumberFormat
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
internal fun formatDistance(
|
||||
context: Context,
|
||||
meters: Float,
|
||||
measurementSystem: MeasurementSystem,
|
||||
): String {
|
||||
when (measurementSystem) {
|
||||
MeasurementSystem.UnitedStates -> {
|
||||
val feet = meters * 3.28084f
|
||||
|
||||
if (feet >= 2640) {
|
||||
return formatMiles(context, feet / 5280f)
|
||||
}
|
||||
return formatFeet(context, feet)
|
||||
}
|
||||
|
||||
MeasurementSystem.UnitedKingdom -> {
|
||||
val yards = meters * 1.09361f
|
||||
|
||||
if (yards >= 880) {
|
||||
return formatMiles(context, yards / 1760f)
|
||||
}
|
||||
return formatYards(context, yards)
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (meters >= 1000) {
|
||||
return formatKilometers(context, meters / 1000f)
|
||||
}
|
||||
return formatMeters(context, meters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatMeters(
|
||||
context: Context,
|
||||
meters: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(meters, MeasureUnit.METER)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(meters)
|
||||
return "$formatted ${context.getString(R.string.unit_meter_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatKilometers(
|
||||
context: Context,
|
||||
kilometers: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(1))
|
||||
.format(Measure(kilometers, MeasureUnit.KILOMETER)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 1
|
||||
}.format(kilometers)
|
||||
return "$formatted ${context.getString(R.string.unit_kilometer_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun formatFeet(
|
||||
context: Context,
|
||||
feet: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(feet, MeasureUnit.FOOT)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(feet)
|
||||
return "$formatted ${context.getString(R.string.unit_foot_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatYards(
|
||||
context: Context,
|
||||
yards: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(yards, MeasureUnit.YARD)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(yards)
|
||||
return "$formatted ${context.getString(R.string.unit_yard_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatMiles(
|
||||
context: Context,
|
||||
miles: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(1))
|
||||
.format(Measure(miles, MeasureUnit.MILE)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 1
|
||||
}.format(miles)
|
||||
return "$formatted ${context.getString(R.string.unit_mile_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
internal fun formatSpeed(
|
||||
context: Context,
|
||||
metersPerSecond: Float,
|
||||
measurementSystem: MeasurementSystem,
|
||||
): String {
|
||||
if (measurementSystem == MeasurementSystem.UnitedStates) {
|
||||
return formatMpH(context, metersPerSecond * 2.2369f)
|
||||
} else {
|
||||
return formatKmH(context, metersPerSecond * 3.6f)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatKmH(
|
||||
context: Context,
|
||||
kmH: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(kmH, MeasureUnit.KILOMETER_PER_HOUR)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(kmH)
|
||||
return "$formatted ${context.getString(R.string.unit_kilometer_per_hour_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatMpH(
|
||||
context: Context,
|
||||
mph: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(mph, MeasureUnit.MILE_PER_HOUR)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(mph)
|
||||
return "$formatted ${context.getString(R.string.unit_mile_per_hour_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
internal fun formatTemperature(
|
||||
context: Context,
|
||||
kelvins: Float,
|
||||
measurementSystem: MeasurementSystem,
|
||||
): String {
|
||||
if (measurementSystem == MeasurementSystem.UnitedStates) {
|
||||
return formatFahrenheit(context, kelvins * 9f / 5f - 459.67f)
|
||||
} else {
|
||||
return formatCelsius(context, kelvins + -273.15f)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatCelsius(
|
||||
context: Context,
|
||||
celsius: Float,
|
||||
): String {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(celsius)
|
||||
return "$formatted°"
|
||||
}
|
||||
|
||||
private fun formatFahrenheit(
|
||||
context: Context,
|
||||
fahrenheit: Float,
|
||||
): String {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(fahrenheit)
|
||||
return "$formatted°"
|
||||
|
||||
}
|
||||
|
||||
internal fun formatPercent(
|
||||
percent: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(percent, MeasureUnit.PERCENT)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(percent)
|
||||
return "$formatted %"
|
||||
}
|
||||
}
|
||||
|
||||
internal fun formatPrecipitation(
|
||||
context: Context,
|
||||
millimeters: Float,
|
||||
measurementSystem: MeasurementSystem,
|
||||
): String {
|
||||
if (measurementSystem == MeasurementSystem.UnitedStates) {
|
||||
return formatInches(context, millimeters / 25.4f)
|
||||
} else {
|
||||
return formatMillimeters(context, millimeters)
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatMillimeters(
|
||||
context: Context,
|
||||
millimeters: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(millimeters, MeasureUnit.MILLIMETER)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(millimeters)
|
||||
return "$formatted ${context.getString(R.string.unit_millimeter_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatInches(
|
||||
context: Context,
|
||||
inches: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(1))
|
||||
.format(Measure(inches, MeasureUnit.INCH)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 1
|
||||
}.format(inches)
|
||||
return "$formatted ${context.getString(R.string.unit_inch_symbol)}"
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package de.mm20.launcher2.ui.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.icu.number.NumberFormatter
|
||||
import android.icu.number.Precision
|
||||
import android.icu.util.Measure
|
||||
import android.icu.util.MeasureUnit
|
||||
import de.mm20.launcher2.ktx.isAtLeastApiLevel
|
||||
import de.mm20.launcher2.preferences.MeasurementSystem
|
||||
import de.mm20.launcher2.ui.R
|
||||
import java.text.NumberFormat
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
internal fun formatDistance(
|
||||
context: Context,
|
||||
meters: Float,
|
||||
measurementSystem: MeasurementSystem,
|
||||
): String {
|
||||
when (measurementSystem) {
|
||||
MeasurementSystem.UnitedStates -> {
|
||||
val feet = meters * 3.28084f
|
||||
|
||||
if (feet >= 2640) {
|
||||
return formatMiles(context, feet / 5280f)
|
||||
}
|
||||
return formatFeet(context, feet)
|
||||
}
|
||||
MeasurementSystem.UnitedKingdom -> {
|
||||
val yards = meters * 1.09361f
|
||||
|
||||
if (yards >= 880) {
|
||||
return formatMiles(context, yards / 1760f)
|
||||
}
|
||||
return formatYards(context, yards)
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (meters >= 1000) {
|
||||
return formatKilometers(context, meters / 1000f)
|
||||
}
|
||||
return formatMeters(context, meters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatMeters(
|
||||
context: Context,
|
||||
meters: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(meters, MeasureUnit.METER)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(meters)
|
||||
return "$formatted ${context.getString(R.string.unit_meter_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatKilometers(
|
||||
context: Context,
|
||||
kilometers: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(1))
|
||||
.format(Measure(kilometers, MeasureUnit.KILOMETER)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 1
|
||||
}.format(kilometers)
|
||||
return "$formatted ${context.getString(R.string.unit_kilometer_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun formatFeet(
|
||||
context: Context,
|
||||
feet: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(feet, MeasureUnit.FOOT)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(feet)
|
||||
return "$formatted ${context.getString(R.string.unit_foot_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatYards(
|
||||
context: Context,
|
||||
yards: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(0))
|
||||
.format(Measure(yards, MeasureUnit.YARD)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 0
|
||||
}.format(yards)
|
||||
return "$formatted ${context.getString(R.string.unit_yard_symbol)}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatMiles(
|
||||
context: Context,
|
||||
miles: Float,
|
||||
): String {
|
||||
if (isAtLeastApiLevel(30)) {
|
||||
return NumberFormatter
|
||||
.withLocale(
|
||||
Locale.getDefault()
|
||||
).unitWidth(NumberFormatter.UnitWidth.NARROW)
|
||||
.precision(Precision.maxFraction(1))
|
||||
.format(Measure(miles, MeasureUnit.MILE)).toString()
|
||||
} else {
|
||||
val formatted = NumberFormat.getInstance().apply {
|
||||
maximumFractionDigits = 1
|
||||
}.format(miles)
|
||||
return "$formatted ${context.getString(R.string.unit_mile_symbol)}"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user