feat: default workout distance unit to the phone's measurement system

Read the phone's measurement preference (Android 14+ Regional preferences
override via the locale's -u-ms- extension, else ICU's locale-derived
US/UK = miles) and default the New Workout composer's km/mi selector to it.
Health Connect distances (always metres) are converted into the chosen unit on
pre-fill instead of being forced to km.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
This commit is contained in:
Claude
2026-06-17 03:08:43 +00:00
parent d4c7577828
commit d8c66a4a25
2 changed files with 53 additions and 4 deletions
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2025 Vitor Pamplona
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts
import android.icu.util.LocaleData
import android.icu.util.ULocale
import java.util.Locale
/**
* Whether the phone's measurement preference favours miles for distance.
*
* Honours the Android 14+ "Regional preferences → Measurement system" override,
* which the platform surfaces through the default locale's Unicode `-u-ms-`
* extension (`metric` / `ussystem` / `uksystem`). When no explicit override is
* set it falls back to ICU's locale-derived measurement system (US and UK both
* use miles for distance), available since API 24.
*/
fun phonePrefersMiles(): Boolean {
val locale = Locale.getDefault(Locale.Category.FORMAT)
locale.getUnicodeLocaleType("ms")?.let { ms ->
return ms == "ussystem" || ms == "uksystem"
}
return when (LocaleData.getMeasurementSystem(ULocale.forLocale(locale))) {
LocaleData.MeasurementSystem.US, LocaleData.MeasurementSystem.UK -> true
else -> false
}
}
@@ -54,7 +54,7 @@ class NewWorkoutViewModel : ViewModel() {
var minutes by mutableStateOf("")
var seconds by mutableStateOf("")
var distance by mutableStateOf("")
var distanceUnit by mutableStateOf(DistanceTag.KILOMETERS)
var distanceUnit by mutableStateOf(if (phonePrefersMiles()) DistanceTag.MILES else DistanceTag.KILOMETERS)
var calories by mutableStateOf("")
var notes by mutableStateOf("")
@@ -100,8 +100,10 @@ class NewWorkoutViewModel : ViewModel() {
seconds = (route.durationSeconds % 60).toString()
}
if (route.distanceMeters > 0) {
distance = ((route.distanceMeters / 1000.0 * 100).roundToInt() / 100.0).toString()
distanceUnit = DistanceTag.KILOMETERS
val miles = phonePrefersMiles()
distanceUnit = if (miles) DistanceTag.MILES else DistanceTag.KILOMETERS
val value = if (miles) route.distanceMeters / DistanceTag.METERS_PER_MILE else route.distanceMeters / 1000.0
distance = ((value * 100).roundToInt() / 100.0).toString()
}
if (route.calories > 0) calories = route.calories.toString()
@@ -127,7 +129,7 @@ class NewWorkoutViewModel : ViewModel() {
minutes = ""
seconds = ""
distance = ""
distanceUnit = DistanceTag.KILOMETERS
distanceUnit = if (phonePrefersMiles()) DistanceTag.MILES else DistanceTag.KILOMETERS
calories = ""
notes = ""
source = SourceTag.MANUAL