taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

commit de360e89efc45a4e0686cd8fc83173ccab134a4d
parent 957162b26597a6d1224f53b1755e1111e051a188
Author: Iván Ávalos <avalos@disroot.org>
Date:   Tue,  3 Feb 2026 19:59:24 +0100

[wallet] show QR codes on withdrawal details

Diffstat:
Mwallet/src/main/java/net/taler/wallet/compose/QrCodeUriComposable.kt | 1+
Mwallet/src/main/java/net/taler/wallet/transactions/ActionButtonComposable.kt | 29++++++++++++++++++-----------
Mwallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt | 18++++++++++++++++++
Mwallet/src/main/java/net/taler/wallet/withdraw/TransactionWithdrawalComposable.kt | 52+++++++++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 88 insertions(+), 12 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/compose/QrCodeUriComposable.kt b/wallet/src/main/java/net/taler/wallet/compose/QrCodeUriComposable.kt @@ -125,6 +125,7 @@ fun ColumnScope.QrCodeUriComposable( } ShareButton( + modifier = if (shareAsQrCode) Modifier.weight(1f) else Modifier, content = talerUri, shareAsQrCode = shareAsQrCode, colors = ButtonDefaults.buttonColors( diff --git a/wallet/src/main/java/net/taler/wallet/transactions/ActionButtonComposable.kt b/wallet/src/main/java/net/taler/wallet/transactions/ActionButtonComposable.kt @@ -135,17 +135,24 @@ private fun ConfirmManualButton( Text(stringResource(R.string.withdraw_manual_ready_details_intro)) } - Button( - onClick = { listener.onActionButtonClicked(tx, ActionListener.Type.SHOW_WIRE_QR) }, - modifier = modifier, - ) { - Icon( - Icons.Default.QrCode, - contentDescription = null, - modifier = Modifier.size(ButtonDefaults.IconSize) - ) - Spacer(Modifier.size(ButtonDefaults.IconSpacing)) - Text(stringResource(R.string.withdraw_manual_ready_details_qr)) + // only show in separate view when there's more than one transfer account + // otherwise it will be shown on withdrawal details directly + if (tx is TransactionWithdrawal + && tx.withdrawalDetails is WithdrawalDetails.ManualTransfer + && tx.withdrawalDetails.exchangeCreditAccountDetails != null + && tx.withdrawalDetails.exchangeCreditAccountDetails.size > 1) { + Button( + onClick = { listener.onActionButtonClicked(tx, ActionListener.Type.SHOW_WIRE_QR) }, + modifier = modifier, + ) { + Icon( + Icons.Default.QrCode, + contentDescription = null, + modifier = Modifier.size(ButtonDefaults.IconSize) + ) + Spacer(Modifier.size(ButtonDefaults.IconSpacing)) + Text(stringResource(R.string.withdraw_manual_ready_details_qr)) + } } } } diff --git a/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt b/wallet/src/main/java/net/taler/wallet/transactions/TransactionWithdrawalFragment.kt @@ -20,10 +20,15 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.platform.ComposeView import net.taler.wallet.compose.TalerSurface import net.taler.wallet.compose.collectAsStateLifecycleAware +import net.taler.wallet.withdraw.QrCodeSpec import net.taler.wallet.withdraw.TransactionWithdrawalComposable class TransactionWithdrawalFragment : TransactionDetailFragment(), ActionListener { @@ -36,9 +41,22 @@ class TransactionWithdrawalFragment : TransactionDetailFragment(), ActionListene setContent { TalerSurface { val t by transactionManager.selectedTransaction.collectAsStateLifecycleAware() + var qrCodes by remember { mutableStateOf<List<QrCodeSpec>>(emptyList()) } + (t as? TransactionWithdrawal)?.let { tx -> + LaunchedEffect(Unit) { + if (tx.withdrawalDetails is WithdrawalDetails.ManualTransfer + && tx.txState.minor == TransactionMinorState.ExchangeWaitReserve) { + if (tx.withdrawalDetails.exchangeCreditAccountDetails?.size == 1) { + val transfer = tx.withdrawalDetails.exchangeCreditAccountDetails[0] + qrCodes = withdrawManager.getQrCodesForPayto(transfer.paytoUri) + } + } + } + TransactionWithdrawalComposable( t = tx, + qrCodes = qrCodes, devMode = devMode, spec = exchangeManager.getSpecForCurrency(tx.amountRaw.currency, tx.scopes), actionListener = this@TransactionWithdrawalFragment, diff --git a/wallet/src/main/java/net/taler/wallet/withdraw/TransactionWithdrawalComposable.kt b/wallet/src/main/java/net/taler/wallet/withdraw/TransactionWithdrawalComposable.kt @@ -25,6 +25,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateMapOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -60,10 +62,12 @@ import net.taler.wallet.transactions.TransactionWithdrawal import net.taler.wallet.transactions.TransitionsComposable import net.taler.wallet.transactions.WithdrawalDetails.ManualTransfer import net.taler.wallet.transactions.WithdrawalExchangeAccountDetails +import net.taler.wallet.transfer.PaytoQrCard @Composable fun TransactionWithdrawalComposable( t: TransactionWithdrawal, + qrCodes: List<QrCodeSpec> = emptyList(), devMode: Boolean, spec: CurrencySpecification?, actionListener: ActionListener, @@ -77,6 +81,13 @@ fun TransactionWithdrawalComposable( horizontalAlignment = Alignment.CenterHorizontally, ) { val context = LocalContext.current + val qrExpandedStates = remember(qrCodes) { + val map = mutableStateMapOf<QrCodeSpec, Boolean>() + qrCodes.forEach { + map[it] = qrCodes.size == 1 + } + map + } TransactionStateComposable(state = t.txState, tx = t) @@ -88,6 +99,34 @@ fun TransactionWithdrawalComposable( ActionButton(tx = t, listener = actionListener) + if (qrCodes.isNotEmpty()) { + Text( + text = stringResource(R.string.withdraw_manual_qr_intro), + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier + .padding( + vertical = 8.dp, + horizontal = 16.dp, + ) + ) + + qrCodes.forEach { spec -> + PaytoQrCard( + expanded = qrExpandedStates[spec]!!, + setExpanded = { expanded -> + if (expanded) { // un-expand all others + qrExpandedStates.forEach { (k, _) -> + qrExpandedStates[k] = false + } + } + // expand only toggled one + qrExpandedStates[spec] = expanded + }, + qrCode = spec, + ) + } + } + if (t.amountRaw != t.amountEffective) { TransactionAmountComposable( label = stringResource(R.string.amount_chosen), @@ -162,12 +201,23 @@ fun TransactionWithdrawalComposablePreview() { url = "exchange.test.taler.net", )) ) + + val qrCodes = listOf( + QrCodeSpec( + type = QrCodeSpec.Type.SPC, + qrContent = "something", + ), +// QrCodeSpec( +// type = QrCodeSpec.Type.EpcQr, +// qrContent = "something", +// ), + ) val listener = object : ActionListener { override fun onActionButtonClicked(tx: Transaction, type: ActionListener.Type) {} } Surface { - TransactionWithdrawalComposable(t, true, null, listener) {} + TransactionWithdrawalComposable(t, qrCodes, true, null, listener) {} } }