From 67dc9b897e4b519b1e3fdc11168af7d893ab73af Mon Sep 17 00:00:00 2001 From: Mathieu Moran Date: Wed, 12 Aug 2026 04:04:21 -0400 Subject: [PATCH] refactor: merge Test tab into Settings, 3 tabs total - Settings tab now has credential fields + network test (Test Pi, Test Server) - Removed standalone Test tab - Bottom nav: Dashcam, Downloads, Settings --- .../java/com/yarisobd/app/MainActivity.kt | 16 ++---- .../java/com/yarisobd/app/SettingsScreen.kt | 55 ++++++++++++++++++- 2 files changed, 57 insertions(+), 14 deletions(-) diff --git a/android/app/src/main/java/com/yarisobd/app/MainActivity.kt b/android/app/src/main/java/com/yarisobd/app/MainActivity.kt index b142580..97f45c0 100644 --- a/android/app/src/main/java/com/yarisobd/app/MainActivity.kt +++ b/android/app/src/main/java/com/yarisobd/app/MainActivity.kt @@ -83,19 +83,15 @@ fun YarisMainScreen(client: OkHttpClient) { NavigationBarItem( selected = selectedTab == 1, onClick = { selectedTab = 1 }, - icon = { Text("�") }, + icon = { Text(".") }, label = { Text("Downloads") } ) NavigationBarItem( selected = selectedTab == 2, - onClick = { selectedTab = 2 }, icon = { Text("⚙️") }, + onClick = { selectedTab = 2 }, + icon = { Text(".") }, label = { Text("Settings") } ) - NavigationBarItem( - selected = selectedTab == 3, - onClick = { selectedTab = 3 }, icon = { Text("�🔌") }, - label = { Text("Test") } - ) } } ) { padding -> @@ -124,11 +120,7 @@ fun YarisMainScreen(client: OkHttpClient) { onCancelUpload = { dashcamVm.cancelUpload(it) }, onRefresh = { dashcamVm.scanDownloads() }, ) - 2 -> SettingsScreen() - 3 -> DualNetworkTestScreen( - piClient = client, - serverClient = client, - ) + 2 -> SettingsScreen(client = client) } } } diff --git a/android/app/src/main/java/com/yarisobd/app/SettingsScreen.kt b/android/app/src/main/java/com/yarisobd/app/SettingsScreen.kt index ea37dd0..074c278 100644 --- a/android/app/src/main/java/com/yarisobd/app/SettingsScreen.kt +++ b/android/app/src/main/java/com/yarisobd/app/SettingsScreen.kt @@ -1,6 +1,7 @@ package com.yarisobd.app import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -9,6 +10,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme @@ -18,21 +20,32 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import com.yarisobd.app.data.CredentialStore +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import okhttp3.OkHttpClient +import okhttp3.Request @Composable -fun SettingsScreen() { +fun SettingsScreen(client: OkHttpClient) { val context = LocalContext.current + val scope = rememberCoroutineScope() var serverUrl by remember { mutableStateOf(CredentialStore.getServerUrl(context)) } var deviceName by remember { mutableStateOf(CredentialStore.getDeviceName(context)) } var apiKey by remember { mutableStateOf("") } var saved by remember { mutableStateOf(false) } val configured = CredentialStore.isConfigured(context) + // Network test state + var piResult by remember { mutableStateOf(null) } + var serverResult by remember { mutableStateOf(null) } + Column( modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp) ) { @@ -82,8 +95,46 @@ fun SettingsScreen() { saved = true }, modifier = Modifier.fillMaxWidth() - ) { Text(if (saved) "✓ Saved — restart app to apply" else "Save") } + ) { Text(if (saved) "✓ Saved" else "Save") } + } + } + + Spacer(Modifier.height(16.dp)) + + // Network test + Card( + modifier = Modifier.fillMaxWidth(), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerHigh) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text("Network Test", style = MaterialTheme.typography.titleMedium) + Spacer(Modifier.height(8.dp)) + Row { Button(onClick = { + piResult = null + scope.launch { piResult = testPi(client) } + }, modifier = Modifier.weight(1f)) { Text("Test Pi") } } + piResult?.let { Text(it, style = MaterialTheme.typography.bodySmall, color = if (it.startsWith("✓")) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error, modifier = Modifier.padding(top = 4.dp)) } + Spacer(Modifier.height(8.dp)) + Row { Button(onClick = { + serverResult = null + scope.launch { serverResult = testServer(client) } + }, modifier = Modifier.weight(1f), colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.secondary)) { Text("Test Server") } } + serverResult?.let { Text(it, style = MaterialTheme.typography.bodySmall, color = if (it.startsWith("✓")) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error, modifier = Modifier.padding(top = 4.dp)) } } } } } + +private suspend fun testPi(client: OkHttpClient): String = withContext(Dispatchers.IO) { + try { + val res = client.newCall(Request.Builder().url("http://10.42.0.1/api/gps/status").get().build()).execute() + if (res.isSuccessful) "✓ Pi OK (${res.code})" else "✗ Pi HTTP ${res.code}" + } catch (e: Exception) { "✗ ${e.message}" } +} + +private suspend fun testServer(client: OkHttpClient): String = withContext(Dispatchers.IO) { + try { + val res = client.newCall(Request.Builder().url("https://yaris.ceaf.dev/health").get().build()).execute() + if (res.isSuccessful) "✓ Server OK (${res.code})" else "✗ Server HTTP ${res.code}" + } catch (e: Exception) { "✗ ${e.message}" } +}