[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-taler-android] branch master updated (39b6926 -> 4af27e7)
From: |
gnunet |
Subject: |
[taler-taler-android] branch master updated (39b6926 -> 4af27e7) |
Date: |
Wed, 27 Mar 2024 18:27:03 +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 39b6926 [wallet] User friendlier p2p insufficient balance error
new 1e9ee99 [wallet] DD51: initial rendering based on currency spec
new c86ae12 [wallet] DD51: enrich some tx amounts with
currencySpecification
new cc59e35 [wallet] DD51: enrich manual withdrawal amounts with
currencySpecification
new f495657 [wallet] Balance layout improvements
new e305ddb [wallet] Display transactions by scopeInfo instead of currency
new 390b562 [wallet] Improve DD51 unit rendering and adapt tests
accordingly
new 4b4cf98 [wallet] Set input digits of some amount inputs
new 8d56c1d [wallet] Cache currencySpec in loadBalances()
new 5b6ba02 [taler-android] Improved and refactored testToString test
new 881eeab [taler-android] Improve handling of currencies with no symbol
new 18a8322 [wallet] Cache currency spec per scope info
new 945620b [wallet] Fix call to loadBalances() from non-UI thread
new 0b91663 [wallet] DD51'd more views, UX improvements and some fixes
new 3d4f7f9 [wallet] Use Backend.json instead of Json
new 4af27e7 [wallet] Use TransactionManager.selectedScope instead of
passing around scopeInfo
The 15 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/cashier/SignedAmount.kt | 8 +-
.../src/main/java/net/taler/common/Amount.kt | 57 +++++++-
.../net/taler/common}/CurrencySpecification.kt | 11 +-
.../src/test/java/net/taler/common/AmountTest.kt | 157 ++++++++++++++++++++-
.../src/main/java/net/taler/wallet/MainActivity.kt | 5 +-
.../src/main/java/net/taler/wallet/MainFragment.kt | 22 +--
.../main/java/net/taler/wallet/MainViewModel.kt | 13 +-
.../java/net/taler/wallet/ReceiveFundsFragment.kt | 15 +-
.../java/net/taler/wallet/SendFundsFragment.kt | 16 ++-
.../net/taler/wallet/balances/BalanceAdapter.kt | 9 +-
.../net/taler/wallet/balances/BalanceManager.kt | 61 +++++++-
.../net/taler/wallet/balances/BalancesFragment.kt | 6 +-
.../net/taler/wallet/compose/AmountInputField.kt | 11 +-
.../net/taler/wallet/deposit/DepositFragment.kt | 8 +-
.../taler/wallet/deposit/MakeDepositComposable.kt | 4 +-
.../wallet/deposit/TransactionDepositComposable.kt | 10 +-
.../taler/wallet/exchanges/ExchangeListFragment.kt | 2 +-
.../java/net/taler/wallet/exchanges/Exchanges.kt | 2 +
.../wallet/payment/TransactionPaymentComposable.kt | 10 +-
.../taler/wallet/peer/OutgoingPullComposable.kt | 2 +-
.../net/taler/wallet/peer/OutgoingPullFragment.kt | 6 +-
.../taler/wallet/peer/OutgoingPushComposable.kt | 2 +-
.../net/taler/wallet/peer/OutgoingPushFragment.kt | 5 +-
.../taler/wallet/peer/TransactionPeerPullCredit.kt | 11 +-
.../taler/wallet/peer/TransactionPeerPullDebit.kt | 13 +-
.../taler/wallet/peer/TransactionPeerPushCredit.kt | 11 +-
.../taler/wallet/peer/TransactionPeerPushDebit.kt | 11 +-
.../wallet/refund/TransactionRefundComposable.kt | 10 +-
.../wallet/transactions/TransactionAdapter.kt | 10 +-
.../transactions/TransactionDepositFragment.kt | 1 +
.../transactions/TransactionDetailFragment.kt | 1 +
.../wallet/transactions/TransactionManager.kt | 32 +++--
.../transactions/TransactionPaymentFragment.kt | 1 +
.../wallet/transactions/TransactionPeerFragment.kt | 16 ++-
.../transactions/TransactionRefreshFragment.kt | 10 +-
.../transactions/TransactionRefundFragment.kt | 4 +-
.../transactions/TransactionWithdrawalFragment.kt | 1 +
.../net/taler/wallet/transactions/Transactions.kt | 2 +-
.../wallet/transactions/TransactionsFragment.kt | 14 +-
.../withdraw/TransactionWithdrawalComposable.kt | 13 +-
.../net/taler/wallet/withdraw/WithdrawManager.kt | 2 +
.../manual/ManualWithdrawSuccessFragment.kt | 2 +
.../taler/wallet/withdraw/manual/ScreenTransfer.kt | 12 +-
.../wallet/withdraw/manual/TransferBitcoin.kt | 4 +-
.../taler/wallet/withdraw/manual/TransferIBAN.kt | 4 +-
wallet/src/main/res/layout/list_item_balance.xml | 20 +--
wallet/src/main/res/values/strings.xml | 2 +
47 files changed, 491 insertions(+), 158 deletions(-)
rename {wallet/src/main/java/net/taler/wallet/balances =>
taler-kotlin-android/src/main/java/net/taler/common}/CurrencySpecification.kt
(85%)
diff --git a/cashier/src/main/java/net/taler/cashier/SignedAmount.kt
b/cashier/src/main/java/net/taler/cashier/SignedAmount.kt
index 4f624ae..45bc3af 100644
--- a/cashier/src/main/java/net/taler/cashier/SignedAmount.kt
+++ b/cashier/src/main/java/net/taler/cashier/SignedAmount.kt
@@ -23,8 +23,10 @@ data class SignedAmount(
val amount: Amount
) {
- override fun toString(): String {
- return if (positive) "$amount" else "-$amount"
- }
+ override fun toString() = toString(showSymbol = true)
+ fun toString(showSymbol: Boolean) = amount.toString(
+ showSymbol = showSymbol,
+ negative = !positive,
+ )
}
diff --git a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
index 5fb36fa..3e3bd0a 100644
--- a/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
+++ b/taler-kotlin-android/src/main/java/net/taler/common/Amount.kt
@@ -16,11 +16,15 @@
package net.taler.common
+import android.os.Build
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable
import kotlinx.serialization.Serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
+import java.text.DecimalFormat
+import java.text.DecimalFormatSymbols
+import java.text.NumberFormat
import kotlin.math.floor
import kotlin.math.pow
import kotlin.math.roundToInt
@@ -54,6 +58,11 @@ public data class Amount(
* of 50_000_000 would correspond to 50 cents.
*/
val fraction: Int,
+
+ /**
+ * Currency specification for amount
+ */
+ val spec: CurrencySpecification? = null,
) : Comparable<Amount> {
public companion object {
@@ -170,6 +179,8 @@ public data class Amount(
return Amount(checkCurrency(currency), this.value, this.fraction)
}
+ fun withSpec(spec: CurrencySpecification?) = copy(spec = spec)
+
public operator fun minus(other: Amount): Amount {
check(currency == other.currency) { "Can only subtract from same
currency" }
var resultValue = value
@@ -196,8 +207,50 @@ public data class Amount(
return "$currency:$amountStr"
}
- override fun toString(): String {
- return "$amountStr $currency"
+ override fun toString() = toString(
+ showSymbol = true,
+ negative = false,
+ )
+
+ fun toString(
+ showSymbol: Boolean = true,
+ negative: Boolean = false,
+ symbols: DecimalFormatSymbols = DecimalFormat().decimalFormatSymbols,
+ ): String {
+ // We clone the object to safely/cleanly modify it
+ val s = symbols.clone() as DecimalFormatSymbols
+ val amount = (if (negative) "-$amountStr" else
amountStr).toBigDecimal()
+
+ // No currency spec, so we render normally
+ if (spec == null) {
+ val format = NumberFormat.getInstance()
+ format.maximumFractionDigits = MAX_FRACTION_LENGTH
+ format.minimumFractionDigits = 0
+ if (Build.VERSION.SDK_INT >= 34) {
+ s.groupingSeparator = s.monetaryGroupingSeparator
+ }
+ s.decimalSeparator = s.monetaryDecimalSeparator
+ (format as DecimalFormat).decimalFormatSymbols = s
+
+ val fmt = format.format(amount)
+ return if (showSymbol) "$fmt $currency" else fmt
+ }
+
+ // There is currency spec, so we can do things right
+ val format = NumberFormat.getCurrencyInstance()
+ format.maximumFractionDigits = spec.numFractionalNormalDigits
+ format.minimumFractionDigits = spec.numFractionalTrailingZeroDigits
+ s.currencySymbol = spec.symbol ?: ""
+ (format as DecimalFormat).decimalFormatSymbols = s
+
+ val fmt = format.format(amount)
+ return if (showSymbol) {
+ // If no symbol, then we use the currency string
+ if (spec.symbol != null) fmt else "$fmt $currency"
+ } else {
+ // We should do better than manually removing the symbol here
+ fmt.replace(s.currencySymbol, "").trim()
+ }
}
override fun compareTo(other: Amount): Int {
diff --git
a/wallet/src/main/java/net/taler/wallet/balances/CurrencySpecification.kt
b/taler-kotlin-android/src/main/java/net/taler/common/CurrencySpecification.kt
similarity index 85%
rename from
wallet/src/main/java/net/taler/wallet/balances/CurrencySpecification.kt
rename to
taler-kotlin-android/src/main/java/net/taler/common/CurrencySpecification.kt
index 5001db4..02113f4 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/CurrencySpecification.kt
+++
b/taler-kotlin-android/src/main/java/net/taler/common/CurrencySpecification.kt
@@ -1,6 +1,6 @@
/*
* This file is part of GNU Taler
- * (C) 2023 Taler Systems S.A.
+ * (C) 2024 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
@@ -14,7 +14,7 @@
* GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-package net.taler.wallet.balances
+package net.taler.common
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -29,5 +29,8 @@ data class CurrencySpecification(
@SerialName("num_fractional_trailing_zero_digits")
val numFractionalTrailingZeroDigits: Int,
@SerialName("alt_unit_names")
- val altUnitNames: Map<String, String>,
-)
\ No newline at end of file
+ val altUnitNames: Map<Int, String>,
+) {
+ // TODO: add support for alt units
+ val symbol: String? get() = altUnitNames[0]
+}
\ No newline at end of file
diff --git a/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt
b/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt
index 7072426..1ea4e70 100644
--- a/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt
+++ b/taler-kotlin-android/src/test/java/net/taler/common/AmountTest.kt
@@ -16,10 +16,12 @@
package net.taler.common
+import android.os.Build
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
+import java.text.DecimalFormatSymbols
import kotlin.random.Random
class AmountTest {
@@ -41,7 +43,6 @@ class AmountTest {
assertEquals("TESTKUDOS", amount.currency)
assertEquals(23, amount.value)
assertEquals((0.42 * 1e8).toInt(), amount.fraction)
- assertEquals("23.42 TESTKUDOS", amount.toString())
str = "EUR:500000000.00000001"
amount = Amount.fromJSONString(str)
@@ -49,7 +50,6 @@ class AmountTest {
assertEquals("EUR", amount.currency)
assertEquals(500000000, amount.value)
assertEquals(1, amount.fraction)
- assertEquals("500000000.00000001 EUR", amount.toString())
str = "EUR:1500000000.00000003"
amount = Amount.fromJSONString(str)
@@ -57,14 +57,158 @@ class AmountTest {
assertEquals("EUR", amount.currency)
assertEquals(1500000000, amount.value)
assertEquals(3, amount.fraction)
- assertEquals("1500000000.00000003 EUR", amount.toString())
}
@Test
fun testToString() {
- Amount.fromString("BITCOINBTC", "0.00000001").let { amount ->
- assertEquals("0.00000001 BITCOINBTC", amount.toString())
- assertEquals("0.00000001", amount.amountStr)
+ amountToString(
+ amount = Amount.fromString("KUDOS", "13.71"),
+ spec = CurrencySpecification(
+ name = "Test (Taler Demostrator)",
+ numFractionalInputDigits = 2,
+ numFractionalNormalDigits = 2,
+ numFractionalTrailingZeroDigits = 2,
+ altUnitNames = mapOf(0 to "ク"),
+ ),
+ rawStr = "13.71",
+ fraction = 71000000,
+ specAmount = "13.71",
+ noSpecAmount = "13.71",
+ currency = "KUDOS",
+ symbol = "ク",
+ )
+
+ amountToString(
+ amount = Amount.fromString("TESTKUDOS", "23.42"),
+ spec = CurrencySpecification(
+ name = "Test (Taler Unstable Demostrator)",
+ numFractionalInputDigits = 0,
+ numFractionalNormalDigits = 0,
+ numFractionalTrailingZeroDigits = 0,
+ altUnitNames = mapOf(0 to "テ"),
+ ),
+ rawStr = "23.42",
+ fraction = 42000000,
+ specAmount = "23",
+ noSpecAmount = "23.42",
+ currency = "TESTKUDOS",
+ symbol = "テ",
+ )
+
+ amountToString(
+ amount = Amount.fromString("BITCOINBTC", "0.00000001"),
+ spec = CurrencySpecification(
+ name = "Bitcoin",
+ numFractionalInputDigits = 8,
+ numFractionalNormalDigits = 8,
+ numFractionalTrailingZeroDigits = 0,
+ altUnitNames = mapOf(
+ 0 to "₿",
+ // TODO: uncomment when units get implemented
+ // and then write tests for units, please
+// -1 to "d₿",
+// -2 to "c₿",
+// -3 to "m₿",
+// -6 to "µ₿",
+// -8 to "sat",
+ ),
+ ),
+ rawStr = "0.00000001",
+ fraction = 1,
+ specAmount = "0.00000001",
+ noSpecAmount = "0.00000001",
+ currency = "BITCOINBTC",
+ symbol = "₿",
+ )
+
+ val specEUR = CurrencySpecification(
+ name = "EUR",
+ numFractionalInputDigits = 2,
+ numFractionalNormalDigits = 2,
+ numFractionalTrailingZeroDigits = 2,
+ altUnitNames = mapOf(0 to "€"),
+ )
+
+ amountToString(
+ amount = Amount.fromString("EUR", "1500000000.00000003"),
+ spec = specEUR,
+ rawStr = "1500000000.00000003",
+ fraction = 3,
+ specAmount = "1,500,000,000.00",
+ noSpecAmount = "1,500,000,000.00000003",
+ currency = "EUR",
+ symbol = "€",
+ )
+
+ amountToString(
+ amount = Amount.fromString("EUR", "500000000.126"),
+ spec = specEUR,
+ rawStr = "500000000.126",
+ fraction = 12600000,
+ specAmount = "500,000,000.13",
+ noSpecAmount = "500,000,000.126",
+ currency = "EUR",
+ symbol = "€",
+ )
+
+ amountToString(
+ amount = Amount.fromString("NOSYMBOL", "13.24"),
+ spec = CurrencySpecification(
+ name = "No symbol!",
+ numFractionalInputDigits = 2,
+ numFractionalNormalDigits = 2,
+ numFractionalTrailingZeroDigits = 2,
+ altUnitNames = mapOf(),
+ ),
+ rawStr = "13.24",
+ fraction = 24000000,
+ specAmount = "13.24",
+ noSpecAmount = "13.24",
+ currency = "NOSYMBOL",
+ symbol = "NOSYMBOL",
+ )
+ }
+
+ private fun amountToString(
+ amount: Amount,
+ spec: CurrencySpecification,
+ rawStr: String,
+ fraction: Int,
+ specAmount: String,
+ noSpecAmount: String,
+ currency: String,
+ symbol: String,
+ ) {
+ val symbols = DecimalFormatSymbols.getInstance()
+ symbols.decimalSeparator = '.'
+ symbols.groupingSeparator = ','
+ symbols.monetaryDecimalSeparator = '.'
+ if (Build.VERSION.SDK_INT >= 34) {
+ symbols.monetaryGroupingSeparator = ','
+ }
+
+ // Only the raw amount
+ assertEquals(rawStr, amount.amountStr)
+ assertEquals(fraction, amount.fraction)
+
+ // The amount without currency spec
+ assertEquals("$noSpecAmount $currency", amount.toString(symbols =
symbols))
+ assertEquals(noSpecAmount, amount.toString(symbols = symbols,
showSymbol = false))
+ assertEquals("-$noSpecAmount $currency", amount.toString(symbols =
symbols, negative = true))
+ assertEquals("-$noSpecAmount", amount.toString(symbols = symbols,
showSymbol = false, negative = true))
+
+ // The amount with currency spec
+ val withSpec = amount.withSpec(spec)
+ assertEquals(specAmount, withSpec.toString(symbols = symbols,
showSymbol = false))
+ assertEquals(specAmount, withSpec.toString(symbols = symbols,
showSymbol = false))
+ assertEquals("-$specAmount", withSpec.toString(symbols = symbols,
showSymbol = false, negative = true))
+ assertEquals("-$specAmount", withSpec.toString(symbols = symbols,
showSymbol = false, negative = true))
+ if (spec.symbol != null) {
+ assertEquals("${symbol}$specAmount", withSpec.toString(symbols =
symbols))
+ assertEquals("-${symbol}$specAmount", withSpec.toString(symbols =
symbols, negative = true))
+ } else {
+ assertEquals("$specAmount $currency", withSpec.toString(symbols =
symbols))
+ assertEquals("-$specAmount $currency", withSpec.toString(symbols =
symbols, negative = true))
}
}
@@ -76,7 +220,6 @@ class AmountTest {
assertEquals(str, amount.toJSONString())
assertEquals("TESTKUDOS123", amount.currency)
assertEquals(maxValue, amount.value)
- assertEquals("$maxValue.99999999 TESTKUDOS123", amount.toString())
// longer currency not accepted
assertThrows<AmountParserException>("longer currency was accepted") {
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
index 65e5c2a..5dfd920 100644
--- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
@@ -346,8 +346,9 @@ class MainActivity : AppCompatActivity(),
OnNavigationItemSelectedListener,
val transactionId = status.response.transactionId
val transaction =
model.transactionManager.getTransactionById(transactionId)
if (transaction != null) {
- val currency = transaction.amountRaw.currency
- model.showTransactions(currency)
+ // TODO: currency what? scopes are the cool thing now
+ // val currency = transaction.amountRaw.currency
+ // model.showTransactions(currency)
Snackbar.make(ui.navView,
getString(R.string.refund_success), LENGTH_LONG).show()
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/MainFragment.kt
b/wallet/src/main/java/net/taler/wallet/MainFragment.kt
index 656db63..9fa9838 100644
--- a/wallet/src/main/java/net/taler/wallet/MainFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainFragment.kt
@@ -24,20 +24,20 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import net.taler.common.EventObserver
-import net.taler.wallet.CurrencyMode.MULTI
-import net.taler.wallet.CurrencyMode.SINGLE
+import net.taler.wallet.ScopeMode.MULTI
+import net.taler.wallet.ScopeMode.SINGLE
import net.taler.wallet.balances.BalanceState
import net.taler.wallet.balances.BalanceState.Success
import net.taler.wallet.balances.BalancesFragment
import net.taler.wallet.databinding.FragmentMainBinding
import net.taler.wallet.transactions.TransactionsFragment
-enum class CurrencyMode { SINGLE, MULTI }
+enum class ScopeMode { SINGLE, MULTI }
class MainFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
- private var currencyMode: CurrencyMode? = null
+ private var scopeMode: ScopeMode? = null
private lateinit var ui: FragmentMainBinding
@@ -54,10 +54,10 @@ class MainFragment : Fragment() {
model.balanceManager.state.observe(viewLifecycleOwner) {
onBalancesChanged(it)
}
- model.transactionsEvent.observe(viewLifecycleOwner, EventObserver {
currency ->
- // we only need to navigate to a dedicated list, when in
multi-currency mode
- if (currencyMode == MULTI) {
- model.transactionManager.selectedCurrency = currency
+ model.transactionsEvent.observe(viewLifecycleOwner, EventObserver {
scopeInfo ->
+ // we only need to navigate to a dedicated list, when in
multi-scope mode
+ if (scopeMode == MULTI) {
+ model.transactionManager.selectedScope = scopeInfo
findNavController().navigate(R.id.action_nav_main_to_nav_transactions)
}
})
@@ -80,14 +80,14 @@ class MainFragment : Fragment() {
if (state !is Success) return
val balances = state.balances
val mode = if (balances.size == 1) SINGLE else MULTI
- if (currencyMode != mode) {
+ if (scopeMode != mode) {
val f = if (mode == SINGLE) {
- model.transactionManager.selectedCurrency =
balances[0].available.currency
+ model.transactionManager.selectedScope = balances[0].scopeInfo
TransactionsFragment()
} else {
BalancesFragment()
}
- currencyMode = mode
+ scopeMode = mode
childFragmentManager.beginTransaction()
.replace(R.id.mainFragmentContainer, f, mode.name)
.commitNow()
diff --git a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
index fe11d6a..5903446 100644
--- a/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainViewModel.kt
@@ -36,6 +36,7 @@ import net.taler.wallet.backend.VersionReceiver
import net.taler.wallet.backend.WalletBackendApi
import net.taler.wallet.backend.WalletCoreVersion
import net.taler.wallet.balances.BalanceManager
+import net.taler.wallet.balances.ScopeInfo
import net.taler.wallet.deposit.DepositManager
import net.taler.wallet.exchanges.ExchangeManager
import net.taler.wallet.payment.PaymentManager
@@ -81,8 +82,8 @@ class MainViewModel(
val accountManager: AccountManager = AccountManager(api, viewModelScope)
val depositManager: DepositManager = DepositManager(api, viewModelScope)
- private val mTransactionsEvent = MutableLiveData<Event<String>>()
- val transactionsEvent: LiveData<Event<String>> = mTransactionsEvent
+ private val mTransactionsEvent = MutableLiveData<Event<ScopeInfo>>()
+ val transactionsEvent: LiveData<Event<ScopeInfo>> = mTransactionsEvent
private val mScanCodeEvent = MutableLiveData<Event<Boolean>>()
val scanCodeEvent: LiveData<Event<Boolean>> = mScanCodeEvent
@@ -99,7 +100,7 @@ class MainViewModel(
Log.i(TAG, "Received notification from wallet-core: $payload")
// Only update balances when we're told they changed
- if (payload.type == "balance-change") {
+ if (payload.type == "balance-change")
viewModelScope.launch(Dispatchers.Main) {
balanceManager.loadBalances()
}
@@ -111,11 +112,11 @@ class MainViewModel(
}
/**
- * Navigates to the given currency's transaction list, when [MainFragment]
is shown.
+ * Navigates to the given scope info's transaction list, when
[MainFragment] is shown.
*/
@UiThread
- fun showTransactions(currency: String) {
- mTransactionsEvent.value = currency.toEvent()
+ fun showTransactions(scopeInfo: ScopeInfo) {
+ mTransactionsEvent.value = scopeInfo.toEvent()
}
@UiThread
diff --git a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
index a25c352..9a83bc8 100644
--- a/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/ReceiveFundsFragment.kt
@@ -51,7 +51,9 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.wallet.compose.AmountInputField
+import net.taler.wallet.compose.DEFAULT_INPUT_DECIMALS
import net.taler.wallet.compose.TalerSurface
import net.taler.wallet.exchanges.ExchangeItem
@@ -59,7 +61,9 @@ class ReceiveFundsFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private val exchangeManager get() = model.exchangeManager
private val withdrawManager get() = model.withdrawManager
+ private val balanceManager get() = model.balanceManager
private val peerManager get() = model.peerManager
+ private val scopeInfo get() = model.transactionManager.selectedScope ?:
error("No scope selected")
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
@@ -68,7 +72,8 @@ class ReceiveFundsFragment : Fragment() {
setContent {
TalerSurface {
ReceiveFundsIntro(
- model.transactionManager.selectedCurrency ?: error("No
currency selected"),
+ scopeInfo.currency,
+ balanceManager.getSpecForScopeInfo(scopeInfo),
this@ReceiveFundsFragment::onManualWithdraw,
this@ReceiveFundsFragment::onPeerPull,
)
@@ -78,7 +83,7 @@ class ReceiveFundsFragment : Fragment() {
override fun onStart() {
super.onStart()
- activity?.setTitle(R.string.transactions_receive_funds)
+
activity?.setTitle(getString(R.string.transactions_receive_funds_title,
scopeInfo.currency))
}
private fun onManualWithdraw(amount: Amount) {
@@ -113,6 +118,7 @@ class ReceiveFundsFragment : Fragment() {
@Composable
private fun ReceiveFundsIntro(
currency: String,
+ spec: CurrencySpecification?,
onManualWithdraw: (Amount) -> Unit,
onPeerPull: (Amount) -> Unit,
) {
@@ -143,10 +149,11 @@ private fun ReceiveFundsIntro(
if (isError)
Text(stringResource(R.string.receive_amount_invalid))
},
isError = isError,
+ numberOfDecimals = spec?.numFractionalInputDigits ?:
DEFAULT_INPUT_DECIMALS,
)
Text(
modifier = Modifier,
- text = currency,
+ text = spec?.symbol ?: currency,
softWrap = false,
style = MaterialTheme.typography.titleLarge,
)
@@ -189,6 +196,6 @@ private fun ReceiveFundsIntro(
@Composable
fun PreviewReceiveFundsIntro() {
Surface {
- ReceiveFundsIntro("TESTKUDOS", {}) {}
+ ReceiveFundsIntro("TESTKUDOS", null, {}) {}
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
index 09d33e2..2581979 100644
--- a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
@@ -48,12 +48,16 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.wallet.compose.AmountInputField
+import net.taler.wallet.compose.DEFAULT_INPUT_DECIMALS
import net.taler.wallet.compose.TalerSurface
class SendFundsFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
+ private val balanceManager get() = model.balanceManager
private val peerManager get() = model.peerManager
+ private val scopeInfo get() = model.transactionManager.selectedScope ?:
error("No scope selected")
override fun onCreateView(
inflater: LayoutInflater,
@@ -63,8 +67,8 @@ class SendFundsFragment : Fragment() {
setContent {
TalerSurface {
SendFundsIntro(
- currency = model.transactionManager.selectedCurrency
- ?: error("No currency selected"),
+ currency = scopeInfo.currency,
+ spec = balanceManager.getSpecForScopeInfo(scopeInfo),
hasSufficientBalance = model::hasSufficientBalance,
onDeposit = this@SendFundsFragment::onDeposit,
onPeerPush = this@SendFundsFragment::onPeerPush,
@@ -75,7 +79,7 @@ class SendFundsFragment : Fragment() {
override fun onStart() {
super.onStart()
- activity?.setTitle(R.string.transactions_send_funds)
+ activity?.setTitle(getString(R.string.transactions_send_funds_title,
scopeInfo.currency))
}
private fun onDeposit(amount: Amount) {
@@ -93,6 +97,7 @@ class SendFundsFragment : Fragment() {
@Composable
private fun SendFundsIntro(
currency: String,
+ spec: CurrencySpecification?,
hasSufficientBalance: (Amount) -> Boolean,
onDeposit: (Amount) -> Unit,
onPeerPush: (Amount) -> Unit,
@@ -129,10 +134,11 @@ private fun SendFundsIntro(
}
},
isError = isError || insufficientBalance,
+ numberOfDecimals = spec?.numFractionalInputDigits ?:
DEFAULT_INPUT_DECIMALS,
)
Text(
modifier = Modifier,
- text = currency,
+ text = spec?.symbol ?: currency,
softWrap = false,
style = MaterialTheme.typography.titleLarge,
)
@@ -185,6 +191,6 @@ private fun SendFundsIntro(
@Composable
fun PreviewSendFundsIntro() {
Surface {
- SendFundsIntro("TESTKUDOS", { true }, {}) {}
+ SendFundsIntro("TESTKUDOS", null, { true }, {}) {}
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
b/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
index efb3d48..f40def4 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/balances/BalanceAdapter.kt
@@ -58,7 +58,6 @@ class BalanceAdapter(private val listener:
BalanceClickListener) : Adapter<Balan
}
inner class BalanceViewHolder(private val v: View) :
RecyclerView.ViewHolder(v) {
- private val currencyView: TextView =
v.findViewById(R.id.balanceCurrencyView)
private val amountView: TextView =
v.findViewById(R.id.balanceAmountView)
private val scopeView: TextView = v.findViewById(R.id.scopeView)
private val balanceInboundAmount: TextView =
v.findViewById(R.id.balanceInboundAmount)
@@ -66,9 +65,8 @@ class BalanceAdapter(private val listener:
BalanceClickListener) : Adapter<Balan
private val pendingView: TextView = v.findViewById(R.id.pendingView)
fun bind(item: BalanceItem) {
- v.setOnClickListener {
listener.onBalanceClick(item.available.currency) }
- currencyView.text = item.currency
- amountView.text = item.available.amountStr
+ v.setOnClickListener { listener.onBalanceClick(item.scopeInfo) }
+ amountView.text = item.available.toString()
val amountIncoming = item.pendingIncoming
if (amountIncoming.isZero()) {
@@ -77,8 +75,7 @@ class BalanceAdapter(private val listener:
BalanceClickListener) : Adapter<Balan
} else {
balanceInboundAmount.visibility = VISIBLE
balanceInboundLabel.visibility = VISIBLE
- balanceInboundAmount.text =
- v.context.getString(R.string.amount_positive,
amountIncoming)
+ balanceInboundAmount.text =
v.context.getString(R.string.amount_positive,
amountIncoming.toString(showSymbol = false))
}
val scopeInfo = item.scopeInfo
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalanceManager.kt
b/wallet/src/main/java/net/taler/wallet/balances/BalanceManager.kt
index 2930c77..42e67cf 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/BalanceManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/balances/BalanceManager.kt
@@ -24,15 +24,24 @@ import androidx.lifecycle.distinctUntilChanged
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+import net.taler.common.CurrencySpecification
import net.taler.wallet.TAG
import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.backend.WalletBackendApi
+import org.json.JSONObject
@Serializable
data class BalanceResponse(
val balances: List<BalanceItem>
)
+@Serializable
+data class GetCurrencySpecificationResponse(
+ val currencySpecification: CurrencySpecification,
+)
+
sealed class BalanceState {
data object None: BalanceState()
data object Loading: BalanceState()
@@ -56,6 +65,8 @@ class BalanceManager(
private val mState = MutableLiveData<BalanceState>(BalanceState.None)
val state: LiveData<BalanceState> = mState.distinctUntilChanged()
+ private val currencySpecs: MutableMap<ScopeInfo, CurrencySpecification?> =
mutableMapOf()
+
@UiThread
fun loadBalances() {
mState.value = BalanceState.Loading
@@ -66,12 +77,60 @@ class BalanceManager(
mState.postValue(BalanceState.Error(it))
}
response.onSuccess {
- mState.postValue(BalanceState.Success(it.balances))
mBalances.postValue(it.balances)
+ scope.launch {
+ // Fetch missing currency specs for all balances
+ it.balances.forEach { balance ->
+ if (!currencySpecs.containsKey(balance.scopeInfo)) {
+ currencySpecs[balance.scopeInfo] =
getCurrencySpecification(balance.scopeInfo)
+ }
+ }
+
+ mState.postValue(
+ BalanceState.Success(it.balances.map { balance ->
+ val spec = currencySpecs[balance.scopeInfo]
+ balance.copy(
+ available = balance.available.withSpec(spec),
+ pendingIncoming =
balance.pendingIncoming.withSpec(spec),
+ pendingOutgoing =
balance.pendingOutgoing.withSpec(spec),
+ )
+ }),
+ )
+ }
}
}
}
+ private suspend fun getCurrencySpecification(scopeInfo: ScopeInfo):
CurrencySpecification? {
+ var spec: CurrencySpecification? = null
+ api.request("getCurrencySpecification",
GetCurrencySpecificationResponse.serializer()) {
+ val json = Json.encodeToString(scopeInfo)
+ Log.d(TAG, "BalanceManager: $json")
+ put("scope", JSONObject(json))
+ }.onSuccess {
+ spec = it.currencySpecification
+ }.onError {
+ Log.e(TAG, "Error getting currency spec for scope $scopeInfo: $it")
+ }
+
+ return spec
+ }
+
+ @Deprecated("Please find spec via scopeInfo instead",
ReplaceWith("getSpecForScopeInfo"))
+ fun getSpecForCurrency(currency: String): CurrencySpecification? {
+ val state = mState.value
+ if (state !is BalanceState.Success) return null
+
+ return state.balances.find { it.currency == currency }?.available?.spec
+ }
+
+ fun getSpecForScopeInfo(scopeInfo: ScopeInfo): CurrencySpecification? {
+ val state = mState.value
+ if (state !is BalanceState.Success) return null
+
+ return state.balances.find { it.scopeInfo == scopeInfo
}?.available?.spec
+ }
+
fun resetBalances() {
mState.value = BalanceState.None
}
diff --git a/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt
b/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt
index cfbbc46..93636ea 100644
--- a/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/balances/BalancesFragment.kt
@@ -39,7 +39,7 @@ import net.taler.wallet.databinding.FragmentBalancesBinding
import net.taler.wallet.showError
interface BalanceClickListener {
- fun onBalanceClick(currency: String)
+ fun onBalanceClick(scopeInfo: ScopeInfo)
}
class BalancesFragment : Fragment(),
@@ -96,8 +96,8 @@ class BalancesFragment : Fragment(),
}
}
- override fun onBalanceClick(currency: String) {
- model.showTransactions(currency)
+ override fun onBalanceClick(scopeInfo: ScopeInfo) {
+ model.showTransactions(scopeInfo)
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt
b/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt
index 077c89a..a524d1b 100644
--- a/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt
+++ b/wallet/src/main/java/net/taler/wallet/compose/AmountInputField.kt
@@ -40,6 +40,8 @@ import kotlin.math.max
import kotlin.math.pow
import kotlin.math.roundToLong
+const val DEFAULT_INPUT_DECIMALS = 2
+
@Composable
fun AmountInputField(
value: String,
@@ -50,7 +52,7 @@ fun AmountInputField(
isError: Boolean = false,
keyboardActions: KeyboardActions = KeyboardActions.Default,
decimalFormatSymbols: DecimalFormatSymbols =
DecimalFormat().decimalFormatSymbols,
- numberOfDecimals: Int = 2,
+ numberOfDecimals: Int = DEFAULT_INPUT_DECIMALS,
) {
var amountInput by remember { mutableStateOf(value) }
@@ -146,7 +148,12 @@ private class AmountInputVisualTransformation(
}
}
- val formattedNumber = intPart + decimalSeparator + fractionPart
+ // Hide trailing decimal separator if decimals are 0
+ val formattedNumber = if (numberOfDecimals > 0) {
+ intPart + decimalSeparator + fractionPart
+ } else {
+ intPart
+ }
val newText = AnnotatedString(
text = formattedNumber,
diff --git a/wallet/src/main/java/net/taler/wallet/deposit/DepositFragment.kt
b/wallet/src/main/java/net/taler/wallet/deposit/DepositFragment.kt
index 28dcc3f..20acee1 100644
--- a/wallet/src/main/java/net/taler/wallet/deposit/DepositFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/deposit/DepositFragment.kt
@@ -37,6 +37,8 @@ import net.taler.wallet.showError
class DepositFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private val depositManager get() = model.depositManager
+ private val balanceManager get() = model.balanceManager
+ private val transactionManager get() = model.transactionManager
override fun onCreateView(
inflater: LayoutInflater,
@@ -46,6 +48,8 @@ class DepositFragment : Fragment() {
val amount = arguments?.getString("amount")?.let {
Amount.fromJSONString(it)
} ?: error("no amount passed")
+ val scopeInfo = transactionManager.selectedScope
+ val spec = scopeInfo?.let { balanceManager.getSpecForScopeInfo(it) }
val receiverName = arguments?.getString("receiverName")
val iban = arguments?.getString("IBAN")
if (receiverName != null && iban != null) {
@@ -57,14 +61,14 @@ class DepositFragment : Fragment() {
val state =
depositManager.depositState.collectAsStateLifecycleAware()
if (amount.currency == CURRENCY_BTC)
MakeBitcoinDepositComposable(
state = state.value,
- amount = amount,
+ amount = amount.withSpec(spec),
bitcoinAddress = null,
onMakeDeposit = { amount, bitcoinAddress ->
depositManager.onDepositButtonClicked(amount,
bitcoinAddress)
},
) else MakeDepositComposable(
state = state.value,
- amount = amount,
+ amount = amount.withSpec(spec),
presetName = receiverName,
presetIban = iban,
onMakeDeposit =
this@DepositFragment::onDepositButtonClicked,
diff --git
a/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositComposable.kt
b/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositComposable.kt
index 97e2eb0..9333ce1 100644
--- a/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/deposit/MakeDepositComposable.kt
@@ -138,13 +138,13 @@ fun MakeDepositComposable(
TransactionAmountComposable(
label = stringResource(R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(amount.spec),
amountType = if (fee.isZero()) Positive else Negative,
)
TransactionAmountComposable(
label = stringResource(R.string.send_amount),
- amount = effectiveAmount,
+ amount = effectiveAmount.withSpec(amount.spec),
amountType = Positive,
)
}
diff --git
a/wallet/src/main/java/net/taler/wallet/deposit/TransactionDepositComposable.kt
b/wallet/src/main/java/net/taler/wallet/deposit/TransactionDepositComposable.kt
index 807d631..817dfac 100644
---
a/wallet/src/main/java/net/taler/wallet/deposit/TransactionDepositComposable.kt
+++
b/wallet/src/main/java/net/taler/wallet/deposit/TransactionDepositComposable.kt
@@ -32,6 +32,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.common.Timestamp
import net.taler.common.toAbsoluteTime
import net.taler.wallet.R
@@ -53,6 +54,7 @@ import net.taler.wallet.transactions.TransitionsComposable
fun TransactionDepositComposable(
t: TransactionDeposit,
devMode: Boolean,
+ spec: CurrencySpecification?,
onTransition: (t: TransactionAction) -> Unit,
) {
val scrollState = rememberScrollState()
@@ -71,7 +73,7 @@ fun TransactionDepositComposable(
TransactionAmountComposable(
label = stringResource(id = R.string.amount_chosen),
- amount = t.amountRaw,
+ amount = t.amountRaw.withSpec(spec),
amountType = AmountType.Neutral,
)
@@ -79,14 +81,14 @@ fun TransactionDepositComposable(
if (!fee.isZero()) {
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(spec),
amountType = AmountType.Negative,
)
}
TransactionAmountComposable(
label = stringResource(id = R.string.amount_sent),
- amount = t.amountEffective,
+ amount = t.amountEffective.withSpec(spec),
amountType = AmountType.Negative,
)
@@ -112,6 +114,6 @@ fun TransactionDepositComposablePreview() {
error = TalerErrorInfo(code = EXCHANGE_GENERIC_KYC_REQUIRED),
)
Surface {
- TransactionDepositComposable(t, true) {}
+ TransactionDepositComposable(t, true, null) {}
}
}
diff --git
a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
index 494b187..5482b5a 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/ExchangeListFragment.kt
@@ -141,7 +141,7 @@ open class ExchangeListFragment : Fragment(),
ExchangeClickListener {
}
override fun onPeerReceive(item: ExchangeItem) {
- transactionManager.selectedCurrency = item.currency
+ transactionManager.selectedScope = item.scopeInfo
findNavController().navigate(R.id.action_global_receiveFunds)
}
diff --git a/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt
b/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt
index af373c2..ce0bd82 100644
--- a/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt
+++ b/wallet/src/main/java/net/taler/wallet/exchanges/Exchanges.kt
@@ -17,6 +17,7 @@
package net.taler.wallet.exchanges
import kotlinx.serialization.Serializable
+import net.taler.wallet.balances.ScopeInfo
import net.taler.wallet.cleanExchange
@Serializable
@@ -25,6 +26,7 @@ data class ExchangeItem(
// can be null before exchange info in wallet-core was fully loaded
val currency: String? = null,
val paytoUris: List<String>,
+ val scopeInfo: ScopeInfo? = null,
) {
val name: String get() = cleanExchange(exchangeBaseUrl)
}
\ No newline at end of file
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 5bbbd97..0f6d661 100644
---
a/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
+++
b/wallet/src/main/java/net/taler/wallet/payment/TransactionPaymentComposable.kt
@@ -32,6 +32,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import net.taler.common.Amount
import net.taler.common.ContractMerchant
+import net.taler.common.CurrencySpecification
import net.taler.common.Timestamp
import net.taler.common.toAbsoluteTime
import net.taler.wallet.R
@@ -57,6 +58,7 @@ import net.taler.wallet.transactions.TransitionsComposable
fun TransactionPaymentComposable(
t: TransactionPayment,
devMode: Boolean,
+ spec: CurrencySpecification?,
onFulfill: (url: String) -> Unit,
onTransition: (t: TransactionAction) -> Unit,
) {
@@ -76,7 +78,7 @@ fun TransactionPaymentComposable(
TransactionAmountComposable(
label = stringResource(id = R.string.transaction_order_total),
- amount = t.amountRaw,
+ amount = t.amountRaw.withSpec(spec),
amountType = AmountType.Neutral,
)
@@ -84,14 +86,14 @@ fun TransactionPaymentComposable(
if (!fee.isZero()) {
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(spec),
amountType = AmountType.Negative,
)
}
TransactionAmountComposable(
label = stringResource(id = R.string.transaction_paid),
- amount = t.amountEffective,
+ amount = t.amountEffective.withSpec(spec),
amountType = AmountType.Negative,
)
@@ -167,6 +169,6 @@ fun TransactionPaymentComposablePreview() {
error = TalerErrorInfo(code =
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED),
)
TalerSurface {
- TransactionPaymentComposable(t = t, devMode = true, onFulfill = {}) {}
+ TransactionPaymentComposable(t = t, devMode = true, spec = null,
onFulfill = {}) {}
}
}
diff --git
a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullComposable.kt
b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullComposable.kt
index d58b0b8..90b520e 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullComposable.kt
@@ -152,7 +152,7 @@ fun OutgoingPullIntroComposable(
val fee = state.amountRaw - state.amountEffective
if (!fee.isZero()) TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(amount.spec),
amountType = AmountType.Negative,
)
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt
b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt
index 0205ae0..8f2fb96 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt
@@ -40,6 +40,7 @@ class OutgoingPullFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private val peerManager get() = model.peerManager
private val transactionManager get() = model.transactionManager
+ private val balanceManager get() = model.balanceManager
override fun onCreateView(
inflater: LayoutInflater,
@@ -49,12 +50,15 @@ class OutgoingPullFragment : Fragment() {
val amount = arguments?.getString("amount")?.let {
Amount.fromJSONString(it)
} ?: error("no amount passed")
+ val scopeInfo = transactionManager.selectedScope
+ val spec = scopeInfo?.let { balanceManager.getSpecForScopeInfo(it) }
+
return ComposeView(requireContext()).apply {
setContent {
TalerSurface {
val state =
peerManager.pullState.collectAsStateLifecycleAware().value
OutgoingPullComposable(
- amount = amount,
+ amount = amount.withSpec(spec),
state = state,
onCreateInvoice =
this@OutgoingPullFragment::onCreateInvoice,
onClose = {
diff --git
a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushComposable.kt
b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushComposable.kt
index ea303d0..d39fdc8 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushComposable.kt
@@ -93,7 +93,7 @@ fun OutgoingPushIntroComposable(
val fee = state.amountEffective - state.amountRaw
Text(
modifier = Modifier.padding(vertical = 16.dp),
- text = stringResource(id = R.string.payment_fee, fee),
+ text = stringResource(id = R.string.payment_fee,
fee.withSpec(amount.spec)),
softWrap = false,
color = MaterialTheme.colorScheme.error,
)
diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt
b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt
index 97dbcc2..01fb566 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt
@@ -41,6 +41,7 @@ class OutgoingPushFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private val peerManager get() = model.peerManager
private val transactionManager get() = model.transactionManager
+ private val balanceManager get() = model.balanceManager
// hacky way to change back action until we have navigation for compose
private val backPressedCallback = object : OnBackPressedCallback(false) {
@@ -57,6 +58,8 @@ class OutgoingPushFragment : Fragment() {
val amount = arguments?.getString("amount")?.let {
Amount.fromJSONString(it)
} ?: error("no amount passed")
+ val scopeInfo = transactionManager.selectedScope
+ val spec = scopeInfo?.let { balanceManager.getSpecForScopeInfo(it) }
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner, backPressedCallback
@@ -67,7 +70,7 @@ class OutgoingPushFragment : Fragment() {
TalerSurface {
val state =
peerManager.pushState.collectAsStateLifecycleAware().value
OutgoingPushComposable(
- amount = amount,
+ amount = amount.withSpec(spec),
state = state,
onSend = this@OutgoingPushFragment::onSend,
onClose = {
diff --git
a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullCredit.kt
b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullCredit.kt
index 898ff69..3b15b6f 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullCredit.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullCredit.kt
@@ -22,6 +22,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.common.Timestamp
import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorCode.EXCHANGE_GENERIC_KYC_REQUIRED
@@ -41,7 +42,7 @@ import net.taler.wallet.transactions.TransactionPeerPullCredit
import net.taler.wallet.transactions.TransactionState
@Composable
-fun ColumnScope.TransactionPeerPullCreditComposable(t:
TransactionPeerPullCredit) {
+fun ColumnScope.TransactionPeerPullCreditComposable(t:
TransactionPeerPullCredit, spec: CurrencySpecification?) {
if (t.error == null) PeerQrCode(
state = t.txState,
talerUri = t.talerUri,
@@ -49,7 +50,7 @@ fun ColumnScope.TransactionPeerPullCreditComposable(t:
TransactionPeerPullCredit
TransactionAmountComposable(
label = stringResource(id = R.string.receive_peer_amount_invoiced),
- amount = t.amountRaw,
+ amount = t.amountRaw.withSpec(spec),
amountType = AmountType.Neutral,
)
@@ -57,14 +58,14 @@ fun ColumnScope.TransactionPeerPullCreditComposable(t:
TransactionPeerPullCredit
if (!fee.isZero()) {
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(spec),
amountType = AmountType.Negative,
)
}
TransactionAmountComposable(
label = stringResource(id = R.string.amount_received),
- amount = t.amountEffective,
+ amount = t.amountEffective.withSpec(spec),
amountType = AmountType.Positive,
)
@@ -93,7 +94,7 @@ fun TransactionPeerPullCreditPreview(loading: Boolean =
false) {
error = TalerErrorInfo(code = EXCHANGE_GENERIC_KYC_REQUIRED),
)
Surface {
- TransactionPeerComposable(t, true) {}
+ TransactionPeerComposable(t, true, null) {}
}
}
diff --git
a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullDebit.kt
b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullDebit.kt
index 783907e..dadff4a 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullDebit.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullDebit.kt
@@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.common.Timestamp
import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorCode.EXCHANGE_GENERIC_KYC_REQUIRED
@@ -38,25 +39,25 @@ import
net.taler.wallet.transactions.TransactionPeerPullDebit
import net.taler.wallet.transactions.TransactionState
@Composable
-fun TransactionPeerPullDebitComposable(t: TransactionPeerPullDebit) {
+fun TransactionPeerPullDebitComposable(t: TransactionPeerPullDebit, spec:
CurrencySpecification?) {
TransactionAmountComposable(
label = stringResource(id = R.string.transaction_order_total),
- amount = t.amountRaw,
+ amount = t.amountRaw.withSpec(spec),
amountType = AmountType.Neutral,
)
-
+
val fee = t.amountEffective - t.amountRaw
if (!fee.isZero()) {
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(spec),
amountType = AmountType.Negative,
)
}
TransactionAmountComposable(
label = stringResource(id = R.string.transaction_paid),
- amount = t.amountEffective,
+ amount = t.amountEffective.withSpec(spec),
amountType = AmountType.Negative,
)
@@ -84,6 +85,6 @@ fun TransactionPeerPullDebitPreview() {
error = TalerErrorInfo(code = EXCHANGE_GENERIC_KYC_REQUIRED),
)
Surface {
- TransactionPeerComposable(t, true) {}
+ TransactionPeerComposable(t, true, null) {}
}
}
diff --git
a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushCredit.kt
b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushCredit.kt
index 75d299b..dbf0fb9 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushCredit.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushCredit.kt
@@ -21,6 +21,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.common.Timestamp
import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorCode.EXCHANGE_GENERIC_KYC_REQUIRED
@@ -38,10 +39,10 @@ import
net.taler.wallet.transactions.TransactionPeerPushCredit
import net.taler.wallet.transactions.TransactionState
@Composable
-fun TransactionPeerPushCreditComposable(t: TransactionPeerPushCredit) {
+fun TransactionPeerPushCreditComposable(t: TransactionPeerPushCredit, spec:
CurrencySpecification?) {
TransactionAmountComposable(
label = stringResource(id = R.string.amount_sent),
- amount = t.amountRaw,
+ amount = t.amountRaw.withSpec(spec),
amountType = AmountType.Neutral,
)
@@ -49,14 +50,14 @@ fun TransactionPeerPushCreditComposable(t:
TransactionPeerPushCredit) {
if (!fee.isZero()) {
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(spec),
amountType = AmountType.Negative,
)
}
TransactionAmountComposable(
label = stringResource(id = R.string.amount_received),
- amount = t.amountEffective,
+ amount = t.amountEffective.withSpec(spec),
amountType = AmountType.Positive,
)
@@ -84,6 +85,6 @@ fun TransactionPeerPushCreditPreview() {
error = TalerErrorInfo(code = EXCHANGE_GENERIC_KYC_REQUIRED),
)
Surface {
- TransactionPeerComposable(t, true) {}
+ TransactionPeerComposable(t, true, null) {}
}
}
diff --git
a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushDebit.kt
b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushDebit.kt
index 909fd47..e592c3e 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushDebit.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushDebit.kt
@@ -30,6 +30,7 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.common.Timestamp
import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorCode.EXCHANGE_GENERIC_KYC_REQUIRED
@@ -52,7 +53,7 @@ import net.taler.wallet.transactions.TransactionPeerPushDebit
import net.taler.wallet.transactions.TransactionState
@Composable
-fun ColumnScope.TransactionPeerPushDebitComposable(t:
TransactionPeerPushDebit) {
+fun ColumnScope.TransactionPeerPushDebitComposable(t:
TransactionPeerPushDebit, spec: CurrencySpecification?) {
if (t.error == null) PeerQrCode(
state = t.txState,
talerUri = t.talerUri,
@@ -60,7 +61,7 @@ fun ColumnScope.TransactionPeerPushDebitComposable(t:
TransactionPeerPushDebit)
TransactionAmountComposable(
label = stringResource(id = R.string.transaction_order_total),
- amount = t.amountRaw,
+ amount = t.amountRaw.withSpec(spec),
amountType = AmountType.Neutral,
)
@@ -68,14 +69,14 @@ fun ColumnScope.TransactionPeerPushDebitComposable(t:
TransactionPeerPushDebit)
if (!fee.isZero()) {
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(spec),
amountType = AmountType.Negative,
)
}
TransactionAmountComposable(
label = stringResource(id = R.string.transaction_paid),
- amount = t.amountEffective,
+ amount = t.amountEffective.withSpec(spec),
amountType = AmountType.Negative,
)
@@ -140,7 +141,7 @@ fun TransactionPeerPushDebitPreview(loading: Boolean =
false) {
)
TalerSurface {
- TransactionPeerComposable(t, true) {}
+ TransactionPeerComposable(t, true, null) {}
}
}
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 82dceb5..637b41a 100644
---
a/wallet/src/main/java/net/taler/wallet/refund/TransactionRefundComposable.kt
+++
b/wallet/src/main/java/net/taler/wallet/refund/TransactionRefundComposable.kt
@@ -31,6 +31,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.common.Timestamp
import net.taler.common.toAbsoluteTime
import net.taler.wallet.R
@@ -54,6 +55,7 @@ import net.taler.wallet.transactions.TransitionsComposable
fun TransactionRefundComposable(
t: TransactionRefund,
devMode: Boolean,
+ spec: CurrencySpecification?,
onTransition: (t: TransactionAction) -> Unit,
) {
val scrollState = rememberScrollState()
@@ -71,19 +73,19 @@ fun TransactionRefundComposable(
)
TransactionAmountComposable(
label = stringResource(id = R.string.transaction_refund),
- amount = t.amountEffective,
+ amount = t.amountEffective.withSpec(spec),
amountType = AmountType.Positive,
)
TransactionAmountComposable(
label = stringResource(id = R.string.transaction_order_total),
- amount = t.amountRaw,
+ amount = t.amountRaw.withSpec(spec),
amountType = AmountType.Neutral,
)
val fee = t.amountRaw - t.amountEffective
if (!fee.isZero()) {
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(spec),
amountType = AmountType.Negative,
)
}
@@ -116,6 +118,6 @@ fun TransactionRefundComposablePreview() {
error = TalerErrorInfo(code =
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED),
)
TalerSurface {
- TransactionRefundComposable(t = t, devMode = true) {}
+ TransactionRefundComposable(t = t, devMode = true, spec = null) {}
}
}
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 c9ae889..22dcc3f 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt
@@ -32,6 +32,7 @@ import androidx.recyclerview.selection.SelectionTracker
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.Adapter
import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import net.taler.common.CurrencySpecification
import net.taler.common.exhaustive
import net.taler.common.toRelativeTime
import net.taler.wallet.R
@@ -47,6 +48,8 @@ internal class TransactionAdapter(
) : Adapter<TransactionViewHolder>() {
private var transactions: List<Transaction> = ArrayList()
+ private var currencySpec: CurrencySpecification? = null
+
lateinit var tracker: SelectionTracker<String>
val keyProvider = TransactionKeyProvider()
@@ -67,6 +70,11 @@ internal class TransactionAdapter(
holder.bind(transaction, tracker.isSelected(transaction.transactionId))
}
+ fun setCurrencySpec(spec: CurrencySpecification?) {
+ this.currencySpec = spec
+ this.notifyDataSetChanged()
+ }
+
fun update(updatedTransactions: List<Transaction>) {
this.transactions = updatedTransactions
this.notifyDataSetChanged()
@@ -183,7 +191,7 @@ internal class TransactionAdapter(
}
private fun bindAmount(transaction: Transaction) {
- val amountStr = transaction.amountEffective.amountStr
+ val amountStr =
transaction.amountEffective.withSpec(currencySpec).toString(showSymbol = false)
when (transaction.amountType) {
AmountType.Positive -> {
amount.text = context.getString(R.string.amount_positive,
amountStr)
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDepositFragment.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDepositFragment.kt
index f23b8d7..d2be3cf 100644
---
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDepositFragment.kt
+++
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDepositFragment.kt
@@ -38,6 +38,7 @@ class TransactionDepositFragment :
TransactionDetailFragment() {
if (t is TransactionDeposit) TransactionDepositComposable(
t = t,
devMode = devMode,
+ spec =
balanceManager.getSpecForCurrency(t.amountRaw.currency),
) {
onTransitionButtonClicked(t, it)
}
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
index 78b728a..09ca05b 100644
---
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
+++
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionDetailFragment.kt
@@ -39,6 +39,7 @@ abstract class TransactionDetailFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
protected val transactionManager by lazy { model.transactionManager }
+ protected val balanceManager by lazy { model.balanceManager }
protected val devMode get() = model.devMode.value == true
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
index 534ed6c..5399287 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionManager.kt
@@ -23,11 +23,15 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.switchMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
import net.taler.wallet.TAG
import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.backend.WalletBackendApi
+import net.taler.wallet.balances.ScopeInfo
import net.taler.wallet.transactions.TransactionAction.Delete
import net.taler.wallet.transactions.TransactionMajorState.Pending
+import org.json.JSONObject
import java.util.LinkedList
sealed class TransactionsResult {
@@ -45,34 +49,34 @@ class TransactionManager(
// FIXME if the app gets killed, this will not be restored and thus be
unexpected null
// we should keep this in a savable, maybe using Hilt and
SavedStateViewModel
- var selectedCurrency: String? = null
+ var selectedScope: ScopeInfo? = null
val searchQuery = MutableLiveData<String>(null)
private val mSelectedTransaction = MutableLiveData<Transaction?>(null)
val selectedTransaction: LiveData<Transaction?> = mSelectedTransaction
- private val allTransactions = HashMap<String, List<Transaction>>()
- private val mTransactions = HashMap<String,
MutableLiveData<TransactionsResult>>()
+ private val allTransactions = HashMap<ScopeInfo, List<Transaction>>()
+ private val mTransactions = HashMap<ScopeInfo,
MutableLiveData<TransactionsResult>>()
val transactions: LiveData<TransactionsResult>
@UiThread
get() = searchQuery.switchMap { query ->
- val currency = selectedCurrency
- check(currency != null) { "Did not select currency before getting
transactions" }
+ val scopeInfo = selectedScope
+ check(scopeInfo != null) { "Did not select scope before getting
transactions" }
loadTransactions(query)
- mTransactions[currency]!! // non-null because filled in
[loadTransactions]
+ mTransactions[scopeInfo]!! // non-null because filled in
[loadTransactions]
}
@UiThread
fun loadTransactions(searchQuery: String? = null) = scope.launch {
- val currency = selectedCurrency ?: return@launch
- val liveData = mTransactions.getOrPut(currency) { MutableLiveData() }
- if (searchQuery == null && allTransactions.containsKey(currency)) {
- liveData.value =
TransactionsResult.Success(allTransactions[currency]!!)
+ val scopeInfo = selectedScope ?: return@launch
+ val liveData = mTransactions.getOrPut(scopeInfo) { MutableLiveData() }
+ if (searchQuery == null && allTransactions.containsKey(scopeInfo)) {
+ liveData.value =
TransactionsResult.Success(allTransactions[scopeInfo]!!)
}
if (liveData.value == null) mProgress.value = true
api.request("getTransactions", Transactions.serializer()) {
if (searchQuery != null) put("search", searchQuery)
- put("currency", currency)
+ put("scopeInfo", JSONObject(Json.encodeToString(scopeInfo)))
}.onError {
liveData.postValue(TransactionsResult.Error(it))
mProgress.postValue(false)
@@ -91,8 +95,8 @@ class TransactionManager(
mSelectedTransaction.value = it
}
- // update all transactions on UiThread if there was a currency
- if (searchQuery == null) allTransactions[currency] = transactions
+ // update all transactions on UiThread if there was a scope info
+ if (searchQuery == null) allTransactions[scopeInfo] = transactions
}
}
@@ -201,7 +205,7 @@ class TransactionManager(
}
fun deleteTransactions(transactionIds: List<String>, onError: (it:
TalerErrorInfo) -> Unit) {
- allTransactions[selectedCurrency]?.filter { transaction ->
+ allTransactions[selectedScope]?.filter { transaction ->
transaction.transactionId in transactionIds
}?.forEach { toBeDeletedTx ->
if (Delete in toBeDeletedTx.txActions) {
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionPaymentFragment.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPaymentFragment.kt
index 27598ab..596a4a9 100644
---
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionPaymentFragment.kt
+++
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPaymentFragment.kt
@@ -37,6 +37,7 @@ class TransactionPaymentFragment :
TransactionDetailFragment() {
TalerSurface {
val t =
transactionManager.selectedTransaction.observeAsState().value
if (t is TransactionPayment) TransactionPaymentComposable(t,
devMode,
+ balanceManager.getSpecForCurrency(t.amountRaw.currency),
onFulfill = { url ->
launchInAppBrowser(requireContext(), url)
},
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt
index c934d89..27809a7 100644
---
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt
+++
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt
@@ -38,6 +38,7 @@ import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.common.toAbsoluteTime
import net.taler.wallet.R
import net.taler.wallet.compose.TalerSurface
@@ -56,7 +57,9 @@ class TransactionPeerFragment : TransactionDetailFragment() {
setContent {
TalerSurface {
val t =
transactionManager.selectedTransaction.observeAsState(null).value
- if (t != null) TransactionPeerComposable(t, devMode) {
+ if (t != null) TransactionPeerComposable(t, devMode,
+ balanceManager.getSpecForCurrency(t.amountRaw.currency),
+ ) {
onTransitionButtonClicked(t, it)
}
}
@@ -68,6 +71,7 @@ class TransactionPeerFragment : TransactionDetailFragment() {
fun TransactionPeerComposable(
t: Transaction,
devMode: Boolean,
+ spec: CurrencySpecification?,
onTransition: (t: TransactionAction) -> Unit,
) {
val scrollState = rememberScrollState()
@@ -84,10 +88,10 @@ fun TransactionPeerComposable(
style = MaterialTheme.typography.bodyLarge,
)
when (t) {
- is TransactionPeerPullCredit ->
TransactionPeerPullCreditComposable(t)
- is TransactionPeerPushCredit ->
TransactionPeerPushCreditComposable(t)
- is TransactionPeerPullDebit ->
TransactionPeerPullDebitComposable(t)
- is TransactionPeerPushDebit ->
TransactionPeerPushDebitComposable(t)
+ is TransactionPeerPullCredit ->
TransactionPeerPullCreditComposable(t, spec)
+ is TransactionPeerPushCredit ->
TransactionPeerPushCreditComposable(t, spec)
+ is TransactionPeerPullDebit ->
TransactionPeerPullDebitComposable(t, spec)
+ is TransactionPeerPushDebit ->
TransactionPeerPushDebitComposable(t, spec)
else -> error("unexpected transaction: ${t::class.simpleName}")
}
TransitionsComposable(t, devMode, onTransition)
@@ -106,7 +110,7 @@ fun TransactionAmountComposable(label: String, amount:
Amount, amountType: Amoun
)
Text(
modifier = Modifier.padding(top = 8.dp, start = 16.dp, end = 16.dp,
bottom = 16.dp),
- text = if (amountType == AmountType.Negative) "-$amount" else
amount.toString(),
+ text = amount.toString(negative = amountType == AmountType.Negative),
fontSize = 24.sp,
color = when (amountType) {
AmountType.Positive -> colorResource(R.color.green)
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefreshFragment.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefreshFragment.kt
index da4b14d..8f474f9 100644
---
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefreshFragment.kt
+++
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefreshFragment.kt
@@ -38,6 +38,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import net.taler.common.Amount
+import net.taler.common.CurrencySpecification
import net.taler.common.Timestamp
import net.taler.common.toAbsoluteTime
import net.taler.wallet.R
@@ -59,7 +60,9 @@ class TransactionRefreshFragment :
TransactionDetailFragment() {
setContent {
TalerSurface {
val t =
transactionManager.selectedTransaction.observeAsState().value
- if (t is TransactionRefresh) TransactionRefreshComposable(t,
devMode) {
+ if (t is TransactionRefresh) TransactionRefreshComposable(t,
devMode,
+ balanceManager.getSpecForCurrency(t.amountRaw.currency),
+ ) {
onTransitionButtonClicked(t, it)
}
}
@@ -71,6 +74,7 @@ class TransactionRefreshFragment :
TransactionDetailFragment() {
private fun TransactionRefreshComposable(
t: TransactionRefresh,
devMode: Boolean,
+ spec: CurrencySpecification?,
onTransition: (t: TransactionAction) -> Unit,
) {
val scrollState = rememberScrollState()
@@ -88,7 +92,7 @@ private fun TransactionRefreshComposable(
)
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = t.amountEffective,
+ amount = t.amountEffective.withSpec(spec),
amountType = AmountType.Negative,
)
TransitionsComposable(t, devMode, onTransition)
@@ -111,6 +115,6 @@ private fun TransactionRefreshComposablePreview() {
error = TalerErrorInfo(code =
TalerErrorCode.WALLET_WITHDRAWAL_KYC_REQUIRED),
)
Surface {
- TransactionRefreshComposable(t, true) {}
+ TransactionRefreshComposable(t, true, null) {}
}
}
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefundFragment.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefundFragment.kt
index cd50be7..7992565 100644
---
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefundFragment.kt
+++
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionRefundFragment.kt
@@ -35,7 +35,9 @@ class TransactionRefundFragment : TransactionDetailFragment()
{
setContent {
TalerSurface {
val t =
transactionManager.selectedTransaction.observeAsState().value
- if (t is TransactionRefund) TransactionRefundComposable(t,
devMode) {
+ if (t is TransactionRefund) TransactionRefundComposable(t,
devMode,
+ balanceManager.getSpecForCurrency(t.amountRaw.currency)
+ ) {
onTransitionButtonClicked(t, it)
}
}
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt
index 969db13..27e59bb 100644
---
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt
+++
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt
@@ -49,6 +49,7 @@ class TransactionWithdrawalFragment :
TransactionDetailFragment(), ActionListene
if (t is TransactionWithdrawal)
TransactionWithdrawalComposable(
t = t,
devMode = devMode,
+ spec =
balanceManager.getSpecForCurrency(t.amountRaw.currency),
actionListener = this@TransactionWithdrawalFragment,
) {
onTransitionButtonClicked(t, it)
diff --git a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
index 9017854..be36a13 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -41,7 +41,7 @@ import net.taler.wallet.R
import net.taler.wallet.TAG
import net.taler.wallet.backend.TalerErrorCode
import net.taler.wallet.backend.TalerErrorInfo
-import net.taler.wallet.balances.CurrencySpecification
+import net.taler.common.CurrencySpecification
import net.taler.wallet.cleanExchange
import net.taler.wallet.refund.RefundPaymentInfo
import net.taler.wallet.transactions.TransactionMajorState.None
diff --git
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
index 3f610d3..5243427 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionsFragment.kt
@@ -37,7 +37,6 @@ import androidx.recyclerview.selection.StorageStrategy
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager.VERTICAL
import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import net.taler.common.Amount
import net.taler.common.fadeIn
import net.taler.common.fadeOut
import net.taler.common.showError
@@ -56,10 +55,11 @@ class TransactionsFragment : Fragment(),
OnTransactionClickListener, ActionMode.
private val model: MainViewModel by activityViewModels()
private val transactionManager by lazy { model.transactionManager }
+ private val balanceManager by lazy { model.balanceManager }
private lateinit var ui: FragmentTransactionsBinding
private val transactionAdapter by lazy { TransactionAdapter(this) }
- private val currency by lazy { transactionManager.selectedCurrency!! }
+ private val scopeInfo by lazy { transactionManager.selectedScope!! }
private var tracker: SelectionTracker<String>? = null
private var actionMode: ActionMode? = null
@@ -108,13 +108,15 @@ class TransactionsFragment : Fragment(),
OnTransactionClickListener, ActionMode.
}
})
- model.balanceManager.state.observe(viewLifecycleOwner) { state ->
+ balanceManager.state.observe(viewLifecycleOwner) { state ->
if (state !is Success) return@observe
val balances = state.balances
// hide extra fab when in single currency mode (uses
MainFragment's FAB)
if (balances.size == 1) ui.mainFab.visibility = INVISIBLE
- balances.find { it.currency == currency }?.available?.let {
amount: Amount ->
- ui.amount.text = amount.amountStr
+
+ balances.find { it.scopeInfo == scopeInfo }?.let { balance ->
+ ui.amount.text = balance.available.toString(showSymbol = false)
+ transactionAdapter.setCurrencySpec(balance.available.spec)
}
}
transactionManager.progress.observe(viewLifecycleOwner) { show ->
@@ -151,7 +153,7 @@ class TransactionsFragment : Fragment(),
OnTransactionClickListener, ActionMode.
override fun onStart() {
super.onStart()
- requireActivity().title =
getString(R.string.transactions_detail_title_currency, currency)
+ requireActivity().title =
getString(R.string.transactions_detail_title_currency, scopeInfo.currency)
}
private fun setupSearch(item: MenuItem) {
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 fda1815..5155b5b 100644
---
a/wallet/src/main/java/net/taler/wallet/withdraw/TransactionWithdrawalComposable.kt
+++
b/wallet/src/main/java/net/taler/wallet/withdraw/TransactionWithdrawalComposable.kt
@@ -38,7 +38,7 @@ import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorCode
import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.cleanExchange
-import net.taler.wallet.balances.CurrencySpecification
+import net.taler.common.CurrencySpecification
import net.taler.wallet.transactions.ActionButton
import net.taler.wallet.transactions.ActionListener
import net.taler.wallet.transactions.AmountType
@@ -61,6 +61,7 @@ import
net.taler.wallet.transactions.WithdrawalExchangeAccountDetails
fun TransactionWithdrawalComposable(
t: TransactionWithdrawal,
devMode: Boolean,
+ spec: CurrencySpecification?,
actionListener: ActionListener,
onTransition: (t: TransactionAction) -> Unit,
) {
@@ -82,7 +83,7 @@ fun TransactionWithdrawalComposable(
TransactionAmountComposable(
label = stringResource(R.string.amount_chosen),
- amount = t.amountRaw,
+ amount = t.amountRaw.withSpec(spec),
amountType = AmountType.Neutral,
)
@@ -90,14 +91,14 @@ fun TransactionWithdrawalComposable(
if (!fee.isZero()) {
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_fees),
- amount = fee,
+ amount = fee.withSpec(spec),
amountType = AmountType.Negative,
)
}
TransactionAmountComposable(
label = stringResource(id = R.string.withdraw_total),
- amount = t.amountEffective,
+ amount = t.amountEffective.withSpec(spec),
amountType = AmountType.Positive,
)
@@ -133,7 +134,7 @@ fun TransactionWithdrawalComposablePreview() {
numFractionalInputDigits = 2,
numFractionalNormalDigits = 2,
numFractionalTrailingZeroDigits = 2,
- altUnitNames = mapOf("0" to "NETZBON"),
+ altUnitNames = mapOf(0 to "NETZBON"),
),
),
),
@@ -148,6 +149,6 @@ fun TransactionWithdrawalComposablePreview() {
}
Surface {
- TransactionWithdrawalComposable(t, true, listener) {}
+ TransactionWithdrawalComposable(t, true, null, listener) {}
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
index 4c9479b..e308b2a 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/WithdrawManager.kt
@@ -31,6 +31,7 @@ import net.taler.common.toEvent
import net.taler.wallet.TAG
import net.taler.wallet.backend.TalerErrorInfo
import net.taler.wallet.backend.WalletBackendApi
+import net.taler.wallet.balances.ScopeInfo
import net.taler.wallet.exchanges.ExchangeFees
import net.taler.wallet.exchanges.ExchangeItem
import net.taler.wallet.transactions.WithdrawalExchangeAccountDetails
@@ -145,6 +146,7 @@ data class ManualWithdrawalDetails(
val amountEffective: Amount,
val withdrawalAccountsList: List<WithdrawalExchangeAccountDetails>,
val ageRestrictionOptions: List<Int>? = null,
+ val scopeInfo: ScopeInfo,
)
@Serializable
diff --git
a/wallet/src/main/java/net/taler/wallet/withdraw/manual/ManualWithdrawSuccessFragment.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/manual/ManualWithdrawSuccessFragment.kt
index f09d275..63413c2 100644
---
a/wallet/src/main/java/net/taler/wallet/withdraw/manual/ManualWithdrawSuccessFragment.kt
+++
b/wallet/src/main/java/net/taler/wallet/withdraw/manual/ManualWithdrawSuccessFragment.kt
@@ -36,6 +36,7 @@ import net.taler.wallet.withdraw.WithdrawStatus
class ManualWithdrawSuccessFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private val withdrawManager by lazy { model.withdrawManager }
+ private val balanceManager by lazy { model.balanceManager }
private lateinit var status: WithdrawStatus.ManualTransferRequired
@@ -66,6 +67,7 @@ class ManualWithdrawSuccessFragment : Fragment() {
TalerSurface {
ScreenTransfer(
status = status,
+ spec =
balanceManager.getSpecForCurrency(status.transactionAmountRaw.currency),
bankAppClick = { onBankAppClick(it) },
shareClick = { onShareClick(it) },
)
diff --git
a/wallet/src/main/java/net/taler/wallet/withdraw/manual/ScreenTransfer.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/manual/ScreenTransfer.kt
index d75c685..35ff89c 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/manual/ScreenTransfer.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/manual/ScreenTransfer.kt
@@ -47,8 +47,8 @@ import androidx.compose.ui.unit.dp
import net.taler.common.Amount
import net.taler.wallet.CURRENCY_BTC
import net.taler.wallet.R
+import net.taler.common.CurrencySpecification
import net.taler.wallet.compose.ShareButton
-import net.taler.wallet.balances.CurrencySpecification
import net.taler.wallet.compose.copyToClipBoard
import net.taler.wallet.transactions.AmountType
import net.taler.wallet.transactions.TransactionAmountComposable
@@ -59,6 +59,7 @@ import net.taler.wallet.withdraw.WithdrawStatus
@Composable
fun ScreenTransfer(
status: WithdrawStatus.ManualTransferRequired,
+ spec: CurrencySpecification?,
bankAppClick: ((transfer: TransferData) -> Unit)?,
shareClick: ((transfer: TransferData) -> Unit)?,
) {
@@ -98,14 +99,14 @@ fun ScreenTransfer(
is TransferData.IBAN -> TransferIBAN(
transfer = transfer,
exchangeBaseUrl = status.exchangeBaseUrl,
- transactionAmountRaw = status.transactionAmountRaw,
- transactionAmountEffective =
status.transactionAmountEffective,
+ transactionAmountRaw =
status.transactionAmountRaw.withSpec(spec),
+ transactionAmountEffective =
status.transactionAmountEffective.withSpec(spec),
)
is TransferData.Bitcoin -> TransferBitcoin(
transfer = transfer,
- transactionAmountRaw = status.transactionAmountRaw,
- transactionAmountEffective =
status.transactionAmountEffective,
+ transactionAmountRaw =
status.transactionAmountRaw.withSpec(spec),
+ transactionAmountEffective =
status.transactionAmountEffective.withSpec(spec),
)
}
@@ -305,6 +306,7 @@ fun ScreenTransferPreview() {
)
),
),
+ spec = null,
bankAppClick = {},
shareClick = {},
)
diff --git
a/wallet/src/main/java/net/taler/wallet/withdraw/manual/TransferBitcoin.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/manual/TransferBitcoin.kt
index 292f1d5..c21ca7e 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/manual/TransferBitcoin.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/manual/TransferBitcoin.kt
@@ -60,7 +60,9 @@ fun TransferBitcoin(
WithdrawalAmountTransfer(
amountRaw = transactionAmountRaw,
amountEffective = transactionAmountEffective,
- conversionAmountRaw = amount,
+ conversionAmountRaw = amount.withSpec(
+ transfer.withdrawalAccount.currencySpecification,
+ ),
)
}
}
diff --git
a/wallet/src/main/java/net/taler/wallet/withdraw/manual/TransferIBAN.kt
b/wallet/src/main/java/net/taler/wallet/withdraw/manual/TransferIBAN.kt
index 1ebee4f..6c1b014 100644
--- a/wallet/src/main/java/net/taler/wallet/withdraw/manual/TransferIBAN.kt
+++ b/wallet/src/main/java/net/taler/wallet/withdraw/manual/TransferIBAN.kt
@@ -82,7 +82,9 @@ fun TransferIBAN(
WithdrawalAmountTransfer(
amountRaw = transactionAmountRaw,
amountEffective = transactionAmountEffective,
- conversionAmountRaw = amount,
+ conversionAmountRaw = amount.withSpec(
+ transfer.withdrawalAccount.currencySpecification,
+ ),
)
}
}
diff --git a/wallet/src/main/res/layout/list_item_balance.xml
b/wallet/src/main/res/layout/list_item_balance.xml
index 82e663f..53e3d89 100644
--- a/wallet/src/main/res/layout/list_item_balance.xml
+++ b/wallet/src/main/res/layout/list_item_balance.xml
@@ -27,32 +27,20 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
- android:textSize="40sp"
- app:layout_constraintEnd_toStartOf="@+id/balanceCurrencyView"
+ style="?textAppearanceDisplaySmall"
+ app:layout_constraintEnd_toStartOf="@+id/pendingView"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="100.50" />
- <TextView
- android:id="@+id/balanceCurrencyView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:textSize="20sp"
- app:layout_constrainedWidth="true"
- app:layout_constraintBottom_toBottomOf="@+id/balanceAmountView"
- app:layout_constraintEnd_toStartOf="@+id/pendingView"
- app:layout_constraintStart_toEndOf="@+id/balanceAmountView"
- app:layout_constraintTop_toTopOf="@+id/balanceAmountView"
- tools:text="TESTKUDOS" />
-
<TextView
android:id="@+id/scopeView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
+ style="?textAppearanceBodyMedium"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/balanceAmountView"
app:layout_constraintBottom_toTopOf="@id/balanceInboundAmount"
@@ -67,6 +55,7 @@
android:layout_height="wrap_content"
android:textColor="@color/green"
android:textSize="20sp"
+ style="?textAppearanceBodyLarge"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/balanceInboundLabel"
app:layout_constraintHorizontal_bias="0.0"
@@ -83,6 +72,7 @@
android:layout_marginStart="8dp"
android:text="@string/balances_inbound_label"
android:textColor="@color/green"
+ style="?textAppearanceBodyMedium"
app:layout_constraintBottom_toBottomOf="@+id/balanceInboundAmount"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/balanceInboundAmount"
diff --git a/wallet/src/main/res/values/strings.xml
b/wallet/src/main/res/values/strings.xml
index 82a67b6..2ec3d40 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -87,7 +87,9 @@ GNU Taler is immune against many types of fraud, such as
phishing of credit card
<string name="transactions_title">Transactions</string>
<string name="transactions_balance">Balance</string>
<string name="transactions_send_funds">Send\nFunds</string>
+ <string name="transactions_send_funds_title">Send %1$s</string>
<string name="transactions_receive_funds">Receive\nFunds</string>
+ <string name="transactions_receive_funds_title">Receive %1$s</string>
<string name="transactions_empty">You don\'t have any transactions</string>
<string name="transactions_empty_search">No transactions found. Try a
different search.</string>
<string name="transactions_error">Could not load
transactions\n\n%s</string>
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [taler-taler-android] branch master updated (39b6926 -> 4af27e7),
gnunet <=
- [taler-taler-android] 04/15: [wallet] Balance layout improvements, gnunet, 2024/03/27
- [taler-taler-android] 05/15: [wallet] Display transactions by scopeInfo instead of currency, gnunet, 2024/03/27
- [taler-taler-android] 02/15: [wallet] DD51: enrich some tx amounts with currencySpecification, gnunet, 2024/03/27
- [taler-taler-android] 14/15: [wallet] Use Backend.json instead of Json, gnunet, 2024/03/27
- [taler-taler-android] 07/15: [wallet] Set input digits of some amount inputs, gnunet, 2024/03/27
- [taler-taler-android] 01/15: [wallet] DD51: initial rendering based on currency spec, gnunet, 2024/03/27
- [taler-taler-android] 03/15: [wallet] DD51: enrich manual withdrawal amounts with currencySpecification, gnunet, 2024/03/27
- [taler-taler-android] 15/15: [wallet] Use TransactionManager.selectedScope instead of passing around scopeInfo, gnunet, 2024/03/27
- [taler-taler-android] 08/15: [wallet] Cache currencySpec in loadBalances(), gnunet, 2024/03/27
- [taler-taler-android] 10/15: [taler-android] Improve handling of currencies with no symbol, gnunet, 2024/03/27