feat(wear): expand Wear OS with quick timer creation, timer detail, dismiss/snooze actions, navigation
This commit is contained in:
parent
3ac658a89e
commit
bf2f7cde50
@ -26,46 +26,107 @@ class WearMainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Navigation State ─────────────────────────────────────────
|
||||
|
||||
enum class WearScreen { TIMELINE, QUICK_TIMER, DETAIL }
|
||||
|
||||
@Composable
|
||||
fun WearApp() {
|
||||
MaterialTheme(
|
||||
colors = Colors(
|
||||
primary = Color(0xFF6C5CE7),
|
||||
secondary = Color(0xFF00D2FF),
|
||||
background = Color(0xFF0A0A0F),
|
||||
surface = Color(0xFF14141F),
|
||||
error = Color(0xFFFF5252),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onBackground = Color(0xFFEEEEFF),
|
||||
onSurface = Color(0xFFEEEEFF),
|
||||
onError = Color.White,
|
||||
onSurfaceVariant = Color(0xFFAAAACC),
|
||||
)
|
||||
) {
|
||||
WearTimelineScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun WearTimelineScreen() {
|
||||
var currentScreen by remember { mutableStateOf(WearScreen.TIMELINE) }
|
||||
var selectedTimer by remember { mutableStateOf<WearTimer?>(null) }
|
||||
val timers = remember { mutableStateListOf<WearTimer>() }
|
||||
|
||||
MaterialTheme(
|
||||
colors = Colors(
|
||||
primary = Color(0xFF5B8DEE),
|
||||
secondary = Color(0xFF00D2FF),
|
||||
background = Color(0xFF0A0B0F),
|
||||
surface = Color(0xFF1A1B26),
|
||||
error = Color(0xFFFF4757),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onBackground = Color(0xFFE8ECF4),
|
||||
onSurface = Color(0xFFE8ECF4),
|
||||
onError = Color.White,
|
||||
onSurfaceVariant = Color(0xFF8B92A8),
|
||||
)
|
||||
) {
|
||||
when (currentScreen) {
|
||||
WearScreen.TIMELINE -> WearTimelineScreen(
|
||||
timers = timers,
|
||||
onTimerClick = { timer ->
|
||||
selectedTimer = timer
|
||||
currentScreen = WearScreen.DETAIL
|
||||
},
|
||||
onQuickTimerClick = { currentScreen = WearScreen.QUICK_TIMER }
|
||||
)
|
||||
WearScreen.QUICK_TIMER -> WearQuickTimerScreen(
|
||||
onCreateTimer = { timer ->
|
||||
timers.add(timer)
|
||||
currentScreen = WearScreen.TIMELINE
|
||||
},
|
||||
onBack = { currentScreen = WearScreen.TIMELINE }
|
||||
)
|
||||
WearScreen.DETAIL -> {
|
||||
selectedTimer?.let { timer ->
|
||||
WearTimerDetailScreen(
|
||||
timer = timer,
|
||||
onDismiss = {
|
||||
timers.removeAll { it.id == timer.id }
|
||||
currentScreen = WearScreen.TIMELINE
|
||||
},
|
||||
onSnooze = { minutes ->
|
||||
val idx = timers.indexOfFirst { it.id == timer.id }
|
||||
if (idx >= 0) {
|
||||
timers[idx] = timer.copy(
|
||||
remainingSeconds = minutes * 60.0,
|
||||
state = "snoozed"
|
||||
)
|
||||
}
|
||||
currentScreen = WearScreen.TIMELINE
|
||||
},
|
||||
onBack = { currentScreen = WearScreen.TIMELINE }
|
||||
)
|
||||
} ?: run { currentScreen = WearScreen.TIMELINE }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Timeline Screen ──────────────────────────────────────────
|
||||
|
||||
@Composable
|
||||
fun WearTimelineScreen(
|
||||
timers: List<WearTimer>,
|
||||
onTimerClick: (WearTimer) -> Unit,
|
||||
onQuickTimerClick: () -> Unit
|
||||
) {
|
||||
Scaffold(
|
||||
timeText = { TimeText() },
|
||||
vignette = { Vignette(vignettePosition = VignettePosition.TopAndBottom) }
|
||||
) {
|
||||
if (timers.isEmpty()) {
|
||||
WearEmptyState()
|
||||
} else {
|
||||
ScalingLazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
if (timers.isEmpty()) {
|
||||
item { WearEmptyState() }
|
||||
} else {
|
||||
items(timers) { timer ->
|
||||
WearTimerChip(timer)
|
||||
WearTimerChip(timer, onClick = { onTimerClick(timer) })
|
||||
}
|
||||
}
|
||||
// Quick timer button at bottom
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
CompactChip(
|
||||
onClick = onQuickTimerClick,
|
||||
label = { Text("+ Quick Timer", fontSize = 12.sp) },
|
||||
colors = ChipDefaults.chipColors(
|
||||
backgroundColor = Color(0xFF5B8DEE)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,11 +160,182 @@ private fun WearEmptyState() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Quick Timer Screen ───────────────────────────────────────
|
||||
|
||||
@Composable
|
||||
fun WearTimerChip(timer: WearTimer) {
|
||||
fun WearQuickTimerScreen(
|
||||
onCreateTimer: (WearTimer) -> Unit,
|
||||
onBack: () -> Unit
|
||||
) {
|
||||
val presets = listOf(
|
||||
"1 min" to 60.0,
|
||||
"3 min" to 180.0,
|
||||
"5 min" to 300.0,
|
||||
"10 min" to 600.0,
|
||||
"15 min" to 900.0,
|
||||
"25 min" to 1500.0,
|
||||
"30 min" to 1800.0,
|
||||
"60 min" to 3600.0
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
timeText = { TimeText() },
|
||||
vignette = { Vignette(vignettePosition = VignettePosition.TopAndBottom) }
|
||||
) {
|
||||
ScalingLazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = "Quick Timer",
|
||||
color = Color(0xFFE8ECF4),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
|
||||
items(presets) { (label, seconds) ->
|
||||
Chip(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { /* navigate to detail */ },
|
||||
onClick = {
|
||||
onCreateTimer(
|
||||
WearTimer(
|
||||
id = java.util.UUID.randomUUID().toString(),
|
||||
label = "Timer ($label)",
|
||||
urgency = "standard",
|
||||
remainingSeconds = seconds,
|
||||
state = "active"
|
||||
)
|
||||
)
|
||||
},
|
||||
label = { Text(label, fontSize = 14.sp) },
|
||||
colors = ChipDefaults.chipColors(
|
||||
backgroundColor = Color(0xFF1A1B26)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
CompactChip(
|
||||
onClick = onBack,
|
||||
label = { Text("Cancel", fontSize = 12.sp) },
|
||||
colors = ChipDefaults.chipColors(
|
||||
backgroundColor = Color(0xFF2A2D3A)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Timer Detail Screen ──────────────────────────────────────
|
||||
|
||||
@Composable
|
||||
fun WearTimerDetailScreen(
|
||||
timer: WearTimer,
|
||||
onDismiss: () -> Unit,
|
||||
onSnooze: (Int) -> Unit,
|
||||
onBack: () -> Unit
|
||||
) {
|
||||
Scaffold(
|
||||
timeText = { TimeText() },
|
||||
vignette = { Vignette(vignettePosition = VignettePosition.TopAndBottom) }
|
||||
) {
|
||||
ScalingLazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = timer.label,
|
||||
color = Color(0xFFE8ECF4),
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
|
||||
item {
|
||||
Text(
|
||||
text = timer.formattedRemaining,
|
||||
color = urgencyColor(timer.urgency),
|
||||
fontSize = 36.sp,
|
||||
fontWeight = FontWeight.Thin,
|
||||
fontFamily = FontFamily.Monospace
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
|
||||
// Dismiss
|
||||
item {
|
||||
Chip(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onDismiss,
|
||||
label = { Text("Dismiss", fontSize = 14.sp) },
|
||||
colors = ChipDefaults.chipColors(
|
||||
backgroundColor = Color(0xFFFF4757)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Snooze options
|
||||
item {
|
||||
Chip(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { onSnooze(5) },
|
||||
label = { Text("Snooze 5 min", fontSize = 14.sp) },
|
||||
colors = ChipDefaults.chipColors(
|
||||
backgroundColor = Color(0xFF2A2D3A)
|
||||
)
|
||||
)
|
||||
}
|
||||
item {
|
||||
Chip(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = { onSnooze(10) },
|
||||
label = { Text("Snooze 10 min", fontSize = 14.sp) },
|
||||
colors = ChipDefaults.chipColors(
|
||||
backgroundColor = Color(0xFF2A2D3A)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Back
|
||||
item {
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
CompactChip(
|
||||
onClick = onBack,
|
||||
label = { Text("Back", fontSize = 12.sp) },
|
||||
colors = ChipDefaults.chipColors(
|
||||
backgroundColor = Color(0xFF2A2D3A)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun urgencyColor(urgency: String): Color {
|
||||
return when (urgency) {
|
||||
"critical" -> Color(0xFFFF4757)
|
||||
"important" -> Color(0xFFFF9F43)
|
||||
"gentle" -> Color(0xFF2ED573)
|
||||
"passive" -> Color(0xFF8B92A8)
|
||||
else -> Color(0xFF5B8DEE)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Timer Chip ───────────────────────────────────────────────
|
||||
|
||||
@Composable
|
||||
fun WearTimerChip(timer: WearTimer, onClick: () -> Unit = {}) {
|
||||
Chip(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = onClick,
|
||||
label = {
|
||||
Text(
|
||||
text = timer.label,
|
||||
@ -120,19 +352,8 @@ fun WearTimerChip(timer: WearTimer) {
|
||||
color = Color(0xFF6C5CE7)
|
||||
)
|
||||
},
|
||||
icon = {
|
||||
Text(
|
||||
text = when (timer.urgency) {
|
||||
"critical" -> "🔴"
|
||||
"important" -> "🟠"
|
||||
"gentle" -> "🟢"
|
||||
else -> "🔵"
|
||||
},
|
||||
fontSize = 16.sp
|
||||
)
|
||||
},
|
||||
colors = ChipDefaults.chipColors(
|
||||
backgroundColor = Color(0xFF14141F)
|
||||
backgroundColor = Color(0xFF1A1B26)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user