1 Commits
Author SHA1 Message Date
MrBlakeandClaude Opus 5.5 62867fcd07 fix(uhr): time picker digits clipped, dial haptics; 0.1.1
- the theme mapped displayLarge to the 88sp clock style, which the M3 time picker uses for its
  hour/minute fields; displayLarge/headline are back to M3 defaults
- tick on every value the dial passes, firmer click when hours settle and on OK

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-17 15:22:32 +02:00
6 changed files with 34 additions and 6 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 54 KiB

+2 -2
View File
@@ -16,8 +16,8 @@ android {
applicationId = "app.uhr" applicationId = "app.uhr"
minSdk = 29 minSdk = 29
targetSdk = 36 targetSdk = 36
versionCode = 1 versionCode = 2
versionName = "0.1.0" versionName = "0.1.1"
} }
// Release key lives outside the repo; without it, release builds fall back to the debug key (local only). // Release key lives outside the repo; without it, release builds fall back to the debug key (local only).
@@ -306,22 +306,49 @@ fun soundName(uri: String?): String {
} }
} }
/** S03: M3 time picker with dial/keyboard switch. */ /**
* S03: M3 time picker with dial/keyboard switch. The Compose dial has no haptics; like the original we tick
* on every value the hand passes and give a firmer click when it settles after release.
*/
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun TimeDialog(hour: Int, minute: Int, onDismiss: () -> Unit, onPick: (Int, Int) -> Unit) { fun TimeDialog(hour: Int, minute: Int, onDismiss: () -> Unit, onPick: (Int, Int) -> Unit) {
val state = rememberTimePickerState(hour, minute, Fmt.is24h) val state = rememberTimePickerState(hour, minute, Fmt.is24h)
var keyboard by remember { mutableStateOf(false) } var keyboard by remember { mutableStateOf(false) }
val view = androidx.compose.ui.platform.LocalView.current
androidx.compose.runtime.LaunchedEffect(state) {
var last = state.hour to state.minute
androidx.compose.runtime.snapshotFlow { state.hour to state.minute }.collect { now ->
if (now != last) {
view.performHapticFeedback(android.view.HapticFeedbackConstants.CLOCK_TICK)
last = now
}
}
}
// the dial switches from hours to minutes when the hour settles: confirm that with a firmer click
androidx.compose.runtime.LaunchedEffect(state) {
var hourMode = state.selection == androidx.compose.material3.TimePickerSelectionMode.Hour
androidx.compose.runtime.snapshotFlow { state.selection }.collect { sel ->
val nowHour = sel == androidx.compose.material3.TimePickerSelectionMode.Hour
if (hourMode && !nowHour) view.performHapticFeedback(android.view.HapticFeedbackConstants.CONFIRM)
hourMode = nowHour
}
}
AlertDialog( AlertDialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
title = { Text(stringResource(R.string.pick_time)) }, title = { Text(stringResource(R.string.pick_time)) },
text = { text = {
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.fillMaxWidth()) {
if (keyboard) TimeInput(state) else TimePicker(state) if (keyboard) TimeInput(state) else TimePicker(state)
TextButton(onClick = { keyboard = !keyboard }) { Text(stringResource(if (keyboard) R.string.use_dial else R.string.use_keyboard)) } TextButton(onClick = { keyboard = !keyboard }) { Text(stringResource(if (keyboard) R.string.use_dial else R.string.use_keyboard)) }
} }
}, },
confirmButton = { TextButton(onClick = { onPick(state.hour, state.minute) }) { Text(stringResource(android.R.string.ok)) } }, confirmButton = {
TextButton(onClick = {
view.performHapticFeedback(android.view.HapticFeedbackConstants.CONFIRM)
onPick(state.hour, state.minute)
}) { Text(stringResource(android.R.string.ok)) }
},
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(android.R.string.cancel)) } }, dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(android.R.string.cancel)) } },
) )
} }
@@ -37,7 +37,8 @@ private val Dark = darkColorScheme(
) )
private val AppTypography = Typography( private val AppTypography = Typography(
displayLarge = WType.clockBig, headlineLarge = WType.headline, titleLarge = WType.title, // displayLarge/headline stay M3 defaults: the time picker uses them for its hour and minute fields
titleLarge = WType.title,
bodyLarge = WType.body, bodyMedium = WType.bodySm, labelLarge = WType.label, labelMedium = WType.label, labelSmall = WType.labelSm, bodyLarge = WType.body, bodyMedium = WType.bodySm, labelLarge = WType.label, labelMedium = WType.label, labelSmall = WType.labelSm,
) )