refactor(utils): use time formats from resources to enable localization

Closes: #151
This commit is contained in:
Nishant Mishra
2025-11-29 15:53:32 +05:30
parent 1e451041fb
commit 8d77333fec
5 changed files with 81 additions and 40 deletions

View File

@@ -32,7 +32,6 @@ import androidx.compose.ui.unit.dp
import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer
import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter
import org.nsh07.pomodoro.R import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.utils.millisecondsToHoursMinutes
@Composable @Composable
fun ColumnScope.ProductivityGraph( fun ColumnScope.ProductivityGraph(
@@ -53,6 +52,9 @@ fun ColumnScope.ProductivityGraph(
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
TimeColumnChart( TimeColumnChart(
modelProducer, modelProducer,
hoursFormat = stringResource(R.string.hours_format),
hoursMinutesFormat = stringResource(R.string.hours_and_minutes_format),
minutesFormat = stringResource(R.string.minutes_format),
axisTypeface = axisTypeface, axisTypeface = axisTypeface,
markerTypeface = markerTypeface, markerTypeface = markerTypeface,
xValueFormatter = CartesianValueFormatter { _, value, _ -> xValueFormatter = CartesianValueFormatter { _, value, _ ->
@@ -63,9 +65,6 @@ fun ColumnScope.ProductivityGraph(
3.0 -> "18 - 24" 3.0 -> "18 - 24"
else -> "" else -> ""
} }
},
yValueFormatter = CartesianValueFormatter { _, value, _ ->
millisecondsToHoursMinutes(value.toLong())
} }
) )
} }

View File

