[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taler-android] branch master updated (15242d1 -> 1cb54ef)
From: |
gnunet |
Subject: |
[taler-taler-android] branch master updated (15242d1 -> 1cb54ef) |
Date: |
Tue, 28 Nov 2023 18:32:59 +0100 |
This is an automated email from the git hooks/post-receive script.
torsten-grote pushed a change to branch master
in repository taler-android.
from 15242d1 Translated using Weblate (German)
new 94ee3a2 [wallet] Show KYC notice in transaction list, and don't
render fees when zero
new ba51b5e [wallet] Proper DB import/export functionality
new 1cb54ef [wallet] Add confirmation dialog for DB import and i18nize
some strings
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../main/java/net/taler/wallet/MainViewModel.kt | 2 +-
.../net/taler/wallet/backend/WalletBackendApi.kt | 23 ++++++
.../wallet/payment/TransactionPaymentComposable.kt | 13 ++--
.../wallet/refund/TransactionRefundComposable.kt | 13 ++--
.../net/taler/wallet/settings/SettingsFragment.kt | 39 ++++++++--
.../net/taler/wallet/settings/SettingsManager.kt | 91 ++++++++++++++++++----
.../wallet/transactions/TransactionAdapter.kt | 27 +++++--
.../withdraw/TransactionWithdrawalComposable.kt | 13 ++--
.../list_item_age.xml => drawable/ic_archive.xml} | 17 ++--
wallet/src/main/res/values/strings.xml | 14 +++-
wallet/src/main/res/xml/settings_main.xml | 8 ++
11 files changed, 206 insertions(+), 54 deletions(-)
copy wallet/src/main/res/{layout/list_item_age.xml => drawable/ic_archive.xml}
(56%)
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/payment/TransactionPaymentComposable.kt
b/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
index c08bc76..7de16b6 100644
---
a/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
+++
b/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
@@ -83,11 +83,14 @@ fun TransactionPaymentComposable(
amount = t.amountRaw,
amountType = AmountType.Neutral,
)
- TransactionAmountComposable(
- label = stringResource(id = R.string.withdraw_fees),
- amount = t.amountEffective - t.amountRaw,
- amountType = AmountType.Negative,
- )
+ val fee = t.amountEffective - t.amountRaw
+ if (!fee.isZero()) {
+ TransactionAmountComposable(
+ label = stringResource(id = R.string.withdraw_fees),
+ amount = fee,
+ amountType = AmountType.Negative,
+ )
+ }
if (t.posConfirmation != null) {
TransactionInfoComposable(
label = stringResource(id =
R.string.payment_confirmation_code),
diff --git
a/wallet/src/main/java/net/taler/wallet/refund/TransactionRefundComposable.kt
b/wallet/src/main/java/net/taler/wallet/refund/TransactionRefundComposable.kt
index ac5e2da..82dceb5 100644
---
a/wallet/src/main/java/net/taler/wallet/refund/TransactionRefundComposable.kt
+++
b/wallet/src/main/java/net/taler/wallet/refund/TransactionRefundComposable.kt
@@ -79,11 +79,14 @@ fun TransactionRefundComposable(
amount = t.amountRaw,
amountType = AmountType.Neutral,
)
- TransactionAmountComposable(
- label = stringResource(id = R.string.withdraw_fees),
- amount = t.amountRaw - t.amountEffective,
- amountType = AmountType.Negative,
- )
+ val fee = t.amountRaw - t.amountEffective
+ if (!fee.isZero()) {
+ TransactionAmountComposable(
+ label = stringResource(id = R.string.withdraw_fees),
+ amount = fee,
+ amountType = AmountType.Negative,
+ )
+ }
TransactionInfoComposable(
label = stringResource(id = R.string.transaction_order),
info = t.paymentInfo?.summary ?: "",
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..f21bd44 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 {
+ showImportDialog()
true
}
-
prefTest.setOnPreferenceClickListener {
model.runIntegrationTest()
true
@@ -141,15 +152,27 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
+ private fun showImportDialog() {
+ MaterialAlertDialogBuilder(requireContext(),
R.style.MaterialAlertDialog_Material3)
+ .setMessage(R.string.settings_dialog_import_message)
+ .setNegativeButton(R.string.import_db) { _, _ ->
+ dbImportLauncher.launch(arrayOf("application/json"))
+ }
+ .setPositiveButton(R.string.cancel) { _, _ ->
+ Snackbar.make(requireView(),
getString(R.string.settings_alert_import_canceled), LENGTH_SHORT).show()
+ }
+ .show()
+ }
+
private fun showResetDialog() {
MaterialAlertDialogBuilder(requireContext(),
R.style.MaterialAlertDialog_Material3)
- .setMessage("Do you really want to reset the wallet and lose all
coins and purchases?")
- .setPositiveButton("Reset") { _, _ ->
+ .setMessage(R.string.settings_dialog_reset_message)
+ .setNegativeButton(R.string.reset) { _, _ ->
model.dangerouslyReset()
- Snackbar.make(requireView(), "Wallet has been reset",
LENGTH_SHORT).show()
+ Snackbar.make(requireView(),
getString(R.string.settings_alert_reset_done), LENGTH_SHORT).show()
}
- .setNegativeButton("Cancel") { _, _ ->
- Snackbar.make(requireView(), "Reset cancelled",
LENGTH_SHORT).show()
+ .setPositiveButton(R.string.cancel) { _, _ ->
+ Snackbar.make(requireView(),
getString(R.string.settings_alert_reset_canceled), LENGTH_SHORT).show()
}
.show()
}
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/java/net/taler/wallet/transactions/TransactionAdapter.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
index 18480e1..c9ae889 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
@@ -39,6 +39,8 @@ import
net.taler.wallet.transactions.TransactionAdapter.TransactionViewHolder
import net.taler.wallet.transactions.TransactionMajorState.Aborted
import net.taler.wallet.transactions.TransactionMajorState.Failed
import net.taler.wallet.transactions.TransactionMajorState.Pending
+import net.taler.wallet.transactions.TransactionMinorState.BankConfirmTransfer
+import net.taler.wallet.transactions.TransactionMinorState.KycRequired
internal class TransactionAdapter(
private val listener: OnTransactionClickListener,
@@ -112,6 +114,14 @@ internal class TransactionAdapter(
private fun bindExtraInfo(transaction: Transaction) {
when {
+ // Goes first so it always shows errors when present
+ transaction.error != null -> {
+ extraInfoView.text =
+ context.getString(R.string.payment_error,
transaction.error!!.userFacingMsg)
+ extraInfoView.setTextColor(red)
+ extraInfoView.visibility = VISIBLE
+ }
+
transaction.txState.major == Aborted -> {
extraInfoView.setText(R.string.payment_aborted)
extraInfoView.setTextColor(red)
@@ -124,11 +134,18 @@ internal class TransactionAdapter(
extraInfoView.visibility = VISIBLE
}
- transaction.error != null -> {
- extraInfoView.text =
- context.getString(R.string.payment_error,
transaction.error!!.userFacingMsg)
- extraInfoView.setTextColor(red)
- extraInfoView.visibility = VISIBLE
+ transaction.txState.major == Pending -> when
(transaction.txState.minor) {
+ BankConfirmTransfer -> {
+
extraInfoView.setText(R.string.withdraw_waiting_confirm)
+ extraInfoView.setTextColor(amountColor)
+ extraInfoView.visibility = VISIBLE
+ }
+ KycRequired -> {
+ extraInfoView.setText(R.string.transaction_action_kyc)
+ extraInfoView.setTextColor(amountColor)
+ extraInfoView.visibility = VISIBLE
+ }
+ else -> extraInfoView.visibility = GONE
}
transaction is TransactionWithdrawal && !transaction.confirmed
-> {
diff --git
a/wallet/src/main/java/net/taler/wallet/withdraw/TransactionWithdrawalComposable.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/TransactionWithdrawalComposable.kt
index 79cfc5e..378e283 100644
---
a/wallet/src/main/java/net/taler/wallet/withdraw/TransactionWithdrawalComposable.kt
+++
b/wallet/src/main/java/net/taler/wallet/withdraw/TransactionWithdrawalComposable.kt
@@ -86,11 +86,14 @@ fun TransactionWithdrawalComposable(
amount = t.amountRaw,
amountType = AmountType.Neutral,
)
- TransactionAmountComposable(
- label = stringResource(id = R.string.withdraw_fees),
- amount = t.amountRaw - t.amountEffective,
- amountType = AmountType.Negative,
- )
+ val fee = t.amountRaw - t.amountEffective
+ if (!fee.isZero()) {
+ TransactionAmountComposable(
+ label = stringResource(id = R.string.withdraw_fees),
+ amount = fee,
+ amountType = AmountType.Negative,
+ )
+ }
TransactionInfoComposable(
label = stringResource(id = R.string.withdraw_exchange),
info = cleanExchange(t.exchangeBaseUrl),
diff --git a/wallet/src/main/res/layout/list_item_age.xml
b/wallet/src/main/res/drawable/ic_archive.xml
similarity index 56%
copy from wallet/src/main/res/layout/list_item_age.xml
copy to wallet/src/main/res/drawable/ic_archive.xml
index 2d3a6e5..58a032c 100644
--- a/wallet/src/main/res/layout/list_item_age.xml
+++ b/wallet/src/main/res/drawable/ic_archive.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?><!--
+<!--
~ This file is part of GNU Taler
~ (C) 2023 Taler Systems S.A.
~
@@ -14,13 +14,8 @@
~ GNU Taler; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
-->
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/text1"
- style="?android:attr/spinnerItemStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:padding="8dp"
- android:singleLine="true"
- android:textAlignment="inherit"
- android:textSize="20sp" />
+<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..09feee6 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -59,6 +59,8 @@ GNU Taler is immune against many types of fraud, such as
phishing of credit card
<string name="currency">Currency</string>
<!-- The count should be mirrored in RTL languages -->
<string name="char_count">%1$d/%2$d</string>
+ <string name="import_db">Import</string>
+ <string name="reset">Reset</string>
<string name="offline">Operation requires internet access. Please ensure
your internet connection works and try again.</string>
<string name="offline_banner">No internet access</string>
@@ -257,10 +259,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>
@@ -271,6 +277,12 @@ GNU Taler is immune against many types of fraud, such as
phishing of credit card
<string name="settings_reset">Reset Wallet (dangerous!)</string>
<string name="settings_reset_summary">Throws away your money</string>
+ <string name="settings_dialog_reset_message">Do you really want to reset
the wallet and lose all coins and purchases?</string>
+ <string name="settings_dialog_import_message">This operation will
overwrite your existing database. Do you want to continue?</string>
+ <string name="settings_alert_reset_done">Wallet has been reset</string>
+ <string name="settings_alert_reset_canceled">Reset cancelled</string>
+ <string name="settings_alert_import_canceled">Import cancelled</string>
+
<string name="refund_title">Refund</string>
<string name="refund_error">Error processing refund</string>
<string name="refund_success">Refund received!</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
@@ -55,6 +55,14 @@
app:title="@string/settings_db_export"
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"
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-taler-android] branch master updated (15242d1 -> 1cb54ef),
gnunet <=