diff options
author | Iván Ávalos <avalos@disroot.org> | 2023-11-14 14:13:51 -0600 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2023-11-28 13:59:37 -0300 |
commit | ba51b5e541d888cafdbf479a7e03a116af7050c5 (patch) | |
tree | a8b893bd8dd0212cba11cd0af75e95eb8b7a7bd0 | |
parent | 94ee3a2f114e0345ea7408aacc30e3da9545474c (diff) | |
download | taler-android-ba51b5e541d888cafdbf479a7e03a116af7050c5.tar.gz taler-android-ba51b5e541d888cafdbf479a7e03a116af7050c5.tar.bz2 taler-android-ba51b5e541d888cafdbf479a7e03a116af7050c5.zip |
[wallet] Proper DB import/export functionality
7 files changed, 150 insertions, 18 deletions
diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt index 15fe7e4..4614474 100644 --- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt +++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt @@ -83,7 +83,7 @@ class MainViewModel( val refundManager = RefundManager(api, viewModelScope) val exchangeManager: ExchangeManager = ExchangeManager(api, viewModelScope) val peerManager: PeerManager = PeerManager(api, exchangeManager, viewModelScope) - val settingsManager: SettingsManager = SettingsManager(app.applicationContext, viewModelScope) + val settingsManager: SettingsManager = SettingsManager(app.applicationContext, api, viewModelScope) val accountManager: AccountManager = AccountManager(api, viewModelScope) val depositManager: DepositManager = DepositManager(api, viewModelScope) diff --git a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt index bf6f371..ea58dd7 100644 --- a/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt +++ b/wallet/src/main/java/net/taler/wallet/backend/WalletBackendApi.kt @@ -23,6 +23,7 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.KSerializer +import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.decodeFromJsonElement import net.taler.wallet.backend.TalerErrorCode.NONE import org.json.JSONObject @@ -86,4 +87,26 @@ class WalletBackendApi( WalletResponse.Error(info) } } + + // Returns raw JSON response instead of serialized object + suspend inline fun rawRequest( + operation: String, + noinline args: (JSONObject.() -> JSONObject)? = null, + ): WalletResponse<JsonObject> = withContext(Dispatchers.Default) { + val json = BackendManager.json + try { + when (val response = sendRequest(operation, args?.invoke(JSONObject()))) { + is ApiResponse.Response -> { + WalletResponse.Success(response.result) + } + is ApiResponse.Error -> { + val error: TalerErrorInfo = json.decodeFromJsonElement(response.error) + WalletResponse.Error(error) + } + } + } catch (e: Exception) { + val info = TalerErrorInfo(NONE, "", e.toString()) + WalletResponse.Error(info) + } + } } diff --git a/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt b/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt index f5826c9..54d6dc0 100644 --- a/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/settings/SettingsFragment.kt @@ -18,6 +18,7 @@ package net.taler.wallet.settings import android.os.Bundle import android.view.View +import androidx.activity.result.contract.ActivityResultContracts.OpenDocument import androidx.activity.result.contract.ActivityResultContracts.CreateDocument import androidx.fragment.app.activityViewModels import androidx.preference.Preference @@ -47,6 +48,7 @@ class SettingsFragment : PreferenceFragmentCompat() { private lateinit var prefWithdrawTest: Preference private lateinit var prefLogcat: Preference private lateinit var prefExportDb: Preference + private lateinit var prefImportDb: Preference private lateinit var prefVersionApp: Preference private lateinit var prefVersionCore: Preference private lateinit var prefVersionExchange: Preference @@ -58,6 +60,7 @@ class SettingsFragment : PreferenceFragmentCompat() { prefWithdrawTest, prefLogcat, prefExportDb, + prefImportDb, prefVersionApp, prefVersionCore, prefVersionExchange, @@ -71,9 +74,13 @@ class SettingsFragment : PreferenceFragmentCompat() { settingsManager.exportLogcat(uri) } private val dbExportLauncher = - registerForActivityResult(CreateDocument("application/x-sqlite3")) { uri -> + registerForActivityResult(CreateDocument("application/json")) { uri -> settingsManager.exportDb(uri) } + private val dbImportLauncher = + registerForActivityResult(OpenDocument()) { uri -> + settingsManager.importDb(uri) + } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.settings_main, rootKey) @@ -81,6 +88,7 @@ class SettingsFragment : PreferenceFragmentCompat() { prefWithdrawTest = findPreference("pref_testkudos")!! prefLogcat = findPreference("pref_logcat")!! prefExportDb = findPreference("pref_export_db")!! + prefImportDb = findPreference("pref_import_db")!! prefVersionApp = findPreference("pref_version_app")!! prefVersionCore = findPreference("pref_version_core")!! prefVersionExchange = findPreference("pref_version_protocol_exchange")!! @@ -127,10 +135,13 @@ class SettingsFragment : PreferenceFragmentCompat() { true } prefExportDb.setOnPreferenceClickListener { - dbExportLauncher.launch("taler-wallet-db-${currentTimeMillis()}.sql") + dbExportLauncher.launch("taler-wallet-db-${currentTimeMillis()}.json") + true + } + prefImportDb.setOnPreferenceClickListener { + dbImportLauncher.launch(arrayOf("application/json")) true } - prefTest.setOnPreferenceClickListener { model.runIntegrationTest() true diff --git a/wallet/src/main/java/net/taler/wallet/settings/SettingsManager.kt b/wallet/src/main/java/net/taler/wallet/settings/SettingsManager.kt index 349c7b1..0b4cbe9 100644 --- a/wallet/src/main/java/net/taler/wallet/settings/SettingsManager.kt +++ b/wallet/src/main/java/net/taler/wallet/settings/SettingsManager.kt @@ -25,14 +25,19 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import net.taler.wallet.R -import net.taler.wallet.backend.WALLET_DB +import net.taler.wallet.backend.WalletBackendApi +import net.taler.wallet.backend.WalletResponse.Error +import net.taler.wallet.backend.WalletResponse.Success +import org.json.JSONObject class SettingsManager( private val context: Context, + private val api: WalletBackendApi, private val scope: CoroutineScope, ) { - fun exportLogcat(uri: Uri?) { if (uri == null) { onLogExportError() @@ -65,20 +70,76 @@ class SettingsManager( onDbExportError() return } + scope.launch(Dispatchers.IO) { - try { - context.contentResolver.openOutputStream(uri, "wt")?.use { outputStream -> - context.openFileInput(WALLET_DB).use { inputStream -> - inputStream.copyTo(outputStream) + when (val response = api.rawRequest("exportDb")) { + is Success -> { + try { + context.contentResolver.openOutputStream(uri, "wt")?.use { outputStream -> + val data = Json.encodeToString(response.result) + val writer = outputStream.bufferedWriter() + writer.write(data) + writer.close() + } + } catch(e: Exception) { + Log.e(SettingsManager::class.simpleName, "Error exporting db: ", e) + withContext(Dispatchers.Main) { + onDbExportError() + } + return@launch } - } ?: onDbExportError() - } catch (e: Exception) { - Log.e(SettingsManager::class.simpleName, "Error exporting db: ", e) - onDbExportError() - return@launch + + withContext(Dispatchers.Main) { + Toast.makeText(context, R.string.settings_db_export_success, LENGTH_LONG).show() + } + } + is Error -> { + Log.e(SettingsManager::class.simpleName, "Error exporting db: ${response.error}") + 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() + return + } + + scope.launch(Dispatchers.IO) { + context.contentResolver.openInputStream(uri)?.use { inputStream -> + try { + val reader = inputStream.bufferedReader() + val strData = reader.readText() + reader.close() + val jsonData = JSONObject(strData) + when (val response = api.rawRequest("importDb") { + put("dump", jsonData) + }) { + is Success -> { + withContext(Dispatchers.Main) { + Toast.makeText(context, R.string.settings_db_import_success, LENGTH_LONG).show() + } + } + is Error -> { + Log.e(SettingsManager::class.simpleName, "Error importing db: ${response.error}") + withContext(Dispatchers.Main) { + onDbImportError() + } + return@launch + } + } + } catch (e: Exception) { + Log.e(SettingsManager::class.simpleName, "Error importing db: ", e) + withContext(Dispatchers.Main) { + onDbImportError() + } + return@launch + } } } } @@ -87,4 +148,8 @@ class SettingsManager( Toast.makeText(context, R.string.settings_db_export_error, LENGTH_LONG).show() } + private fun onDbImportError() { + Toast.makeText(context, R.string.settings_db_import_error, LENGTH_LONG).show() + } + } diff --git a/wallet/src/main/res/drawable/ic_archive.xml b/wallet/src/main/res/drawable/ic_archive.xml new file mode 100644 index 0000000..58a032c --- /dev/null +++ b/wallet/src/main/res/drawable/ic_archive.xml @@ -0,0 +1,21 @@ +<!-- + ~ This file is part of GNU Taler + ~ (C) 2023 Taler Systems S.A. + ~ + ~ GNU Taler is free software; you can redistribute it and/or modify it under the + ~ terms of the GNU General Public License as published by the Free Software + ~ Foundation; either version 3, or (at your option) any later version. + ~ + ~ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + ~ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + ~ A PARTICULAR PURPOSE. See the GNU General Public License for more details. + ~ + ~ You should have received a copy of the GNU General Public License along with + ~ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + --> + +<vector android:height="24dp" android:tint="?attr/colorControlNormal" + android:viewportHeight="24" android:viewportWidth="24" + android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="@android:color/white" android:pathData="M20.54,5.23l-1.39,-1.68C18.88,3.21 18.47,3 18,3H6c-0.47,0 -0.88,0.21 -1.16,0.55L3.46,5.23C3.17,5.57 3,6.02 3,6.5V19c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V6.5c0,-0.48 -0.17,-0.93 -0.46,-1.27zM12,17.5L6.5,12H10v-2h4v2h3.5L12,17.5zM5.12,5l0.81,-1h12l0.94,1H5.12z"/> +</vector> diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml index 3b05ae9..2533820 100644 --- a/wallet/src/main/res/values/strings.xml +++ b/wallet/src/main/res/values/strings.xml @@ -257,10 +257,14 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card <string name="settings_logcat_summary">Save internal log</string> <string name="settings_logcat_error">Error exporting log</string> <string name="settings_logcat_success">Log exported to file</string> - <string name="settings_db_export">Database</string> + <string name="settings_db_export">Export database</string> <string name="settings_db_export_summary">Save internal database</string> + <string name="settings_db_import">Import database</string> + <string name="settings_db_import_summary">Restore database from file</string> <string name="settings_db_export_error">Error exporting database</string> + <string name="settings_db_import_error">Error importing database</string> <string name="settings_db_export_success">Database exported to file</string> + <string name="settings_db_import_success">Database imported from file</string> <string name="settings_version_app">App Version</string> <string name="settings_version_core">Wallet Core Version</string> <string name="settings_version_protocol_exchange">Supported Exchange Versions</string> diff --git a/wallet/src/main/res/xml/settings_main.xml b/wallet/src/main/res/xml/settings_main.xml index 3a6d991..739e119 100644 --- a/wallet/src/main/res/xml/settings_main.xml +++ b/wallet/src/main/res/xml/settings_main.xml @@ -56,6 +56,14 @@ tools:isPreferenceVisible="true" /> <Preference + app:icon="@drawable/ic_archive" + app:isPreferenceVisible="false" + app:key="pref_import_db" + app:summary="@string/settings_db_import_summary" + app:title="@string/settings_db_import" + tools:isPreferenceVisible="true" /> + + <Preference app:icon="@drawable/ic_account_balance_wallet" app:isPreferenceVisible="false" app:key="pref_version_app" |