summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIván Ávalos <avalos@disroot.org>2023-02-23 11:30:29 -0600
committerTorsten Grote <t@grobox.de>2023-03-21 11:52:36 -0300
commit1c979ef1d0efd8bdaed7dda292825c41f1d48893 (patch)
tree57f27d92794b032a5ae7513b380ccfcbd0c2ea4b
parent0532aa1e215b957397432a8b0c03b7f867ab8cb0 (diff)
downloadtaler-android-1c979ef1d0efd8bdaed7dda292825c41f1d48893.tar.gz
taler-android-1c979ef1d0efd8bdaed7dda292825c41f1d48893.tar.bz2
taler-android-1c979ef1d0efd8bdaed7dda292825c41f1d48893.zip
[wallet] Add support for expiration of peer payments
bug 0007439
-rw-r--r--wallet/src/main/java/net/taler/wallet/compose/NumericInputField.kt71
-rw-r--r--wallet/src/main/java/net/taler/wallet/compose/SelectionChip.kt48
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/ExpirationComposable.kt128
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt4
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt24
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt4
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt44
-rw-r--r--wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt10
-rw-r--r--wallet/src/main/res/values/strings.xml7
9 files changed, 316 insertions, 24 deletions
diff --git a/wallet/src/main/java/net/taler/wallet/compose/NumericInputField.kt b/wallet/src/main/java/net/taler/wallet/compose/NumericInputField.kt
new file mode 100644
index 0000000..c9d2fc5
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/compose/NumericInputField.kt
@@ -0,0 +1,71 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2023 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.compose
+
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Remove
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun NumericInputField(
+ modifier: Modifier = Modifier,
+ value: Long,
+ onValueChange: (Long) -> Unit,
+ readOnly: Boolean = true,
+ label: @Composable () -> Unit,
+ minValue: Long? = 0L,
+ maxValue: Long? = null,
+) {
+ OutlinedTextField(
+ modifier = modifier,
+ value = value.toString(),
+ readOnly = readOnly,
+ onValueChange = {
+ val dd = it.toLongOrNull() ?: 0
+ onValueChange(dd)
+ },
+ trailingIcon = {
+ Row {
+ IconButton(
+ content = { Icon(Icons.Default.Remove, "add1") },
+ onClick = {
+ if (minValue != null && value - 1 >= minValue) {
+ onValueChange(value - 1)
+ }
+ },
+ )
+ IconButton(
+ content = { Icon(Icons.Default.Add, "add1") },
+ onClick = {
+ if (maxValue != null && value + 1 <= maxValue) {
+ onValueChange(value + 1)
+ }
+ },
+ )
+ }
+ },
+ label = label,
+ )
+} \ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/compose/SelectionChip.kt b/wallet/src/main/java/net/taler/wallet/compose/SelectionChip.kt
new file mode 100644
index 0000000..454bbfa
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/compose/SelectionChip.kt
@@ -0,0 +1,48 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2023 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.compose
+
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SuggestionChip
+import androidx.compose.material3.SuggestionChipDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun <T> SelectionChip(
+ label: @Composable () -> Unit,
+ modifier: Modifier = Modifier,
+ selected: Boolean,
+ value: T,
+ onSelected: (T) -> Unit,
+) {
+ val theme = MaterialTheme.colorScheme
+ SuggestionChip(
+ label = label,
+ modifier = modifier,
+ onClick = {
+ onSelected(value)
+ },
+ colors = SuggestionChipDefaults.suggestionChipColors(
+ containerColor = if (selected) theme.primaryContainer else Color.Transparent,
+ labelColor = if (selected) theme.onPrimaryContainer else theme.onSurface
+ )
+ )
+} \ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/peer/ExpirationComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/ExpirationComposable.kt
new file mode 100644
index 0000000..4393e47
--- /dev/null
+++ b/wallet/src/main/java/net/taler/wallet/peer/ExpirationComposable.kt
@@ -0,0 +1,128 @@
+/*
+ * This file is part of GNU Taler
+ * (C) 2023 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.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import net.taler.wallet.R
+import net.taler.wallet.compose.NumericInputField
+import net.taler.wallet.compose.SelectionChip
+import net.taler.wallet.compose.TalerSurface
+
+enum class ExpirationOption(val hours: Long) {
+ DAYS_1(24),
+ DAYS_7(24 * 7),
+ DAYS_30(24 * 30),
+ CUSTOM(-1)
+}
+
+@Composable
+fun ExpirationComposable(
+ modifier: Modifier = Modifier,
+ option: ExpirationOption,
+ hours: Long,
+ onOptionChange: (ExpirationOption) -> Unit,
+ onHoursChange: (Long) -> Unit,
+) {
+ val options = listOf(
+ ExpirationOption.DAYS_1 to stringResource(R.string.send_peer_expiration_1d),
+ ExpirationOption.DAYS_7 to stringResource(R.string.send_peer_expiration_7d),
+ ExpirationOption.DAYS_30 to stringResource(R.string.send_peer_expiration_30d),
+ ExpirationOption.CUSTOM to stringResource(R.string.send_peer_expiration_custom),
+ )
+ Column(
+ modifier = modifier,
+ ) {
+ LazyRow(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ ) {
+ items(items = options, key = { it.first }) {
+ SelectionChip(
+ label = { Text(it.second) },
+ modifier = Modifier.padding(horizontal = 4.dp),
+ selected = it.first == option,
+ value = it.first,
+ onSelected = { o ->
+ onOptionChange(o)
+ if (o != ExpirationOption.CUSTOM) {
+ onHoursChange(o.hours)
+ }
+ },
+ )
+ }
+ }
+
+ if (option == ExpirationOption.CUSTOM) {
+ val d = hours / 24L
+ val h = hours - d * 24L
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ ) {
+ NumericInputField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f)
+ .padding(end = 4.dp),
+ value = d,
+ onValueChange = {
+ onHoursChange(it * 24 + h)
+ },
+ label = { Text(stringResource(R.string.send_peer_expiration_days)) },
+ maxValue = 365,
+ )
+ NumericInputField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .weight(1f)
+ .padding(start = 4.dp),
+ value = h,
+ onValueChange = {
+ onHoursChange(d * 24 + it)
+ },
+ label = { Text(stringResource(R.string.send_peer_expiration_hours)) },
+ maxValue = 23,
+ )
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+fun ExpirationComposablePreview() {
+ TalerSurface {
+ var option = ExpirationOption.CUSTOM
+ var hours = 25L
+ ExpirationComposable(
+ option = option,
+ hours = hours,
+ onOptionChange = { option = it }
+ ) { hours = it }
+ }
+} \ No newline at end of file
diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt
index 79030f1..565aeb1 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullFragment.kt
@@ -76,7 +76,7 @@ class OutgoingPullFragment : Fragment() {
if (!requireActivity().isChangingConfigurations) peerManager.resetPullPayment()
}
- private fun onCreateInvoice(amount: Amount, summary: String, exchange: ExchangeItem) {
- peerManager.initiatePeerPullCredit(amount, summary, exchange)
+ private fun onCreateInvoice(amount: Amount, summary: String, hours: Long, exchange: ExchangeItem) {
+ peerManager.initiatePeerPullCredit(amount, summary, hours, exchange)
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt
index a7cd2a8..a8f24fd 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPullIntroComposable.kt
@@ -50,6 +50,7 @@ import net.taler.wallet.exchanges.ExchangeItem
import net.taler.wallet.transactions.AmountType
import net.taler.wallet.transactions.TransactionAmountComposable
import net.taler.wallet.transactions.TransactionInfoComposable
+import net.taler.wallet.peer.ExpirationOption.DAYS_1
import kotlin.random.Random
@OptIn(ExperimentalMaterial3Api::class)
@@ -57,7 +58,7 @@ import kotlin.random.Random
fun OutgoingPullIntroComposable(
amount: Amount,
state: OutgoingState,
- onCreateInvoice: (amount: Amount, subject: String, exchange: ExchangeItem) -> Unit,
+ onCreateInvoice: (amount: Amount, subject: String, hours: Long, exchange: ExchangeItem) -> Unit,
) {
val scrollState = rememberScrollState()
Column(
@@ -72,7 +73,6 @@ fun OutgoingPullIntroComposable(
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
- .padding(top = 16.dp, start = 16.dp, end = 16.dp)
.focusRequester(focusRequester),
singleLine = true,
value = subject,
@@ -96,7 +96,7 @@ fun OutgoingPullIntroComposable(
Text(
modifier = Modifier
.fillMaxWidth()
- .padding(top = 5.dp, end = 16.dp),
+ .padding(top = 5.dp),
color = if (subject.isBlank()) MaterialTheme.colorScheme.error else Color.Unspecified,
text = stringResource(R.string.char_count, subject.length, MAX_LENGTH_SUBJECT),
textAlign = TextAlign.End,
@@ -119,6 +119,19 @@ fun OutgoingPullIntroComposable(
label = stringResource(id = R.string.withdraw_exchange),
info = if (exchangeItem == null) "" else cleanExchange(exchangeItem.exchangeBaseUrl),
)
+ Text(
+ modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp),
+ text = stringResource(R.string.send_peer_expiration_period),
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ var option by rememberSaveable { mutableStateOf(DAYS_1) }
+ var hours by rememberSaveable { mutableStateOf(1L) }
+ ExpirationComposable(
+ modifier = Modifier.padding(top = 8.dp, bottom = 16.dp),
+ option = option,
+ hours = hours,
+ onOptionChange = { option = it }
+ ) { hours = it }
Button(
modifier = Modifier.padding(16.dp),
enabled = subject.isNotBlank() && state is OutgoingChecked,
@@ -126,6 +139,7 @@ fun OutgoingPullIntroComposable(
onCreateInvoice(
amount,
subject,
+ hours,
exchangeItem ?: error("clickable without exchange")
)
},
@@ -142,7 +156,7 @@ fun PreviewReceiveFundsCheckingIntro() {
OutgoingPullIntroComposable(
Amount.fromDouble("TESTKUDOS", 42.23),
if (Random.nextBoolean()) OutgoingIntro else OutgoingChecking,
- ) { _, _, _ -> }
+ ) { _, _, _, _ -> }
}
}
@@ -156,6 +170,6 @@ fun PreviewReceiveFundsCheckedIntro() {
OutgoingPullIntroComposable(
Amount.fromDouble("TESTKUDOS", 42.23),
OutgoingChecked(amountRaw, amountEffective, exchangeItem)
- ) { _, _, _ -> }
+ ) { _, _, _, _ -> }
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt
index 7019757..0aa029b 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushFragment.kt
@@ -74,7 +74,7 @@ class OutgoingPushFragment : Fragment() {
if (!requireActivity().isChangingConfigurations) peerManager.resetPushPayment()
}
- private fun onSend(amount: Amount, summary: String) {
- peerManager.initiatePeerPushDebit(amount, summary)
+ private fun onSend(amount: Amount, summary: String, hours: Long) {
+ peerManager.initiatePeerPushDebit(amount, summary, hours)
}
}
diff --git a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt
index 33e8390..63542a8 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/OutgoingPushIntroComposable.kt
@@ -16,7 +16,6 @@
package net.taler.wallet.peer
-import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@@ -29,12 +28,16 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
@@ -42,6 +45,7 @@ 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.peer.ExpirationOption.DAYS_1
import kotlin.random.Random
@OptIn(ExperimentalMaterial3Api::class)
@@ -49,7 +53,7 @@ import kotlin.random.Random
fun OutgoingPushIntroComposable(
state: OutgoingState,
amount: Amount,
- onSend: (amount: Amount, summary: String) -> Unit,
+ onSend: (amount: Amount, summary: String, hours: Long) -> Unit,
) {
val scrollState = rememberScrollState()
Column(
@@ -58,9 +62,9 @@ fun OutgoingPushIntroComposable(
.padding(16.dp)
.verticalScroll(scrollState),
horizontalAlignment = CenterHorizontally,
- verticalArrangement = spacedBy(16.dp),
) {
Text(
+ modifier = Modifier.padding(vertical = 16.dp),
text = amount.toString(),
softWrap = false,
style = MaterialTheme.typography.titleLarge,
@@ -68,6 +72,7 @@ fun OutgoingPushIntroComposable(
if (state is OutgoingChecked) {
val fee = state.amountEffective - state.amountRaw
Text(
+ modifier = Modifier.padding(vertical = 16.dp),
text = stringResource(id = R.string.payment_fee, fee),
softWrap = false,
color = MaterialTheme.colorScheme.error,
@@ -75,8 +80,11 @@ fun OutgoingPushIntroComposable(
}
var subject by rememberSaveable { mutableStateOf("") }
+ val focusRequester = remember { FocusRequester() }
OutlinedTextField(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .focusRequester(focusRequester),
singleLine = true,
value = subject,
onValueChange = { input ->
@@ -93,21 +101,37 @@ fun OutgoingPushIntroComposable(
)
}
)
+ LaunchedEffect(Unit) {
+ focusRequester.requestFocus()
+ }
Text(
modifier = Modifier
- .fillMaxWidth(),
+ .fillMaxWidth()
+ .padding(top = 5.dp),
color = if (subject.isBlank()) MaterialTheme.colorScheme.error else Color.Unspecified,
text = stringResource(R.string.char_count, subject.length, MAX_LENGTH_SUBJECT),
textAlign = TextAlign.End,
)
Text(
+ modifier = Modifier.padding(top = 16.dp, start = 16.dp, end = 16.dp),
+ text = stringResource(R.string.send_peer_expiration_period),
+ style = MaterialTheme.typography.bodyMedium,
+ )
+ var option by rememberSaveable { mutableStateOf(DAYS_1) }
+ var hours by rememberSaveable { mutableStateOf(DAYS_1.hours) }
+ ExpirationComposable(
+ modifier = Modifier.padding(top = 8.dp, bottom = 16.dp),
+ option = option,
+ hours = hours,
+ onOptionChange = { option = it }
+ ) { hours = it }
+ Text(
+ modifier = Modifier.padding(top = 8.dp, bottom = 16.dp),
text = stringResource(R.string.send_peer_warning),
)
Button(
enabled = state is OutgoingChecked && subject.isNotBlank(),
- onClick = {
- onSend(amount, subject)
- },
+ onClick = { onSend(amount, subject, hours) },
) {
Text(text = stringResource(R.string.send_peer_create_button))
}
@@ -119,7 +143,7 @@ fun OutgoingPushIntroComposable(
fun PeerPushIntroComposableCheckingPreview() {
Surface {
val state = if (Random.nextBoolean()) OutgoingIntro else OutgoingChecking
- OutgoingPushIntroComposable(state, Amount.fromDouble("TESTKUDOS", 42.23)) { _, _ -> }
+ OutgoingPushIntroComposable(state, Amount.fromDouble("TESTKUDOS", 42.23)) { _, _, _ -> }
}
}
@@ -130,6 +154,6 @@ fun PeerPushIntroComposableCheckedPreview() {
val amountEffective = Amount.fromDouble("TESTKUDOS", 42.42)
val amountRaw = Amount.fromDouble("TESTKUDOS", 42.23)
val state = OutgoingChecked(amountRaw, amountEffective)
- OutgoingPushIntroComposable(state, amountEffective) { _, _ -> }
+ OutgoingPushIntroComposable(state, amountEffective) { _, _, _ -> }
}
}
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 f031d44..f7796bb 100644
--- a/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt
+++ b/wallet/src/main/java/net/taler/wallet/peer/PeerManager.kt
@@ -34,7 +34,7 @@ import net.taler.wallet.backend.WalletBackendApi
import net.taler.wallet.exchanges.ExchangeItem
import net.taler.wallet.exchanges.ExchangeManager
import org.json.JSONObject
-import java.util.concurrent.TimeUnit.DAYS
+import java.util.concurrent.TimeUnit.HOURS
const val MAX_LENGTH_SUBJECT = 100
@@ -82,10 +82,10 @@ class PeerManager(
}
}
- fun initiatePeerPullCredit(amount: Amount, summary: String, exchange: ExchangeItem) {
+ fun initiatePeerPullCredit(amount: Amount, summary: String, expirationHours: Long, exchange: ExchangeItem) {
_outgoingPullState.value = OutgoingCreating
scope.launch(Dispatchers.IO) {
- val expiry = Timestamp.fromMillis(System.currentTimeMillis() + DAYS.toMillis(3))
+ val expiry = Timestamp.fromMillis(System.currentTimeMillis() + HOURS.toMillis(expirationHours))
api.request("initiatePeerPullCredit", InitiatePeerPullPaymentResponse.serializer()) {
put("exchangeBaseUrl", exchange.exchangeBaseUrl)
put("partialContractTerms", JSONObject().apply {
@@ -125,10 +125,10 @@ class PeerManager(
}
}
- fun initiatePeerPushDebit(amount: Amount, summary: String) {
+ fun initiatePeerPushDebit(amount: Amount, summary: String, expirationHours: Long) {
_outgoingPushState.value = OutgoingCreating
scope.launch(Dispatchers.IO) {
- val expiry = Timestamp.fromMillis(System.currentTimeMillis() + DAYS.toMillis(3))
+ val expiry = Timestamp.fromMillis(System.currentTimeMillis() + HOURS.toMillis(expirationHours))
api.request("initiatePeerPushDebit", InitiatePeerPullCreditResponse.serializer()) {
put("amount", amount.toJSONString())
put("partialContractTerms", JSONObject().apply {
diff --git a/wallet/src/main/res/values/strings.xml b/wallet/src/main/res/values/strings.xml
index 52dacfe..c313248 100644
--- a/wallet/src/main/res/values/strings.xml
+++ b/wallet/src/main/res/values/strings.xml
@@ -151,6 +151,13 @@ GNU Taler is immune against many types of fraud, such as phishing of credit card
<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="send_peer_expiration_period">Expires in</string>
+ <string name="send_peer_expiration_1d">1 day</string>
+ <string name="send_peer_expiration_7d">7 days</string>
+ <string name="send_peer_expiration_30d">30 days</string>
+ <string name="send_peer_expiration_custom">Custom</string>
+ <string name="send_peer_expiration_days">Days</string>
+ <string name="send_peer_expiration_hours">Hours</string>
<string name="send_peer_purpose">Purpose</string>
<string name="pay_peer_title">Pay invoice</string>