aboutsummaryrefslogtreecommitdiff
path: root/wallet/src
diff options
context:
space:
mode:
authorTorsten Grote <t@grobox.de>2022-09-07 16:28:04 -0300
committerTorsten Grote <t@grobox.de>2022-09-07 16:44:16 -0300
commite350d497abe560aeeef88081ae93d73135ece00f (patch)
tree0d13269331b16875e56fbafb8af8f9bdf3cfba77 /wallet/src
parent6876951cb30f057a852937a533d5d51d26645680 (diff)
downloadtaler-android-e350d497abe560aeeef88081ae93d73135ece00f.tar.gz
taler-android-e350d497abe560aeeef88081ae93d73135ece00f.tar.bz2
taler-android-e350d497abe560aeeef88081ae93d73135ece00f.zip
[wallet] implement prototype for handling incoming pay-pull URI
Diffstat (limited to 'wallet/src')
-rw-r--r--wallet/src/main/java/net/taler/wallet/MainActivity.kt4
-rw-r--r--wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt12
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/IncomingComposable.kt (renamed from wallet/src/main/java/net/taler/wallet/peer/PeerPullPaymentComposable.kt)65
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/IncomingPullPaymentFragment.kt (renamed from wallet/src/main/java/net/taler/wallet/peer/PullPaymentFragment.kt)10
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/IncomingPushPaymentFragment.kt68
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/IncomingState.kt (renamed from wallet/src/main/java/net/taler/wallet/peer/PeerIncomingState.kt)25
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt (renamed from wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt)10
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt (renamed from wallet/src/main/java/net/taler/wallet/peer/PeerPullIntroComposable.kt)4
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt (renamed from wallet/src/main/java/net/taler/wallet/peer/PeerPullResultComposable.kt)28
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt (renamed from wallet/src/main/java/net/taler/wallet/peer/PeerPushComposable.kt)4
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt (renamed from wallet/src/main/java/net/taler/wallet/peer/PeerPushResultComposable.kt)28
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt (renamed from wallet/src/main/java/net/taler/wallet/peer/PeerOutgoingState.kt)14
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt77
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushCredit.kt77
-rw-r--r--wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt3
-rw-r--r--wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt4
-rw-r--r--wallet/src/main/res/navigation/nav_graph.xml18
-rw-r--r--wallet/src/main/res/values/strings.xml5
18 files changed, 343 insertions, 113 deletions
diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
index 2797a69..df974ff 100644
--- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
@@ -257,6 +257,10 @@ class MainActivity : AppCompatActivity(), OnNavigationItemSelectedListener,
nav.navigate(R.id.action_global_prompt_pull_payment)
model.peerManager.checkPeerPullPayment(u)
}
+ action.startsWith("pay-push/") -> {
+ nav.navigate(R.id.action_global_prompt_push_payment)
+ model.peerManager.checkPeerPushPayment(u)
+ }
else -> {
showError(R.string.error_unsupported_uri, "From: $from\nURI: $u")
}
diff --git a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
index 27f2c96..290c91b 100644
--- a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
@@ -28,9 +28,9 @@ import androidx.navigation.findNavController
import com.google.android.material.composethemeadapter.MdcTheme
import net.taler.common.Amount
import net.taler.wallet.compose.collectAsStateLifecycleAware
-import net.taler.wallet.peer.PeerOutgoingIntro
-import net.taler.wallet.peer.PeerPushIntroComposable
-import net.taler.wallet.peer.PeerPushResultComposable
+import net.taler.wallet.peer.OutgoingIntro
+import net.taler.wallet.peer.OutgoingPushIntroComposable
+import net.taler.wallet.peer.OutgoingPushResultComposable
class SendFundsFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
@@ -45,12 +45,12 @@ class SendFundsFragment : Fragment() {
MdcTheme {
Surface {
val state = peerManager.pushState.collectAsStateLifecycleAware()
- if (state.value is PeerOutgoingIntro) {
+ if (state.value is OutgoingIntro) {
val currency = transactionManager.selectedCurrency
?: error("No currency selected")
- PeerPushIntroComposable(currency, this@SendFundsFragment::onSend)
+ OutgoingPushIntroComposable(currency, this@SendFundsFragment::onSend)
} else {
- PeerPushResultComposable(state.value) {
+ OutgoingPushResultComposable(state.value) {
findNavController().popBackStack()
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerPullPaymentComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/IncomingComposable.kt
index fff74ea..0095bc4 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPullPaymentComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/IncomingComposable.kt
@@ -17,6 +17,7 @@
package net.taler.wallet.peer
import android.annotation.SuppressLint
+import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
@@ -49,10 +50,26 @@ import net.taler.common.Amount
import net.taler.wallet.R
import net.taler.wallet.backend.TalerErrorInfo
+data class IncomingData(
+ @StringRes val intro: Int,
+ @StringRes val button: Int,
+)
+
+val incomingPush = IncomingData(
+ intro = R.string.receive_peer_payment_intro,
+ button = R.string.receive_peer_payment_title,
+)
+
+val incomingPull = IncomingData(
+ intro = R.string.pay_peer_intro,
+ button = R.string.payment_button_confirm,
+)
+
@Composable
-fun PeerPullPaymentComposable(
- state: State<PeerIncomingState>,
- onAccept: (PeerIncomingTerms) -> Unit,
+fun IncomingComposable(
+ state: State<IncomingState>,
+ data: IncomingData,
+ onAccept: (IncomingTerms) -> Unit,
) {
val scrollState = rememberScrollState()
Column(
@@ -64,15 +81,16 @@ fun PeerPullPaymentComposable(
modifier = Modifier
.padding(16.dp)
.align(CenterHorizontally),
- text = stringResource(id = R.string.pay_peer_intro))
+ text = stringResource(id = data.intro),
+ )
when (val s = state.value) {
- PeerIncomingChecking -> PeerPullCheckingComposable()
- is PeerIncomingTerms -> PeerPullTermsComposable(s, onAccept)
- is PeerIncomingAccepting -> PeerPullTermsComposable(s, onAccept)
- PeerIncomingAccepted -> {
+ IncomingChecking -> PeerPullCheckingComposable()
+ is IncomingTerms -> PeerPullTermsComposable(s, onAccept, data)
+ is IncomingAccepting -> PeerPullTermsComposable(s, onAccept, data)
+ IncomingAccepted -> {
// we navigate away, don't show anything
}
- is PeerIncomingError -> PeerPullErrorComposable(s)
+ is IncomingError -> PeerPullErrorComposable(s)
}
}
}
@@ -88,8 +106,9 @@ fun ColumnScope.PeerPullCheckingComposable() {
@Composable
fun ColumnScope.PeerPullTermsComposable(
- terms: PeerIncomingTerms,
- onAccept: (PeerIncomingTerms) -> Unit,
+ terms: IncomingTerms,
+ onAccept: (IncomingTerms) -> Unit,
+ data: IncomingData,
) {
Text(
modifier = Modifier
@@ -126,7 +145,7 @@ fun ColumnScope.PeerPullTermsComposable(
style = MaterialTheme.typography.body1,
)
}
- if (terms is PeerIncomingAccepting) {
+ if (terms is IncomingAccepting) {
CircularProgressIndicator(
modifier = Modifier
.padding(end = 64.dp)
@@ -144,7 +163,7 @@ fun ColumnScope.PeerPullTermsComposable(
onClick = { onAccept(terms) },
) {
Text(
- text = stringResource(id = R.string.payment_button_confirm),
+ text = stringResource(id = data.button),
)
}
}
@@ -153,7 +172,7 @@ fun ColumnScope.PeerPullTermsComposable(
}
@Composable
-fun ColumnScope.PeerPullErrorComposable(s: PeerIncomingError) {
+fun ColumnScope.PeerPullErrorComposable(s: IncomingError) {
Text(
modifier = Modifier
.align(CenterHorizontally)
@@ -169,8 +188,8 @@ fun ColumnScope.PeerPullErrorComposable(s: PeerIncomingError) {
fun PeerPullCheckingPreview() {
Surface {
@SuppressLint("UnrememberedMutableState")
- val s = mutableStateOf(PeerIncomingChecking)
- PeerPullPaymentComposable(s) {}
+ val s = mutableStateOf(IncomingChecking)
+ IncomingComposable(s, incomingPush) {}
}
}
@@ -178,7 +197,7 @@ fun PeerPullCheckingPreview() {
@Composable
fun PeerPullTermsPreview() {
Surface {
- val terms = PeerIncomingTerms(
+ val terms = IncomingTerms(
amount = Amount.fromDouble("TESTKUDOS", 42.23),
contractTerms = PeerContractTerms(
summary = "This is a long test summary that can be more than one line long for sure",
@@ -189,7 +208,7 @@ fun PeerPullTermsPreview() {
@SuppressLint("UnrememberedMutableState")
val s = mutableStateOf(terms)
- PeerPullPaymentComposable(s) {}
+ IncomingComposable(s, incomingPush) {}
}
}
@@ -197,7 +216,7 @@ fun PeerPullTermsPreview() {
@Composable
fun PeerPullAcceptingPreview() {
Surface {
- val terms = PeerIncomingTerms(
+ val terms = IncomingTerms(
amount = Amount.fromDouble("TESTKUDOS", 42.23),
contractTerms = PeerContractTerms(
summary = "This is a long test summary that can be more than one line long for sure",
@@ -207,8 +226,8 @@ fun PeerPullAcceptingPreview() {
)
@SuppressLint("UnrememberedMutableState")
- val s = mutableStateOf(PeerIncomingAccepting(terms))
- PeerPullPaymentComposable(s) {}
+ val s = mutableStateOf(IncomingAccepting(terms))
+ IncomingComposable(s, incomingPush) {}
}
}
@@ -217,7 +236,7 @@ fun PeerPullAcceptingPreview() {
fun PeerPullPayErrorPreview() {
Surface {
@SuppressLint("UnrememberedMutableState")
- val s = mutableStateOf(PeerIncomingError(TalerErrorInfo(42, "hint", "msg")))
- PeerPullPaymentComposable(s) {}
+ val s = mutableStateOf(IncomingError(TalerErrorInfo(42, "hint", "msg")))
+ IncomingComposable(s, incomingPush) {}
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PullPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/peer/IncomingPullPaymentFragment.kt
index 71b1bcc..cd2f39b 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PullPaymentFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/IncomingPullPaymentFragment.kt
@@ -31,7 +31,7 @@ import net.taler.wallet.MainViewModel
import net.taler.wallet.R
import net.taler.wallet.compose.collectAsStateLifecycleAware
-class PullPaymentFragment : Fragment() {
+class IncomingPullPaymentFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private val peerManager get() = model.peerManager
@@ -41,8 +41,8 @@ class PullPaymentFragment : Fragment() {
savedInstanceState: Bundle?,
): View {
lifecycleScope.launchWhenResumed {
- peerManager.paymentState.collect {
- if (it is PeerIncomingAccepted) {
+ peerManager.incomingPullState.collect {
+ if (it is IncomingAccepted) {
findNavController().navigate(R.id.action_promptPullPayment_to_nav_main)
}
}
@@ -51,8 +51,8 @@ class PullPaymentFragment : Fragment() {
setContent {
MdcTheme {
Surface {
- val state = peerManager.paymentState.collectAsStateLifecycleAware()
- PeerPullPaymentComposable(state) { terms ->
+ val state = peerManager.incomingPullState.collectAsStateLifecycleAware()
+ IncomingComposable(state, incomingPull) { terms ->
peerManager.acceptPeerPullPayment(terms)
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/IncomingPushPaymentFragment.kt b/wallet/src/main/java/net/taler/wallet/peer/IncomingPushPaymentFragment.kt
new file mode 100644
index 0000000..8429ecc
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/peer/IncomingPushPaymentFragment.kt
@@ -0,0 +1,68 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2022 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.peer
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.compose.material.Surface
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.activityViewModels
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.fragment.findNavController
+import com.google.android.material.composethemeadapter.MdcTheme
+import net.taler.wallet.MainViewModel
+import net.taler.wallet.R
+import net.taler.wallet.compose.collectAsStateLifecycleAware
+
+class IncomingPushPaymentFragment : Fragment() {
+ private val model: MainViewModel by activityViewModels()
+ private val peerManager get() = model.peerManager
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ): View {
+ lifecycleScope.launchWhenResumed {
+ peerManager.incomingPushState.collect {
+ if (it is IncomingAccepted) {
+ findNavController().navigate(R.id.action_promptPushPayment_to_nav_main)
+ }
+ }
+ }
+ return ComposeView(requireContext()).apply {
+ setContent {
+ MdcTheme {
+ Surface {
+ val state = peerManager.incomingPushState.collectAsStateLifecycleAware()
+ IncomingComposable(state, incomingPush) { terms ->
+ peerManager.acceptPeerPushPayment(terms)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ activity?.setTitle(R.string.receive_peer_payment_title)
+ }
+}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerIncomingState.kt b/wallet/src/main/java/net/taler/wallet/peer/IncomingState.kt
index c021c2f..7ca38c4 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerIncomingState.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/IncomingState.kt
@@ -20,21 +20,21 @@ import kotlinx.serialization.Serializable
import net.taler.common.Amount
import net.taler.wallet.backend.TalerErrorInfo
-sealed class PeerIncomingState
-object PeerIncomingChecking : PeerIncomingState()
-open class PeerIncomingTerms(
+sealed class IncomingState
+object IncomingChecking : IncomingState()
+open class IncomingTerms(
val amount: Amount,
val contractTerms: PeerContractTerms,
val id: String,
-) : PeerIncomingState()
+) : IncomingState()
-class PeerIncomingAccepting(s: PeerIncomingTerms) :
- PeerIncomingTerms(s.amount, s.contractTerms, s.id)
+class IncomingAccepting(s: IncomingTerms) :
+ IncomingTerms(s.amount, s.contractTerms, s.id)
-object PeerIncomingAccepted : PeerIncomingState()
-data class PeerIncomingError(
+object IncomingAccepted : IncomingState()
+data class IncomingError(
val info: TalerErrorInfo,
-) : PeerIncomingState()
+) : IncomingState()
@Serializable
data class PeerContractTerms(
@@ -48,3 +48,10 @@ data class CheckPeerPullPaymentResponse(
val contractTerms: PeerContractTerms,
val peerPullPaymentIncomingId: String,
)
+
+@Serializable
+data class CheckPeerPushPaymentResponse(
+ val amount: Amount,
+ val contractTerms: PeerContractTerms,
+ val peerPushPaymentIncomingId: String,
+)
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt
index be79e9d..b1593ff 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt
@@ -32,7 +32,7 @@ import net.taler.wallet.R
import net.taler.wallet.compose.collectAsStateLifecycleAware
import net.taler.wallet.exchanges.ExchangeItem
-class PeerPullFragment : Fragment() {
+class OutgoingPullFragment : Fragment() {
private val model: MainViewModel by activityViewModels()
private val exchangeManager get() = model.exchangeManager
private val peerManager get() = model.peerManager
@@ -51,16 +51,16 @@ class PeerPullFragment : Fragment() {
MdcTheme {
Surface {
val state = peerManager.pullState.collectAsStateLifecycleAware()
- if (state.value is PeerOutgoingIntro) {
+ if (state.value is OutgoingIntro) {
val exchangeState =
exchangeFlow.collectAsStateLifecycleAware(initial = null)
- PeerPullIntroComposable(
+ OutgoingPullIntroComposable(
amount = amount,
exchangeState = exchangeState,
- onCreateInvoice = this@PeerPullFragment::onCreateInvoice,
+ onCreateInvoice = this@OutgoingPullFragment::onCreateInvoice,
)
} else {
- PeerPullResultComposable(state.value) {
+ OutgoingPullResultComposable(state.value) {
findNavController().popBackStack()
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerPullIntroComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt
index 02f2c7c..a338836 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPullIntroComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt
@@ -50,7 +50,7 @@ import net.taler.wallet.cleanExchange
import net.taler.wallet.exchanges.ExchangeItem
@Composable
-fun PeerPullIntroComposable(
+fun OutgoingPullIntroComposable(
amount: Amount,
exchangeState: State<ExchangeItem?>,
onCreateInvoice: (amount: Amount, exchange: ExchangeItem) -> Unit,
@@ -124,6 +124,6 @@ fun PreviewReceiveFundsIntro() {
@SuppressLint("UnrememberedMutableState")
val exchangeFlow =
mutableStateOf(ExchangeItem("https://example.org", "TESTKUDOS", emptyList()))
- PeerPullIntroComposable(Amount.fromDouble("TESTKUDOS", 42.23), exchangeFlow) { _, _ -> }
+ OutgoingPullIntroComposable(Amount.fromDouble("TESTKUDOS", 42.23), exchangeFlow) { _, _ -> }
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerPullResultComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt
index d37ca4b..2c4001f 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPullResultComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullResultComposable.kt
@@ -55,7 +55,7 @@ import net.taler.wallet.compose.getQrCodeSize
import org.json.JSONObject
@Composable
-fun PeerPullResultComposable(state: PeerOutgoingState, onClose: () -> Unit) {
+fun OutgoingPullResultComposable(state: OutgoingState, onClose: () -> Unit) {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
@@ -68,10 +68,10 @@ fun PeerPullResultComposable(state: PeerOutgoingState, onClose: () -> Unit) {
text = stringResource(id = R.string.receive_peer_invoice_instruction),
)
when (state) {
- PeerOutgoingIntro -> error("Result composable with PullPaymentIntro")
- is PeerOutgoingCreating -> PeerPullCreatingComposable()
- is PeerOutgoingResponse -> PeerPullResponseComposable(state)
- is PeerOutgoingError -> PeerPullErrorComposable(state)
+ OutgoingIntro -> error("Result composable with PullPaymentIntro")
+ is OutgoingCreating -> PeerPullCreatingComposable()
+ is OutgoingResponse -> PeerPullResponseComposable(state)
+ is OutgoingError -> PeerPullErrorComposable(state)
}
Button(modifier = Modifier
.padding(16.dp)
@@ -94,7 +94,7 @@ private fun ColumnScope.PeerPullCreatingComposable() {
}
@Composable
-private fun ColumnScope.PeerPullResponseComposable(state: PeerOutgoingResponse) {
+private fun ColumnScope.PeerPullResponseComposable(state: OutgoingResponse) {
val qrCodeSize = getQrCodeSize()
Image(
modifier = Modifier
@@ -135,7 +135,7 @@ private fun ColumnScope.PeerPullResponseComposable(state: PeerOutgoingResponse)
}
@Composable
-private fun ColumnScope.PeerPullErrorComposable(state: PeerOutgoingError) {
+private fun ColumnScope.PeerPullErrorComposable(state: OutgoingError) {
Text(
modifier = Modifier
.align(CenterHorizontally)
@@ -150,7 +150,7 @@ private fun ColumnScope.PeerPullErrorComposable(state: PeerOutgoingError) {
@Composable
fun PeerPullCreatingPreview() {
Surface {
- PeerPullResultComposable(PeerOutgoingCreating) {}
+ OutgoingPullResultComposable(OutgoingCreating) {}
}
}
@@ -159,8 +159,8 @@ fun PeerPullCreatingPreview() {
fun PeerPullResponsePreview() {
Surface {
val talerUri = "https://example.org/foo/bar/can/be/very/long/url/so/fit/it/on/screen"
- val response = PeerOutgoingResponse(talerUri, QrCodeManager.makeQrCode(talerUri))
- PeerPullResultComposable(response) {}
+ val response = OutgoingResponse(talerUri, QrCodeManager.makeQrCode(talerUri))
+ OutgoingPullResultComposable(response) {}
}
}
@@ -169,8 +169,8 @@ fun PeerPullResponsePreview() {
fun PeerPullResponseLandscapePreview() {
Surface {
val talerUri = "https://example.org/foo/bar/can/be/very/long/url/so/fit/it/on/screen"
- val response = PeerOutgoingResponse(talerUri, QrCodeManager.makeQrCode(talerUri))
- PeerPullResultComposable(response) {}
+ val response = OutgoingResponse(talerUri, QrCodeManager.makeQrCode(talerUri))
+ OutgoingPullResultComposable(response) {}
}
}
@@ -179,7 +179,7 @@ fun PeerPullResponseLandscapePreview() {
fun PeerPullErrorPreview() {
Surface {
val json = JSONObject().apply { put("foo", "bar") }
- val response = PeerOutgoingError(TalerErrorInfo(42, "hint", "message", json))
- PeerPullResultComposable(response) {}
+ val response = OutgoingError(TalerErrorInfo(42, "hint", "message", json))
+ OutgoingPullResultComposable(response) {}
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerPushComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt
index 1399fbb..72c8862 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPushComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt
@@ -47,7 +47,7 @@ import net.taler.wallet.R
import net.taler.wallet.getAmount
@Composable
-fun PeerPushIntroComposable(
+fun OutgoingPushIntroComposable(
currency: String,
onSend: (amount: Amount, summary: String) -> Unit,
) {
@@ -134,6 +134,6 @@ fun PeerPushIntroComposable(
@Composable
fun PeerPushIntroComposablePreview() {
Surface {
- PeerPushIntroComposable("TESTKUDOS") { _, _ -> }
+ OutgoingPushIntroComposable("TESTKUDOS") { _, _ -> }
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerPushResultComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt
index b33fc4f..6d8b5dc 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPushResultComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushResultComposable.kt
@@ -55,7 +55,7 @@ import net.taler.wallet.compose.getQrCodeSize
import org.json.JSONObject
@Composable
-fun PeerPushResultComposable(state: PeerOutgoingState, onClose: () -> Unit) {
+fun OutgoingPushResultComposable(state: OutgoingState, onClose: () -> Unit) {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
@@ -68,10 +68,10 @@ fun PeerPushResultComposable(state: PeerOutgoingState, onClose: () -> Unit) {
text = stringResource(id = R.string.send_peer_payment_instruction),
)
when (state) {
- PeerOutgoingIntro -> error("Result composable with PullPaymentIntro")
- is PeerOutgoingCreating -> PeerPushCreatingComposable()
- is PeerOutgoingResponse -> PeerPushResponseComposable(state)
- is PeerOutgoingError -> PeerPushErrorComposable(state)
+ OutgoingIntro -> error("Result composable with PullPaymentIntro")
+ is OutgoingCreating -> PeerPushCreatingComposable()
+ is OutgoingResponse -> PeerPushResponseComposable(state)
+ is OutgoingError -> PeerPushErrorComposable(state)
}
Button(modifier = Modifier
.padding(16.dp)
@@ -94,7 +94,7 @@ private fun ColumnScope.PeerPushCreatingComposable() {
}
@Composable
-private fun ColumnScope.PeerPushResponseComposable(state: PeerOutgoingResponse) {
+private fun ColumnScope.PeerPushResponseComposable(state: OutgoingResponse) {
val qrCodeSize = getQrCodeSize()
Image(
modifier = Modifier
@@ -135,7 +135,7 @@ private fun ColumnScope.PeerPushResponseComposable(state: PeerOutgoingResponse)
}
@Composable
-private fun ColumnScope.PeerPushErrorComposable(state: PeerOutgoingError) {
+private fun ColumnScope.PeerPushErrorComposable(state: OutgoingError) {
Text(
modifier = Modifier
.align(CenterHorizontally)
@@ -150,7 +150,7 @@ private fun ColumnScope.PeerPushErrorComposable(state: PeerOutgoingError) {
@Composable
fun PeerPushCreatingPreview() {
Surface {
- PeerPushResultComposable(PeerOutgoingCreating) {}
+ OutgoingPushResultComposable(OutgoingCreating) {}
}
}
@@ -159,8 +159,8 @@ fun PeerPushCreatingPreview() {
fun PeerPushResponsePreview() {
Surface {
val talerUri = "https://example.org/foo/bar/can/be/very/long/url/so/fit/it/on/screen"
- val response = PeerOutgoingResponse(talerUri, QrCodeManager.makeQrCode(talerUri))
- PeerPushResultComposable(response) {}
+ val response = OutgoingResponse(talerUri, QrCodeManager.makeQrCode(talerUri))
+ OutgoingPushResultComposable(response) {}
}
}
@@ -169,8 +169,8 @@ fun PeerPushResponsePreview() {
fun PeerPushResponseLandscapePreview() {
Surface {
val talerUri = "https://example.org/foo/bar/can/be/very/long/url/so/fit/it/on/screen"
- val response = PeerOutgoingResponse(talerUri, QrCodeManager.makeQrCode(talerUri))
- PeerPushResultComposable(response) {}
+ val response = OutgoingResponse(talerUri, QrCodeManager.makeQrCode(talerUri))
+ OutgoingPushResultComposable(response) {}
}
}
@@ -179,7 +179,7 @@ fun PeerPushResponseLandscapePreview() {
fun PeerPushErrorPreview() {
Surface {
val json = JSONObject().apply { put("foo", "bar") }
- val response = PeerOutgoingError(TalerErrorInfo(42, "hint", "message", json))
- PeerPushResultComposable(response) {}
+ val response = OutgoingError(TalerErrorInfo(42, "hint", "message", json))
+ OutgoingPushResultComposable(response) {}
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerOutgoingState.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt
index 0b6b2a8..0e01056 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerOutgoingState.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingState.kt
@@ -20,17 +20,17 @@ import android.graphics.Bitmap
import kotlinx.serialization.Serializable
import net.taler.wallet.backend.TalerErrorInfo
-sealed class PeerOutgoingState
-object PeerOutgoingIntro : PeerOutgoingState()
-object PeerOutgoingCreating : PeerOutgoingState()
-data class PeerOutgoingResponse(
+sealed class OutgoingState
+object OutgoingIntro : OutgoingState()
+object OutgoingCreating : OutgoingState()
+data class OutgoingResponse(
val talerUri: String,
val qrCode: Bitmap,
-) : PeerOutgoingState()
+) : OutgoingState()
-data class PeerOutgoingError(
+data class OutgoingError(
val info: TalerErrorInfo,
-) : PeerOutgoingState()
+) : OutgoingState()
@Serializable
data class InitiatePeerPullPaymentResponse(
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt b/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt
index 5bfd030..b02b2b6 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt
@@ -34,17 +34,20 @@ class PeerManager(
private val scope: CoroutineScope,
) {
- private val _pullState = MutableStateFlow<PeerOutgoingState>(PeerOutgoingIntro)
- val pullState: StateFlow<PeerOutgoingState> = _pullState
+ private val _outgoingPullState = MutableStateFlow<OutgoingState>(OutgoingIntro)
+ val pullState: StateFlow<OutgoingState> = _outgoingPullState
- private val _pushState = MutableStateFlow<PeerOutgoingState>(PeerOutgoingIntro)
- val pushState: StateFlow<PeerOutgoingState> = _pushState
+ private val _outgoingPushState = MutableStateFlow<OutgoingState>(OutgoingIntro)
+ val pushState: StateFlow<OutgoingState> = _outgoingPushState
- private val _paymentState = MutableStateFlow<PeerIncomingState>(PeerIncomingChecking)
- val paymentState: StateFlow<PeerIncomingState> = _paymentState
+ private val _incomingPullState = MutableStateFlow<IncomingState>(IncomingChecking)
+ val incomingPullState: StateFlow<IncomingState> = _incomingPullState
+
+ private val _incomingPushState = MutableStateFlow<IncomingState>(IncomingChecking)
+ val incomingPushState: StateFlow<IncomingState> = _incomingPushState
fun initiatePullPayment(amount: Amount, exchange: ExchangeItem) {
- _pullState.value = PeerOutgoingCreating
+ _outgoingPullState.value = OutgoingCreating
scope.launch(Dispatchers.IO) {
api.request("initiatePeerPullPayment", InitiatePeerPullPaymentResponse.serializer()) {
put("exchangeBaseUrl", exchange.exchangeBaseUrl)
@@ -54,20 +57,20 @@ class PeerManager(
})
}.onSuccess {
val qrCode = QrCodeManager.makeQrCode(it.talerUri)
- _pullState.value = PeerOutgoingResponse(it.talerUri, qrCode)
+ _outgoingPullState.value = OutgoingResponse(it.talerUri, qrCode)
}.onError { error ->
Log.e(TAG, "got initiatePeerPullPayment error result $error")
- _pullState.value = PeerOutgoingError(error)
+ _outgoingPullState.value = OutgoingError(error)
}
}
}
fun resetPullPayment() {
- _pullState.value = PeerOutgoingIntro
+ _outgoingPullState.value = OutgoingIntro
}
fun initiatePeerPushPayment(amount: Amount, summary: String) {
- _pushState.value = PeerOutgoingCreating
+ _outgoingPushState.value = OutgoingCreating
scope.launch(Dispatchers.IO) {
api.request("initiatePeerPushPayment", InitiatePeerPushPaymentResponse.serializer()) {
put("amount", amount.toJSONString())
@@ -76,46 +79,78 @@ class PeerManager(
})
}.onSuccess { response ->
val qrCode = QrCodeManager.makeQrCode(response.talerUri)
- _pushState.value = PeerOutgoingResponse(response.talerUri, qrCode)
+ _outgoingPushState.value = OutgoingResponse(response.talerUri, qrCode)
}.onError { error ->
Log.e(TAG, "got initiatePeerPushPayment error result $error")
- _pushState.value = PeerOutgoingError(error)
+ _outgoingPushState.value = OutgoingError(error)
}
}
}
fun resetPushPayment() {
- _pushState.value = PeerOutgoingIntro
+ _outgoingPushState.value = OutgoingIntro
}
fun checkPeerPullPayment(talerUri: String) {
- _paymentState.value = PeerIncomingChecking
+ _incomingPullState.value = IncomingChecking
scope.launch(Dispatchers.IO) {
api.request("checkPeerPullPayment", CheckPeerPullPaymentResponse.serializer()) {
put("talerUri", talerUri)
}.onSuccess { response ->
- _paymentState.value = PeerIncomingTerms(
+ _incomingPullState.value = IncomingTerms(
amount = response.amount,
contractTerms = response.contractTerms,
id = response.peerPullPaymentIncomingId,
)
}.onError { error ->
Log.e(TAG, "got checkPeerPushPayment error result $error")
- _paymentState.value = PeerIncomingError(error)
+ _incomingPullState.value = IncomingError(error)
}
}
}
- fun acceptPeerPullPayment(terms: PeerIncomingTerms) {
- _paymentState.value = PeerIncomingAccepting(terms)
+ fun acceptPeerPullPayment(terms: IncomingTerms) {
+ _incomingPullState.value = IncomingAccepting(terms)
scope.launch(Dispatchers.IO) {
api.request<Unit>("acceptPeerPullPayment") {
put("peerPullPaymentIncomingId", terms.id)
}.onSuccess {
- _paymentState.value = PeerIncomingAccepted
+ _incomingPullState.value = IncomingAccepted
+ }.onError { error ->
+ Log.e(TAG, "got checkPeerPushPayment error result $error")
+ _incomingPullState.value = IncomingError(error)
+ }
+ }
+ }
+
+ fun checkPeerPushPayment(talerUri: String) {
+ _incomingPushState.value = IncomingChecking
+ scope.launch(Dispatchers.IO) {
+ api.request("checkPeerPushPayment", CheckPeerPushPaymentResponse.serializer()) {
+ put("talerUri", talerUri)
+ }.onSuccess { response ->
+ _incomingPushState.value = IncomingTerms(
+ amount = response.amount,
+ contractTerms = response.contractTerms,
+ id = response.peerPushPaymentIncomingId,
+ )
+ }.onError { error ->
+ Log.e(TAG, "got checkPeerPushPayment error result $error")
+ _incomingPushState.value = IncomingError(error)
+ }
+ }
+ }
+
+ fun acceptPeerPushPayment(terms: IncomingTerms) {
+ _incomingPushState.value = IncomingAccepting(terms)
+ scope.launch(Dispatchers.IO) {
+ api.request<Unit>("acceptPeerPushPayment") {
+ put("peerPushPaymentIncomingId", terms.id)
+ }.onSuccess {
+ _incomingPushState.value = IncomingAccepted
}.onError { error ->
Log.e(TAG, "got checkPeerPushPayment error result $error")
- _paymentState.value = PeerIncomingError(error)
+ _incomingPushState.value = IncomingError(error)
}
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushCredit.kt b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushCredit.kt
new file mode 100644
index 0000000..b986f57
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPushCredit.kt
@@ -0,0 +1,77 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2022 Taler Systems S.A.
+ *
+ * GNU Taler is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 3, or (at your option) any later version.
+ *
+ * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+package net.taler.wallet.peer
+
+import androidx.compose.material.Surface
+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.Timestamp
+import net.taler.wallet.R
+import net.taler.wallet.transactions.AmountType
+import net.taler.wallet.transactions.PeerInfoShort
+import net.taler.wallet.transactions.TransactionAmountComposable
+import net.taler.wallet.transactions.TransactionInfoComposable
+import net.taler.wallet.transactions.TransactionPeerComposable
+import net.taler.wallet.transactions.TransactionPeerPushCredit
+
+@Composable
+fun TransactionPeerPushCreditComposable(t: TransactionPeerPushCredit) {
+ TransactionAmountComposable(
+ label = stringResource(id = R.string.send_peer_payment_amount_received),
+ amount = t.amountEffective,
+ amountType = AmountType.Positive,
+ )
+ TransactionAmountComposable(
+ label = stringResource(id = R.string.send_peer_payment_amount_sent),
+ amount = t.amountRaw,
+ amountType = AmountType.Neutral,
+ )
+ val fee = t.amountRaw - t.amountEffective
+ if (!fee.isZero()) {
+ TransactionAmountComposable(
+ label = stringResource(id = R.string.withdraw_fees),
+ amount = fee,
+ amountType = AmountType.Negative,
+ )
+ }
+ TransactionInfoComposable(
+ label = stringResource(id = R.string.withdraw_manual_ready_subject),
+ info = t.info.summary ?: "",
+ )
+}
+
+@Preview
+@Composable
+fun TransactionPeerPushCreditPreview() {
+ val t = TransactionPeerPushCredit(
+ transactionId = "transactionId",
+ timestamp = Timestamp(System.currentTimeMillis() - 360 * 60 * 1000),
+ pending = true,
+ exchangeBaseUrl = "https://exchange.example.org/",
+ amountRaw = Amount.fromDouble("TESTKUDOS", 42.23),
+ amountEffective = Amount.fromDouble("TESTKUDOS", 42.1337),
+ info = PeerInfoShort(
+ expiration = Timestamp(System.currentTimeMillis() + 60 * 60 * 1000),
+ summary = "test invoice",
+ ),
+ )
+ Surface {
+ TransactionPeerComposable(t) {}
+ }
+}
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 9b0c208..749ec30 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt
@@ -50,6 +50,7 @@ import net.taler.common.toAbsoluteTime
import net.taler.wallet.R
import net.taler.wallet.peer.TransactionPeerPullCreditComposable
import net.taler.wallet.peer.TransactionPeerPullDebitComposable
+import net.taler.wallet.peer.TransactionPeerPushCreditComposable
import net.taler.wallet.peer.TransactionPeerPushDebitComposable
class TransactionPeerFragment : TransactionDetailFragment() {
@@ -89,7 +90,7 @@ fun TransactionPeerComposable(t: Transaction, onDelete: () -> Unit) {
)
when (t) {
is TransactionPeerPullCredit -> TransactionPeerPullCreditComposable(t)
- is TransactionPeerPushCredit -> TODO()
+ is TransactionPeerPushCredit -> TransactionPeerPushCreditComposable(t)
is TransactionPeerPullDebit -> TransactionPeerPullDebitComposable(t)
is TransactionPeerPushDebit -> TransactionPeerPushDebitComposable(t)
else -> error("unexpected transaction: ${t::class.simpleName}")
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 6ef6c88..97ac5ea 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -361,7 +361,7 @@ class TransactionPeerPushCredit(
@Transient
override val amountType = AmountType.Positive
override fun getTitle(context: Context): String {
- return context.getString(R.string.transaction_peer_push_debit)
+ return context.getString(R.string.transaction_peer_push_credit)
}
- override val generalTitleRes = R.string.withdraw_title
+ override val generalTitleRes = R.string.transaction_peer_push_credit
}
diff --git a/wallet/src/main/res/navigation/nav_graph.xml b/wallet/src/main/res/navigation/nav_graph.xml
index 3170216..f9060c5 100644
--- a/wallet/src/main/res/navigation/nav_graph.xml
+++ b/wallet/src/main/res/navigation/nav_graph.xml
@@ -131,7 +131,7 @@
<fragment
android:id="@+id/nav_peer_pull"
- android:name="net.taler.wallet.peer.PeerPullFragment"
+ android:name="net.taler.wallet.peer.OutgoingPullFragment"
android:label="@string/receive_peer_title">
<argument
android:name="amount"
@@ -142,7 +142,7 @@
<fragment
android:id="@+id/promptPullPayment"
- android:name="net.taler.wallet.peer.PullPaymentFragment"
+ android:name="net.taler.wallet.peer.IncomingPullPaymentFragment"
android:label="@string/pay_peer_title">
<action
android:id="@+id/action_promptPullPayment_to_nav_main"
@@ -151,6 +151,16 @@
</fragment>
<fragment
+ android:id="@+id/promptPushPayment"
+ android:name="net.taler.wallet.peer.IncomingPushPaymentFragment"
+ android:label="@string/receive_peer_payment_title">
+ <action
+ android:id="@+id/action_promptPushPayment_to_nav_main"
+ app:destination="@id/nav_main"
+ app:popUpTo="@id/nav_main" />
+ </fragment>
+
+ <fragment
android:id="@+id/nav_transactions"
android:name="net.taler.wallet.transactions.TransactionsFragment"
android:label="@string/transactions_title"
@@ -290,6 +300,10 @@
app:destination="@id/promptPullPayment" />
<action
+ android:id="@+id/action_global_prompt_push_payment"
+ app:destination="@id/promptPushPayment" />
+
+ <action
android:id="@+id/action_global_pending_operations"
app:destination="@id/nav_pending_operations" />
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
index 8601b14..ab8984c 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -99,6 +99,7 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card
<string name="transaction_peer_push_debit">Push payment</string>
<string name="transaction_peer_pull_credit">Invoice</string>
<string name="transaction_peer_pull_debit">Invoice paid</string>
+ <string name="transaction_peer_push_credit">Push payment</string>
<string name="payment_title">Payment</string>
<string name="payment_fee">+%s payment fee</string>
@@ -127,9 +128,13 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card
<string name="send_peer_create_button">Send funds now</string>
<string name="send_peer_warning">Warning: Funds will leave the wallet immediately.</string>
<string name="send_peer_payment_instruction">Let the payee scan this QR code to receive:</string>
+ <string name="send_peer_payment_amount_received">Amount received</string>
+ <string name="send_peer_payment_amount_sent">Amount sent</string>
<string name="pay_peer_title">Pay invoice</string>
<string name="pay_peer_intro">Do you want to pay this invoice?</string>
+ <string name="receive_peer_payment_title">Receive payment</string>
+ <string name="receive_peer_payment_intro">Do you want to receive this payment?</string>
<string name="withdraw_initiated">Withdrawal initiated</string>
<string name="withdraw_title">Withdrawal</string>