From 6b40387727acb4693ba39901ec9471adcf61820e Mon Sep 17 00:00:00 2001 From: yyoyoian-pixel <279225925+yyoyoian-pixel@users.noreply.github.com> Date: Mon, 27 Apr 2026 19:33:36 +0200 Subject: [PATCH] fix(android): replace clipboard auto-detect with manual Paste button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Android 13+ restricts clipboard access for background-to-foreground transitions — the auto-detect banner never appeared on resume. Replace with a permanent Paste button that reads clipboard on tap (user interaction grants clipboard permission). Also: Export button is now icon-only (share icon) to save space. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../com/therealaleph/mhrv/ui/ConfigSharing.kt | 68 ++++++------------- 1 file changed, 19 insertions(+), 49 deletions(-) diff --git a/android/app/src/main/java/com/therealaleph/mhrv/ui/ConfigSharing.kt b/android/app/src/main/java/com/therealaleph/mhrv/ui/ConfigSharing.kt index 50626c4..6c499e5 100644 --- a/android/app/src/main/java/com/therealaleph/mhrv/ui/ConfigSharing.kt +++ b/android/app/src/main/java/com/therealaleph/mhrv/ui/ConfigSharing.kt @@ -63,13 +63,9 @@ fun ConfigSharingBar( val clipboard = LocalClipboardManager.current val scope = rememberCoroutineScope() - val clipText = clipboard.getText()?.text.orEmpty() - val hasConfigInClipboard = clipText.isNotEmpty() && ConfigStore.looksLikeConfig(clipText) - var showExportDialog by remember { mutableStateOf(false) } var showImportConfirm by remember { mutableStateOf(false) } var pendingImport by remember { mutableStateOf(null) } - var showQrDialog by remember { mutableStateOf(false) } // QR scanner launcher — fires the ZXing embedded scanner activity. val scanLauncher = rememberLauncherForActivityResult(ScanContract()) { result -> @@ -83,58 +79,32 @@ fun ConfigSharingBar( } } - // --- Paste from clipboard banner --- - if (hasConfigInClipboard) { - Card( - modifier = Modifier.fillMaxWidth(), - colors = CardDefaults.cardColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - ), - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 12.dp, vertical = 8.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween, - ) { - Text( - "Config detected in clipboard", - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer, - modifier = Modifier.weight(1f), - ) - FilledTonalButton( - onClick = { - val decoded = ConfigStore.decode(clipText) - if (decoded != null) { - pendingImport = decoded - showImportConfirm = true - } else { - scope.launch { onSnackbar(ctx.getString(R.string.snack_invalid_config)) } - } - }, - ) { - Icon(Icons.Default.ContentPaste, null, modifier = Modifier.size(18.dp)) - Spacer(Modifier.width(4.dp)) - Text(stringResource(R.string.btn_import_clipboard)) - } - } - } - } - - // --- Export + Scan row --- + // --- Export + Paste + Scan row --- Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { + IconButton(onClick = { showExportDialog = true }) { + Icon(Icons.Default.Share, contentDescription = stringResource(R.string.btn_export_config)) + } + // Manual paste — reads clipboard on tap. Android 13+ restricts + // background clipboard access, so auto-detect doesn't work. + // User interaction (tap) grants clipboard permission. OutlinedButton( - onClick = { showExportDialog = true }, - modifier = Modifier.weight(1f), + onClick = { + val text = clipboard.getText()?.text.orEmpty() + val decoded = ConfigStore.decode(text) + if (decoded != null) { + pendingImport = decoded + showImportConfirm = true + } else { + scope.launch { onSnackbar(ctx.getString(R.string.snack_invalid_config)) } + } + }, ) { - Icon(Icons.Default.Share, null, modifier = Modifier.size(18.dp)) + Icon(Icons.Default.ContentPaste, null, modifier = Modifier.size(18.dp)) Spacer(Modifier.width(4.dp)) - Text(stringResource(R.string.btn_export_config)) + Text("Paste") } OutlinedButton( onClick = {