From ded0a0f0ea39e2eefd9f7f3000d64e617ccaab9c Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Fri, 27 Feb 2026 23:10:12 -0800 Subject: [PATCH] =?UTF-8?q?feat(wear):=20add=20Wear=20OS=20app=20=E2=80=94?= =?UTF-8?q?=20Compose=20for=20Wear,=20timeline=20screen,=20timer=20chips,?= =?UTF-8?q?=20Material=20theme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/wear/build.gradle.kts | 66 ++++++++ android/wear/src/main/AndroidManifest.xml | 33 ++++ .../com/chronomind/wear/WearMainActivity.kt | 158 ++++++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 android/wear/build.gradle.kts create mode 100644 android/wear/src/main/AndroidManifest.xml create mode 100644 android/wear/src/main/java/com/chronomind/wear/WearMainActivity.kt diff --git a/android/wear/build.gradle.kts b/android/wear/build.gradle.kts new file mode 100644 index 0000000..80e6007 --- /dev/null +++ b/android/wear/build.gradle.kts @@ -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) +} diff --git a/android/wear/src/main/AndroidManifest.xml b/android/wear/src/main/AndroidManifest.xml new file mode 100644 index 0000000..432c96f --- /dev/null +++ b/android/wear/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/wear/src/main/java/com/chronomind/wear/WearMainActivity.kt b/android/wear/src/main/java/com/chronomind/wear/WearMainActivity.kt new file mode 100644 index 0000000..3040dee --- /dev/null +++ b/android/wear/src/main/java/com/chronomind/wear/WearMainActivity.kt @@ -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() } + + 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) + } +}