From 851475aa984b266b16a6bca3a7f9ccd9b140b39f Mon Sep 17 00:00:00 2001 From: fenopy Date: Wed, 24 Jun 2026 13:22:23 -0500 Subject: [PATCH] remove android build --- android/app/build.gradle.kts | 57 -- android/app/proguard-rules.pro | 2 - android/app/src/main/AndroidManifest.xml | 27 - .../main/java/com/kxbridge/MainActivity.kt | 56 -- .../java/com/kxbridge/ui/PrinterScreen.kt | 594 ------------------ .../main/java/com/kxbridge/ui/SetupScreen.kt | 70 --- .../main/java/com/kxbridge/ui/theme/Color.kt | 13 - .../main/java/com/kxbridge/ui/theme/Theme.kt | 30 - .../kxbridge/viewmodel/PrinterViewModel.kt | 83 --- .../res/drawable/ic_launcher_background.xml | 4 - .../res/drawable/ic_launcher_foreground.xml | 15 - .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../main/res/mipmap-anydpi/ic_launcher.xml | 5 - .../res/mipmap-anydpi/ic_launcher_round.xml | 5 - .../app/src/main/res/values-night/themes.xml | 4 - android/app/src/main/res/values/strings.xml | 4 - android/app/src/main/res/values/themes.xml | 4 - android/build.gradle.kts | 6 - android/gradle.properties | 4 - .../gradle/wrapper/gradle-wrapper.properties | 7 - android/settings.gradle.kts | 17 - 22 files changed, 1017 deletions(-) delete mode 100644 android/app/build.gradle.kts delete mode 100644 android/app/proguard-rules.pro delete mode 100644 android/app/src/main/AndroidManifest.xml delete mode 100644 android/app/src/main/java/com/kxbridge/MainActivity.kt delete mode 100644 android/app/src/main/java/com/kxbridge/ui/PrinterScreen.kt delete mode 100644 android/app/src/main/java/com/kxbridge/ui/SetupScreen.kt delete mode 100644 android/app/src/main/java/com/kxbridge/ui/theme/Color.kt delete mode 100644 android/app/src/main/java/com/kxbridge/ui/theme/Theme.kt delete mode 100644 android/app/src/main/java/com/kxbridge/viewmodel/PrinterViewModel.kt delete mode 100644 android/app/src/main/res/drawable/ic_launcher_background.xml delete mode 100644 android/app/src/main/res/drawable/ic_launcher_foreground.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi/ic_launcher.xml delete mode 100644 android/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml delete mode 100644 android/app/src/main/res/values-night/themes.xml delete mode 100644 android/app/src/main/res/values/strings.xml delete mode 100644 android/app/src/main/res/values/themes.xml delete mode 100644 android/build.gradle.kts delete mode 100644 android/gradle.properties delete mode 100644 android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 android/settings.gradle.kts diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts deleted file mode 100644 index d1703d2..0000000 --- a/android/app/build.gradle.kts +++ /dev/null @@ -1,57 +0,0 @@ -plugins { - id("com.android.application") - id("org.jetbrains.kotlin.android") - id("org.jetbrains.kotlin.plugin.compose") - id("org.jetbrains.kotlin.plugin.serialization") -} - -android { - namespace = "com.kxbridge" - compileSdk = 34 - - defaultConfig { - applicationId = "com.kxbridge" - minSdk = 26 - targetSdk = 34 - versionCode = 1 - versionName = "1.0" - } - - buildTypes { - release { - isMinifyEnabled = true - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - - kotlinOptions { - jvmTarget = "17" - } - - buildFeatures { - compose = true - } -} - -dependencies { - val composeBom = platform("androidx.compose:compose-bom:2024.05.00") - implementation(composeBom) - implementation("androidx.compose.ui:ui") - implementation("androidx.compose.ui:ui-tooling-preview") - implementation("androidx.compose.material3:material3") - implementation("androidx.activity:activity-compose:1.9.0") - implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0") - implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.0") - implementation("com.squareup.okhttp3:okhttp:4.12.0") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1") - debugImplementation("androidx.compose.ui:ui-tooling") -} diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro deleted file mode 100644 index 1d5102f..0000000 --- a/android/app/proguard-rules.pro +++ /dev/null @@ -1,2 +0,0 @@ --keep class com.kxbridge.data.model.** { *; } --keepattributes *Annotation* diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index c54fa50..0000000 --- a/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/android/app/src/main/java/com/kxbridge/MainActivity.kt b/android/app/src/main/java/com/kxbridge/MainActivity.kt deleted file mode 100644 index 64996e0..0000000 --- a/android/app/src/main/java/com/kxbridge/MainActivity.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.kxbridge - -import android.content.Context -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.activity.viewModels -import androidx.compose.runtime.* -import com.kxbridge.ui.PrinterScreen -import com.kxbridge.ui.SetupScreen -import com.kxbridge.ui.theme.KxBridgeTheme -import com.kxbridge.viewmodel.PrinterViewModel - -private const val PREFS = "kxbridge" -private const val KEY_URL = "server_url" - -class MainActivity : ComponentActivity() { - - private val viewModel: PrinterViewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - - val prefs = getSharedPreferences(PREFS, Context.MODE_PRIVATE) - val savedUrl = prefs.getString(KEY_URL, null) - if (savedUrl != null) { - viewModel.connect(savedUrl) - } - - setContent { - KxBridgeTheme { - var serverUrl by remember { mutableStateOf(savedUrl ?: "") } - var showSetup by remember { mutableStateOf(savedUrl == null) } - - if (showSetup) { - SetupScreen( - initialUrl = serverUrl, - onConnect = { url -> - prefs.edit().putString(KEY_URL, url).apply() - serverUrl = url - showSetup = false - viewModel.connect(url) - }, - ) - } else { - PrinterScreen( - viewModel = viewModel, - onChangeServer = { showSetup = true }, - ) - } - } - } - } -} diff --git a/android/app/src/main/java/com/kxbridge/ui/PrinterScreen.kt b/android/app/src/main/java/com/kxbridge/ui/PrinterScreen.kt deleted file mode 100644 index 3f84cbc..0000000 --- a/android/app/src/main/java/com/kxbridge/ui/PrinterScreen.kt +++ /dev/null @@ -1,594 +0,0 @@ -package com.kxbridge.ui - -import android.graphics.Bitmap -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Settings -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp -import com.kxbridge.data.model.AmsSlot -import com.kxbridge.data.model.PrinterState -import com.kxbridge.ui.theme.Blue300 -import com.kxbridge.ui.theme.ErrorRed -import com.kxbridge.ui.theme.Orange400 -import com.kxbridge.viewmodel.PrinterViewModel -import com.kxbridge.viewmodel.UiState -import kotlin.math.roundToInt - -private enum class TempTarget { NOZZLE, BED } - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun PrinterScreen( - viewModel: PrinterViewModel, - onChangeServer: () -> Unit, -) { - val uiState by viewModel.uiState.collectAsState() - val cameraFrame by viewModel.cameraFrame.collectAsState() - val cameraEnabled by viewModel.cameraEnabled.collectAsState() - var showCancelDialog by remember { mutableStateOf(false) } - - if (showCancelDialog) { - AlertDialog( - onDismissRequest = { showCancelDialog = false }, - title = { Text("Stop print?") }, - text = { Text("This will stop the current print job.") }, - confirmButton = { - TextButton(onClick = { - showCancelDialog = false - viewModel.cancel() - }) { Text("Stop print", color = ErrorRed) } - }, - dismissButton = { - TextButton(onClick = { showCancelDialog = false }) { Text("Keep printing") } - }, - ) - } - - Scaffold( - topBar = { - TopAppBar( - title = { - val name = (uiState as? UiState.Success)?.state?.printerName - ?.takeIf { it.isNotBlank() } ?: "KX-Bridge" - Text(name, maxLines = 1, overflow = TextOverflow.Ellipsis) - }, - actions = { - IconButton(onClick = onChangeServer) { - Icon(Icons.Default.Settings, contentDescription = "Change server") - } - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.surface, - ), - ) - }, - ) { padding -> - Box( - modifier = Modifier - .fillMaxSize() - .padding(padding), - contentAlignment = Alignment.Center, - ) { - when (val s = uiState) { - is UiState.Loading -> CircularProgressIndicator() - is UiState.Error -> ErrorContent(s.message) - is UiState.Success -> PrinterContent( - state = s.state, - cameraFrame = cameraFrame, - cameraEnabled = cameraEnabled, - onPause = viewModel::pause, - onResume = viewModel::resume, - onCancel = { showCancelDialog = true }, - onSetTemp = viewModel::setTemperature, - onToggleLight = { on -> viewModel.setLight(on) }, - onToggleCamera = viewModel::setCameraEnabled, - ) - } - } - } -} - -@Composable -private fun ErrorContent(message: String) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - Text("Cannot reach bridge", style = MaterialTheme.typography.titleMedium, color = ErrorRed) - Text( - message, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), - ) - } -} - -@Composable -private fun PrinterContent( - state: PrinterState, - cameraFrame: Bitmap?, - cameraEnabled: Boolean, - onPause: () -> Unit, - onResume: () -> Unit, - onCancel: () -> Unit, - onSetTemp: (nozzle: Double, bed: Double) -> Unit, - onToggleLight: (Boolean) -> Unit, - onToggleCamera: (Boolean) -> Unit, -) { - var tempDialog by remember { mutableStateOf(null) } - - if (tempDialog != null) { - TempDialog( - target = tempDialog!!, - currentNozzle = state.nozzleTarget, - currentBed = state.bedTarget, - onConfirm = { nozzle, bed -> - onSetTemp(nozzle, bed) - tempDialog = null - }, - onDismiss = { tempDialog = null }, - ) - } - - Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()) - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(12.dp), - ) { - StatusBadge(state.printState) - - LightCard( - isOn = state.lightOn, - brightness = state.lightBrightness, - onToggle = onToggleLight, - ) - - CameraCard( - cameraFrame = cameraFrame, - cameraEnabled = cameraEnabled, - onToggle = onToggleCamera, - ) - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - TempCard( - label = "Nozzle", - current = state.nozzleTemp, - target = state.nozzleTarget, - activeColor = Orange400, - modifier = Modifier.weight(1f), - onClick = { tempDialog = TempTarget.NOZZLE }, - ) - TempCard( - label = "Bed", - current = state.bedTemp, - target = state.bedTarget, - activeColor = Blue300, - modifier = Modifier.weight(1f), - onClick = { tempDialog = TempTarget.BED }, - ) - } - - if (state.isActive || state.printState == "complete") { - PrintProgressCard(state) - } - - if (state.isActive) { - PrintControls( - isPrinting = state.isPrinting, - onPause = onPause, - onResume = onResume, - onCancel = onCancel, - ) - } - - if (state.amsSlots.isNotEmpty()) { - FilamentCard(slots = state.amsSlots, mode = state.filamentMode) - } - } -} - -@Composable -private fun StatusBadge(printState: String) { - val (label, color) = when (printState) { - "printing" -> "Printing" to Orange400 - "paused" -> "Paused" to Blue300 - "complete" -> "Complete" to Color(0xFF66BB6A) - "error" -> "Error" to ErrorRed - "cancelled" -> "Cancelled" to MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f) - else -> "Standby" to MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f) - } - Surface( - color = color.copy(alpha = 0.15f), - shape = MaterialTheme.shapes.small, - ) { - Text( - text = label, - color = color, - style = MaterialTheme.typography.labelLarge, - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp), - ) - } -} - -@Composable -private fun CameraCard( - cameraFrame: Bitmap?, - cameraEnabled: Boolean, - onToggle: (Boolean) -> Unit, -) { - Card( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), - ) { - Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - "Camera", - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f), - ) - Switch(checked = cameraEnabled, onCheckedChange = onToggle) - } - if (cameraEnabled) { - if (cameraFrame != null) { - Image( - bitmap = cameraFrame.asImageBitmap(), - contentDescription = "Camera stream", - contentScale = ContentScale.Fit, - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4f / 3f), - ) - } else { - Box( - modifier = Modifier - .fillMaxWidth() - .aspectRatio(4f / 3f) - .background(Color.Black.copy(alpha = 0.4f)), - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator() - } - } - } - } - } -} - -@Composable -private fun TempCard( - label: String, - current: Double, - target: Double, - activeColor: Color, - modifier: Modifier = Modifier, - onClick: () -> Unit = {}, -) { - val isHeating = target > 0.0 - val indicatorColor = if (isHeating) activeColor else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f) - - Card( - onClick = onClick, - modifier = modifier, - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(4.dp), - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.Top, - ) { - Text( - label, - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f), - ) - Text( - "Tap to set", - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f), - ) - } - Text( - text = "${current.roundToInt()}°", - style = MaterialTheme.typography.headlineMedium, - color = indicatorColor, - fontWeight = FontWeight.Bold, - ) - Text( - text = if (isHeating) "→ ${target.roundToInt()}°" else "Off", - style = MaterialTheme.typography.bodySmall, - color = indicatorColor.copy(alpha = 0.7f), - ) - } - } -} - -@Composable -private fun PrintProgressCard(state: PrinterState) { - Card( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - if (state.filename.isNotBlank()) { - Text( - text = state.filename, - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f), - ) - } - - val pct = (state.progress * 100).roundToInt() - LinearProgressIndicator( - progress = { state.progress.toFloat().coerceIn(0f, 1f) }, - modifier = Modifier.fillMaxWidth(), - color = MaterialTheme.colorScheme.primary, - ) - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text("$pct%", style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold) - val curr = state.currLayer - val total = state.totalLayers - if (curr != null && total != null && total > 0) { - Text( - "Layer $curr / $total", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f), - ) - } - } - - val remain = state.remainTime - if (state.printDuration > 0 || (remain != null && remain > 0)) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - if (state.printDuration > 0) { - TimeLabel( - label = "Elapsed", - seconds = state.printDuration.toInt(), - modifier = Modifier.weight(1f), - ) - } - if (remain != null && remain > 0) { - val estimate = state.printDuration.toInt() + remain - TimeLabel(label = "Est. Total", seconds = estimate, modifier = Modifier.weight(1f)) - TimeLabel(label = "Remaining", seconds = remain, modifier = Modifier.weight(1f)) - } - } - } - } - } -} - -@Composable -private fun TimeLabel(label: String, seconds: Int, modifier: Modifier = Modifier) { - Column(modifier = modifier) { - Text( - label, - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), - ) - Text( - formatDuration(seconds), - style = MaterialTheme.typography.bodySmall, - fontWeight = FontWeight.Medium, - ) - } -} - -private fun formatDuration(totalSeconds: Int): String { - val h = totalSeconds / 3600 - val m = (totalSeconds % 3600) / 60 - val s = totalSeconds % 60 - return when { - h > 0 -> "${h}h ${m}m" - m > 0 -> "${m}m ${s}s" - else -> "${s}s" - } -} - -@Composable -private fun PrintControls( - isPrinting: Boolean, - onPause: () -> Unit, - onResume: () -> Unit, - onCancel: () -> Unit, -) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - if (isPrinting) { - Button(onClick = onPause, modifier = Modifier.weight(1f)) { Text("Pause") } - } else { - Button(onClick = onResume, modifier = Modifier.weight(1f)) { Text("Resume") } - } - OutlinedButton( - onClick = onCancel, - modifier = Modifier.weight(1f), - colors = ButtonDefaults.outlinedButtonColors(contentColor = ErrorRed), - ) { - Text("Stop") - } - } -} - -@Composable -private fun LightCard(isOn: Boolean, brightness: Int, onToggle: (Boolean) -> Unit) { - Card( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Column { - Text("Light", style = MaterialTheme.typography.labelLarge) - Text( - if (isOn) "$brightness%" else "Off", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), - ) - } - Switch( - checked = isOn, - onCheckedChange = onToggle, - colors = SwitchDefaults.colors( - checkedThumbColor = Orange400, - checkedTrackColor = Orange400.copy(alpha = 0.5f), - ), - ) - } - } -} - -@Composable -private fun FilamentCard(slots: List, mode: String) { - Card( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), - ) { - Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - Text( - text = when (mode) { - "ace_direct" -> "AMS Direct" - "ace_hub" -> "AMS Hub" - else -> "Filament" - }, - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f), - ) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - slots.forEach { slot -> FilamentSlot(slot, Modifier.weight(1f)) } - } - } - } -} - -@Composable -private fun FilamentSlot(slot: AmsSlot, modifier: Modifier = Modifier) { - val slotColor = if (slot.color.size >= 3) { - Color(slot.color[0] / 255f, slot.color[1] / 255f, slot.color[2] / 255f) - } else { - MaterialTheme.colorScheme.onSurface.copy(alpha = 0.25f) - } - - Column( - modifier = modifier, - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(4.dp), - ) { - Box( - modifier = Modifier - .size(32.dp) - .background(slotColor, shape = CircleShape), - ) - Text( - text = slot.type.ifBlank { "–" }, - style = MaterialTheme.typography.labelSmall, - textAlign = TextAlign.Center, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - if (slot.brand.isNotBlank()) { - Text( - text = slot.brand, - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f), - textAlign = TextAlign.Center, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } - } -} - -@Composable -private fun TempDialog( - target: TempTarget, - currentNozzle: Double, - currentBed: Double, - onConfirm: (nozzle: Double, bed: Double) -> Unit, - onDismiss: () -> Unit, -) { - val label = if (target == TempTarget.NOZZLE) "Nozzle" else "Bed" - val currentValue = if (target == TempTarget.NOZZLE) currentNozzle else currentBed - var input by remember { mutableStateOf(currentValue.roundToInt().toString()) } - - AlertDialog( - onDismissRequest = onDismiss, - title = { Text("Set $label Temperature") }, - text = { - OutlinedTextField( - value = input, - onValueChange = { input = it.filter { c -> c.isDigit() } }, - label = { Text("Temperature (°C)") }, - suffix = { Text("°C") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - singleLine = true, - ) - }, - confirmButton = { - TextButton(onClick = { - val newTemp = input.toDoubleOrNull() ?: currentValue - if (target == TempTarget.NOZZLE) onConfirm(newTemp, currentBed) - else onConfirm(currentNozzle, newTemp) - }) { Text("Set") } - }, - dismissButton = { - TextButton(onClick = onDismiss) { Text("Cancel") } - }, - ) -} diff --git a/android/app/src/main/java/com/kxbridge/ui/SetupScreen.kt b/android/app/src/main/java/com/kxbridge/ui/SetupScreen.kt deleted file mode 100644 index fe58b9b..0000000 --- a/android/app/src/main/java/com/kxbridge/ui/SetupScreen.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.kxbridge.ui - -import androidx.compose.foundation.layout.* -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.* -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.unit.dp - -@Composable -fun SetupScreen(initialUrl: String = "", onConnect: (String) -> Unit) { - var url by remember { mutableStateOf(initialUrl.ifBlank { "http://" }) } - - fun submit() { - val trimmed = url.trim() - if (trimmed.length > 7) onConnect(trimmed) - } - - Column( - modifier = Modifier - .fillMaxSize() - .padding(32.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text( - text = "KX-Bridge", - style = MaterialTheme.typography.headlineLarge, - color = MaterialTheme.colorScheme.primary, - ) - - Spacer(Modifier.height(8.dp)) - - Text( - text = "Enter your bridge server URL", - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - - Spacer(Modifier.height(40.dp)) - - OutlinedTextField( - value = url, - onValueChange = { url = it }, - label = { Text("Server URL") }, - placeholder = { Text("http://192.168.1.x:7125") }, - singleLine = true, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Uri, - imeAction = ImeAction.Done, - ), - keyboardActions = KeyboardActions(onDone = { submit() }), - modifier = Modifier.fillMaxWidth(), - ) - - Spacer(Modifier.height(24.dp)) - - Button( - onClick = ::submit, - modifier = Modifier.fillMaxWidth(), - enabled = url.trim().length > 7, - ) { - Text("Connect") - } - } -} diff --git a/android/app/src/main/java/com/kxbridge/ui/theme/Color.kt b/android/app/src/main/java/com/kxbridge/ui/theme/Color.kt deleted file mode 100644 index f171246..0000000 --- a/android/app/src/main/java/com/kxbridge/ui/theme/Color.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.kxbridge.ui.theme - -import androidx.compose.ui.graphics.Color - -val Orange400 = Color(0xFFFF6B35) -val Orange600 = Color(0xFFE84E0F) // darker for light-mode contrast on white -val Blue300 = Color(0xFF4FC3F7) -val Blue600 = Color(0xFF0288D1) // darker for light-mode contrast on white -val Surface900 = Color(0xFF121212) -val Surface800 = Color(0xFF1E1E1E) -val Surface700 = Color(0xFF2C2C2C) -val OnSurfaceDark = Color(0xFFE0E0E0) -val ErrorRed = Color(0xFFCF6679) diff --git a/android/app/src/main/java/com/kxbridge/ui/theme/Theme.kt b/android/app/src/main/java/com/kxbridge/ui/theme/Theme.kt deleted file mode 100644 index 5cea4e7..0000000 --- a/android/app/src/main/java/com/kxbridge/ui/theme/Theme.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.kxbridge.ui.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable - -private val DarkColors = darkColorScheme( - primary = Orange400, - secondary = Blue300, - background = Surface900, - surface = Surface800, - surfaceVariant = Surface700, - onBackground = OnSurfaceDark, - onSurface = OnSurfaceDark, - error = ErrorRed, -) - -private val LightColors = lightColorScheme( - primary = Orange600, - secondary = Blue600, - error = ErrorRed, -) - -@Composable -fun KxBridgeTheme(content: @Composable () -> Unit) { - val colors = if (isSystemInDarkTheme()) DarkColors else LightColors - MaterialTheme(colorScheme = colors, content = content) -} diff --git a/android/app/src/main/java/com/kxbridge/viewmodel/PrinterViewModel.kt b/android/app/src/main/java/com/kxbridge/viewmodel/PrinterViewModel.kt deleted file mode 100644 index 1754d16..0000000 --- a/android/app/src/main/java/com/kxbridge/viewmodel/PrinterViewModel.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.kxbridge.viewmodel - -import android.graphics.Bitmap -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.kxbridge.data.PrinterRepository -import com.kxbridge.data.model.PrinterState -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch - -sealed interface UiState { - data object Loading : UiState - data class Error(val message: String) : UiState - data class Success(val state: PrinterState) : UiState -} - -class PrinterViewModel : ViewModel() { - - private val _uiState = MutableStateFlow(UiState.Loading) - val uiState: StateFlow = _uiState - - private val _cameraFrame = MutableStateFlow(null) - val cameraFrame: StateFlow = _cameraFrame - - private val _cameraEnabled = MutableStateFlow(false) - val cameraEnabled: StateFlow = _cameraEnabled - - private var pollJob: Job? = null - private var cameraJob: Job? = null - private var repo: PrinterRepository? = null - - fun connect(baseUrl: String) { - pollJob?.cancel() - repo = PrinterRepository(baseUrl.trimEnd('/')) - _uiState.value = UiState.Loading - pollJob = viewModelScope.launch(Dispatchers.IO) { - repo!!.stateFlow().collect { result -> - _uiState.value = result.fold( - onSuccess = { UiState.Success(it) }, - onFailure = { UiState.Error(it.message ?: "Connection failed") }, - ) - } - } - } - - fun pause() = command { it.pause() } - fun resume() = command { it.resume() } - fun cancel() = command { it.cancel() } - - fun setLight(on: Boolean, brightness: Int = 80) = command { it.setLight(on, brightness) } - fun setTemperature(nozzle: Double, bed: Double) = command { it.setTemperature(nozzle, bed) } - - fun setCameraEnabled(enabled: Boolean) { - _cameraEnabled.value = enabled - if (enabled) { - command { it.cameraStart() } - cameraJob?.cancel() - cameraJob = viewModelScope.launch(Dispatchers.IO) { - repo?.cameraFrames()?.collect { _cameraFrame.value = it } - } - } else { - cameraJob?.cancel() - cameraJob = null - _cameraFrame.value = null - command { it.cameraStop() } - } - } - - override fun onCleared() { - super.onCleared() - cameraJob?.cancel() - if (_cameraEnabled.value) { - viewModelScope.launch(Dispatchers.IO) { repo?.cameraStop() } - } - } - - private fun command(block: (PrinterRepository) -> Unit) { - viewModelScope.launch(Dispatchers.IO) { repo?.let(block) } - } -} diff --git a/android/app/src/main/res/drawable/ic_launcher_background.xml b/android/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 65f94ad..0000000 --- a/android/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/android/app/src/main/res/drawable/ic_launcher_foreground.xml b/android/app/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index b9f1e2e..0000000 --- a/android/app/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 6b78462..0000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 6b78462..0000000 --- a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/android/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi/ic_launcher.xml deleted file mode 100644 index 6b78462..0000000 --- a/android/app/src/main/res/mipmap-anydpi/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/android/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml deleted file mode 100644 index 6b78462..0000000 --- a/android/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/android/app/src/main/res/values-night/themes.xml b/android/app/src/main/res/values-night/themes.xml deleted file mode 100644 index c08b4c9..0000000 --- a/android/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,4 +0,0 @@ - - -