commit 60a1a97a811029e32bc787c58c826e746e8bc144 parent 59749fe139b7de27bfc142f02da093db612d5098 Author: fsb2 <benjamin.fehrensen@bfh.ch> Date: Thu, 10 Apr 2025 15:56:54 +0200 New Design Diffstat:
19 files changed, 266 insertions(+), 229 deletions(-)
diff --git a/wallee-c2ec/app/build.gradle.kts b/wallee-c2ec/app/build.gradle.kts @@ -65,6 +65,8 @@ dependencies { implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) implementation(libs.androidx.navigation.compose) + implementation(libs.androidx.material.icons.extended.android) + implementation(libs.androidx.compose.material) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/wallee-c2ec/app/src/main/AndroidManifest.xml b/wallee-c2ec/app/src/main/AndroidManifest.xml @@ -30,22 +30,13 @@ <activity android:name=".withdrawal.WithdrawalActivity" android:launchMode="singleTop" - android:exported="true" + android:exported="false" android:theme="@style/Theme.Walleec2ec"> </activity> - <activity android:name=".withdrawal.ManageActivity" - android:exported="true" - android:theme="@style/Theme.Walleec2ec"> - </activity> - - - <activity - android:name=".withdrawal.SummaryActivity" - android:exported="true" + android:exported="false" android:theme="@style/Theme.Walleec2ec"> </activity> </application> - </manifest> \ No newline at end of file diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/taler/TerminalClientImplementation.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/client/taler/TerminalClientImplementation.kt @@ -181,6 +181,7 @@ class TerminalClientImplementation ( } } catch (ex: Exception) { println("withdrawal status request failed: ${ex.message}") + callback(Optional.empty()) } } 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 @@ -23,7 +23,6 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable @@ -31,10 +30,11 @@ 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.res.colorResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp +import ch.bfh.habej2.wallee_c2ec.R @Composable fun AmountScreen( @@ -46,6 +46,7 @@ fun AmountScreen( val uiState by model.uiState.collectAsState() val configuration = LocalConfiguration.current + Column( Modifier.width(configuration.screenWidthDp.dp), verticalArrangement = Arrangement.Center, @@ -56,7 +57,9 @@ fun AmountScreen( navigateToWhenAmountEntered() } - Text(uiState.amountError, color = Color.Red) + TalerLogo(paddingBottom = 0.dp) + + Text(uiState.amountError, color = colorResource(id = R.color.bfh_red)) TextField( uiState.amountStr, @@ -68,17 +71,17 @@ fun AmountScreen( }, label = { Text(text = "Withdrawal amount") }, keyboardOptions = KeyboardOptions( - autoCorrect = false, + autoCorrectEnabled = false, keyboardType = KeyboardType.Number ) ) - Text(text = "fees: ${if (uiState.withdrawalFees.currency == "") "" else uiState.withdrawalFees}") + Text(text = "Fees: ${if (uiState.withdrawalFees.currency == "") "" else uiState.withdrawalFees}") - Text(text = "total: ${if (uiState.withdrawalFees.currency == "") "" else uiState.amount.plus(uiState.withdrawalFees)}") + Text(text = "Total: ${if (uiState.withdrawalFees.currency == "") "" else uiState.amount.plus(uiState.withdrawalFees)}") TalerButton( - text = "withdraw", + text = "Continue", onClick = { println("clicked 'pay'") model.setupWithdrawal(activity) @@ -86,6 +89,6 @@ fun AmountScreen( enableExpr = {model.validAmount(uiState.amountStr) && uiState.withdrawalFees.currency != ""} ) - TalerButton(text = "abort", onClick = { model.withdrawalOperationFailed(activity) }) + TalerButton(text = "Exit", onClick = { model.withdrawalOperationFailed(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 @@ -40,13 +40,21 @@ import com.wallee.android.till.sdk.data.TransactionProcessingBehavior import java.util.Currency @Composable -fun AuthorizePaymentScreen(model: WithdrawalViewModel, activity: Activity, client: ApiClient) { +fun AuthorizePaymentScreen( + model: WithdrawalViewModel, + activity: Activity, + client: ApiClient, + navigateToWhenRegistered: () -> Unit +) { val uiState by model.uiState.collectAsState() val configuration = LocalConfiguration.current val withdrawalAmount = LineItem - .ListBuilder(uiState.encodedWopid, uiState.amount.plus(uiState.withdrawalFees).toBigDecimal()) + .ListBuilder( + uiState.encodedWopid, + uiState.amount.plus(uiState.withdrawalFees).toBigDecimal() + ) .build() LaunchedEffect(Unit) { @@ -58,7 +66,13 @@ fun AuthorizePaymentScreen(model: WithdrawalViewModel, activity: Activity, clien .setCurrency(Currency.getInstance(uiState.currency)) .setInvoiceReference(uiState.encodedWopid) .setMerchantReference(uiState.encodedWopid) - .setCustomText(activity.getString(R.string.wallee_custom_text)) + .setCustomText( + String.format( + activity.getString(R.string.wallee_custom_text), + uiState.currency, + uiState.amount + ) + ) .setTransactionProcessingBehavior(TransactionProcessingBehavior.COMPLETE_IMMEDIATELY) .build() @@ -85,10 +99,14 @@ fun AuthorizePaymentScreen(model: WithdrawalViewModel, activity: Activity, clien WithdrawalState.READY_FOR_AUTHORIZATION -> WalleeProcessingTransaction() WithdrawalState.AUTHORIZATION_TRIGGERED -> WalleeProcessingTransaction() WithdrawalState.AUTHORIZATION_FAILED -> model.withdrawalOperationFailed(activity) - WithdrawalState.AUTHORIZED -> WalleeCompletingPayment(client, model) // TODO: Maybe following is not needed because completion immediately is specified: "WalleeCompletingPayment(client, model)" + WithdrawalState.E2EC_TIMEOUT -> model.withdrawalOperationFailed(activity) + WithdrawalState.AUTHORIZED -> WalleeCompletingPayment( + client, + model + ) // TODO: Maybe following is not needed because completion immediately is specified: "WalleeCompletingPayment(client, model)" WithdrawalState.COMPLETION_TRIGGERED -> WalleeProcessingTransaction() - WithdrawalState.COMPLETED -> model.talerCheckWalleeRequest(activity) - WithdrawalState.COMPLETION_FAILED -> model.withdrawalOperationFailed(activity) + WithdrawalState.COMPLETED -> navigateToWhenRegistered() + WithdrawalState.COMPLETION_FAILED -> navigateToWhenRegistered() } } } @@ -101,7 +119,9 @@ private fun WalleeCompletingPayment( val uiState by model.uiState.collectAsState() - Text(text = "completing transaction...") + TalerLogo() + + Text(text = "Completing transaction...") LaunchedEffect(key1 = Unit) { @@ -114,10 +134,14 @@ private fun WalleeCompletingPayment( ) model.setCompleting() } + } } @Composable private fun WalleeProcessingTransaction() { - Text(text = "processing transaction...") + + TalerLogo() + + Text(text = "Processing transaction...") } 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 @@ -22,6 +22,7 @@ import android.app.Activity import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -29,16 +30,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration 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=" - ) -) @Composable fun ExchangeSelectionScreen( @@ -57,7 +49,8 @@ fun ExchangeSelectionScreen( TalerLogo() - Text(text = "Choose the exchange to withdraw from") + Text(text = "Choose the exchange to withdraw from", + modifier = Modifier.padding(bottom = 15.dp)) exchanges.forEach { Row { @@ -71,6 +64,6 @@ fun ExchangeSelectionScreen( } } - TalerButton(text = "abort", onClick = { activity.finish() }) + TalerButton(text = "Back", onClick = { activity.finish() }) } } diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/Exchanges.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/Exchanges.kt @@ -0,0 +1,12 @@ +package ch.bfh.habej2.wallee_c2ec.withdrawal + +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=" + ) +) 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 @@ -23,10 +23,8 @@ 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.runtime.mutableStateOf import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -100,18 +98,21 @@ class ManageActivity : ComponentActivity() { Column( horizontalAlignment = Alignment.CenterHorizontally ) { + + TalerLogo() + TalerButton( text = "Execute Final Balance", onClick = { walleeClient.executeFinalBalance() } ) TalerButton( - text = "${if (INSTANT_SETTLEMENT_ENABLED.value) "disable" else "enable"} instant settlement", + text = "${if (INSTANT_SETTLEMENT_ENABLED.value) "Disable" else "Enable"} instant settlement", onClick = { INSTANT_SETTLEMENT_ENABLED.value = !INSTANT_SETTLEMENT_ENABLED.value } ) TalerButton( - text = "back", + text = "Back", onClick = { onDestroy() this@ManageActivity.finish() 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 @@ -19,8 +19,12 @@ package ch.bfh.habej2.wallee_c2ec.withdrawal import android.app.Activity +import android.os.Handler +import android.os.Looper +import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -31,8 +35,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.unit.dp +import ch.bfh.habej2.wallee_c2ec.R -private const val MAX_RETRIES = 4 @Composable fun RegisterParametersScreen( @@ -52,7 +56,7 @@ fun RegisterParametersScreen( uiState.encodedWopid ) } - if (model.uiState.value.setupWithdrawalRetries < 1 && + if (model.uiState.value.setupWithdrawalRetries <= MAX_TRIES && model.uiState.value.withdrawalState == WithdrawalState.UNREADY_FOR_AUTHORIZATION) { model.setAuthorizationReadyOrRetry() } @@ -69,14 +73,17 @@ fun RegisterParametersScreen( return } - if (uiState.setupWithdrawalRetries > MAX_RETRIES-1 && - uiState.withdrawalState == WithdrawalState.UNREADY_FOR_AUTHORIZATION) { - println("maximal retries exceeded") + if (uiState.withdrawalState == WithdrawalState.E2EC_TIMEOUT) { + println("Operation timed out") + Handler(Looper.getMainLooper()).post { + Toast.makeText(activity.baseContext, activity.getText( + R.string.timeout), Toast.LENGTH_SHORT).show()} model.withdrawalOperationFailed(activity) return } - Text(text = "Scan the QR Code with your Taler Wallet to") + Text(text = "Scan the QR Code with your Taler Wallet to", + modifier = Modifier.padding(top = 15.dp)) Text(text = "register the withdrawal parameters") QRCode( @@ -86,6 +93,6 @@ fun RegisterParametersScreen( ) ) - TalerButton(text = "abort", onClick = { model.withdrawalOperationFailed(activity) }) + TalerButton(text = "Exit", onClick = { model.withdrawalOperationFailed(activity) }) } } \ 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 @@ -1,115 +0,0 @@ -// This file is part of taler-cashless2ecash. -// Copyright (C) 2024 Joel Häberli -// -// taler-cashless2ecash is free software: you can redistribute it and/or modify it -// under the terms of the GNU Affero General Public License as published -// by the Free Software Foundation, either version 3 of the License, -// or (at your option) any later version. -// -// taler-cashless2ecash 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 -// Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// -// SPDX-License-Identifier: AGPL3.0-or-later - -package ch.bfh.habej2.wallee_c2ec.withdrawal - -import android.content.Intent -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -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.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.platform.LocalConfiguration -import androidx.compose.ui.unit.dp -import ch.bfh.habej2.wallee_c2ec.MainActivity -import ch.bfh.habej2.wallee_c2ec.client.taler.model.Amount -import com.squareup.moshi.Json - -data class Summary( - @Json(name = "amount") val amount: Amount, - @Json(name = "fees") val fees: Amount, - @Json(name = "currency") val currency: String, - @Json(name = "encodedWopid") val encodedWopid: String, - var success: Boolean = true -) - -class SummaryActivity : ComponentActivity() { - - companion object { - var summary: Summary = reset() - - private fun reset(): Summary = Summary( - Amount("", 0, 0), - Amount("", 0, 0), - "", - "" - ) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setContent { - val configuration = LocalConfiguration.current - if (summary.success) { - Column( - Modifier.width(configuration.screenWidthDp.dp), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - 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)}") - - FinishButton() - } - } else { - Column( - Modifier.width(configuration.screenWidthDp.dp), - 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() - } - } - } - } - - @Composable - private fun FinishButton() { - TalerButton( - text = "finish", - onClick = { - this@SummaryActivity.startActivity( - Intent( - baseContext, - MainActivity::class.java - ) - ) - reset() - finish() - } - ) - } -} -\ No newline at end of file diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/SummaryScreen.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/SummaryScreen.kt @@ -0,0 +1,97 @@ +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 +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DoneAll +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import ch.bfh.habej2.wallee_c2ec.R + +@SuppressLint("ResourceAsColor") +@Composable +fun SummaryScreen( + model: WithdrawalViewModel, + activity: Activity +) { + val uiState by model.uiState.collectAsState() + + Spacer(modifier = Modifier.height(32.dp)) + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + TalerLogo() + + when (uiState.withdrawalState) { + WithdrawalState.COMPLETED -> { + Icon( + imageVector = Icons.Filled.DoneAll, + contentDescription = "All steps successful", + tint = colorResource(R.color.bfh_mediumgreen), + modifier = Modifier.size(64.dp) + ) + + Text( + text = "Hurrah! Taler's market capitalization just surged!", + style = MaterialTheme.typography.titleMedium, + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { + Text( + text = "Summary", + style = MaterialTheme.typography.titleSmall.copy(fontWeight = FontWeight.SemiBold) + ) + Text(text = "Amount: ${uiState.currency} ${uiState.amount.toString(true)}") + Text(text = "Fees: ${uiState.currency} ${uiState.withdrawalFees.toString(true)}") + } + } + + WithdrawalState.COMPLETION_FAILED -> { + Icon( + imageVector = Icons.Filled.Warning, + contentDescription = "Failed authorizing payment", + tint = colorResource(R.color.bfh_red), + modifier = Modifier.size(64.dp) + ) + + Text( + text = "Failed to authorize payment.", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.error, + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "Withdrawal aborted. Please try again.", + textAlign = TextAlign.Center + ) + } + + else -> {} + } + TalerButton(text = "Done", onClick = { activity.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 @@ -1,5 +1,6 @@ package ch.bfh.habej2.wallee_c2ec.withdrawal +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button @@ -18,8 +19,9 @@ fun TalerButton(text: String, onClick: () -> Unit, enableExpr: () -> Boolean = { onClick = onClick, enabled = enableExpr(), modifier = Modifier + .padding(bottom = 15.dp) .width((configuration.screenWidthDp * 0.8).dp), - shape = RoundedCornerShape(0.dp) + shape = RoundedCornerShape(15.dp) ) { Text(text = text) } 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 @@ -1,12 +1,17 @@ package ch.bfh.habej2.wallee_c2ec.withdrawal import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import ch.bfh.habej2.wallee_c2ec.R @Composable -fun TalerLogo() = Image( - painter = painterResource(id = R.drawable.taler_logo), - contentDescription = "Taler logo" +fun TalerLogo(paddingBottom: Dp =30.dp) = Image( + painter = painterResource(id = R.drawable.gnutaler), + contentDescription = "Taler logo", + modifier = androidx.compose.ui.Modifier + .padding(start = 30.dp, end = 30.dp, top = 15.dp, bottom = paddingBottom) ) diff --git a/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalActivity.kt b/wallee-c2ec/app/src/main/java/ch/bfh/habej2/wallee_c2ec/withdrawal/WithdrawalActivity.kt @@ -26,6 +26,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import ch.bfh.habej2.wallee_c2ec.client.wallee.WalleeResponseHandler +import ch.bfh.habej2.wallee_c2ec.ui.theme.Walleec2ecTheme import com.wallee.android.till.sdk.ApiClient import com.wallee.android.till.sdk.Utils @@ -42,31 +43,43 @@ class WithdrawalActivity : ComponentActivity() { walleeClient = ApiClient(walleeResponseHandler) walleeClient.bind(this) walleeClient.checkApiServiceCompatibility() - model = WithdrawalViewModel(walleeClient) setContent { val navController = rememberNavController() - NavHost(navController = navController, startDestination = "chooseExchangeScreen") { - composable("chooseExchangeScreen") { - ExchangeSelectionScreen(model, this@WithdrawalActivity) { - navController.navigate("amountScreen") + var startDestination = "chooseExchangeScreen" + if (exchanges.size == 1) { + model.chooseExchange(exchanges[0], this@WithdrawalActivity) + startDestination = "amountScreen" + } + + Walleec2ecTheme { + NavHost(navController = navController, startDestination = startDestination) { + composable("chooseExchangeScreen") { + ExchangeSelectionScreen(model, this@WithdrawalActivity) { + navController.navigate("amountScreen") + } } - } - composable("amountScreen") { - AmountScreen(model, this@WithdrawalActivity) { - navController.navigate("registerParametersScreen") + composable("amountScreen") { + AmountScreen(model, this@WithdrawalActivity) { + navController.navigate("registerParametersScreen") + } } - } - composable("registerParametersScreen") { - RegisterParametersScreen(model, this@WithdrawalActivity) { - navController.navigate("authorizePaymentScreen") + composable("registerParametersScreen") { + RegisterParametersScreen(model, this@WithdrawalActivity) { + navController.navigate("authorizePaymentScreen") + } + } + composable("authorizePaymentScreen") { + AuthorizePaymentScreen(model, this@WithdrawalActivity, walleeClient) { + navController.navigate("summaryScreen") + } + } + composable("summaryScreen") { + SummaryScreen(model, this@WithdrawalActivity) } - } - composable("authorizePaymentScreen") { - AuthorizePaymentScreen(model, this@WithdrawalActivity, walleeClient) } } } @@ -84,7 +97,6 @@ class WithdrawalActivity : ComponentActivity() { super.onResume() println("onResume called") - val extras = intent.extras extras?.let { when { 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 @@ -19,7 +19,6 @@ package ch.bfh.habej2.wallee_c2ec.withdrawal import android.app.Activity -import android.content.Intent import android.os.Handler import android.os.Looper import android.widget.Toast @@ -38,13 +37,13 @@ import ch.bfh.habej2.wallee_c2ec.client.taler.config.TalerTerminalConfig import ch.bfh.habej2.wallee_c2ec.client.taler.encoding.CyptoUtils import ch.bfh.habej2.wallee_c2ec.client.taler.model.Amount import ch.bfh.habej2.wallee_c2ec.client.taler.model.AmountParserException -import ch.bfh.habej2.wallee_c2ec.client.taler.model.TerminalWithdrawalConfirmationRequest 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.TransactionCompletionResponse import com.wallee.android.till.sdk.data.TransactionResponse +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch @@ -114,9 +113,13 @@ interface Withdrawal { * | COMPLETED | | COMPLETION_FAILED | * +-------------------+ +-------------------------+ */ + +val MAX_TRIES = 3 + enum class WithdrawalState { UNREADY_FOR_AUTHORIZATION, READY_FOR_AUTHORIZATION, + E2EC_TIMEOUT, AUTHORIZATION_TRIGGERED, AUTHORIZED, AUTHORIZATION_FAILED, @@ -278,11 +281,16 @@ class WithdrawalViewModel( _uiState.value.transactionCompletion = completion } - fun setAuthorizationReadyOrRetry() = viewModelScope.launch { + fun setAuthorizationReadyOrRetry() = viewModelScope.launch(Dispatchers.IO) { terminalClient!!.retrieveWithdrawalStatus(uiState.value.encodedWopid, 10000) { if (!it.isPresent) { - println("setting retry because no status was present") - _uiState.value.setupWithdrawalRetries += 1 + if (_uiState.value.setupWithdrawalRetries > MAX_TRIES) { + println("setting e2ec timeout") + _uiState.value.withdrawalState = WithdrawalState.E2EC_TIMEOUT + } else { + println("setting retry because no status was present") + _uiState.value.setupWithdrawalRetries += 1 + } } else if (uiState.value.withdrawalState == WithdrawalState.AUTHORIZATION_TRIGGERED) { println("authorization triggered, waiting for completion") } else { @@ -292,32 +300,6 @@ class WithdrawalViewModel( } } - fun talerCheckWalleeRequest(activity: Activity) { - viewModelScope.launch { - terminalClient!!.sendConfirmationRequest( - _uiState.value.encodedWopid, - TerminalWithdrawalConfirmationRequest( - _uiState.value.encodedWopid, - _uiState.value.withdrawalFees.toJSONString() - ) - ) { - if (!it) { - withdrawalOperationFailed(activity) - return@sendConfirmationRequest - } - SummaryActivity.summary = - Summary( - _uiState.value.amount, - _uiState.value.withdrawalFees, - _uiState.value.currency, - _uiState.value.encodedWopid - ) - val intent = Intent(activity, SummaryActivity::class.java) - activity.startActivity(intent) - } - } - } - fun withdrawalOperationFailed(activity: Activity) { println("withdrawal operation failed called") Handler(Looper.getMainLooper()).post { diff --git a/wallee-c2ec/app/src/main/res/values/colors.xml b/wallee-c2ec/app/src/main/res/values/colors.xml @@ -1,10 +1,14 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <color name="purple_200">#FFBB86FC</color> - <color name="purple_500">#FF6200EE</color> - <color name="purple_700">#FF3700B3</color> - <color name="teal_200">#FF03DAC5</color> - <color name="teal_700">#FF018786</color> + <color name="bfh_grey">#697d91</color> + <color name="bfh_orange">#f6bd26</color> + <color name="bfh_blue">#2d6071</color> + <color name="bfh_darkgreen">#556455</color> + <color name="bfh_mediumgreen">#699673</color> + <color name="bfh_darkblue">#506e96</color> + <color name="bfh_mediumblue">#699bbe</color> + <color name="bfh_red">#d25a50</color> + <color name="taler_blue">#0042b3</color> <color name="black">#FF000000</color> <color name="white">#FFFFFFFF</color> </resources> \ No newline at end of file diff --git a/wallee-c2ec/app/src/main/res/values/strings.xml b/wallee-c2ec/app/src/main/res/values/strings.xml @@ -1,13 +1,15 @@ <resources> <string name="app_name">wallee-c2ec</string> - <string name="title_activity_exchange">ExchangeActivity</string> - <string name="title_activity_withdrawal_creation">WithdrawalCreationActivity</string> - <string name="title_activity_payment">PaymentActivity</string> + <string name="title_activity_exchange">Exchange Activity</string> + <string name="title_activity_withdrawal_creation">Withdrawal Creation Activity</string> + <string name="title_activity_payment">Payment Activity</string> <string name="not_connected">Backend not available. Retry later.</string> <string name="not_fees">No fees defined. Please choose a different exchange.</string> <string name="no_complete_reply">No valid transaction completion reply.</string> <string name="not_compatible">SDK is not compatible.</string> <string name="no_authorization">No authorization received.</string> - <string name="wallee_custom_text">Withdraw digital cash to your Taler Wallet</string> + <string name="wallee_custom_text">Pay %s %s to top-up your Taler Wallet.</string> <string name="aborted">Withdrawal aborted</string> + <string name="max_reties_exceeded">Maximal retries exceeded</string> + <string name="timeout">Operation timed out. Please retry.</string> </resources> \ No newline at end of file diff --git a/wallee-c2ec/app/src/main/res/values/themes.xml b/wallee-c2ec/app/src/main/res/values/themes.xml @@ -1,5 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - <style name="Theme.Walleec2ec" parent="android:Theme.Material.Light.NoActionBar" /> + <style name="Theme.Walleec2ec" parent="android:Theme.Material.Light.NoActionBar"> + <item name="colorPrimary">@color/taler_blue</item> <!-- Top bar color --> + <item name="android:colorSecondary">@color/bfh_orange</item> + </style> + <style name="Theme.SecureVote" parent="android:Theme.Material.Light.DarkActionBar"> + <item name="colorPrimary">@color/taler_blue</item> <!-- Top bar color --> + <item name="android:colorSecondary">@color/bfh_orange</item> + </style>> + </resources> \ No newline at end of file diff --git a/wallee-c2ec/gradle/libs.versions.toml b/wallee-c2ec/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] -agp = "8.3.1" -appcompat = "1.6.1" +agp = "8.3.2" +appcompat = "1.7.0" commonsCodec = "1.16.1" core = "3.5.0" kotlin = "1.9.0" @@ -15,6 +15,9 @@ moshiKotlin = "1.15.1" okhttp = "4.12.0" sdk = "0.9.20" navigationCompose = "2.7.7" +materialIconsExtendedAndroid = "1.7.8" +composeMaterial = "1.4.1" +material3Android = "1.3.2" [libraries] androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } @@ -38,6 +41,9 @@ moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "mosh okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } wallee-sdk = { module = "com.wallee.android.till:sdk", version.ref = "sdk" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } +androidx-material-icons-extended-android = { group = "androidx.compose.material", name = "material-icons-extended-android", version.ref = "materialIconsExtendedAndroid" } +androidx-compose-material = { group = "androidx.wear.compose", name = "compose-material", version.ref = "composeMaterial" } +androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" }