feat(wear): add Wear OS app — Compose for Wear, timeline screen, timer chips, Material theme

This commit is contained in:
saravanakumardb1 2026-02-27 23:10:12 -08:00
parent 9c34a92b9e
commit ded0a0f0ea
3 changed files with 257 additions and 0 deletions

View File

@ -0,0 +1,66 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
}
android {
namespace = "com.chronomind.wear"
compileSdk = 35
defaultConfig {
applicationId = "com.chronomind.wear"
minSdk = 30
targetSdk = 35
versionCode = 1
versionName = "1.0.0"
}
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
}
dependencies {
// Core
implementation(libs.androidx.core.ktx)
// Wear Compose
implementation(libs.androidx.wear.compose.material)
implementation(libs.androidx.wear.compose.foundation)
// Compose core (needed for Wear Compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.tooling.preview)
debugImplementation(libs.androidx.compose.ui.tooling)
// Activity
implementation(libs.androidx.activity.compose)
// Kotlin
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.serialization.json)
// Tiles
implementation(libs.androidx.wear.tiles)
}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:name="android.hardware.type.watch" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="ChronoMind"
android:supportsRtl="true"
android:theme="@android:style/Theme.DeviceDefault">
<uses-library
android:name="com.google.android.wearable"
android:required="true" />
<activity
android:name=".WearMainActivity"
android:exported="true"
android:taskAffinity="">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,158 @@
package com.chronomind.wear
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.wear.compose.foundation.lazy.ScalingLazyColumn
import androidx.wear.compose.foundation.lazy.items
import androidx.wear.compose.material.*
class WearMainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
WearApp()
}
}
}
@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() {
val timers = remember { mutableStateListOf<WearTimer>() }
Scaffold(
timeText = { TimeText() },
vignette = { Vignette(vignettePosition = VignettePosition.TopAndBottom) }
) {
if (timers.isEmpty()) {
WearEmptyState()
} else {
ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
items(timers) { timer ->
WearTimerChip(timer)
}
}
}
}
}
@Composable
private fun WearEmptyState() {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "",
fontSize = 32.sp
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "No timers",
color = Color(0xFFAAAACC),
fontSize = 14.sp,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Create from phone",
color = Color(0xFF666688),
fontSize = 11.sp,
textAlign = TextAlign.Center
)
}
}
}
@Composable
fun WearTimerChip(timer: WearTimer) {
Chip(
modifier = Modifier.fillMaxWidth(),
onClick = { /* navigate to detail */ },
label = {
Text(
text = timer.label,
maxLines = 1,
fontSize = 14.sp
)
},
secondaryLabel = {
Text(
text = timer.formattedRemaining,
fontSize = 12.sp,
fontFamily = FontFamily.Monospace,
fontWeight = FontWeight.Bold,
color = Color(0xFF6C5CE7)
)
},
icon = {
Text(
text = when (timer.urgency) {
"critical" -> "🔴"
"important" -> "🟠"
"gentle" -> "🟢"
else -> "🔵"
},
fontSize = 16.sp
)
},
colors = ChipDefaults.chipColors(
backgroundColor = Color(0xFF14141F)
)
)
}
// MARK: - Wear Timer Model (simplified from phone)
data class WearTimer(
val id: String,
val label: String,
val urgency: String,
val remainingSeconds: Double,
val state: String
) {
val formattedRemaining: String
get() {
val total = remainingSeconds.toInt().coerceAtLeast(0)
val h = total / 3600
val m = (total % 3600) / 60
val s = total % 60
return if (h > 0) String.format("%02d:%02d:%02d", h, m, s)
else String.format("%02d:%02d", m, s)
}
}