gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-android] branch master updated: [wallet] implement prototyp


From: gnunet
Subject: [taler-taler-android] branch master updated: [wallet] implement prototype for handling incoming pay-push URI
Date: Wed, 07 Sep 2022 20:49:07 +0200

This is an automated email from the git hooks/post-receive script.

torsten-grote pushed a commit to branch master
in repository taler-android.

The following commit(s) were added to refs/heads/master by this push:
     new 6876951  [wallet] implement prototype for handling incoming pay-push 
URI
6876951 is described below

commit 6876951cb30f057a852937a533d5d51d26645680
Author: Torsten Grote <t@grobox.de>
AuthorDate: Wed Sep 7 15:48:00 2022 -0300

    [wallet] implement prototype for handling incoming pay-push URI
---
 .../src/main/java/net/taler/wallet/MainActivity.kt |   4 +
 .../java/net/taler/wallet/SendFundsFragment.kt     |   4 +-
 .../net/taler/wallet/peer/PeerIncomingState.kt     |  50 +++++
 .../main/java/net/taler/wallet/peer/PeerManager.kt |  86 ++++----
 .../net/taler/wallet/peer/PeerOutgoingState.kt     |  47 +++++
 .../java/net/taler/wallet/peer/PeerPullFragment.kt |   2 +-
 ...ullComposable.kt => PeerPullIntroComposable.kt} |   0
 .../taler/wallet/peer/PeerPullPaymentComposable.kt | 223 +++++++++++++++++++++
 .../taler/wallet/peer/PeerPullResultComposable.kt  |  22 +-
 .../taler/wallet/peer/PeerPushResultComposable.kt  |  22 +-
 ...{PeerPullFragment.kt => PullPaymentFragment.kt} |  46 ++---
 .../taler/wallet/peer/TransactionPeerPullDebit.kt  |  77 +++++++
 .../wallet/transactions/TransactionPeerFragment.kt |   3 +-
 .../net/taler/wallet/transactions/Transactions.kt  |   4 +-
 wallet/src/main/res/navigation/nav_graph.xml       |  14 ++
 wallet/src/main/res/values/strings.xml             |   4 +
 16 files changed, 508 insertions(+), 100 deletions(-)

diff --git a/wallet/src/main/java/net/taler/wallet/MainActivity.kt 
b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
index ea604c4..2797a69 100644
--- a/wallet/src/main/java/net/taler/wallet/MainActivity.kt
+++ b/wallet/src/main/java/net/taler/wallet/MainActivity.kt
@@ -253,6 +253,10 @@ class MainActivity : AppCompatActivity(), 
OnNavigationItemSelectedListener,
                     model.showProgressBar.value = true
                     model.refundManager.refund(u).observe(this, 
Observer(::onRefundResponse))
                 }
