cashless2ecash

cashless2ecash: pay with cards for digital cash (experimental)
Log | Files | Refs | README

commit b71b7bb8133b63908a59177df9c156a23f4cbe17
parent 2a29c5bbed11f65ee1f3cffac4846bb4111c9d05
Author: Joel-Haeberli <haebu@rubigen.ch>
Date:   Sat, 23 Nov 2024 12:50:14 +0100

impr: state management

Diffstat:
Mwallee-c2ec/.idea/misc.xml | 2+-
Awallee-c2ec/.idea/other.xml | 330+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mwallee-c2ec/app/release/app-release.apk | 0
Mwallee-c2ec/app/release/baselineProfiles/0/app-release.dm | 0
Mwallee-c2ec/app/release/baselineProfiles/1/app-release.dm | 0
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/MainActivity.kt | 23++++++++++++++---------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/wallee/WalleeResponseHandler.kt | 61++++++++++++++++++++++++++++++++++++-------------------------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AmountScreen.kt | 34+++++++++++++---------------------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AuthorizePaymentScreen.kt | 101++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/ExchangeSelectionScreen.kt | 43++++++-------------------------------------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/ManageActivity.kt | 50++++++++++++++++++++++++--------------------------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/RegisterParametersScreen.kt | 74++++++++++++++++++++++++++++++++------------------------------------------
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/SummaryActivity.kt | 48+++++++++++++++++++++++-------------------------
Awallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/TalerButton.kt | 27+++++++++++++++++++++++++++
Awallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/TalerLogo.kt | 12++++++++++++
Mwallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalViewModel.kt | 228++++++++++---------------------------------------------------------------------
16 files changed, 611 insertions(+), 422 deletions(-)

