commit 84e5efcef482e432f01be7bbf0f24d9a097aff38
parent b202bbfd7c234c1b03c69749db88d3def896b3ab
Author: Iván Ávalos <avalos@disroot.org>
Date: Thu, 20 Aug 2026 17:39:01 +0200
[wallet] allow exporting sqlite3 DB from settings
Diffstat:
7 files changed, 74 insertions(+), 22 deletions(-)
diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt
@@ -29,7 +29,7 @@ import net.taler.wallet.backend.TalerErrorCode.NONE
import org.json.JSONObject
import java.io.File
-private const val WALLET_DB = "talerwalletdb.sqlite3"
+const val WALLET_DB = "talerwalletdb.sqlite3"
@OptIn(DelicateCoroutinesApi::class)
class WalletBackendApi(
diff --git a/wallet/src/main/java/net/taler/wallet/main/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/main/MainActivity.kt
@@ -190,7 +190,7 @@ class MainActivity : FragmentActivity() {
confirmButton = {
Button(onClick = {
showDbExportConfirm = false
- dbExportLauncher.launch("talerwalletdb.sqlite3")
+ dbExportLauncher.launch("taler-wallet-db-${System.currentTimeMillis()}.sqlite3")
}) {
Text(stringResource(R.string.wallet_export_database_confirm))
}
diff --git a/wallet/src/main/java/net/taler/wallet/settings/SettingsManager.kt b/wallet/src/main/java/net/taler/wallet/settings/SettingsManager.kt
@@ -31,6 +31,7 @@ import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorInfo
+import net.taler.wallet.backend.WALLET_DB
import net.taler.wallet.main.ViewMode
import net.taler.wallet.backend.WalletBackendApi
import net.taler.wallet.backend.WalletResponse.Error
@@ -194,7 +195,7 @@ class SettingsManager(
scope.launch(Dispatchers.IO) {
try {
context.contentResolver.openOutputStream(uri, "wt")?.use { outputStream ->
- val dbFile = File(context.filesDir, "talerwalletdb.sqlite3")
+ val dbFile = File(context.filesDir, WALLET_DB)
dbFile.inputStream().use { it.copyTo(outputStream) }
} ?: onDbExportError()
} catch (e: Exception) {
diff --git a/wallet/src/main/java/net/taler/wallet/settings/SettingsScreen.kt b/wallet/src/main/java/net/taler/wallet/settings/SettingsScreen.kt
@@ -53,6 +53,8 @@ import androidx.compose.material.icons.filled.DomainAdd
import androidx.compose.material.icons.filled.LocalAtm
import androidx.compose.material.icons.filled.Memory
import androidx.compose.material.icons.filled.VideoLibrary
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
@@ -60,10 +62,14 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
@@ -131,6 +137,10 @@ fun SettingsScreen(
settingsManager.importDb(it)
}
}
+ val rawDbExportLauncher = rememberLauncherForActivityResult(CreateDocument("application/octet-stream")) { uri ->
+ uri?.let { settingsManager.exportRawDb(it) }
+ }
+ var pendingDbExport by remember { mutableStateOf<DbExportType?>(null) }
val biometricEnrollLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
@@ -233,7 +243,14 @@ fun SettingsScreen(
title = stringResource(R.string.settings_db_export),
summary = stringResource(R.string.settings_db_export_summary),
icon = ImageVector.vectorResource(R.drawable.ic_unarchive),
- onClick = { dbExportLauncher.launch("taler-wallet-db-${System.currentTimeMillis()}.json") }
+ onClick = { pendingDbExport = DbExportType.Json }
+ )
+
+ SettingsItem(
+ title = stringResource(R.string.settings_db_export_raw),
+ summary = stringResource(R.string.settings_db_export_raw_summary),
+ icon = ImageVector.vectorResource(R.drawable.ic_database),
+ onClick = { pendingDbExport = DbExportType.Raw }
)
SettingsItem(
@@ -253,6 +270,24 @@ fun SettingsScreen(
}
)
+ if (model.databaseBackend != WalletDatabaseBackend.Sqlite) SettingsItem(
+ title = stringResource(R.string.settings_migrate_db),
+ summary = stringResource(R.string.settings_migrate_db_summary),
+ icon = Icons.Default.Memory,
+ onClick = {
+ MaterialAlertDialogBuilder(context)
+ .setMessage(R.string.settings_dialog_migrate_db_message)
+ .setNegativeButton(R.string.settings_migrate_db) { _, _ ->
+ model.enableMigrateNativeDb { onShowError(it) }
+ scope.launch { snackbarHostState.showSnackbar(migrateDoneMessage) }
+ }
+ .setPositiveButton(R.string.cancel) { _, _ ->
+ scope.launch { snackbarHostState.showSnackbar(migrateCanceledMessage) }
+ }
+ .show()
+ }
+ )
+
SettingsItem(
title = stringResource(R.string.settings_version_app),
summary = "${BuildConfig.VERSION_NAME} (${BuildConfig.FLAVOR} ${BuildConfig.VERSION_CODE})",
@@ -292,24 +327,6 @@ fun SettingsScreen(
}
)
- if (model.databaseBackend != WalletDatabaseBackend.Sqlite) SettingsItem(
- title = stringResource(R.string.settings_migrate_db),
- summary = stringResource(R.string.settings_migrate_db_summary),
- icon = Icons.Default.Memory,
- onClick = {
- MaterialAlertDialogBuilder(context)
- .setMessage(R.string.settings_dialog_migrate_db_message)
- .setNegativeButton(R.string.settings_migrate_db) { _, _ ->
- model.enableMigrateNativeDb { onShowError(it) }
- scope.launch { snackbarHostState.showSnackbar(migrateDoneMessage) }
- }
- .setPositiveButton(R.string.cancel) { _, _ ->
- scope.launch { snackbarHostState.showSnackbar(migrateCanceledMessage) }
- }
- .show()
- }
- )
-
SettingsItem(
title = stringResource(R.string.settings_reset),
summary = stringResource(R.string.settings_reset_summary),
@@ -329,6 +346,32 @@ fun SettingsScreen(
.show()
}
)
+
+ pendingDbExport?.let { exportType ->
+ AlertDialog(
+ onDismissRequest = { pendingDbExport = null },
+ title = { Text(stringResource(R.string.wallet_export_database)) },
+ text = { Text(stringResource(R.string.wallet_export_database_warning)) },
+ confirmButton = {
+ Button(onClick = {
+ pendingDbExport = null
+ when (exportType) {
+ DbExportType.Json ->
+ dbExportLauncher.launch("taler-wallet-db-${System.currentTimeMillis()}.json")
+ DbExportType.Raw ->
+ rawDbExportLauncher.launch("taler-wallet-db-${System.currentTimeMillis()}.sqlite3")
+ }
+ }) {
+ Text(stringResource(R.string.wallet_export_database_confirm))
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = { pendingDbExport = null }) {
+ Text(stringResource(R.string.cancel))
+ }
+ }
+ )
+ }
}
}
}
@@ -497,3 +540,5 @@ private fun promptAuthEnrollment(onPromptEnrollment: (Intent) -> Unit) {
onPromptEnrollment(intent)
}
}
+
+private enum class DbExportType { Json, Raw }
diff --git a/wallet/src/main/res/values-de/strings.xml b/wallet/src/main/res/values-de/strings.xml
@@ -128,6 +128,8 @@
<string name="withdraw_manual_ready_receiver">Empfänger</string>
<string name="exchange_delete_force">Löschung erzwingen (bereinigen)</string>
<string name="settings_db_export_summary">Interne Datenbank speichern</string>
+ <string name="settings_db_export_raw">Rohdatenbank exportieren</string>
+ <string name="settings_db_export_raw_summary">Rohe SQLite-Datenbankdatei speichern</string>
<string name="settings_db_clear_error">Fehler beim Bereinigen der Datenbank</string>
<string name="settings_alert_reset_canceled">Zurücksetzen abgebrochen</string>
<string name="exchange_not_contacted">Zahlungsdienstleister nicht kontaktiert</string>
diff --git a/wallet/src/main/res/values-fr/strings.xml b/wallet/src/main/res/values-fr/strings.xml
@@ -265,6 +265,8 @@
<string name="exchange_tos_forget">Refuser les conditions d\'utilisation</string>
<string name="exchange_tos_view">Voir les conditions d\'utilisation</string>
<string name="settings_db_export_summary">Sauver la base de donnée interne</string>
+ <string name="settings_db_export_raw">Exporter la base de données brute</string>
+ <string name="settings_db_export_raw_summary">Sauver le fichier brut de la base de données SQLite</string>
<string name="settings_db_import_summary">Restauration de la base de données à partir d\'un fichier</string>
<string name="settings_dialog_reset_message">Voulez-vous vraiment réinitialiser le portefeuille et perdre toutes les pièces et tous les achats ?</string>
<string name="transaction_preparing_kyc">KYC requis, préparation du test</string>
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
@@ -505,6 +505,8 @@ GNU Taler is immune to many types of fraud such as credit card data theft, phish
<string name="settings_db_export_message">Exporting database, please wait until confirmation</string>
<string name="settings_db_export_success">Database exported to file</string>
<string name="settings_db_export_summary">Save internal database</string>
+ <string name="settings_db_export_raw">Export raw database</string>
+ <string name="settings_db_export_raw_summary">Save raw SQLite database file</string>
<string name="settings_db_import">Import database</string>
<string name="settings_db_import_error">Error importing database</string>
<string name="settings_db_import_message">Importing database, please wait until confirmation</string>