Scan the enrollment QR in-app instead of relying on the camera app
Not every phone's camera app hands a scanned aiapp:// URI off to the app reliably, which made enrollment an annoying multi-step process. The Settings screen now scans the QR itself via zxing-android-embedded's ScanContract (offline, no Play Services/ML Kit download) and feeds the decoded URI through the existing parseEnrollmentUri. The aiapp://enroll deep link stays as a fallback for cameras that do redirect. Verified on the emulator: Scan QR code launches the scanner, prompts for camera permission, shows a live preview, and backing out returns to Settings cleanly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
This commit is contained in:
1 parent
99bcc341c1
commit
da2c110429
5 files changed
+56
-13
No files matched your search
@@ -1,5 +1,6 @@
|
||||
package com.example.aiapp
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -9,6 +10,7 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
@@ -21,12 +23,16 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import com.journeyapps.barcodescanner.ScanContract
|
||||
import com.journeyapps.barcodescanner.ScanIntentResult
|
||||
import com.journeyapps.barcodescanner.ScanOptions
|
||||
|
||||
/**
|
||||
* Server address and token. The normal path is scanning the server's
|
||||
* terminal QR (which lands in MainActivity and never shows this screen);
|
||||
* these fields are the fallback for typing the same three values by hand.
|
||||
* [onBack] is null on first run, when there is nothing to go back to.
|
||||
* Server address and token. The normal path is the "Scan QR code" button
|
||||
* below, which decodes the server's terminal QR itself; these fields are
|
||||
* the fallback for typing the same three values by hand. [onBack] is null
|
||||
* on first run, when there is nothing to go back to.
|
||||
*/
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
@@ -42,6 +48,19 @@ fun SettingsScreen(
|
||||
var token by remember { mutableStateOf("") }
|
||||
var error by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
val scanLauncher = rememberLauncherForActivityResult(ScanContract()) { result: ScanIntentResult ->
|
||||
// Null contents means the user backed out of the scanner -- not an
|
||||
// error, so nothing to report.
|
||||
val contents = result.contents ?: return@rememberLauncherForActivityResult
|
||||
val settings = parseEnrollmentUri(Uri.parse(contents))
|
||||
if (settings == null) {
|
||||
error = "Not a valid enrollment code"
|
||||
} else {
|
||||
saveServerSettings(context, settings)
|
||||
onSaved(settings)
|
||||
}
|
||||
}
|
||||
|
||||
Column(Modifier.fillMaxSize().padding(16.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
@@ -55,13 +74,19 @@ fun SettingsScreen(
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(
|
||||
"The easy way: run ai-server on the backend and scan the QR it prints " +
|
||||
"with the phone's camera. Or type the same values here.",
|
||||
"The easy way: run ai-server on the backend and scan the QR it prints. " +
|
||||
"Or type the same values here.",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
|
||||
OutlinedButton(
|
||||
onClick = { scanLauncher.launch(ScanOptions().setDesiredBarcodeFormats(ScanOptions.QR_CODE)) },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) { Text("Scan QR code") }
|
||||
Spacer(Modifier.height(16.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
value = host,
|
||||
onValueChange = { host = it },
|
||||
|
||||
Reference in new issue
Block a user