@@ -130,6 +130,10 @@ fun StatsScreen(
) { ) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val hoursFormat = stringResource(R.string.hours_format)
val hoursMinutesFormat = stringResource(R.string.hours_and_minutes_format)
val minutesFormat = stringResource(R.string.minutes_format)
var lastWeekStatExpanded by rememberSaveable { mutableStateOf(false) } var lastWeekStatExpanded by rememberSaveable { mutableStateOf(false) }
var lastMonthStatExpanded by rememberSaveable { mutableStateOf(false) } var lastMonthStatExpanded by rememberSaveable { mutableStateOf(false) }
@@ -223,7 +227,10 @@ fun StatsScreen(
) )
Text( Text(
remember(todayStat) { remember(todayStat) {
millisecondsToHoursMinutes(todayStat?.totalFocusTime() ?: 0) millisecondsToHoursMinutes(
todayStat?.totalFocusTime() ?: 0,
hoursMinutesFormat
)
}, },
style = typography.displaySmall, style = typography.displaySmall,
color = colorScheme.onPrimaryContainer, color = colorScheme.onPrimaryContainer,
@@ -249,7 +256,10 @@ fun StatsScreen(
) )
Text( Text(
remember(todayStat) { remember(todayStat) {
millisecondsToHoursMinutes(todayStat?.breakTime ?: 0) millisecondsToHoursMinutes(
todayStat?.breakTime ?: 0,
hoursMinutesFormat
)
}, },
style = typography.displaySmall, style = typography.displaySmall,
color = colorScheme.onTertiaryContainer, color = colorScheme.onTertiaryContainer,
@@ -282,7 +292,8 @@ fun StatsScreen(
millisecondsToHoursMinutes( millisecondsToHoursMinutes(
remember(lastWeekAverageFocusTimes) { remember(lastWeekAverageFocusTimes) {
lastWeekAverageFocusTimes.sum().toLong() lastWeekAverageFocusTimes.sum().toLong()
} },
hoursMinutesFormat
), ),
style = typography.displaySmall style = typography.displaySmall
) )
@@ -295,7 +306,10 @@ fun StatsScreen(
} }
item { item {
TimeColumnChart( TimeColumnChart(
lastWeekSummaryChartData.first, modelProducer = lastWeekSummaryChartData.first,
hoursFormat = hoursFormat,
hoursMinutesFormat = hoursMinutesFormat,
minutesFormat = minutesFormat,
modifier = Modifier.padding(start = 16.dp), modifier = Modifier.padding(start = 16.dp),
axisTypeface = axisTypeface, axisTypeface = axisTypeface,
markerTypeface = markerTypeface, markerTypeface = markerTypeface,
@@ -361,7 +375,8 @@ fun StatsScreen(
millisecondsToHoursMinutes( millisecondsToHoursMinutes(
remember(lastMonthAverageFocusTimes) { remember(lastMonthAverageFocusTimes) {
lastMonthAverageFocusTimes.sum().toLong() lastMonthAverageFocusTimes.sum().toLong()
} },
hoursMinutesFormat
), ),
style = typography.displaySmall style = typography.displaySmall
) )
@@ -374,7 +389,10 @@ fun StatsScreen(
} }
item { item {
TimeColumnChart( TimeColumnChart(
lastMonthSummaryChartData.first, modelProducer = lastMonthSummaryChartData.first,
hoursFormat = hoursFormat,
hoursMinutesFormat = hoursMinutesFormat,
minutesFormat = minutesFormat,
modifier = Modifier.padding(start = 16.dp), modifier = Modifier.padding(start = 16.dp),
axisTypeface = axisTypeface, axisTypeface = axisTypeface,
markerTypeface = markerTypeface, markerTypeface = markerTypeface,
@@ -441,7 +459,8 @@ fun StatsScreen(
millisecondsToHoursMinutes( millisecondsToHoursMinutes(
remember(lastYearAverageFocusTimes) { remember(lastYearAverageFocusTimes) {
lastYearAverageFocusTimes.sum().toLong() lastYearAverageFocusTimes.sum().toLong()
} },
hoursMinutesFormat
), ),
style = typography.displaySmall style = typography.displaySmall
) )
@@ -454,7 +473,10 @@ fun StatsScreen(
} }
item { item {
TimeLineChart( TimeLineChart(
lastYearSummaryChartData.first, modelProducer = lastYearSummaryChartData.first,
hoursFormat = hoursFormat,
hoursMinutesFormat = hoursMinutesFormat,
minutesFormat = minutesFormat,
modifier = Modifier.padding(start = 16.dp), modifier = Modifier.padding(start = 16.dp),
axisTypeface = axisTypeface, axisTypeface = axisTypeface,
markerTypeface = markerTypeface, markerTypeface = markerTypeface,

View File

@@ -68,6 +68,9 @@ import org.nsh07.pomodoro.utils.millisecondsToMinutes
@Composable @Composable
fun TimeColumnChart( fun TimeColumnChart(
modelProducer: CartesianChartModelProducer, modelProducer: CartesianChartModelProducer,
hoursFormat: String,
hoursMinutesFormat: String,
minutesFormat: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
axisTypeface: Typeface = Typeface.DEFAULT, axisTypeface: Typeface = Typeface.DEFAULT,
markerTypeface: Typeface = Typeface.DEFAULT, markerTypeface: Typeface = Typeface.DEFAULT,
@@ -76,9 +79,9 @@ fun TimeColumnChart(
xValueFormatter: CartesianValueFormatter = CartesianValueFormatter.Default, xValueFormatter: CartesianValueFormatter = CartesianValueFormatter.Default,
yValueFormatter: CartesianValueFormatter = CartesianValueFormatter { _, value, _ -> yValueFormatter: CartesianValueFormatter = CartesianValueFormatter { _, value, _ ->
if (value >= 60 * 60 * 1000) { if (value >= 60 * 60 * 1000) {
millisecondsToHours(value.toLong()) millisecondsToHours(value.toLong(), hoursFormat)
} else { } else {
millisecondsToMinutes(value.toLong()) millisecondsToMinutes(value.toLong(), minutesFormat)
} }
}, },
markerValueFormatter: DefaultCartesianMarker.ValueFormatter = DefaultCartesianMarker.ValueFormatter { _, targets -> markerValueFormatter: DefaultCartesianMarker.ValueFormatter = DefaultCartesianMarker.ValueFormatter { _, targets ->
@@ -88,9 +91,9 @@ fun TimeColumnChart(
} else 0L } else 0L
if (value >= 60 * 60 * 1000) { if (value >= 60 * 60 * 1000) {
millisecondsToHoursMinutes(value) millisecondsToHoursMinutes(value, hoursMinutesFormat)
} else { } else {
millisecondsToMinutes(value) millisecondsToMinutes(value, minutesFormat)
} }
}, },
animationSpec: AnimationSpec<Float>? = null animationSpec: AnimationSpec<Float>? = null
@@ -179,7 +182,13 @@ private fun TimeColumnChartPreview() {
} }
TomatoTheme { TomatoTheme {
Surface { Surface {
TimeColumnChart(thickness = 8.dp, modelProducer = modelProducer) TimeColumnChart(
thickness = 8.dp,
modelProducer = modelProducer,
hoursFormat = "%dh",
hoursMinutesFormat = "%dh %dm",
minutesFormat = "%dm"
)
} }
} }
} }

View File

@@ -74,6 +74,9 @@ import org.nsh07.pomodoro.utils.millisecondsToMinutes
@Composable @Composable
fun TimeLineChart( fun TimeLineChart(
modelProducer: CartesianChartModelProducer, modelProducer: CartesianChartModelProducer,
hoursFormat: String,
hoursMinutesFormat: String,
minutesFormat: String,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
axisTypeface: Typeface = Typeface.DEFAULT, axisTypeface: Typeface = Typeface.DEFAULT,
markerTypeface: Typeface = Typeface.DEFAULT, markerTypeface: Typeface = Typeface.DEFAULT,
@@ -82,9 +85,9 @@ fun TimeLineChart(
xValueFormatter: CartesianValueFormatter = CartesianValueFormatter.Default, xValueFormatter: CartesianValueFormatter = CartesianValueFormatter.Default,
yValueFormatter: CartesianValueFormatter = CartesianValueFormatter { _, value, _ -> yValueFormatter: CartesianValueFormatter = CartesianValueFormatter { _, value, _ ->
if (value >= 60 * 60 * 1000) { if (value >= 60 * 60 * 1000) {
millisecondsToHours(value.toLong()) millisecondsToHours(value.toLong(), hoursFormat)
} else { } else {
millisecondsToMinutes(value.toLong()) millisecondsToMinutes(value.toLong(), minutesFormat)
} }
}, },
markerValueFormatter: DefaultCartesianMarker.ValueFormatter = DefaultCartesianMarker.ValueFormatter { _, targets -> markerValueFormatter: DefaultCartesianMarker.ValueFormatter = DefaultCartesianMarker.ValueFormatter { _, targets ->
@@ -94,9 +97,9 @@ fun TimeLineChart(
} else 0L } else 0L
if (value >= 60 * 60 * 1000) { if (value >= 60 * 60 * 1000) {
millisecondsToHoursMinutes(value) millisecondsToHoursMinutes(value, hoursMinutesFormat)
} else { } else {
millisecondsToMinutes(value) millisecondsToMinutes(value, minutesFormat)
} }
}, },
animationSpec: AnimationSpec<Float>? = null animationSpec: AnimationSpec<Float>? = null
@@ -203,7 +206,12 @@ private fun TimeLineChartPreview() {
} }
TomatoTheme { TomatoTheme {
Surface { Surface {
TimeLineChart(modelProducer = modelProducer) TimeLineChart(
modelProducer = modelProducer,
hoursFormat = "%dh",
hoursMinutesFormat = "%dh %dm",
minutesFormat = "%dm"
)
} }
} }
} }

View File

@@ -22,25 +22,40 @@
<string name="always_on_display">Always On Display</string> <string name="always_on_display">Always On Display</string>
<string name="always_on_display_desc">Tap anywhere when viewing the timer to switch to AOD mode</string> <string name="always_on_display_desc">Tap anywhere when viewing the timer to switch to AOD mode</string>
<string name="app_name">Tomato</string> <string name="app_name">Tomato</string>
<string name="app_name_plus">Tomato+</string>
<string name="appearance">Appearance</string>
<string name="black_theme">Black theme</string> <string name="black_theme">Black theme</string>
<string name="black_theme_desc">Use a pure black dark theme</string> <string name="black_theme_desc">Use a pure black dark theme</string>
<string name="bmc">BuyMeACoffee</string>
<string name="break_">Break</string> <string name="break_">Break</string>
<string name="choose_color_scheme">Choose color scheme</string> <string name="choose_color_scheme">Choose color scheme</string>
<string name="choose_language">Choose language</string>
<string name="choose_theme">Choose theme</string> <string name="choose_theme">Choose theme</string>
<string name="color">Color</string> <string name="color">Color</string>
<string name="color_scheme">Color scheme</string> <string name="color_scheme">Color scheme</string>
<string name="completed">Completed</string> <string name="completed">Completed</string>
<string name="dark">Dark</string> <string name="dark">Dark</string>
<string name="dnd">Do Not Disturb</string>
<string name="dnd_desc">Turn on DND when running a Focus timer</string>
<string name="durations">Durations</string>
<string name="dynamic">Dynamic</string> <string name="dynamic">Dynamic</string>
<string name="dynamic_color">Dynamic color</string>
<string name="dynamic_color_desc">Adapt theme colors from your wallpaper</string>
<string name="exit">Exit</string> <string name="exit">Exit</string>
<string name="focus">Focus</string> <string name="focus">Focus</string>
<string name="focus_per_day_avg">focus per day (avg)</string> <string name="focus_per_day_avg">focus per day (avg)</string>
<string name="get_plus">Get Tomato+</string>
<string name="help_with_translation">Help with translation</string>
<string name="hours_and_minutes_format">%dh %dm</string>
<string name="hours_format">%dh</string>
<string name="language">Language</string>
<string name="last_month">Last month</string> <string name="last_month">Last month</string>
<string name="last_week">Last week</string> <string name="last_week">Last week</string>
<string name="last_year">Last year</string> <string name="last_year">Last year</string>
<string name="light">Light</string> <string name="light">Light</string>
<string name="long_break">Long break</string> <string name="long_break">Long break</string>
<string name="min_remaining_notification">%1$s min remaining</string> <string name="min_remaining_notification">%1$s min remaining</string>
<string name="minutes_format">%dm</string>
<string name="monthly_productivity_analysis">Monthly productivity analysis</string> <string name="monthly_productivity_analysis">Monthly productivity analysis</string>
<string name="more">More</string> <string name="more">More</string>
<string name="more_info">More info</string> <string name="more_info">More info</string>
@@ -51,13 +66,16 @@
<string name="pomodoro_info">A \"session\" is a sequence of pomodoro intervals that contain focus intervals, short break intervals, and a long break interval. The last break of a session is always a long break.</string> <string name="pomodoro_info">A \"session\" is a sequence of pomodoro intervals that contain focus intervals, short break intervals, and a long break interval. The last break of a session is always a long break.</string>
<string name="productivity_analysis">Productivity analysis</string> <string name="productivity_analysis">Productivity analysis</string>
<string name="productivity_analysis_desc">Focus durations at different times of the day</string> <string name="productivity_analysis_desc">Focus durations at different times of the day</string>
<string name="rate_on_google_play">Rate on Google Play</string>
<string name="restart">Restart</string> <string name="restart">Restart</string>
<string name="selected">Selected</string>
<string name="session_length">Session length</string> <string name="session_length">Session length</string>
<string name="session_length_desc">Focus intervals in one session: %1$d</string> <string name="session_length_desc">Focus intervals in one session: %1$d</string>
<string name="settings">Settings</string> <string name="settings">Settings</string>
<string name="short_break">Short break</string> <string name="short_break">Short break</string>
<string name="skip">Skip</string> <string name="skip">Skip</string>
<string name="skip_to_next">Skip to next</string> <string name="skip_to_next">Skip to next</string>
<string name="sound">Sound</string>
<string name="start">Start</string> <string name="start">Start</string>
<string name="start_next">Start next</string> <string name="start_next">Start next</string>
<string name="stats">Stats</string> <string name="stats">Stats</string>
@@ -70,28 +88,13 @@
<string name="timer">Timer</string> <string name="timer">Timer</string>
<string name="timer_progress">Timer progress</string> <string name="timer_progress">Timer progress</string>
<string name="timer_session_count">%1$d of %2$d</string> <string name="timer_session_count">%1$d of %2$d</string>
<string name="timer_settings_reset_info">Reset the timer to change settings</string>
<string name="today">Today</string> <string name="today">Today</string>
<string name="tomato_foss">Tomato FOSS</string>
<string name="tomato_foss_desc">All features are unlocked in this version. If my app made a difference in your life, please consider supporting me by donating on %1$s.</string>
<string name="up_next">Up next</string> <string name="up_next">Up next</string>
<string name="up_next_notification">Up next: %1$s (%2$s)</string> <string name="up_next_notification">Up next: %1$s (%2$s)</string>
<string name="vibrate">Vibration</string> <string name="vibrate">Vibration</string>
<string name="vibrate_desc">Vibrate when a timer completes</string> <string name="vibrate_desc">Vibrate when a timer completes</string>
<string name="weekly_productivity_analysis">Weekly productivity analysis</string> <string name="weekly_productivity_analysis">Weekly productivity analysis</string>
<string name="appearance">Appearance</string>
<string name="durations">Durations</string>
<string name="sound">Sound</string>
<string name="dnd">Do Not Disturb</string>
<string name="dnd_desc">Turn on DND when running a Focus timer</string>
<string name="app_name_plus">Tomato+</string>
<string name="get_plus">Get Tomato+</string>
<string name="dynamic_color">Dynamic color</string>
<string name="dynamic_color_desc">Adapt theme colors from your wallpaper</string>
<string name="tomato_foss">Tomato FOSS</string>
<string name="tomato_foss_desc">All features are unlocked in this version. If my app made a difference in your life, please consider supporting me by donating on %1$s.</string>
<string name="language">Language</string>
<string name="choose_language">Choose language</string>
<string name="rate_on_google_play">Rate on Google Play</string>
<string name="bmc">BuyMeACoffee</string>
<string name="selected">Selected</string>
<string name="help_with_translation">Help with translation</string>
<string name="timer_settings_reset_info">Reset the timer to change settings</string>
</resources> </resources>