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
This commit is contained in:
@@ -83,19 +83,15 @@ fun YarisMainScreen(client: OkHttpClient) {
|
|||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
selected = selectedTab == 1,
|
selected = selectedTab == 1,
|
||||||
onClick = { selectedTab = 1 },
|
onClick = { selectedTab = 1 },
|
||||||
icon = { Text("�") },
|
icon = { Text(".") },
|
||||||
label = { Text("Downloads") }
|
label = { Text("Downloads") }
|
||||||
)
|
)
|
||||||
NavigationBarItem(
|
NavigationBarItem(
|
||||||
selected = selectedTab == 2,
|
selected = selectedTab == 2,
|
||||||
onClick = { selectedTab = 2 }, icon = { Text("⚙️") },
|
onClick = { selectedTab = 2 },
|
||||||
|
icon = { Text(".") },
|
||||||
label = { Text("Settings") }
|
label = { Text("Settings") }
|
||||||
)
|
)
|
||||||
NavigationBarItem(
|
|
||||||
selected = selectedTab == 3,
|
|
||||||
onClick = { selectedTab = 3 }, icon = { Text("�🔌") },
|
|
||||||
label = { Text("Test") }
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { padding ->
|
) { padding ->
|
||||||
@@ -124,11 +120,7 @@ fun YarisMainScreen(client: OkHttpClient) {
|
|||||||
onCancelUpload = { dashcamVm.cancelUpload(it) },
|
onCancelUpload = { dashcamVm.cancelUpload(it) },
|
||||||
onRefresh = { dashcamVm.scanDownloads() },
|
onRefresh = { dashcamVm.scanDownloads() },
|
||||||
)
|
)
|
||||||
2 -> SettingsScreen()
|
2 -> SettingsScreen(client = client)
|
||||||
3 -> DualNetworkTestScreen(
|
|
||||||
piClient = client,
|
|
||||||
serverClient = client,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.yarisobd.app
|
package com.yarisobd.app
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
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.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -18,21 +20,32 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.yarisobd.app.data.CredentialStore
|
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
|
@Composable
|
||||||
fun SettingsScreen() {
|
fun SettingsScreen(client: OkHttpClient) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
var serverUrl by remember { mutableStateOf(CredentialStore.getServerUrl(context)) }
|
var serverUrl by remember { mutableStateOf(CredentialStore.getServerUrl(context)) }
|
||||||
var deviceName by remember { mutableStateOf(CredentialStore.getDeviceName(context)) }
|
var deviceName by remember { mutableStateOf(CredentialStore.getDeviceName(context)) }
|
||||||
var apiKey by remember { mutableStateOf("") }
|
var apiKey by remember { mutableStateOf("") }
|
||||||
var saved by remember { mutableStateOf(false) }
|
var saved by remember { mutableStateOf(false) }
|
||||||
val configured = CredentialStore.isConfigured(context)
|
val configured = CredentialStore.isConfigured(context)
|
||||||
|
|
||||||
|
// Network test state
|
||||||
|
var piResult by remember { mutableStateOf<String?>(null) }
|
||||||
|
var serverResult by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp)
|
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp)
|
||||||
) {
|
) {
|
||||||
@@ -82,8 +95,46 @@ fun SettingsScreen() {
|
|||||||
saved = true
|
saved = true
|
||||||
},
|
},
|
||||||
modifier = Modifier.fillMaxWidth()
|
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}" }
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user