diff options
author | Iván Ávalos <avalos@disroot.org> | 2024-02-12 16:43:50 -0600 |
---|---|---|
committer | Torsten Grote <t@grobox.de> | 2024-03-27 14:26:40 -0300 |
commit | 1e9ee99cb07c595132fa96935e3f2b7c88dd586a (patch) | |
tree | 613ed3380a5a0cb493feb910576365f210028d3c | |
parent | 39b6926dd32b0731d87ea8daa94c1d4c29d3d193 (diff) | |
download | taler-android-1e9ee99cb07c595132fa96935e3f2b7c88dd586a.tar.gz taler-android-1e9ee99cb07c595132fa96935e3f2b7c88dd586a.tar.bz2 taler-android-1e9ee99cb07c595132fa96935e3f2b7c88dd586a.zip |
[wallet] DD51: initial rendering based on currency spec
bug 0008329
(cherry picked from commit 4789103406f44ba797c7bfee3112cc6aaa228683)
14 files changed, 105 insertions, 35 deletions
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..d6188cf 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,14 @@ 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.NumberFormat import kotlin.math.floor import kotlin.math.pow import kotlin.math.roundToInt @@ -54,6 +57,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 +178,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 +206,40 @@ 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, + ): String { + val symbols = DecimalFormat().decimalFormatSymbols + + val format = if (showSymbol) { + NumberFormat.getCurrencyInstance() + } else { + // Make sure monetary separators are the ones used! + symbols.decimalSeparator = symbols.monetaryDecimalSeparator + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + symbols.groupingSeparator = symbols.monetaryGroupingSeparator + } + + NumberFormat.getInstance() + } + + if (spec != null) { + symbols.currencySymbol = spec.symbol(this) + format.maximumFractionDigits = spec.numFractionalNormalDigits + format.minimumFractionDigits = spec.numFractionalTrailingZeroDigits + } else { + symbols.currencySymbol = currency + } + + (format as DecimalFormat).decimalFormatSymbols = symbols + + return format.format((if (negative) "-$amountStr" else amountStr).toBigDecimal()) } 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 index 5001db4..4aa8968 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 + fun symbol(amount: Amount): String = altUnitNames[0] ?: amount.currency +}
\ No newline at end of file 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..0b87dee 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) @@ -67,8 +66,7 @@ class BalanceAdapter(private val listener: BalanceClickListener) : Adapter<Balan fun bind(item: BalanceItem) { v.setOnClickListener { listener.onBalanceClick(item.available.currency) } - currencyView.text = item.currency - amountView.text = item.available.amountStr + 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) } 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..9b5beaf 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() @@ -67,11 +76,38 @@ class BalanceManager( } response.onSuccess { mState.postValue(BalanceState.Success(it.balances)) - mBalances.postValue(it.balances) + scope.launch { + // Get currency spec for all balances) + mState.postValue( + BalanceState.Success(it.balances.map { balance -> + val spec = getCurrencySpecification(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 + } + fun resetBalances() { mState.value = BalanceState.None } diff --git a/wallet/src/main/java/net/taler/wallet/balances/Balances.kt b/wallet/src/main/java/net/taler/wallet/balances/Balances.kt index dff2ffb..3e0acc9 100644 --- a/wallet/src/main/java/net/taler/wallet/balances/Balances.kt +++ b/wallet/src/main/java/net/taler/wallet/balances/Balances.kt @@ -19,6 +19,7 @@ package net.taler.wallet.balances import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import net.taler.common.Amount +import net.taler.common.CurrencySpecification @Serializable data class BalanceItem( 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/transactions/TransactionAdapter.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt index c9ae889..6fade86 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionAdapter.kt @@ -183,7 +183,7 @@ internal class TransactionAdapter( } private fun bindAmount(transaction: Transaction) { - val amountStr = transaction.amountEffective.amountStr + val amountStr = transaction.amountEffective.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/TransactionPeerFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt index c934d89..7feedd1 100644 --- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt +++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt @@ -106,7 +106,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/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..d89fbb5 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 @@ -113,8 +112,9 @@ class TransactionsFragment : Fragment(), OnTransactionClickListener, ActionMode. 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 + // TODO: find via scopeInfo instead of currency + balances.find { it.currency == currency }?.let { balance -> + ui.amount.text = balance.available.toString(showSymbol = false) } } transactionManager.progress.observe(viewLifecycleOwner) { show -> 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..7e8f94f 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 @@ -133,7 +133,7 @@ fun TransactionWithdrawalComposablePreview() { numFractionalInputDigits = 2, numFractionalNormalDigits = 2, numFractionalTrailingZeroDigits = 2, - altUnitNames = mapOf("0" to "NETZBON"), + altUnitNames = mapOf(0 to "NETZBON"), ), ), ), 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/ScreenTransfer.kt b/wallet/src/main/java/net/taler/wallet/withdraw/manual/ScreenTransfer.kt index d75c685..86972cf 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 diff --git a/wallet/src/main/res/layout/list_item_balance.xml b/wallet/src/main/res/layout/list_item_balance.xml index 82e663f..483b1ce 100644 --- a/wallet/src/main/res/layout/list_item_balance.xml +++ b/wallet/src/main/res/layout/list_item_balance.xml @@ -28,7 +28,7 @@ android:layout_height="wrap_content" android:layout_marginEnd="8dp" android:textSize="40sp" - app:layout_constraintEnd_toStartOf="@+id/balanceCurrencyView" + app:layout_constraintEnd_toStartOf="@+id/pendingView" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toStartOf="parent" @@ -36,19 +36,6 @@ 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" |