+                action.startsWith("pay-pull/") -> {
+                    nav.navigate(R.id.action_global_prompt_pull_payment)
+                    model.peerManager.checkPeerPullPayment(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 c67b345..27f2c96 100644
--- a/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/SendFundsFragment.kt
@@ -28,7 +28,7 @@ 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.PeerPaymentIntro
+import net.taler.wallet.peer.PeerOutgoingIntro
 import net.taler.wallet.peer.PeerPushIntroComposable
 import net.taler.wallet.peer.PeerPushResultComposable
 
@@ -45,7 +45,7 @@ class SendFundsFragment : Fragment() {
             MdcTheme {
                 Surface {
                     val state = 
peerManager.pushState.collectAsStateLifecycleAware()
-                    if (state.value is PeerPaymentIntro) {
+                    if (state.value is PeerOutgoingIntro) {
                         val currency = transactionManager.selectedCurrency
                             ?: error("No currency selected")
                         PeerPushIntroComposable(currency, 
this@SendFundsFragment::onSend)
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerIncomingState.kt 
b/wallet/src/main/java/net/taler/wallet/peer/PeerIncomingState.kt
new file mode 100644
index 0000000..c021c2f
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/peer/PeerIncomingState.kt
@@ -0,0 +1,50 @@
+/*
+ * 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 kotlinx.serialization.Serializable
+import net.taler.common.Amount
+import net.taler.wallet.backend.TalerErrorInfo
+
+sealed class PeerIncomingState
+object PeerIncomingChecking : PeerIncomingState()
+open class PeerIncomingTerms(
+    val amount: Amount,
+    val contractTerms: PeerContractTerms,
+    val id: String,
+) : PeerIncomingState()
+
+class PeerIncomingAccepting(s: PeerIncomingTerms) :
+    PeerIncomingTerms(s.amount, s.contractTerms, s.id)
+
+object PeerIncomingAccepted : PeerIncomingState()
+data class PeerIncomingError(
+    val info: TalerErrorInfo,
+) : PeerIncomingState()
+
+@Serializable
+data class PeerContractTerms(
+    val summary: String,
+    val amount: Amount,
+)
+
+@Serializable
+data class CheckPeerPullPaymentResponse(
+    val amount: Amount,
+    val contractTerms: PeerContractTerms,
+    val peerPullPaymentIncomingId: String,
+)
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 898dcfd..5bfd030 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt
@@ -16,18 +16,15 @@
 
 package net.taler.wallet.peer
 
-import android.graphics.Bitmap
 import android.util.Log
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.launch
-import kotlinx.serialization.Serializable
 import net.taler.common.Amount
 import net.taler.common.QrCodeManager
 import net.taler.wallet.TAG
-import net.taler.wallet.backend.TalerErrorInfo
 import net.taler.wallet.backend.WalletBackendApi
 import net.taler.wallet.exchanges.ExchangeItem
 import org.json.JSONObject
@@ -37,14 +34,17 @@ class PeerManager(
     private val scope: CoroutineScope,
 ) {
 
-    private val _pullState = 
MutableStateFlow<PeerPaymentState>(PeerPaymentIntro)
-    val pullState: StateFlow<PeerPaymentState> = _pullState
+    private val _pullState = 
MutableStateFlow<PeerOutgoingState>(PeerOutgoingIntro)
+    val pullState: StateFlow<PeerOutgoingState> = _pullState
 
-    private val _pushState = 
MutableStateFlow<PeerPaymentState>(PeerPaymentIntro)
-    val pushState: StateFlow<PeerPaymentState> = _pushState
+    private val _pushState = 
MutableStateFlow<PeerOutgoingState>(PeerOutgoingIntro)
+    val pushState: StateFlow<PeerOutgoingState> = _pushState
+
+    private val _paymentState = 
MutableStateFlow<PeerIncomingState>(PeerIncomingChecking)
+    val paymentState: StateFlow<PeerIncomingState> = _paymentState
 
     fun initiatePullPayment(amount: Amount, exchange: ExchangeItem) {
-        _pullState.value = PeerPaymentCreating
+        _pullState.value = PeerOutgoingCreating
         scope.launch(Dispatchers.IO) {
             api.request("initiatePeerPullPayment", 
InitiatePeerPullPaymentResponse.serializer()) {
                 put("exchangeBaseUrl", exchange.exchangeBaseUrl)
@@ -54,20 +54,20 @@ class PeerManager(
                 })
             }.onSuccess {
                 val qrCode = QrCodeManager.makeQrCode(it.talerUri)
-                _pullState.value = PeerPaymentResponse(it.talerUri, qrCode)
+                _pullState.value = PeerOutgoingResponse(it.talerUri, qrCode)
             }.onError { error ->
                 Log.e(TAG, "got initiatePeerPullPayment error result $error")
-                _pullState.value = PeerPaymentError(error)
+                _pullState.value = PeerOutgoingError(error)
             }
         }
     }
 
     fun resetPullPayment() {
-        _pullState.value = PeerPaymentIntro
+        _pullState.value = PeerOutgoingIntro
     }
 
     fun initiatePeerPushPayment(amount: Amount, summary: String) {
-        _pushState.value = PeerPaymentCreating
+        _pushState.value = PeerOutgoingCreating
         scope.launch(Dispatchers.IO) {
             api.request("initiatePeerPushPayment", 
InitiatePeerPushPaymentResponse.serializer()) {
                 put("amount", amount.toJSONString())
@@ -76,42 +76,48 @@ class PeerManager(
                 })
             }.onSuccess { response ->
                 val qrCode = QrCodeManager.makeQrCode(response.talerUri)
-                _pushState.value = PeerPaymentResponse(response.talerUri, 
qrCode)
+                _pushState.value = PeerOutgoingResponse(response.talerUri, 
qrCode)
             }.onError { error ->
                 Log.e(TAG, "got initiatePeerPushPayment error result $error")
-                _pushState.value = PeerPaymentError(error)
+                _pushState.value = PeerOutgoingError(error)
             }
         }
     }
 
     fun resetPushPayment() {
-        _pushState.value = PeerPaymentIntro
+        _pushState.value = PeerOutgoingIntro
     }
 
-}
-
-sealed class PeerPaymentState
-object PeerPaymentIntro : PeerPaymentState()
-object PeerPaymentCreating : PeerPaymentState()
-data class PeerPaymentResponse(
-    val talerUri: String,
-    val qrCode: Bitmap,
-) : PeerPaymentState()
-
-data class PeerPaymentError(
-    val info: TalerErrorInfo,
-) : PeerPaymentState()
+    fun checkPeerPullPayment(talerUri: String) {
+        _paymentState.value = PeerIncomingChecking
+        scope.launch(Dispatchers.IO) {
+            api.request("checkPeerPullPayment", 
CheckPeerPullPaymentResponse.serializer()) {
+                put("talerUri", talerUri)
+            }.onSuccess { response ->
+                _paymentState.value = PeerIncomingTerms(
+                    amount = response.amount,
+                    contractTerms = response.contractTerms,
+                    id = response.peerPullPaymentIncomingId,
+                )
+            }.onError { error ->
+                Log.e(TAG, "got checkPeerPushPayment error result $error")
+                _paymentState.value = PeerIncomingError(error)
+            }
+        }
+    }
 
-@Serializable
-data class InitiatePeerPullPaymentResponse(
-    /**
-     * Taler URI for the other party to make the payment that was requested.
-     */
-    val talerUri: String,
-)
+    fun acceptPeerPullPayment(terms: PeerIncomingTerms) {
+        _paymentState.value = PeerIncomingAccepting(terms)
+        scope.launch(Dispatchers.IO) {
+            api.request<Unit>("acceptPeerPullPayment") {
+                put("peerPullPaymentIncomingId", terms.id)
+            }.onSuccess {
+                _paymentState.value = PeerIncomingAccepted
+            }.onError { error ->
+                Log.e(TAG, "got checkPeerPushPayment error result $error")
+                _paymentState.value = PeerIncomingError(error)
+            }
+        }
+    }
 
-@Serializable
-data class InitiatePeerPushPaymentResponse(
-    val exchangeBaseUrl: String,
-    val talerUri: String,
-)
+}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerOutgoingState.kt 
b/wallet/src/main/java/net/taler/wallet/peer/PeerOutgoingState.kt
new file mode 100644
index 0000000..0b6b2a8
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/peer/PeerOutgoingState.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.graphics.Bitmap
+import kotlinx.serialization.Serializable
+import net.taler.wallet.backend.TalerErrorInfo
+
+sealed class PeerOutgoingState
+object PeerOutgoingIntro : PeerOutgoingState()
+object PeerOutgoingCreating : PeerOutgoingState()
+data class PeerOutgoingResponse(
+    val talerUri: String,
+    val qrCode: Bitmap,
+) : PeerOutgoingState()
+
+data class PeerOutgoingError(
+    val info: TalerErrorInfo,
+) : PeerOutgoingState()
+
+@Serializable
+data class InitiatePeerPullPaymentResponse(
+    /**
+     * Taler URI for the other party to make the payment that was requested.
+     */
+    val talerUri: String,
+)
+
+@Serializable
+data class InitiatePeerPushPaymentResponse(
+    val exchangeBaseUrl: String,
+    val talerUri: String,
+)
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt 
b/wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt
index d38ae34..be79e9d 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt
@@ -51,7 +51,7 @@ class PeerPullFragment : Fragment() {
                 MdcTheme {
                     Surface {
                         val state = 
peerManager.pullState.collectAsStateLifecycleAware()
-                        if (state.value is PeerPaymentIntro) {
+                        if (state.value is PeerOutgoingIntro) {
                             val exchangeState =
                                 
exchangeFlow.collectAsStateLifecycleAware(initial = null)
                             PeerPullIntroComposable(
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerPullComposable.kt 
b/wallet/src/main/java/net/taler/wallet/peer/PeerPullIntroComposable.kt
similarity index 100%
rename from wallet/src/main/java/net/taler/wallet/peer/PeerPullComposable.kt
rename to wallet/src/main/java/net/taler/wallet/peer/PeerPullIntroComposable.kt
diff --git 
a/wallet/src/main/java/net/taler/wallet/peer/PeerPullPaymentComposable.kt 
b/wallet/src/main/java/net/taler/wallet/peer/PeerPullPaymentComposable.kt
new file mode 100644
index 0000000..fff74ea
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/peer/PeerPullPaymentComposable.kt
@@ -0,0 +1,223 @@
+/*
+ * 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.annotation.SuppressLint
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.Card
+import androidx.compose.material.CircularProgressIndicator
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.ui.Alignment.Companion.CenterHorizontally
+import androidx.compose.ui.Alignment.Companion.End
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import net.taler.common.Amount
+import net.taler.wallet.R
+import net.taler.wallet.backend.TalerErrorInfo
+
+@Composable
+fun PeerPullPaymentComposable(
+    state: State<PeerIncomingState>,
+    onAccept: (PeerIncomingTerms) -> Unit,
+) {
+    val scrollState = rememberScrollState()
+    Column(
+        modifier = Modifier
+            .fillMaxSize()
+            .verticalScroll(scrollState),
+    ) {
+        Text(
+            modifier = Modifier
+                .padding(16.dp)
+                .align(CenterHorizontally),
+            text = stringResource(id = R.string.pay_peer_intro))
+        when (val s = state.value) {
+            PeerIncomingChecking -> PeerPullCheckingComposable()
+            is PeerIncomingTerms -> PeerPullTermsComposable(s, onAccept)
+            is PeerIncomingAccepting -> PeerPullTermsComposable(s, onAccept)
+            PeerIncomingAccepted -> {
+                // we navigate away, don't show anything
+            }
+            is PeerIncomingError -> PeerPullErrorComposable(s)
+        }
+    }
+}
+
+@Composable
+fun ColumnScope.PeerPullCheckingComposable() {
+    CircularProgressIndicator(
+        modifier = Modifier
+            .align(CenterHorizontally)
+            .fillMaxSize(0.75f),
+    )
+}
+
+@Composable
+fun ColumnScope.PeerPullTermsComposable(
+    terms: PeerIncomingTerms,
+    onAccept: (PeerIncomingTerms) -> Unit,
+) {
+    Text(
+        modifier = Modifier
+            .padding(16.dp)
+            .align(CenterHorizontally),
+        text = terms.contractTerms.summary,
+        style = MaterialTheme.typography.h5,
+    )
+    Spacer(modifier = Modifier.weight(1f))
+    Card(modifier = Modifier.fillMaxWidth()) {
+        Column(
+            modifier = Modifier.padding(8.dp)
+        ) {
+            Row(
+                modifier = Modifier.align(End),
+            ) {
+                Text(
+                    text = stringResource(id = 
R.string.payment_label_amount_total),
+                    style = MaterialTheme.typography.body1,
+                )
+                Text(
+                    modifier = Modifier.padding(start = 8.dp),
+                    text = terms.contractTerms.amount.toString(),
+                    style = MaterialTheme.typography.body1,
+                    fontWeight = FontWeight.Bold,
+                )
+            }
+            val fee =
+                Amount.zero(terms.amount.currency) // terms.amount - 
terms.contractTerms.amount
+            if (!fee.isZero()) {
+                Text(
+                    modifier = Modifier.align(End),
+                    text = stringResource(id = R.string.payment_fee, fee),
+                    style = MaterialTheme.typography.body1,
+                )
+            }
+            if (terms is PeerIncomingAccepting) {
+                CircularProgressIndicator(
+                    modifier = Modifier
+                        .padding(end = 64.dp)
+                        .align(End),
+                )
+            } else {
+                Button(
+                    modifier = Modifier
+                        .align(End)
+                        .padding(top = 8.dp),
+                    colors = ButtonDefaults.buttonColors(
+                        backgroundColor = colorResource(R.color.green),
+                        contentColor = Color.White,
+                    ),
+                    onClick = { onAccept(terms) },
+                ) {
+                    Text(
+                        text = stringResource(id = 
R.string.payment_button_confirm),
+                    )
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun ColumnScope.PeerPullErrorComposable(s: PeerIncomingError) {
+    Text(
+        modifier = Modifier
+            .align(CenterHorizontally)
+            .padding(horizontal = 32.dp),
+        text = s.info.userFacingMsg,
+        style = MaterialTheme.typography.h5,
+        color = colorResource(id = R.color.red),
+    )
+}
+
+@Preview
+@Composable
+fun PeerPullCheckingPreview() {
+    Surface {
+        @SuppressLint("UnrememberedMutableState")
+        val s = mutableStateOf(PeerIncomingChecking)
+        PeerPullPaymentComposable(s) {}
+    }
+}
+
+@Preview
+@Composable
+fun PeerPullTermsPreview() {
+    Surface {
+        val terms = PeerIncomingTerms(
+            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",
+                amount = Amount.fromDouble("TESTKUDOS", 23.42),
+            ),
+            id = "ID123",
+        )
+
+        @SuppressLint("UnrememberedMutableState")
+        val s = mutableStateOf(terms)
+        PeerPullPaymentComposable(s) {}
+    }
+}
+
+@Preview
+@Composable
+fun PeerPullAcceptingPreview() {
+    Surface {
+        val terms = PeerIncomingTerms(
+            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",
+                amount = Amount.fromDouble("TESTKUDOS", 23.42),
+            ),
+            id = "ID123",
+        )
+
+        @SuppressLint("UnrememberedMutableState")
+        val s = mutableStateOf(PeerIncomingAccepting(terms))
+        PeerPullPaymentComposable(s) {}
+    }
+}
+
+@Preview
+@Composable
+fun PeerPullPayErrorPreview() {
+    Surface {
+        @SuppressLint("UnrememberedMutableState")
+        val s = mutableStateOf(PeerIncomingError(TalerErrorInfo(42, "hint", 
"msg")))
+        PeerPullPaymentComposable(s) {}
+    }
+}
diff --git 
a/wallet/src/main/java/net/taler/wallet/peer/PeerPullResultComposable.kt 
b/wallet/src/main/java/net/taler/wallet/peer/PeerPullResultComposable.kt
index 0b9b546..d37ca4b 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPullResultComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/PeerPullResultComposable.kt
@@ -55,7 +55,7 @@ import net.taler.wallet.compose.getQrCodeSize
 import org.json.JSONObject
 
 @Composable
-fun PeerPullResultComposable(state: PeerPaymentState, onClose: () -> Unit) {
+fun PeerPullResultComposable(state: PeerOutgoingState, onClose: () -> Unit) {
     val scrollState = rememberScrollState()
     Column(
         modifier = Modifier
@@ -68,10 +68,10 @@ fun PeerPullResultComposable(state: PeerPaymentState, 
onClose: () -> Unit) {
             text = stringResource(id = 
R.string.receive_peer_invoice_instruction),
         )
         when (state) {
-            PeerPaymentIntro -> error("Result composable with 
PullPaymentIntro")
-            is PeerPaymentCreating -> PeerPullCreatingComposable()
-            is PeerPaymentResponse -> PeerPullResponseComposable(state)
-            is PeerPaymentError -> PeerPullErrorComposable(state)
+            PeerOutgoingIntro -> error("Result composable with 
PullPaymentIntro")
+            is PeerOutgoingCreating -> PeerPullCreatingComposable()
+            is PeerOutgoingResponse -> PeerPullResponseComposable(state)
+            is PeerOutgoingError -> PeerPullErrorComposable(state)
         }
         Button(modifier = Modifier
             .padding(16.dp)
@@ -94,7 +94,7 @@ private fun ColumnScope.PeerPullCreatingComposable() {
 }
 
 @Composable
-private fun ColumnScope.PeerPullResponseComposable(state: PeerPaymentResponse) 
{
+private fun ColumnScope.PeerPullResponseComposable(state: 
PeerOutgoingResponse) {
     val qrCodeSize = getQrCodeSize()
     Image(
         modifier = Modifier
@@ -135,7 +135,7 @@ private fun ColumnScope.PeerPullResponseComposable(state: 
PeerPaymentResponse) {
 }
 
 @Composable
-private fun ColumnScope.PeerPullErrorComposable(state: PeerPaymentError) {
+private fun ColumnScope.PeerPullErrorComposable(state: PeerOutgoingError) {
     Text(
         modifier = Modifier
             .align(CenterHorizontally)
@@ -150,7 +150,7 @@ private fun ColumnScope.PeerPullErrorComposable(state: 
PeerPaymentError) {
 @Composable
 fun PeerPullCreatingPreview() {
     Surface {
-        PeerPullResultComposable(PeerPaymentCreating) {}
+        PeerPullResultComposable(PeerOutgoingCreating) {}
     }
 }
 
@@ -159,7 +159,7 @@ fun PeerPullCreatingPreview() {
 fun PeerPullResponsePreview() {
     Surface {
         val talerUri = 
"https://example.org/foo/bar/can/be/very/long/url/so/fit/it/on/screen";
-        val response = PeerPaymentResponse(talerUri, 
QrCodeManager.makeQrCode(talerUri))
+        val response = PeerOutgoingResponse(talerUri, 
QrCodeManager.makeQrCode(talerUri))
         PeerPullResultComposable(response) {}
     }
 }
@@ -169,7 +169,7 @@ fun PeerPullResponsePreview() {
 fun PeerPullResponseLandscapePreview() {
     Surface {
         val talerUri = 
"https://example.org/foo/bar/can/be/very/long/url/so/fit/it/on/screen";
-        val response = PeerPaymentResponse(talerUri, 
QrCodeManager.makeQrCode(talerUri))
+        val response = PeerOutgoingResponse(talerUri, 
QrCodeManager.makeQrCode(talerUri))
         PeerPullResultComposable(response) {}
     }
 }
@@ -179,7 +179,7 @@ fun PeerPullResponseLandscapePreview() {
 fun PeerPullErrorPreview() {
     Surface {
         val json = JSONObject().apply { put("foo", "bar") }
-        val response = PeerPaymentError(TalerErrorInfo(42, "hint", "message", 
json))
+        val response = PeerOutgoingError(TalerErrorInfo(42, "hint", "message", 
json))
         PeerPullResultComposable(response) {}
     }
 }
diff --git 
a/wallet/src/main/java/net/taler/wallet/peer/PeerPushResultComposable.kt 
b/wallet/src/main/java/net/taler/wallet/peer/PeerPushResultComposable.kt
index f3d1a79..b33fc4f 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPushResultComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/PeerPushResultComposable.kt
@@ -55,7 +55,7 @@ import net.taler.wallet.compose.getQrCodeSize
 import org.json.JSONObject
 
 @Composable
-fun PeerPushResultComposable(state: PeerPaymentState, onClose: () -> Unit) {
+fun PeerPushResultComposable(state: PeerOutgoingState, onClose: () -> Unit) {
     val scrollState = rememberScrollState()
     Column(
         modifier = Modifier
@@ -68,10 +68,10 @@ fun PeerPushResultComposable(state: PeerPaymentState, 
onClose: () -> Unit) {
             text = stringResource(id = R.string.send_peer_payment_instruction),
         )
         when (state) {
-            PeerPaymentIntro -> error("Result composable with 
PullPaymentIntro")
-            is PeerPaymentCreating -> PeerPushCreatingComposable()
-            is PeerPaymentResponse -> PeerPushResponseComposable(state)
-            is PeerPaymentError -> PeerPushErrorComposable(state)
+            PeerOutgoingIntro -> error("Result composable with 
PullPaymentIntro")
+            is PeerOutgoingCreating -> PeerPushCreatingComposable()
+            is PeerOutgoingResponse -> PeerPushResponseComposable(state)
+            is PeerOutgoingError -> PeerPushErrorComposable(state)
         }
         Button(modifier = Modifier
             .padding(16.dp)
@@ -94,7 +94,7 @@ private fun ColumnScope.PeerPushCreatingComposable() {
 }
 
 @Composable
-private fun ColumnScope.PeerPushResponseComposable(state: PeerPaymentResponse) 
{
+private fun ColumnScope.PeerPushResponseComposable(state: 
PeerOutgoingResponse) {
     val qrCodeSize = getQrCodeSize()
     Image(
         modifier = Modifier
@@ -135,7 +135,7 @@ private fun ColumnScope.PeerPushResponseComposable(state: 
PeerPaymentResponse) {
 }
 
 @Composable
-private fun ColumnScope.PeerPushErrorComposable(state: PeerPaymentError) {
+private fun ColumnScope.PeerPushErrorComposable(state: PeerOutgoingError) {
     Text(
         modifier = Modifier
             .align(CenterHorizontally)
@@ -150,7 +150,7 @@ private fun ColumnScope.PeerPushErrorComposable(state: 
PeerPaymentError) {
 @Composable
 fun PeerPushCreatingPreview() {
     Surface {
-        PeerPushResultComposable(PeerPaymentCreating) {}
+        PeerPushResultComposable(PeerOutgoingCreating) {}
     }
 }
 
@@ -159,7 +159,7 @@ fun PeerPushCreatingPreview() {
 fun PeerPushResponsePreview() {
     Surface {
         val talerUri = 
"https://example.org/foo/bar/can/be/very/long/url/so/fit/it/on/screen";
-        val response = PeerPaymentResponse(talerUri, 
QrCodeManager.makeQrCode(talerUri))
+        val response = PeerOutgoingResponse(talerUri, 
QrCodeManager.makeQrCode(talerUri))
         PeerPushResultComposable(response) {}
     }
 }
@@ -169,7 +169,7 @@ fun PeerPushResponsePreview() {
 fun PeerPushResponseLandscapePreview() {
     Surface {
         val talerUri = 
"https://example.org/foo/bar/can/be/very/long/url/so/fit/it/on/screen";
-        val response = PeerPaymentResponse(talerUri, 
QrCodeManager.makeQrCode(talerUri))
+        val response = PeerOutgoingResponse(talerUri, 
QrCodeManager.makeQrCode(talerUri))
         PeerPushResultComposable(response) {}
     }
 }
@@ -179,7 +179,7 @@ fun PeerPushResponseLandscapePreview() {
 fun PeerPushErrorPreview() {
     Surface {
         val json = JSONObject().apply { put("foo", "bar") }
-        val response = PeerPaymentError(TalerErrorInfo(42, "hint", "message", 
json))
+        val response = PeerOutgoingError(TalerErrorInfo(42, "hint", "message", 
json))
         PeerPushResultComposable(response) {}
     }
 }
diff --git a/wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt 
b/wallet/src/main/java/net/taler/wallet/peer/PullPaymentFragment.kt
similarity index 53%
copy from wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt
copy to wallet/src/main/java/net/taler/wallet/peer/PullPaymentFragment.kt
index d38ae34..71b1bcc 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerPullFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/PullPaymentFragment.kt
@@ -24,17 +24,15 @@ import androidx.compose.material.Surface
 import androidx.compose.ui.platform.ComposeView
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.navigation.findNavController
+import androidx.lifecycle.lifecycleScope
+import androidx.navigation.fragment.findNavController
 import com.google.android.material.composethemeadapter.MdcTheme
-import net.taler.common.Amount
 import net.taler.wallet.MainViewModel
 import net.taler.wallet.R
 import net.taler.wallet.compose.collectAsStateLifecycleAware
-import net.taler.wallet.exchanges.ExchangeItem
 
-class PeerPullFragment : Fragment() {
+class PullPaymentFragment : Fragment() {
     private val model: MainViewModel by activityViewModels()
-    private val exchangeManager get() = model.exchangeManager
     private val peerManager get() = model.peerManager
 
     override fun onCreateView(
@@ -42,27 +40,20 @@ class PeerPullFragment : Fragment() {
         container: ViewGroup?,
         savedInstanceState: Bundle?,
     ): View {
-        val amount = arguments?.getString("amount")?.let {
-            Amount.fromJSONString(it)
-        } ?: error("no amount passed")
-        val exchangeFlow = 
exchangeManager.findExchangeForCurrency(amount.currency)
+        lifecycleScope.launchWhenResumed {
+            peerManager.paymentState.collect {
+                if (it is PeerIncomingAccepted) {
+                    
findNavController().navigate(R.id.action_promptPullPayment_to_nav_main)
+                }
+            }
+        }
         return ComposeView(requireContext()).apply {
             setContent {
                 MdcTheme {
                     Surface {
-                        val state = 
peerManager.pullState.collectAsStateLifecycleAware()
-                        if (state.value is PeerPaymentIntro) {
-                            val exchangeState =
-                                
exchangeFlow.collectAsStateLifecycleAware(initial = null)
-                            PeerPullIntroComposable(
-                                amount = amount,
-                                exchangeState = exchangeState,
-                                onCreateInvoice = 
this@PeerPullFragment::onCreateInvoice,
-                            )
-                        } else {
-                            PeerPullResultComposable(state.value) {
-                                findNavController().popBackStack()
-                            }
+                        val state = 
peerManager.paymentState.collectAsStateLifecycleAware()
+                        PeerPullPaymentComposable(state) { terms ->
+                            peerManager.acceptPeerPullPayment(terms)
                         }
                     }
                 }
@@ -72,15 +63,6 @@ class PeerPullFragment : Fragment() {
 
     override fun onStart() {
         super.onStart()
-        activity?.setTitle(R.string.receive_peer_title)
-    }
-
-    override fun onDestroy() {
-        super.onDestroy()
-        if (!requireActivity().isChangingConfigurations) 
peerManager.resetPullPayment()
-    }
-
-    private fun onCreateInvoice(amount: Amount, exchange: ExchangeItem) {
-        peerManager.initiatePullPayment(amount, exchange)
+        activity?.setTitle(R.string.pay_peer_title)
     }
 }
diff --git 
a/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullDebit.kt 
b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullDebit.kt
new file mode 100644
index 0000000..823126b
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/peer/TransactionPeerPullDebit.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.TransactionPeerPullDebit
+
+@Composable
+fun TransactionPeerPullDebitComposable(t: TransactionPeerPullDebit) {
+    TransactionAmountComposable(
+        label = stringResource(id = R.string.transaction_paid),
+        amount = t.amountEffective,
+        amountType = AmountType.Negative,
+    )
+    TransactionAmountComposable(
+        label = stringResource(id = R.string.transaction_order_total),
+        amount = t.amountRaw,
+        amountType = AmountType.Neutral,
+    )
+    val fee = t.amountEffective - t.amountRaw
+    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 TransactionPeerPullDebitPreview() {
+    val t = TransactionPeerPullDebit(
+        transactionId = "transactionId",
+        timestamp = Timestamp(System.currentTimeMillis() - 360 * 60 * 1000),
+        pending = true,
+        exchangeBaseUrl = "https://exchange.example.org/";,
+        amountRaw = Amount.fromDouble("TESTKUDOS", 42.1337),
+        amountEffective = Amount.fromDouble("TESTKUDOS", 42.23),
+        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 f1afb41..9b0c208 100644
--- 
a/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt
+++ 
b/wallet/src/main/java/net/taler/wallet/transactions/TransactionPeerFragment.kt
@@ -49,6 +49,7 @@ import net.taler.common.Amount
 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.TransactionPeerPushDebitComposable
 
 class TransactionPeerFragment : TransactionDetailFragment() {
@@ -89,7 +90,7 @@ fun TransactionPeerComposable(t: Transaction, onDelete: () -> 
Unit) {
         when (t) {
             is TransactionPeerPullCredit -> 
TransactionPeerPullCreditComposable(t)
             is TransactionPeerPushCredit -> TODO()
-            is TransactionPeerPullDebit -> TODO()
+            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 6f72567..6ef6c88 100644
--- a/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
+++ b/wallet/src/main/java/net/taler/wallet/transactions/Transactions.kt
@@ -280,9 +280,9 @@ class TransactionPeerPullDebit(
     @Transient
     override val amountType = AmountType.Negative
     override fun getTitle(context: Context): String {
-        return context.getString(R.string.transaction_peer_push_debit)
+        return context.getString(R.string.transaction_peer_pull_debit)
     }
-    override val generalTitleRes = R.string.payment_title
+    override val generalTitleRes = R.string.transaction_peer_pull_debit
 }
 
 /**
diff --git a/wallet/src/main/res/navigation/nav_graph.xml 
b/wallet/src/main/res/navigation/nav_graph.xml
index e3d526e..3170216 100644
--- a/wallet/src/main/res/navigation/nav_graph.xml
+++ b/wallet/src/main/res/navigation/nav_graph.xml
@@ -140,6 +140,16 @@
             app:nullable="true" />
     </fragment>
 
+    <fragment
+        android:id="@+id/promptPullPayment"
+        android:name="net.taler.wallet.peer.PullPaymentFragment"
+        android:label="@string/pay_peer_title">
+        <action
+            android:id="@+id/action_promptPullPayment_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"
@@ -275,6 +285,10 @@
         android:id="@+id/action_global_promptTip"
         app:destination="@id/promptTip" />
 
+    <action
+        android:id="@+id/action_global_prompt_pull_payment"
+        app:destination="@id/promptPullPayment" />
+
     <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 96a3453..8601b14 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -98,6 +98,7 @@ GNU Taler is immune against many types of fraud, such as 
phishing of credit card
     <string name="transaction_refresh">Coin expiry change fee</string>
     <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="payment_title">Payment</string>
     <string name="payment_fee">+%s payment fee</string>
@@ -127,6 +128,9 @@ GNU Taler is immune against many types of fraud, such as 
phishing of credit card
     <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="pay_peer_title">Pay invoice</string>
+    <string name="pay_peer_intro">Do you want to pay this invoice?</string>
+
     <string name="withdraw_initiated">Withdrawal initiated</string>
     <string name="withdraw_title">Withdrawal</string>
     <string name="withdraw_total">Withdraw</string>

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]