commit 19a70ef20a1b5dd17633c4200a0c1a3c6724d1f3
parent 3e96a4baf7bc38ee1b1e03d7087297b9e66ce2bb
Author: Iván Ávalos <avalos@disroot.org>
Date: Thu, 20 Aug 2026 10:45:25 +0200
[wallet] add raw DB export to init error screen
Diffstat:
7 files changed, 78 insertions(+), 20 deletions(-)
diff --git a/wallet/src/main/java/net/taler/wallet/main/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/main/MainActivity.kt
@@ -44,10 +44,12 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -124,6 +126,10 @@ class MainActivity : FragmentActivity() {
val logExportLauncher = rememberLauncherForActivityResult(CreateDocument("text/plain")) { uri ->
uri?.let { model.settingsManager.exportLogcat(it) }
}
+ val dbExportLauncher = rememberLauncherForActivityResult(CreateDocument("application/octet-stream")) { uri ->
+ uri?.let { model.settingsManager.exportRawDb(it) }
+ }
+ var showDbExportConfirm by remember { mutableStateOf(false) }
LaunchedEffect(initError) {
if (initError == null) {
@@ -141,7 +147,7 @@ class MainActivity : FragmentActivity() {
model = model,
error = error,
onExportLogs = { logExportLauncher.launch("taler-wallet-logcat.txt") },
- onRetry = { model.startWallet() },
+ onExportDb = { showDbExportConfirm = true },
)
} ?: run {
WalletNavHost(
@@ -175,6 +181,27 @@ class MainActivity : FragmentActivity() {
onDismiss = { errorInfo = null }
)
}
+
+ if (showDbExportConfirm) {
+ AlertDialog(
+ onDismissRequest = { showDbExportConfirm = false },
+ title = { Text(stringResource(R.string.wallet_export_database)) },
+ text = { Text(stringResource(R.string.wallet_export_database_warning)) },
+ confirmButton = {
+ Button(onClick = {
+ showDbExportConfirm = false
+ dbExportLauncher.launch("talerwalletdb.sqlite3")
+ }) {
+ Text(stringResource(R.string.wallet_export_database_confirm))
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = { showDbExportConfirm = false }) {
+ Text(stringResource(R.string.cancel))
+ }
+ }
+ )
+ }
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/main/WalletInitErrorScreen.kt b/wallet/src/main/java/net/taler/wallet/main/WalletInitErrorScreen.kt
@@ -18,7 +18,6 @@ package net.taler.wallet.main
import androidx.compose.foundation.layout.Arrangement
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
@@ -26,39 +25,27 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Refresh
-import androidx.compose.material.icons.rounded.ErrorOutline
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
-import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import kotlinx.serialization.ExperimentalSerializationApi
-import kotlinx.serialization.json.Json
import net.taler.wallet.BottomInsetsSpacer
import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorInfo
-import net.taler.wallet.compose.CopyToClipboardButton
import net.taler.wallet.compose.ErrorComposable
-import net.taler.wallet.compose.ExpandableCard
import net.taler.wallet.compose.GlobalScaffold
-import net.taler.wallet.compose.ShareButton
@OptIn(ExperimentalSerializationApi::class)
@Composable
@@ -66,7 +53,7 @@ fun WalletInitErrorScreen(
model: MainViewModel,
error: TalerErrorInfo,
onExportLogs: () -> Unit,
- onRetry: () -> Unit,
+ onExportDb: () -> Unit,
modifier: Modifier = Modifier,
) {
val devMode by model.devMode.observeAsState(false)
@@ -110,15 +97,15 @@ fun WalletInitErrorScreen(
Button(
modifier = Modifier.fillMaxWidth(),
- onClick = onRetry,
+ onClick = onExportDb,
) {
Icon(
- Icons.Default.Refresh,
+ painterResource(R.drawable.ic_database),
contentDescription = null,
modifier = Modifier.size(ButtonDefaults.IconSize),
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
- Text(stringResource(R.string.transactions_retry))
+ Text(stringResource(R.string.wallet_export_database))
}
BottomInsetsSpacer()
diff --git a/wallet/src/main/java/net/taler/wallet/settings/SettingsManager.kt b/wallet/src/main/java/net/taler/wallet/settings/SettingsManager.kt
@@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
-import kotlinx.serialization.json.JsonObject
import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.main.ViewMode
@@ -39,6 +38,7 @@ import net.taler.wallet.backend.WalletResponse.Success
import net.taler.wallet.balances.BalanceManager
import net.taler.wallet.main.TAG
import org.json.JSONObject
+import java.io.File
class SettingsManager(
private val context: Context,
@@ -185,6 +185,31 @@ class SettingsManager(
}
}
+ fun exportRawDb(uri: Uri?) {
+ if (uri == null) {
+ onDbExportError()
+ return
+ }
+
+ scope.launch(Dispatchers.IO) {
+ try {
+ context.contentResolver.openOutputStream(uri, "wt")?.use { outputStream ->
+ val dbFile = File(context.filesDir, "talerwalletdb.sqlite3")
+ dbFile.inputStream().use { it.copyTo(outputStream) }
+ } ?: onDbExportError()
+ } catch (e: Exception) {
+ Log.e(SettingsManager::class.simpleName, "Error exporting raw db: ", e)
+ withContext(Dispatchers.Main) {
+ onDbExportError()
+ }
+ return@launch
+ }
+ withContext(Dispatchers.Main) {
+ Toast.makeText(context, R.string.settings_db_export_success, LENGTH_LONG).show()
+ }
+ }
+ }
+
fun importDb(uri: Uri?) {
if (uri == null) {
onDbImportError()
diff --git a/wallet/src/main/res/drawable/ic_database.xml b/wallet/src/main/res/drawable/ic_database.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M480,840Q329,840 224.5,793.5Q120,747 120,680L120,280Q120,214 225.5,167Q331,120 480,120Q629,120 734.5,167Q840,214 840,280L840,680Q840,747 735.5,793.5Q631,840 480,840ZM480,361Q569,361 659,335.5Q749,310 760,281Q749,252 659.5,226Q570,200 480,200Q389,200 301.5,225.5Q214,251 200,281Q214,311 301.5,336Q389,361 480,361ZM480,560Q522,560 561,556Q600,552 635.5,544.5Q671,537 702.5,526Q734,515 760,501L760,381Q734,395 702.5,406Q671,417 635.5,424.5Q600,432 561,436Q522,440 480,440Q438,440 398,436Q358,432 322.5,424.5Q287,417 256,406Q225,395 200,381L200,501Q225,515 256,526Q287,537 322.5,544.5Q358,552 398,556Q438,560 480,560ZM480,760Q526,760 573.5,753Q621,746 661,734.5Q701,723 728,708.5Q755,694 760,679L760,581Q734,595 702.5,606Q671,617 635.5,624.5Q600,632 561,636Q522,640 480,640Q438,640 398,636Q358,632 322.5,624.5Q287,617 256,606Q225,595 200,581L200,680Q205,695 231.5,709Q258,723 298,734.5Q338,746 386,753Q434,760 480,760Z"/>
+</vector>
diff --git a/wallet/src/main/res/values-de/strings.xml b/wallet/src/main/res/values-de/strings.xml
@@ -403,6 +403,9 @@
<string name="wallet_init_error_title">Initialisierungsfehler</string>
<string name="wallet_init_error_message">Beim Starten der Wallet ist ein Fehler aufgetreten. Dies kann nach einem App-Update oder bei inkompatiblen Wallet-Daten passieren. Bitte versuche es erneut, und falls das Problem bestehen bleibt, exportiere die Protokolle und melde dieses Problem den Entwicklern.</string>
<string name="wallet_export_logs">Protokolle exportieren</string>
+ <string name="wallet_export_database">Datenbank exportieren</string>
+ <string name="wallet_export_database_warning">Achtung: Diese Datei enthält vertrauliche Daten, mit denen andere auf Ihr Geld zugreifen könnten. Exportieren Sie sie nur an einen sicheren Ort und teilen Sie sie nur mit jemandem, dem Sie vertrauen.</string>
+ <string name="wallet_export_database_confirm">Trotzdem exportieren</string>
<string name="receive_peer_create_button_amount">%1$s anfordern</string>
<string name="send_peer_create_button_amount">%1$s übertragen</string>
<string name="send_deposits_no_currencies_error">Es sind keine bekannten Währungen für Einzahlungen verfügbar.</string>
diff --git a/wallet/src/main/res/values-fr/strings.xml b/wallet/src/main/res/values-fr/strings.xml
@@ -364,6 +364,9 @@
<string name="wallet_init_error_title">Erreur d\'initialisation</string>
<string name="wallet_init_error_message">Une erreur est survenue lors du démarrage du portefeuille. Cela peut arriver après une mise à jour de l\'application ou si les données du portefeuille sont incompatibles. Veuillez réessayer et, si le problème persiste, exportez les journaux et signalez ce problème aux développeurs.</string>
<string name="wallet_export_logs">Exporter les journaux</string>
+ <string name="wallet_export_database">Exporter la base de données</string>
+ <string name="wallet_export_database_warning">Attention : ce fichier contient des données sensibles qui pourraient donner à d\'autres personnes accès à votre argent. Exportez-le uniquement vers un endroit sûr et partagez-le avec une personne de confiance.</string>
+ <string name="wallet_export_database_confirm">Exporter quand même</string>
<string name="save">Sauvegarder</string>
<string name="warning">Avertissement</string>
<string name="biometric_prompt_title">Déverrouillez pour utiliser le portefeuille</string>
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
@@ -67,6 +67,9 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish
<string name="error_export">Export error diagnostics</string>
<string name="wallet_init_error_title">Initialization error</string>
<string name="wallet_init_error_message">An error occurred while starting the wallet. This can happen after an app update or if the wallet data is incompatible. Please retry, and if the problem persists, export the logs and report this issue to the developers.</string>
+ <string name="wallet_export_database">Export database</string>
+ <string name="wallet_export_database_warning">Warning: This file contains sensitive data that could grant others access to your money. Only export it to a safe location and share it with someone you trust.</string>
+ <string name="wallet_export_database_confirm">Export anyway</string>
<string name="wallet_export_logs">Export logs</string>
<string name="import_db">Import</string>
<string name="language">Language</string>