diff --git a/wallee-c2ec/.idea/misc.xml b/wallee-c2ec/.idea/misc.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK"> <output url="file://$PROJECT_DIR$/build/classes" /> </component> <component name="ProjectType"> diff --git a/wallee-c2ec/.idea/other.xml b/wallee-c2ec/.idea/other.xml @@ -0,0 +1,329 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="direct_access_persist.xml"> + <option name="deviceSelectionList"> + <list> + <PersistentDeviceSelectionData> + <option name="api" value="27" /> + <option name="brand" value="DOCOMO" /> + <option name="codename" value="F01L" /> + <option name="id" value="F01L" /> + <option name="manufacturer" value="FUJITSU" /> + <option name="name" value="F-01L" /> + <option name="screenDensity" value="360" /> + <option name="screenX" value="720" /> + <option name="screenY" value="1280" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="28" /> + <option name="brand" value="DOCOMO" /> + <option name="codename" value="SH-01L" /> + <option name="id" value="SH-01L" /> + <option name="manufacturer" value="SHARP" /> + <option name="name" value="AQUOS sense2 SH-01L" /> + <option name="screenDensity" value="480" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2160" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="Lenovo" /> + <option name="codename" value="TB370FU" /> + <option name="id" value="TB370FU" /> + <option name="manufacturer" value="Lenovo" /> + <option name="name" value="Tab P12" /> + <option name="screenDensity" value="340" /> + <option name="screenX" value="1840" /> + <option name="screenY" value="2944" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="31" /> + <option name="brand" value="samsung" /> + <option name="codename" value="a51" /> + <option name="id" value="a51" /> + <option name="manufacturer" value="Samsung" /> + <option name="name" value="Galaxy A51" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2400" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="google" /> + <option name="codename" value="akita" /> + <option name="id" value="akita" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 8a" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2400" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="33" /> + <option name="brand" value="samsung" /> + <option name="codename" value="b0q" /> + <option name="id" value="b0q" /> + <option name="manufacturer" value="Samsung" /> + <option name="name" value="Galaxy S22 Ultra" /> + <option name="screenDensity" value="600" /> + <option name="screenX" value="1440" /> + <option name="screenY" value="3088" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="32" /> + <option name="brand" value="google" /> + <option name="codename" value="bluejay" /> + <option name="id" value="bluejay" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 6a" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2400" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="google" /> + <option name="codename" value="caiman" /> + <option name="id" value="caiman" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 9 Pro" /> + <option name="screenDensity" value="360" /> + <option name="screenX" value="960" /> + <option name="screenY" value="2142" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="google" /> + <option name="codename" value="comet" /> + <option name="id" value="comet" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 9 Pro Fold" /> + <option name="screenDensity" value="390" /> + <option name="screenX" value="2076" /> + <option name="screenY" value="2152" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="29" /> + <option name="brand" value="samsung" /> + <option name="codename" value="crownqlteue" /> + <option name="id" value="crownqlteue" /> + <option name="manufacturer" value="Samsung" /> + <option name="name" value="Galaxy Note9" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="2220" /> + <option name="screenY" value="1080" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="samsung" /> + <option name="codename" value="dm3q" /> + <option name="id" value="dm3q" /> + <option name="manufacturer" value="Samsung" /> + <option name="name" value="Galaxy S23 Ultra" /> + <option name="screenDensity" value="600" /> + <option name="screenX" value="1440" /> + <option name="screenY" value="3088" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="samsung" /> + <option name="codename" value="e1q" /> + <option name="id" value="e1q" /> + <option name="manufacturer" value="Samsung" /> + <option name="name" value="Galaxy S24" /> + <option name="screenDensity" value="480" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2340" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="33" /> + <option name="brand" value="google" /> + <option name="codename" value="felix" /> + <option name="id" value="felix" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel Fold" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="2208" /> + <option name="screenY" value="1840" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="google" /> + <option name="codename" value="felix" /> + <option name="id" value="felix" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel Fold" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="2208" /> + <option name="screenY" value="1840" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="33" /> + <option name="brand" value="google" /> + <option name="codename" value="felix_camera" /> + <option name="id" value="felix_camera" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel Fold (Camera-enabled)" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="2208" /> + <option name="screenY" value="1840" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="33" /> + <option name="brand" value="samsung" /> + <option name="codename" value="gts8uwifi" /> + <option name="id" value="gts8uwifi" /> + <option name="manufacturer" value="Samsung" /> + <option name="name" value="Galaxy Tab S8 Ultra" /> + <option name="screenDensity" value="320" /> + <option name="screenX" value="1848" /> + <option name="screenY" value="2960" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="google" /> + <option name="codename" value="husky" /> + <option name="id" value="husky" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 8 Pro" /> + <option name="screenDensity" value="390" /> + <option name="screenX" value="1008" /> + <option name="screenY" value="2244" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="30" /> + <option name="brand" value="motorola" /> + <option name="codename" value="java" /> + <option name="id" value="java" /> + <option name="manufacturer" value="Motorola" /> + <option name="name" value="G20" /> + <option name="screenDensity" value="280" /> + <option name="screenX" value="720" /> + <option name="screenY" value="1600" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="google" /> + <option name="codename" value="komodo" /> + <option name="id" value="komodo" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 9 Pro XL" /> + <option name="screenDensity" value="360" /> + <option name="screenX" value="1008" /> + <option name="screenY" value="2244" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="33" /> + <option name="brand" value="google" /> + <option name="codename" value="lynx" /> + <option name="id" value="lynx" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 7a" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2400" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="31" /> + <option name="brand" value="google" /> + <option name="codename" value="oriole" /> + <option name="id" value="oriole" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 6" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2400" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="33" /> + <option name="brand" value="google" /> + <option name="codename" value="panther" /> + <option name="id" value="panther" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 7" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2400" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="samsung" /> + <option name="codename" value="q5q" /> + <option name="id" value="q5q" /> + <option name="manufacturer" value="Samsung" /> + <option name="name" value="Galaxy Z Fold5" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1812" /> + <option name="screenY" value="2176" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="samsung" /> + <option name="codename" value="q6q" /> + <option name="id" value="q6q" /> + <option name="manufacturer" value="Samsung" /> + <option name="name" value="Galaxy Z Fold6" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1856" /> + <option name="screenY" value="2160" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="30" /> + <option name="brand" value="google" /> + <option name="codename" value="r11" /> + <option name="id" value="r11" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel Watch" /> + <option name="screenDensity" value="320" /> + <option name="screenX" value="384" /> + <option name="screenY" value="384" /> + <option name="type" value="WEAR_OS" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="30" /> + <option name="brand" value="google" /> + <option name="codename" value="redfin" /> + <option name="id" value="redfin" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 5" /> + <option name="screenDensity" value="440" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2340" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="google" /> + <option name="codename" value="shiba" /> + <option name="id" value="shiba" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 8" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2400" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="33" /> + <option name="brand" value="google" /> + <option name="codename" value="tangorpro" /> + <option name="id" value="tangorpro" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel Tablet" /> + <option name="screenDensity" value="320" /> + <option name="screenX" value="1600" /> + <option name="screenY" value="2560" /> + </PersistentDeviceSelectionData> + <PersistentDeviceSelectionData> + <option name="api" value="34" /> + <option name="brand" value="google" /> + <option name="codename" value="tokay" /> + <option name="id" value="tokay" /> + <option name="manufacturer" value="Google" /> + <option name="name" value="Pixel 9" /> + <option name="screenDensity" value="420" /> + <option name="screenX" value="1080" /> + <option name="screenY" value="2424" /> + </PersistentDeviceSelectionData> + </list> + </option> + </component> +</project> +\ No newline at end of file diff --git a/wallee-c2ec/app/release/app-release.apk b/wallee-c2ec/app/release/app-release.apk Binary files differ. diff --git a/wallee-c2ec/app/release/baselineProfiles/0/app-release.dm b/wallee-c2ec/app/release/baselineProfiles/0/app-release.dm Binary files differ. diff --git a/wallee-c2ec/app/release/baselineProfiles/1/app-release.dm b/wallee-c2ec/app/release/baselineProfiles/1/app-release.dm Binary files differ. diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/MainActivity.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/MainActivity.kt @@ -24,15 +24,15 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.Button import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import ch.bfh.habej2.wallee_c2ec.ui.theme.Walleec2ecTheme import ch.bfh.habej2.wallee_c2ec.withdrawal.ManageActivity +import ch.bfh.habej2.wallee_c2ec.withdrawal.TalerButton +import ch.bfh.habej2.wallee_c2ec.withdrawal.TalerLogo import ch.bfh.habej2.wallee_c2ec.withdrawal.WithdrawalActivity class MainActivity : ComponentActivity() { @@ -50,14 +50,19 @@ class MainActivity : ComponentActivity() { Column( horizontalAlignment = Alignment.CenterHorizontally ) { - Text(text = "Withdraw Taler using Wallee") - Button(onClick = { ctx.startActivity(Intent(this@MainActivity, WithdrawalActivity::class.java)) }) { - Text(text = "Start Withdrawal") - } - Button(onClick = { ctx.startActivity(Intent(this@MainActivity, ManageActivity::class.java)) }) { - Text(text = "Manage") - } + TalerLogo() + + TalerButton( + text = "Withdrawal", + onClick = { ctx.startActivity(Intent(this@MainActivity, WithdrawalActivity::class.java)) } + ) + + TalerButton( + text = "Settings", + onClick = { ctx.startActivity(Intent(this@MainActivity, ManageActivity::class.java)) } + ) + } } } diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/wallee/WalleeResponseHandler.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/wallee/WalleeResponseHandler.kt @@ -29,53 +29,36 @@ class WalleeResponseHandler( private val activity: Activity, ) : ResponseHandler() { + private val enableDebugLogs = true + lateinit var model: WithdrawalViewModel override fun authorizeTransactionReply(response: TransactionResponse?) { - println("C2EC-TRANSACTION-RESPONSE: $response") - if (response == null) { model.withdrawalOperationFailed(activity) activity.finish() return } - println("C2EC-TRANSACTION-RESPONSE: $response") - - if (response.transaction.metaData.isNotEmpty()) { - response.transaction.metaData.entries.forEach { - println("METADATA: ${it.key}=${it.value}") - } - } else { - println("METADATA EMPTY") - } - - println("TRANSACTION: ${response.transaction.totalAmountIncludingTax} ${response.transaction.currency}") - response.transaction.lineItems.forEach { - println("TRANSACTION: LINE-ITEM id=${it.id}, name=${it.name}, amount-including-taxes=${it.totalAmountIncludingTax}") - } - println("TRANSACTION: ${response.state}") - println("TRANSACTION: ${response.authorizationCode}") - println("TRANSACTION: ${response.acquirerId}") - println("TRANSACTION: ${response.reserveReference}") + if (enableDebugLogs) + this.printTransactionResponse(response) - model.updateWalleeTransactionReply(response, activity) + model.updateWalleeTransactionReply(response) } override fun completeTransactionReply(response: TransactionCompletionResponse?) { - println("C2EC-COMPLETION-RESPONSE: $response") - if (response == null) { model.withdrawalOperationFailed(activity) activity.finish() return } - println("C2EC-COMPLETION-RESPONSE: ${response.transactionCompletion}") + if (enableDebugLogs) + this.printCompletionResponse(response) - model.updateWalleeTransactionCompletion(response, activity) + model.updateWalleeTransactionCompletion(response) } override fun executeFinalBalanceReply(result: FinalBalanceResult?) { @@ -95,7 +78,35 @@ class WalleeResponseHandler( println("terminal app sdk compatible: $isCompatible") if (isCompatible == null || !isCompatible) { // just dont start withdrawals when api is not compatible + println("not starting application because sdk version not compatible") activity.finish() } } + + private fun printTransactionResponse(response: TransactionResponse) { + + println("C2EC-TRANSACTION-RESPONSE: $response") + + if (response.transaction.metaData.isNotEmpty()) { + response.transaction.metaData.entries.forEach { + println("METADATA: ${it.key}=${it.value}") + } + } else { + println("METADATA EMPTY") + } + + println("TRANSACTION: ${response.transaction.totalAmountIncludingTax} ${response.transaction.currency}") + response.transaction.lineItems.forEach { + println("TRANSACTION: LINE-ITEM id=${it.id}, name=${it.name}, amount-including-taxes=${it.totalAmountIncludingTax}") + } + println("TRANSACTION: ${response.state}") + println("TRANSACTION: ${response.authorizationCode}") + println("TRANSACTION: ${response.acquirerId}") + println("TRANSACTION: ${response.reserveReference}") + } + + private fun printCompletionResponse(response: TransactionCompletionResponse) { + + println("C2EC-COMPLETION-RESPONSE: ${response.transactionCompletion}") + } } \ No newline at end of file diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AmountScreen.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AmountScreen.kt @@ -52,6 +52,10 @@ fun AmountScreen( horizontalAlignment = Alignment.CenterHorizontally ) { + if (uiState.encodedWopid.isNotBlank()) { + navigateToWhenAmountEntered() + } + Text(uiState.amountError, color = Color.Red) TextField( @@ -73,27 +77,15 @@ fun AmountScreen( Text(text = "total: ${if (uiState.withdrawalFees.currency == "") "" else uiState.amount.plus(uiState.withdrawalFees)}") - Button(onClick = { - println("clicked 'pay'") - model.setupWithdrawal(activity, navigateToWhenAmountEntered) - }, enabled = model.validAmount(uiState.amountStr) && uiState.withdrawalFees.currency != "") { - Text(text = "withdraw") - } + TalerButton( + text = "withdraw", + onClick = { + println("clicked 'pay'") + model.setupWithdrawal(activity, navigateToWhenAmountEntered) + }, + enableExpr = {model.validAmount(uiState.amountStr) && uiState.withdrawalFees.currency != ""} + ) - Button(onClick = { - model.withdrawalOperationFailed(activity) - }) { - Text(text = "abort") - } + TalerButton(text = "abort", onClick = { model.withdrawalOperationFailed(activity) }) } } - -//@Preview -//@Composable -//fun AmountScreenPreview() { -// -// val m = WithdrawalViewModel() -// m.validateInput("15.0") -// -// AmountScreen(model = m, Activity()) {} -//} diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AuthorizePaymentScreen.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/AuthorizePaymentScreen.kt @@ -19,23 +19,22 @@ package ch.bfh.habej2.wallee_c2ec.withdrawal import android.app.Activity -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.width -import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.unit.dp import com.wallee.android.till.sdk.ApiClient import com.wallee.android.till.sdk.data.LineItem import com.wallee.android.till.sdk.data.Transaction +import com.wallee.android.till.sdk.data.TransactionCompletion import com.wallee.android.till.sdk.data.TransactionProcessingBehavior import java.util.Currency @@ -55,46 +54,74 @@ fun AuthorizePaymentScreen(model: WithdrawalViewModel, activity: Activity, clien horizontalAlignment = Alignment.CenterHorizontally ) { - if (uiState.transactionState == TransactionState.AUTHORIZATION_PENDING) { + when (uiState.transactionState) { + TransactionState.UNREADY_FOR_AUTHORIZATION -> model.withdrawalOperationFailed(activity) + TransactionState.READY_FOR_AUTHORIZATION -> WalleeAuthorizePayment( + withdrawalAmount = withdrawalAmount, + uiState = uiState, + model = model, + activity = activity, + client = client + ) + TransactionState.AUTHORIZATION_TRIGGERED -> WalleeProcessingTransaction() + TransactionState.AUTHORIZATION_FAILED -> model.withdrawalOperationFailed(activity) + TransactionState.AUTHORIZED -> WalleeProcessingTransaction() // Maybe following is not needed because completion immediately is specified... WalleeCompletingPayment(client, model) + TransactionState.COMPLETION_TRIGGERED -> WalleeProcessingTransaction() + TransactionState.COMPLETED -> model.talerCheckWalleeRequest(activity) + TransactionState.COMPLETION_FAILED -> model.withdrawalOperationFailed(activity) + } + } +} + +@Composable +private fun WalleeCompletingPayment( + client: ApiClient, + model: WithdrawalViewModel +) { + + val uiState by model.uiState.collectAsState() - Text(text = "Authorizing transaction...") + Text(text = "completing transaction...") - val transaction = Transaction.Builder(withdrawalAmount) - .setCurrency(Currency.getInstance(uiState.currency)) - .setInvoiceReference(uiState.encodedWopid) - .setMerchantReference(uiState.encodedWopid) - .setTransactionProcessingBehavior(TransactionProcessingBehavior.COMPLETE_IMMEDIATELY) + LaunchedEffect(Unit) { + model.setCompleting() + client.completeTransaction( + TransactionCompletion.Builder(uiState.transaction!!.transaction.lineItems) + .setCurrency(uiState.transaction!!.transaction.currency) + .setReserveReference(uiState.transaction!!.reserveReference!!) .build() + ) + } +} - try { - model.setAuthorizing() - client.authorizeTransaction(transaction) - } catch (e: Exception) { - println("FAILED authorizing transaction ${e.message}") - model.withdrawalOperationFailed(activity) - e.printStackTrace() - } +@Composable +private fun WalleeProcessingTransaction() { + Text(text = "processing transaction...") +} - Button(onClick = { - model.withdrawalOperationFailed(activity) - }) { - Text(text = "abort") - } +@Composable +private fun WalleeAuthorizePayment( + withdrawalAmount: List<LineItem>, + uiState: WithdrawalOperationState, + model: WithdrawalViewModel, + activity: Activity, + client: ApiClient +) = LaunchedEffect(Unit) { + if (uiState.transactionState == TransactionState.READY_FOR_AUTHORIZATION) { + val transaction = Transaction.Builder(withdrawalAmount) + .setCurrency(Currency.getInstance(uiState.currency)) + .setInvoiceReference(uiState.encodedWopid) + .setMerchantReference(uiState.encodedWopid) + .setTransactionProcessingBehavior(TransactionProcessingBehavior.COMPLETE_IMMEDIATELY) + .build() + try { + model.setAuthorizing() + client.authorizeTransaction(transaction) + } catch (e: Exception) { + println("FAILED authorizing transaction ${e.message}") + model.withdrawalOperationFailed(activity) + e.printStackTrace() } } } - -//@Preview -//@Composable -//fun AuthorizePaymentScreenPreview() { -// -// val m = WithdrawalViewModel() -// m.mockExchange() -// m.mockCurrency() -// m.validateInput("75.50") -// m.mockWopid() -// m.mockTransaction() -// -// AuthorizePaymentScreen(model = m, activity = Activity(), client = ApiClient(WalleeResponseHandler(WithdrawalActivity(), m))) -//} diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/ExchangeSelectionScreen.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/ExchangeSelectionScreen.kt @@ -23,40 +23,21 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.width -import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.font.FontVariation.width -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import ch.bfh.habej2.wallee_c2ec.client.taler.config.TalerTerminalConfig var exchanges = listOf( - TalerTerminalConfig( "BFH Taler (CHF)", "https://terminals.chf.taler.net", "Wallee-2", "YTLYIPPOTNqen//Rl7uc58FUTb8K3Kk0Zp/OSlC0Ufk=" - ), - - TalerTerminalConfig( - "Test System (CHF)", - "http://taler-c2ec.ti.bfh.ch", - "Wallee-3", - "YHrpzeHUyybGT5gOY9VAiZ7QAV/icOXtHPRKZTXv2n8=" - ), - - TalerTerminalConfig( - "Test System (CHF, https)", - "https://taler-c2ec.ti.bfh.ch", - "Wallee-3", - "YHrpzeHUyybGT5gOY9VAiZ7QAV/icOXtHPRKZTXv2n8=" - ), + ) ) @Composable @@ -74,33 +55,22 @@ fun ExchangeSelectionScreen( horizontalAlignment = Alignment.CenterHorizontally ) { + TalerLogo() + Text(text = "Choose the exchange to withdraw from") exchanges.forEach { Row { Column( ) { - Button(onClick = { + TalerButton(text = it.displayName, onClick = { model.chooseExchange(it, activity) onNavigateToWithdrawal() - }, modifier = Modifier.width(configuration.screenWidthDp.dp)) { - Text(text = it.displayName) - } + }) } } } - Button(onClick = { activity.finish() }) { - Text(text = "abort") - } + TalerButton(text = "abort", onClick = { activity.finish() }) } } - -//@Preview -//@Composable -//fun ExchangeSelectionScreenPreview() { -// -// ExchangeSelectionScreen(model = WithdrawalViewModel(), activity = Activity()) { -// -// } -//} -\ No newline at end of file diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/ManageActivity.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/ManageActivity.kt @@ -100,32 +100,30 @@ class ManageActivity : ComponentActivity() { Column( horizontalAlignment = Alignment.CenterHorizontally ) { - Button(onClick = { - walleeClient.executeFinalBalance() - }) { - Text(text = "Execute Final Balance") - } - - //TestTransactionScreen(walleeClient) - - Button(onClick = { - SIM_WALLET_ENABLED.value = !SIM_WALLET_ENABLED.value - }) { - Text(text = "${if (SIM_WALLET_ENABLED.value) "disable" else "enable"} wallet simulation") - } - - Button(onClick = { - INSTANT_SETTLEMENT_ENABLED.value = !INSTANT_SETTLEMENT_ENABLED.value - }) { - Text(text = "${if (INSTANT_SETTLEMENT_ENABLED.value) "disable" else "enable"} instant settlement") - } - - Button(onClick = { - onDestroy() - this@ManageActivity.finish() - }) { - Text(text = "back") - } + TalerButton( + text = "Execute Final Balance", + onClick = { walleeClient.executeFinalBalance() } + ) + + TalerButton( + text = "${if (INSTANT_SETTLEMENT_ENABLED.value) "disable" else "enable"} instant settlement", + onClick = { INSTANT_SETTLEMENT_ENABLED.value = !INSTANT_SETTLEMENT_ENABLED.value } + ) + + TalerButton( + text = "back", + onClick = { + onDestroy() + this@ManageActivity.finish() + } + ) + +// The following few lines are meant for local testing, debugging etc. +// TestTransactionScreen(walleeClient) +// TalerButton( +// text = "${if (SIM_WALLET_ENABLED.value) "disable" else "enable"} wallet simulation", +// onClick = { SIM_WALLET_ENABLED.value = !SIM_WALLET_ENABLED.value } +// ) } } } diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/RegisterParametersScreen.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/RegisterParametersScreen.kt @@ -18,7 +18,6 @@ package ch.bfh.habej2.wallee_c2ec.withdrawal -import android.annotation.SuppressLint import android.app.Activity import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -26,6 +25,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -33,7 +33,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.unit.dp -@SuppressLint("StateFlowValueCalledInComposition") +private const val MAX_RETRIES = 3 // one retry runs 10sec -> 3x10sec = 30sec + @Composable fun RegisterParametersScreen( model: WithdrawalViewModel, @@ -44,17 +45,41 @@ fun RegisterParametersScreen( val uiState by model.uiState.collectAsState() val configuration = LocalConfiguration.current + LaunchedEffect(Unit) { + // this will run exactly once -> Unit as parameter to LaunchedEffect. + if (ManageActivity.SIM_WALLET_ENABLED.value) { + ManageActivity.simulateParameterRegistration( + uiState.terminalsApiBasePath, + uiState.encodedWopid + ) + } + if (model.uiState.value.setupWithdrawalRetries < 1) { + println("starting authorization") + model.startAuthorizationWhenReadyOrSetRetry() + } + } + Column( Modifier.width(configuration.screenWidthDp.dp), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { + if (uiState.transactionState == TransactionState.READY_FOR_AUTHORIZATION) { + navigateToWhenRegistered() + return + } + + if (uiState.setupWithdrawalRetries > MAX_RETRIES && + uiState.transactionState == TransactionState.UNREADY_FOR_AUTHORIZATION) { + println("maximal retries exceeded") + model.withdrawalOperationFailed(activity) + return + } + Text(text = "Scan the QR Code with your Taler Wallet to") Text(text = "register the withdrawal parameters") - // Text(text = "QR-Code content: ${TalerConstants.formatTalerUri(uiState.exchangeBankIntegrationApiUrl, uiState.encodedWopid)}") - println("QR-CODE: ${TalerConstants.formatTalerUri(uiState.terminalsApiBasePath, uiState.encodedWopid)}") QRCode( TalerConstants.formatTalerUri( uiState.terminalsApiBasePath, @@ -62,41 +87,6 @@ fun RegisterParametersScreen( ) ) - AbortButton(model = model, activity = activity) - Button(onClick = { - if (ManageActivity.SIM_WALLET_ENABLED.value) { - ManageActivity.simulateParameterRegistration( - uiState.terminalsApiBasePath, - uiState.encodedWopid - ) - } - model.startAuthorizationWhenReadyOrAbort(navigateToWhenRegistered) { - activity.finish() - } - }) { - Text(text = "authorize") - } + TalerButton(text = "abort", onClick = { model.withdrawalOperationFailed(activity) }) } -} - -@Composable -fun AbortButton(model: WithdrawalViewModel, activity: Activity) { - Button(onClick = { - model.withdrawalOperationFailed(activity) - }) { - Text(text = "abort") - } -} - -//@Preview -//@Composable -//fun RegisterParametersScreenPreview() { -// -// val m = WithdrawalViewModel() -// m.mockExchange() -// m.mockCurrency() -// m.validateInput("105.70") -// m.mockWopid() -// -// RegisterParametersScreen(model = m, activity = Activity()) {} -//} -\ No newline at end of file +} +\ No newline at end of file diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/SummaryActivity.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/SummaryActivity.kt @@ -22,20 +22,20 @@ import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.compose.foundation.background +import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.width -import androidx.compose.material3.Button +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.unit.dp import ch.bfh.habej2.wallee_c2ec.MainActivity -import ch.bfh.habej2.wallee_c2ec.client.taler.TerminalClientImplementation import ch.bfh.habej2.wallee_c2ec.client.taler.model.Amount import com.squareup.moshi.Json @@ -71,18 +71,13 @@ class SummaryActivity : ComponentActivity() { verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - Text( - text = "Amount authorized", - color = Color.Yellow, - modifier = Modifier.background(color = Color.Black) - ) + Image(imageVector = Icons.Filled.CheckCircle, contentDescription = "Amount authorized successful") + Text(text = "Summary") Text(text = "Amount: ${summary.amount.toString(true)}") Text(text = "Fees: ${summary.fees.toString(true)}") - // Text(text = "Withdrawal Operation ID (QR Code):") - // QRCode(qrCodeContent = summary.encodedWopid, 2.dp) - finishButton() + FinishButton() } } else { Column( @@ -90,28 +85,31 @@ class SummaryActivity : ComponentActivity() { verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { + Image(imageVector = Icons.Filled.Warning, contentDescription = "Failed authorizing payment") + Text(text = "Failed authorizing payment.") Text(text = "Withdrawal aborted.") - finishButton() + FinishButton() } } } } @Composable - private fun finishButton() { - Button(onClick = { - this@SummaryActivity.startActivity( - Intent( - baseContext, - MainActivity::class.java + private fun FinishButton() { + TalerButton( + text = "finish", + onClick = { + this@SummaryActivity.startActivity( + Intent( + baseContext, + MainActivity::class.java + ) ) - ) - reset() - finish() - }) { - Text(text = "finish") - } + reset() + finish() + } + ) } } \ No newline at end of file diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/TalerButton.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/TalerButton.kt @@ -0,0 +1,26 @@ +package ch.bfh.habej2.wallee_c2ec.withdrawal + +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.unit.dp + +@Composable +fun TalerButton(text: String, onClick: () -> Unit, enableExpr: () -> Boolean = { true }) { + + val configuration = LocalConfiguration.current + + Button( + onClick = onClick, + enabled = enableExpr(), + modifier = Modifier + .width((configuration.screenWidthDp * 0.8).dp), + shape = RoundedCornerShape(0.dp) + ) { + Text(text = text) + } +} +\ No newline at end of file diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/TalerLogo.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/TalerLogo.kt @@ -0,0 +1,12 @@ +package ch.bfh.habej2.wallee_c2ec.withdrawal + +import androidx.compose.foundation.Image +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.painterResource +import ch.bfh.habej2.wallee_c2ec.R + +@Composable +fun TalerLogo() = Image( + painter = painterResource(id = R.drawable.taler_logo), + contentDescription = "Taler logo" +) diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalViewModel.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalViewModel.kt @@ -23,6 +23,7 @@ import android.content.Intent import androidx.compose.runtime.Stable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel @@ -38,7 +39,6 @@ import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalSetup import ch.bfh.habej2.wallee_c2ec.withdrawal.WithdrawalViewModel.Companion.generateTransactionIdentifier import com.wallee.android.till.sdk.ApiClient import com.wallee.android.till.sdk.data.State -import com.wallee.android.till.sdk.data.TransactionCompletion import com.wallee.android.till.sdk.data.TransactionCompletionResponse import com.wallee.android.till.sdk.data.TransactionResponse import kotlinx.coroutines.flow.MutableStateFlow @@ -51,10 +51,9 @@ import java.util.UUID object TalerConstants { const val TALER_INTEGRATION = "/taler-integration" - const val TALER_INTEGRATION_WITHDRAWAL_OPERATION = "/withdrawal-operation" fun formatTalerUri(terminalsApiBasePath: String, encodedWopid: String) = - "taler://withdraw/${stripLeadingProtocolIfPresent(terminalsApiBasePath)}/$encodedWopid" // "taler://withdraw/${stripLeadingProtocolIfPresent(terminalsApiBasePath)}$TALER_INTEGRATION_WITHDRAWAL_OPERATION/$encodedWopid" + "taler://withdraw/${stripLeadingProtocolIfPresent(terminalsApiBasePath)}/$encodedWopid" private fun stripLeadingProtocolIfPresent(terminalsApiBasePath: String) = terminalsApiBasePath .removePrefix("https://") @@ -64,6 +63,7 @@ object TalerConstants { @Stable interface WithdrawalOperationState { val requestUid: String + val setupWithdrawalRetries: Int val terminalsApiBasePath: String val encodedWopid: String val amount: Amount @@ -78,17 +78,19 @@ interface WithdrawalOperationState { } enum class TransactionState { - AUTHORIZATION_PENDING, - AUTHORIZATION_STARTED, + UNREADY_FOR_AUTHORIZATION, + READY_FOR_AUTHORIZATION, + AUTHORIZATION_TRIGGERED, AUTHORIZED, AUTHORIZATION_FAILED, - COMPLETING, + COMPLETION_TRIGGERED, COMPLETION_FAILED, COMPLETED, } private class MutableWithdrawalOperationState : WithdrawalOperationState { override val requestUid: String by derivedStateOf { UUID.randomUUID().toString() } + override var setupWithdrawalRetries: Int by mutableIntStateOf(0) override var terminalsApiBasePath: String by mutableStateOf("") override var encodedWopid: String by mutableStateOf("") override var amount: Amount by mutableStateOf(Amount("",0, 0)) @@ -96,7 +98,7 @@ private class MutableWithdrawalOperationState : WithdrawalOperationState { override var amountError: String by mutableStateOf("") override var currency: String by mutableStateOf("") override var withdrawalFees: Amount by mutableStateOf(Amount("", 0,0)) - override var transactionState: TransactionState by mutableStateOf(TransactionState.AUTHORIZATION_PENDING) + override var transactionState: TransactionState by mutableStateOf(TransactionState.UNREADY_FOR_AUTHORIZATION) override var transaction: TransactionResponse? by mutableStateOf(null) override var transactionCompletion: TransactionCompletionResponse? by mutableStateOf(null) override val transactionId: String by derivedStateOf { generateTransactionIdentifier() } @@ -107,8 +109,6 @@ class WithdrawalViewModel( vararg closeables: Closeable ) : ViewModel(*closeables) { - private val signals = Signals() - private var exchangeSelected = false private var terminalClient: TerminalClient? = null private val _uiState = MutableStateFlow(MutableWithdrawalOperationState()) @@ -159,7 +159,6 @@ class WithdrawalViewModel( TerminalClient.FormatAmount(_uiState.value.amount) ) - signals.doOnReady(navigateToWhenAmountEntered) viewModelScope.launch { terminalClient!!.setupWithdrawal(setupReq) { if (!it.isPresent) { @@ -168,10 +167,8 @@ class WithdrawalViewModel( val wopid = it.get().withdrawalId println("retrieved WOPID from c2ec: $wopid") _uiState.value.encodedWopid = wopid - signals.setReady() } } - signals.block() } fun validateInput(amount: String): Boolean { @@ -207,10 +204,14 @@ class WithdrawalViewModel( } fun setAuthorizing() { - _uiState.value.transactionState = TransactionState.AUTHORIZATION_STARTED + _uiState.value.transactionState = TransactionState.AUTHORIZATION_TRIGGERED + } + + fun setCompleting() { + _uiState.value.transactionState = TransactionState.COMPLETION_TRIGGERED } - fun updateWalleeTransactionReply(response: TransactionResponse, activity: Activity) { + fun updateWalleeTransactionReply(response: TransactionResponse) { println("updating wallee transaction: $response") _uiState.value.transaction = response @@ -219,67 +220,39 @@ class WithdrawalViewModel( TransactionState.AUTHORIZED else TransactionState.AUTHORIZATION_FAILED - - println("authorization state: ${_uiState.value.transactionState}") - if (_uiState.value.transactionState == TransactionState.AUTHORIZATION_FAILED) { - println("authorization failed... aborting withdrawal") - withdrawalOperationFailed(activity) - return - } - - _uiState.value.transactionState = TransactionState.COMPLETING - - walleeClient.completeTransaction( - TransactionCompletion.Builder(response.transaction.lineItems) - .setCurrency(response.transaction.currency) - .setReserveReference(response.reserveReference!!) - .build() - ) } fun updateWalleeTransactionCompletion( - completion: TransactionCompletionResponse, - activity: Activity + completion: TransactionCompletionResponse ) { if (completion.state == State.FAILED) { println("completion of the transaction failed... aborting") _uiState.value.transactionState = TransactionState.COMPLETION_FAILED - withdrawalOperationFailed(activity) return } - _uiState.value.transactionState = TransactionState.COMPLETED - _uiState.value.transactionCompletion = completion - if (ManageActivity.INSTANT_SETTLEMENT_ENABLED.value) { walleeClient.executeFinalBalance() } - confirmationRequest(activity) + _uiState.value.transactionState = TransactionState.COMPLETED + _uiState.value.transactionCompletion = completion } - fun startAuthorizationWhenReadyOrAbort( - onSuccess: () -> Unit, - onFailure: () -> Unit - ) = viewModelScope.launch { - println("long-polling for parameter selection") - val sigs = Signals() - sigs.doOnReady(onSuccess) - sigs.doOnError(onFailure) - println("registered callbacks for parameter selection") - println("executing parameter selection long-polling request") - try { - recursiveRetries(uiState.value.encodedWopid, sigs) - } catch (ex: Exception) { - println("error occured: $ex") - sigs.setError() + fun startAuthorizationWhenReadyOrSetRetry() = viewModelScope.launch { + terminalClient!!.retrieveWithdrawalStatus(uiState.value.encodedWopid, 10000) { + if (!it.isPresent) { + println("setting retry because no status was present") + _uiState.value.setupWithdrawalRetries += 1 + } else { + println("withdrawal status: ${it.get().status }") + _uiState.value.transactionState = TransactionState.READY_FOR_AUTHORIZATION + } } - println("awaiting response for parameter selection") - sigs.block() } - private fun confirmationRequest(activity: Activity) { + fun talerCheckWalleeRequest(activity: Activity) { viewModelScope.launch { terminalClient!!.sendConfirmationRequest( _uiState.value.encodedWopid, @@ -305,21 +278,9 @@ class WithdrawalViewModel( } } - private fun recursiveRetries(wopid: String, sigs: Signals, retries: Int = 0, max: Int = 3) { - terminalClient!!.retrieveWithdrawalStatus(wopid, 10000) { - if (it.isPresent) { - sigs.setReady() - } else { - if (retries < max) { - recursiveRetries(wopid, sigs, retries+1, max) - } - } - } - } - fun withdrawalOperationFailed(activity: Activity) { viewModelScope.launch { - if (_uiState.value.transactionState == TransactionState.AUTHORIZATION_PENDING || + if (_uiState.value.transactionState == TransactionState.UNREADY_FOR_AUTHORIZATION || _uiState.value.transactionState == TransactionState.AUTHORIZATION_FAILED || _uiState.value.transactionState == TransactionState.COMPLETION_FAILED) { terminalClient!!.abortWithdrawal(uiState.value.encodedWopid) { @@ -360,141 +321,10 @@ class WithdrawalViewModel( Optional.empty() } } - -// var currency = "" -// var valueFraction = "" -// if (inp.contains(":")) { -// val splitted = inp.split(":") -// currency = splitted[0] -// if (currency != uiState.value.currency) { -// println("illegal currency $currency. expected ${uiState.value.currency}") -// return Optional.empty() -// } -// valueFraction = splitted[1] -// } else { -// valueFraction = inp -// } -// -// val points = valueFraction.count { it == '.' } -// if (points > 1) { -// return Optional.empty() -// } -// -// if (points == 1) { -// val valueStr = valueFraction.split(".")[0] -// val fracStr = valueFraction.split(".")[1] -// return try { -// val value = valueStr.toLong() -// var frac = 0 -// if (fracStr.isNotEmpty()) { -// frac = fracStr.toInt() -// } -// Optional.of(Amount(uiState.value.currency, value, frac)) -// } catch (ex: NumberFormatException) { -// println(ex.message) -// Optional.empty() -// } -// } -// -// return try { -// val value = valueFraction.toLong() -// Optional.of(Amount(uiState.value.currency, value, 0)) -// } catch (ex: NumberFormatException) { -// println(ex.message) -// Optional.empty() -// } } fun resetAmountStr() { println("resetting amountStr. was ${_uiState.value.amountStr}") _uiState.value.amountStr = "" } - - class Signals { - - private var listeners = mutableListOf<() -> Unit>() - private var errors = mutableListOf<() -> Unit>() - private var ready = false - private var error = false - - fun doOnReady(f: () -> Unit) { - listeners.add(f) - } - - fun doOnError(e: () -> Unit) { - errors.add(e) - } - - fun setError() { - error = true - ready = true - } - - fun setReady() { - ready = true - } - - fun block() { - if (error) { - errors.forEach { it() } - reset() - return - } - while (!ready) { - //println("waiting for ready") - Thread.sleep(5L) - } - if (error) { - errors.forEach { it() } - } else { - listeners.forEach { it() } - } - reset() - } - - private fun reset() { - listeners = mutableListOf() - errors = mutableListOf() - error = false - ready = false - } - } - - // API's FOR TESTING / PREVIEW -// fun mockExchange() { -// val cfg = TalerTerminalConfig( -// "CHF Exchange (BFH)", -// "http://taler-c2ec.ti.bfh.ch", -// "salut", -// "ciao" -// ) -// -// _uiState.value.exchangeBankIntegrationApiUrl Amount(0,0)= "${cfg.terminalApiBaseUrl}${TalerConstants.TALER_INTEGRATION}" -// //chooseExchange(cfg, Activity()) -// } -// -// fun mockCurrency() { -// updateCurrency("CHF") -// } -// -// fun mockWopid() { -// _uiState.value.encodedWopid = TerminalClientMock().mockWopidOrReservePubKey() -// } -// -// fun mockTransaction() { -// -// val mockedWopid = TerminalClientMock().mockWopidOrReservePubKey() -// _uiState.value.transaction = TransactionResponse.Builder( -// Transaction.Builder( -// listOf( -// LineItem.Builder("id", BigDecimal(23.50)).build() -// ) -// ) -// .setMerchantReference(mockedWopid) -// .setInvoiceReference(mockedWopid) -// .build(), -// State.SUCCESSFUL, -// ResultCode("judihui", "the judihui result code indicates absolute happiness"), -// listOf()).build() -// } } \ No newline at end of file