From 4007aea5a38947642c329ee82a3a276487dfafc4 Mon Sep 17 00:00:00 2001 From: Niko Date: Mon, 22 Jul 2024 22:20:45 +0200 Subject: [PATCH 01/10] feat(bank-sdk): Skonto screen. Bottom bar redesign PP-648 --- .../bank/sdk/capture/skonto/SkontoFragment.kt | 62 ++++++++++++++----- .../capture/skonto/SkontoFragmentContract.kt | 1 + .../capture/skonto/SkontoFragmentViewModel.kt | 21 ++++++- .../section/SkontoFooterSectionColors.kt | 3 + .../sdk/src/main/res/values-en/strings.xml | 1 + bank-sdk/sdk/src/main/res/values/strings.xml | 1 + .../ui/theme/typography/GiniTypography.kt | 2 + 7 files changed, 71 insertions(+), 20 deletions(-) diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt index d8e7946be..772abeda2 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt @@ -91,6 +91,7 @@ import net.gini.android.capture.ui.components.textinput.amount.GiniAmountTextInp import net.gini.android.capture.ui.components.topbar.GiniTopBar import net.gini.android.capture.ui.components.topbar.GiniTopBarColors import net.gini.android.capture.ui.theme.GiniTheme +import net.gini.android.capture.ui.theme.typography.bold import net.gini.android.capture.view.InjectedViewAdapterInstance import java.math.BigDecimal import java.math.RoundingMode @@ -255,7 +256,8 @@ private fun ScreenReadyState( onHelpClicked = onHelpClicked, customBottomNavBarAdapter = customBottomNavBarAdapter, onProceedClicked = onProceedClicked, - isSkontoSectionActive = state.isSkontoSectionActive + isSkontoSectionActive = state.isSkontoSectionActive, + savedAmount = state.savedAmount ) }) { Column( @@ -752,6 +754,7 @@ private fun WithoutSkontoSection( @Composable private fun FooterSection( totalAmount: SkontoData.Amount, + savedAmount: SkontoData.Amount, discountValue: BigDecimal, colors: SkontoFooterSectionColors, isBottomNavigationBarEnabled: Boolean, @@ -765,11 +768,18 @@ private fun FooterSection( val animatedTotalAmount by animateFloatAsState( targetValue = totalAmount.amount.toFloat(), label = "totalAmount" ) + val animatedSavedAmount by animateFloatAsState( + targetValue = savedAmount.amount.toFloat(), label = "savedAmount" + ) val animatedDiscountAmount by animateFloatAsState( targetValue = discountValue.toFloat(), label = "discountAmount" ) val totalPriceText = "${currencyFormatterWithoutSymbol().format(animatedTotalAmount)} ${totalAmount.currencyCode}" + + val savedAmountText = + "${currencyFormatterWithoutSymbol().format(animatedSavedAmount)} ${savedAmount.currencyCode}" + val discountLabelText = stringResource( id = R.string.gbs_skonto_section_footer_label_discount, animatedDiscountAmount.formatAsDiscountPercentage() @@ -801,24 +811,16 @@ private fun FooterSection( Column( modifier = Modifier.padding(start = 20.dp, end = 20.dp, top = 20.dp) ) { - Text( - text = stringResource(id = R.string.gbs_skonto_section_footer_title), - style = GiniTheme.typography.body1, - color = colors.titleTextColor, - ) - Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = 4.dp), - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically, - ) { + Row { Text( - text = totalPriceText, - style = GiniTheme.typography.headline5, - color = colors.amountTextColor, + modifier = Modifier.weight(0.1f), + text = stringResource(id = R.string.gbs_skonto_section_footer_title), + style = GiniTheme.typography.body1, + color = colors.titleTextColor, ) - AnimatedVisibility(visible = isSkontoSectionActive) { + AnimatedVisibility( + visible = isSkontoSectionActive + ) { Box( modifier = Modifier .height(IntrinsicSize.Min) @@ -837,6 +839,31 @@ private fun FooterSection( } } } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 4.dp), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + text = totalPriceText, + style = GiniTheme.typography.headline5.bold(), + color = colors.amountTextColor, + ) + } + AnimatedVisibility( + visible = isSkontoSectionActive + ) { + Text( + text = stringResource( + id = R.string.gbs_skonto_section_footer_label_save, + savedAmountText + ), + style = GiniTheme.typography.caption1, + color = colors.savedAmountTextColor, + ) + } } val buttonPaddingHorizontal = if (isBottomNavigationBarEnabled) 0.dp else 20.dp Row( @@ -923,4 +950,5 @@ private val previewState = SkontoFragmentContract.State.Ready( skontoEdgeCase = SkontoFragmentContract.SkontoEdgeCase.PayByCashOnly, edgeCaseInfoDialogVisible = false, skontoAmountValidation = SkontoFragmentContract.State.Ready.SkontoAmountValidation.Invalid.SkontoAmountGreaterOfFullAmount, + savedAmount = SkontoData.Amount(BigDecimal("3"), "EUR") ) \ No newline at end of file diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt index 0150215e2..c43b1a25a 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt @@ -16,6 +16,7 @@ internal object SkontoFragmentContract { val discountDueDate: LocalDate, val fullAmount: SkontoData.Amount, val totalAmount: SkontoData.Amount, + val savedAmount: SkontoData.Amount, val paymentMethod: SkontoData.SkontoPaymentMethod, val skontoEdgeCase: SkontoEdgeCase?, val edgeCaseInfoDialogVisible: Boolean, diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt index 5ecab38e0..07ca28602 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt @@ -34,6 +34,9 @@ internal class SkontoFragmentViewModel( val totalAmount = if (isSkontoSectionActive) data.skontoAmountToPay else data.fullAmountToPay + val savedAmountValue = calculateSavedAmount(data.skontoAmountToPay.amount, data.fullAmountToPay.amount) + val savedAmount = SkontoData.Amount(savedAmountValue, data.fullAmountToPay.currencyCode) + return SkontoFragmentContract.State.Ready( isSkontoSectionActive = true, paymentInDays = data.skontoRemainingDays, @@ -48,7 +51,8 @@ internal class SkontoFragmentViewModel( skontoAmountValidation = validateSkontoAmount( skontoAmount = data.skontoAmountToPay, fullAmount = data.fullAmountToPay - ) + ), + savedAmount = savedAmount ) } @@ -80,12 +84,16 @@ internal class SkontoFragmentViewModel( val newSkontoAmount = currentState.skontoAmount.copy(amount = newValue) val newTotalAmount = currentState.totalAmount.copy(amount = totalAmount) + val savedAmountValue = calculateSavedAmount(newSkontoAmount.amount, currentState.fullAmount.amount) + val savedAmount = SkontoData.Amount(savedAmountValue, currentState.fullAmount.currencyCode) + stateFlow.emit( currentState.copy( skontoAmount = newSkontoAmount, discountAmount = discount, totalAmount = newTotalAmount, - skontoAmountValidation = validateSkontoAmount(newSkontoAmount, currentState.fullAmount) + skontoAmountValidation = validateSkontoAmount(newSkontoAmount, currentState.fullAmount), + savedAmount = savedAmount, ) ) } @@ -118,11 +126,15 @@ internal class SkontoFragmentViewModel( ) ) + val savedAmountValue = calculateSavedAmount(skontoAmount, newValue) + val savedAmount = SkontoData.Amount(savedAmountValue, currentState.fullAmount.currencyCode) + stateFlow.emit( currentState.copy( skontoAmount = currentState.skontoAmount.copy(amount = skontoAmount), fullAmount = currentState.fullAmount.copy(amount = newValue), - totalAmount = currentState.totalAmount.copy(amount = totalAmount) + totalAmount = currentState.totalAmount.copy(amount = totalAmount), + savedAmount = savedAmount, ) ) } @@ -152,6 +164,9 @@ internal class SkontoFragmentViewModel( .multiply(BigDecimal("100")) } + private fun calculateSavedAmount(skontoAmount: BigDecimal, fullAmount: BigDecimal) = + fullAmount.minus(skontoAmount).abs() + private fun validateSkontoAmount( skontoAmount: SkontoData.Amount, fullAmount: SkontoData.Amount diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/colors/section/SkontoFooterSectionColors.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/colors/section/SkontoFooterSectionColors.kt index f565d4cf7..12eec0d2f 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/colors/section/SkontoFooterSectionColors.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/colors/section/SkontoFooterSectionColors.kt @@ -11,6 +11,7 @@ data class SkontoFooterSectionColors( val cardBackgroundColor: Color, val titleTextColor: Color, val amountTextColor: Color, + val savedAmountTextColor: Color, val discountLabelColorScheme: DiscountLabelColorScheme, val continueButtonColors: GiniButtonColors, ) { @@ -22,6 +23,7 @@ data class SkontoFooterSectionColors( cardBackgroundColor: Color = GiniTheme.colorScheme.card.container, titleTextColor: Color = GiniTheme.colorScheme.text.primary, amountTextColor: Color = GiniTheme.colorScheme.text.primary, + savedAmountTextColor: Color = GiniTheme.colorScheme.text.success, discountLabelColorScheme: DiscountLabelColorScheme = DiscountLabelColorScheme.colors(), continueButtonColors: GiniButtonColors = GiniButtonColors.colors(), ) = SkontoFooterSectionColors( @@ -30,6 +32,7 @@ data class SkontoFooterSectionColors( amountTextColor = amountTextColor, discountLabelColorScheme = discountLabelColorScheme, continueButtonColors = continueButtonColors, + savedAmountTextColor = savedAmountTextColor, ) } diff --git a/bank-sdk/sdk/src/main/res/values-en/strings.xml b/bank-sdk/sdk/src/main/res/values-en/strings.xml index d55018d75..06b278024 100644 --- a/bank-sdk/sdk/src/main/res/values-en/strings.xml +++ b/bank-sdk/sdk/src/main/res/values-en/strings.xml @@ -64,6 +64,7 @@ Total %1$s Skonto discount + Save %1$s Continue to pay diff --git a/bank-sdk/sdk/src/main/res/values/strings.xml b/bank-sdk/sdk/src/main/res/values/strings.xml index d619b5979..d541c819a 100644 --- a/bank-sdk/sdk/src/main/res/values/strings.xml +++ b/bank-sdk/sdk/src/main/res/values/strings.xml @@ -64,6 +64,7 @@ Gesamtpreis %1$s Skonto + %1$s sparen Zahlung fortsetzen diff --git a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/theme/typography/GiniTypography.kt b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/theme/typography/GiniTypography.kt index 716c37180..e15102324 100644 --- a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/theme/typography/GiniTypography.kt +++ b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/theme/typography/GiniTypography.kt @@ -3,6 +3,7 @@ package net.gini.android.capture.ui.theme.typography import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight import net.gini.android.capture.R @@ -23,6 +24,7 @@ data class GiniTypography( val overline: TextStyle = TextStyle.Default, ) +fun TextStyle.bold() = this.copy(fontWeight = FontWeight.Bold) @Composable fun extractGiniTypography() = GiniTypography( From 1121de6802a47857b96fd9e0de698ac3f13170a6 Mon Sep 17 00:00:00 2001 From: Niko Date: Tue, 23 Jul 2024 16:10:48 +0200 Subject: [PATCH 02/10] feat(bank-sdk): Skonto screen. Help icon remove. Invoice Preview remove PP-648 --- .../bank/sdk/capture/skonto/SkontoFragment.kt | 67 ++----------------- 1 file changed, 7 insertions(+), 60 deletions(-) diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt index 772abeda2..7ee8990cc 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt @@ -4,7 +4,6 @@ package net.gini.android.bank.sdk.capture.skonto import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.icu.util.Calendar -import android.icu.util.TimeUnit import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -51,15 +50,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.focus.onFocusEvent import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -95,18 +90,12 @@ import net.gini.android.capture.ui.theme.typography.bold import net.gini.android.capture.view.InjectedViewAdapterInstance import java.math.BigDecimal import java.math.RoundingMode -import java.time.Instant import java.time.LocalDate -import java.time.LocalTime -import java.time.ZoneId -import java.time.ZoneOffset -import java.time.ZonedDateTime -import java.time.chrono.ChronoLocalDate import java.time.format.DateTimeFormatter class SkontoFragment : Fragment() { - val args: SkontoFragmentArgs by navArgs() + private val args: SkontoFragmentArgs by navArgs() private val isBottomNavigationBarEnabled = GiniCapture.getInstance().isBottomNavigationBarEnabled @@ -171,7 +160,6 @@ private fun ScreenContent( onFullAmountChange = viewModel::onFullAmountFieldChanged, isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, onBackClicked = {}, - onHelpClicked = {}, customBottomNavBarAdapter = customBottomNavBarAdapter, onProceedClicked = {}, onInfoBannerClicked = viewModel::onInfoBannerClicked, @@ -187,7 +175,6 @@ private fun ScreenStateContent( onFullAmountChange: (BigDecimal) -> Unit, onDueDateChanged: (LocalDate) -> Unit, onBackClicked: () -> Unit, - onHelpClicked: () -> Unit, onProceedClicked: () -> Unit, isBottomNavigationBarEnabled: Boolean, customBottomNavBarAdapter: InjectedViewAdapterInstance?, @@ -206,7 +193,6 @@ private fun ScreenStateContent( onDueDateChanged = onDueDateChanged, onFullAmountChange = onFullAmountChange, onBackClicked = onBackClicked, - onHelpClicked = onHelpClicked, isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, customBottomNavBarAdapter = customBottomNavBarAdapter, onProceedClicked = onProceedClicked, @@ -220,7 +206,6 @@ private fun ScreenStateContent( @Composable private fun ScreenReadyState( onBackClicked: () -> Unit, - onHelpClicked: () -> Unit, onProceedClicked: () -> Unit, state: SkontoFragmentContract.State.Ready, onDiscountSectionActiveChange: (Boolean) -> Unit, @@ -243,7 +228,6 @@ private fun ScreenReadyState( isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, colors = screenColorScheme.topAppBarColors, onBackClicked = onBackClicked, - onHelpClicked = onHelpClicked, ) }, bottomBar = { @@ -253,7 +237,6 @@ private fun ScreenReadyState( totalAmount = state.totalAmount, isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, onBackClicked = onBackClicked, - onHelpClicked = onHelpClicked, customBottomNavBarAdapter = customBottomNavBarAdapter, onProceedClicked = onProceedClicked, isSkontoSectionActive = state.isSkontoSectionActive, @@ -265,10 +248,7 @@ private fun ScreenReadyState( .padding(it) .verticalScroll(scrollState) ) { - YourInvoiceScanSection( - modifier = Modifier.padding(vertical = 8.dp), - colorScheme = screenColorScheme.invoiceScanSectionColors, - ) + SkontoSection( modifier = Modifier.padding(vertical = 16.dp), colors = screenColorScheme.skontoSectionColors, @@ -322,7 +302,6 @@ private fun ScreenReadyState( @Composable private fun TopAppBar( onBackClicked: () -> Unit, - onHelpClicked: () -> Unit, modifier: Modifier = Modifier, isBottomNavigationBarEnabled: Boolean, colors: GiniTopBarColors, @@ -335,11 +314,6 @@ private fun TopAppBar( AnimatedVisibility(visible = !isBottomNavigationBarEnabled) { NavigationActionBack(onClick = onBackClicked) } - }, - actions = { - AnimatedVisibility(visible = !isBottomNavigationBarEnabled) { - NavigationActionHelp(onClick = onHelpClicked) - } }) } @@ -359,23 +333,6 @@ private fun NavigationActionBack( } } -@Composable -private fun NavigationActionHelp( - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { - IconButton( - modifier = modifier, - onClick = onClick - ) { - Icon( - modifier = Modifier, - painter = painterResource(net.gini.android.capture.R.drawable.gc_help_icon), - contentDescription = null, - ) - } -} - @Composable private fun YourInvoiceScanSection( modifier: Modifier = Modifier, @@ -452,8 +409,6 @@ private fun SkontoSection( val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") var isDatePickerVisible by remember { mutableStateOf(false) } - val dueDateFieldFocusRequester = remember { FocusRequester() } - val focusManager = LocalFocusManager.current Card( modifier = modifier.fillMaxWidth(), @@ -760,7 +715,6 @@ private fun FooterSection( isBottomNavigationBarEnabled: Boolean, isSkontoSectionActive: Boolean, onBackClicked: () -> Unit, - onHelpClicked: () -> Unit, onProceedClicked: () -> Unit, modifier: Modifier = Modifier, customBottomNavBarAdapter: InjectedViewAdapterInstance?, @@ -794,7 +748,6 @@ private fun FooterSection( with(customBottomNavBarAdapter.viewAdapter) { setTotalPriceText(totalPriceText) setProceedButtonEnabled(proceedEnabled) // TODO Integrate validation - setOnHelpClickListener(onHelpClicked) setOnBackClickListener(onBackClicked) setDiscountLabelText(discountLabelText) setDiscountLabelVisible(isSkontoSectionActive) @@ -824,7 +777,7 @@ private fun FooterSection( Box( modifier = Modifier .height(IntrinsicSize.Min) - .padding(horizontal = 12.dp) + .padding(horizontal = 4.dp) .background( colors.discountLabelColorScheme.backgroundColor, RoundedCornerShape(4.dp) @@ -865,7 +818,8 @@ private fun FooterSection( ) } } - val buttonPaddingHorizontal = if (isBottomNavigationBarEnabled) 0.dp else 20.dp + val buttonPaddingStart = if (isBottomNavigationBarEnabled) 0.dp else 20.dp + val buttonPaddingEnd = if (isBottomNavigationBarEnabled) 48.dp else 24.dp Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, @@ -880,17 +834,11 @@ private fun FooterSection( GiniButton( modifier = Modifier .weight(0.1f) - .padding(horizontal = buttonPaddingHorizontal), + .padding(start = buttonPaddingStart, end = buttonPaddingEnd), text = stringResource(id = R.string.gbs_skonto_section_footer_continue_button_text), onClick = onProceedClicked, giniButtonColors = colors.continueButtonColors ) - AnimatedVisibility(visible = isBottomNavigationBarEnabled) { - NavigationActionHelp( - modifier = Modifier.padding(horizontal = 4.dp), - onClick = onHelpClicked - ) - } } } } @@ -922,9 +870,8 @@ private fun ScreenReadyStatePreview() { onDiscountAmountChange = {}, onDueDateChanged = {}, onFullAmountChange = {}, - onHelpClicked = {}, onBackClicked = {}, - isBottomNavigationBarEnabled = false, + isBottomNavigationBarEnabled = true, onProceedClicked = {}, customBottomNavBarAdapter = null, onInfoDialogDismissed = {}, From 449aa3e292eb55a1c10e1e6b474bfe76be1cfdda Mon Sep 17 00:00:00 2001 From: Niko Date: Tue, 23 Jul 2024 16:54:26 +0200 Subject: [PATCH 03/10] feat(bank-sdk): Skonto screen. Fix percentage calculation PP-648 --- .../bank/sdk/capture/skonto/SkontoFragmentViewModel.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt index 07ca28602..c74c44c65 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt @@ -73,9 +73,7 @@ internal class SkontoFragmentViewModel( fun onSkontoAmountFieldChanged(newValue: BigDecimal) = viewModelScope.launch { val currentState = stateFlow.value as? SkontoFragmentContract.State.Ready ?: return@launch - val discount = calculateDiscount(newValue, currentState.fullAmount.amount).coerceAtLeast( - BigDecimal.ZERO - ) + val discount = calculateDiscount(newValue, currentState.fullAmount.amount) val totalAmount = if (currentState.isSkontoSectionActive) newValue else @@ -162,6 +160,7 @@ internal class SkontoFragmentViewModel( return BigDecimal.ONE .minus(skontoAmount.divide(fullAmount, 4, RoundingMode.HALF_UP)) .multiply(BigDecimal("100")) + .coerceAtLeast(BigDecimal.ZERO) } private fun calculateSavedAmount(skontoAmount: BigDecimal, fullAmount: BigDecimal) = From 3b7b61d4d3ea1be35f0a536fc5030b274318001c Mon Sep 17 00:00:00 2001 From: Niko Date: Tue, 23 Jul 2024 16:58:52 +0200 Subject: [PATCH 04/10] feat(bank-sdk): Skonto screen. Fix currency display PP-648 --- .../gini/android/bank/sdk/capture/skonto/SkontoFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt index 7ee8990cc..7961f5a27 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt @@ -729,10 +729,10 @@ private fun FooterSection( targetValue = discountValue.toFloat(), label = "discountAmount" ) val totalPriceText = - "${currencyFormatterWithoutSymbol().format(animatedTotalAmount)} ${totalAmount.currencyCode}" + "${currencyFormatterWithoutSymbol().format(animatedTotalAmount).trim()} ${totalAmount.currencyCode}" val savedAmountText = - "${currencyFormatterWithoutSymbol().format(animatedSavedAmount)} ${savedAmount.currencyCode}" + "${currencyFormatterWithoutSymbol().format(animatedSavedAmount).trim()} ${savedAmount.currencyCode}" val discountLabelText = stringResource( id = R.string.gbs_skonto_section_footer_label_discount, From 1bd20b3841dfee7119182dfef0c3887caf180d0f Mon Sep 17 00:00:00 2001 From: Niko Date: Tue, 23 Jul 2024 17:13:34 +0200 Subject: [PATCH 05/10] feat(bank-sdk): Skonto screen. Fix currency display PP-648 --- .../android/bank/sdk/capture/skonto/SkontoFragment.kt | 2 ++ .../ui/components/picker/date/GiniDatePickerDialog.kt | 6 ++++-- .../textinput/amount/DecimalInputVisualTransformation.kt | 7 ++++++- .../ui/components/textinput/amount/GiniAmountTextInput.kt | 8 +++++++- capture-sdk/sdk/src/main/res/values-en/strings.xml | 4 ++++ capture-sdk/sdk/src/main/res/values/strings.xml | 4 ++++ 6 files changed, 27 insertions(+), 4 deletions(-) diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt index 7961f5a27..3fec2b143 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt @@ -490,6 +490,7 @@ private fun SkontoSection( ) GiniAmountTextInput( amount = amount.amount, + currencyCode = amount.currencyCode, modifier = Modifier .fillMaxWidth() .padding(top = 16.dp), @@ -691,6 +692,7 @@ private fun WithoutSkontoSection( enabled = isActive, colors = colors.amountFieldColors, amount = amount.amount, + currencyCode = amount.currencyCode, onValueChange = onFullAmountChange, label = stringResource(id = R.string.gbs_skonto_section_without_discount_field_amount_hint), trailingContent = { diff --git a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/picker/date/GiniDatePickerDialog.kt b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/picker/date/GiniDatePickerDialog.kt index 01e19ea03..308e7506d 100644 --- a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/picker/date/GiniDatePickerDialog.kt +++ b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/picker/date/GiniDatePickerDialog.kt @@ -18,10 +18,12 @@ import androidx.compose.material3.rememberDatePickerState import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment 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 androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties +import net.gini.android.capture.R import net.gini.android.capture.ui.theme.GiniTheme import java.time.Instant import java.time.LocalDate @@ -79,7 +81,7 @@ fun GiniDatePickerDialog( modifier = Modifier.padding(horizontal = 8.dp), onClick = { onDismissRequest() }) { Text( - text = "Cancel", + text = stringResource(id = R.string.gc_date_picker_cancel), style = GiniTheme.typography.body1 ) } @@ -94,7 +96,7 @@ fun GiniDatePickerDialog( } }) { Text( - text = "Select", + text = stringResource(id = R.string.gc_date_picker_select), style = GiniTheme.typography.body1 ) } diff --git a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/DecimalInputVisualTransformation.kt b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/DecimalInputVisualTransformation.kt index 5cd42893d..bd4e43654 100644 --- a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/DecimalInputVisualTransformation.kt +++ b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/DecimalInputVisualTransformation.kt @@ -7,12 +7,17 @@ import androidx.compose.ui.text.input.VisualTransformation import java.text.DecimalFormatSymbols class DecimalInputVisualTransformation( + private val currencyCode: String, + private val isCurrencyCodeDisplay: Boolean, private val decimalFormatter: DecimalFormatter, ) : VisualTransformation { override fun filter(text: AnnotatedString): TransformedText { val source = text.text - val formatted = decimalFormatter.formatDigits(source) + var formatted = decimalFormatter.formatDigits(source).trim() + if (isCurrencyCodeDisplay) { + formatted += " $currencyCode" + } val offsetMapping = CustomOffsetMapping( source = source, diff --git a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/GiniAmountTextInput.kt b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/GiniAmountTextInput.kt index 81e847f1f..f1becd33d 100644 --- a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/GiniAmountTextInput.kt +++ b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/GiniAmountTextInput.kt @@ -30,6 +30,7 @@ import java.text.NumberFormat @Composable fun GiniAmountTextInput( amount: BigDecimal, + currencyCode: String, label: String, modifier: Modifier = Modifier, onValueChange: (BigDecimal) -> Unit, @@ -63,7 +64,11 @@ fun GiniAmountTextInput( }, trailingContent = trailingContent, colors = colors, - visualTransformation = DecimalInputVisualTransformation(decimalFormatter = decimalFormatter), + visualTransformation = DecimalInputVisualTransformation( + decimalFormatter = decimalFormatter, + currencyCode = currencyCode, + isCurrencyCodeDisplay = !enabled, + ), ) } @@ -91,6 +96,7 @@ private fun GiniTextInputPreview() { amount = BigDecimal("1234"), label = "Label Text", trailingContent = { }, + currencyCode = "EUR", onValueChange = {} ) } diff --git a/capture-sdk/sdk/src/main/res/values-en/strings.xml b/capture-sdk/sdk/src/main/res/values-en/strings.xml index 5eb9e7e8a..8f62f2e79 100644 --- a/capture-sdk/sdk/src/main/res/values-en/strings.xml +++ b/capture-sdk/sdk/src/main/res/values-en/strings.xml @@ -155,4 +155,8 @@ Useful tips \n\nTake a photo IBAN detected + + Select + Cancel + \ No newline at end of file diff --git a/capture-sdk/sdk/src/main/res/values/strings.xml b/capture-sdk/sdk/src/main/res/values/strings.xml index 58ef48076..54a4d1fab 100644 --- a/capture-sdk/sdk/src/main/res/values/strings.xml +++ b/capture-sdk/sdk/src/main/res/values/strings.xml @@ -173,4 +173,8 @@ Schließen, Schaltfläche \n\nMachen Sie ein Foto IBAN erkannt + + Auswählen + Abbrechen + From d6a937fd83215b7294a73280f21cfca0c996b622 Mon Sep 17 00:00:00 2001 From: Niko Date: Wed, 24 Jul 2024 14:21:56 +0200 Subject: [PATCH 06/10] feat(bank-sdk): Skonto screen. Fix issues PP-648 --- .../bank/sdk/capture/skonto/SkontoFragment.kt | 14 +++++++++++--- .../sdk/capture/skonto/SkontoFragmentViewModel.kt | 4 ++-- .../sdk/src/main/res/navigation/gbs_nav_graph.xml | 5 +++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt index 3fec2b143..d68a574ba 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt @@ -66,6 +66,8 @@ import androidx.compose.ui.window.DialogProperties import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.navigation.NavController +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import net.gini.android.bank.sdk.GiniBank import net.gini.android.bank.sdk.R @@ -124,6 +126,7 @@ class SkontoFragment : Fragment() { viewModel = viewModel, isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, customBottomNavBarAdapter = customBottomNavBarAdapter, + navController = findNavController(), ) } } @@ -143,6 +146,7 @@ class SkontoFragment : Fragment() { @Composable private fun ScreenContent( + navController: NavController, viewModel: SkontoFragmentViewModel, modifier: Modifier = Modifier, screenColorScheme: SkontoScreenColors = SkontoScreenColors.colors(), @@ -159,7 +163,7 @@ private fun ScreenContent( onDueDateChanged = viewModel::onSkontoDueDateChanged, onFullAmountChange = viewModel::onFullAmountFieldChanged, isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, - onBackClicked = {}, + onBackClicked = { navController.navigate(SkontoFragmentDirections.toCaptureFragment()) }, customBottomNavBarAdapter = customBottomNavBarAdapter, onProceedClicked = {}, onInfoBannerClicked = viewModel::onInfoBannerClicked, @@ -731,10 +735,14 @@ private fun FooterSection( targetValue = discountValue.toFloat(), label = "discountAmount" ) val totalPriceText = - "${currencyFormatterWithoutSymbol().format(animatedTotalAmount).trim()} ${totalAmount.currencyCode}" + "${ + currencyFormatterWithoutSymbol().format(animatedTotalAmount).trim() + } ${totalAmount.currencyCode}" val savedAmountText = - "${currencyFormatterWithoutSymbol().format(animatedSavedAmount).trim()} ${savedAmount.currencyCode}" + "${ + currencyFormatterWithoutSymbol().format(animatedSavedAmount).trim() + } ${savedAmount.currencyCode}" val discountLabelText = stringResource( id = R.string.gbs_skonto_section_footer_label_discount, diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt index c74c44c65..c75d9f7dd 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt @@ -38,7 +38,7 @@ internal class SkontoFragmentViewModel( val savedAmount = SkontoData.Amount(savedAmountValue, data.fullAmountToPay.currencyCode) return SkontoFragmentContract.State.Ready( - isSkontoSectionActive = true, + isSkontoSectionActive = isSkontoSectionActive, paymentInDays = data.skontoRemainingDays, discountAmount = discount, skontoAmount = data.skontoAmountToPay, @@ -164,7 +164,7 @@ internal class SkontoFragmentViewModel( } private fun calculateSavedAmount(skontoAmount: BigDecimal, fullAmount: BigDecimal) = - fullAmount.minus(skontoAmount).abs() + fullAmount.minus(skontoAmount).coerceAtLeast(BigDecimal.ZERO) private fun validateSkontoAmount( skontoAmount: SkontoData.Amount, diff --git a/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml b/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml index 6c9889d7c..c54a54f24 100644 --- a/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml +++ b/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml @@ -103,6 +103,11 @@ + \ No newline at end of file From 4d923ef601e78626a67c5032b5470d8650d4e3ab Mon Sep 17 00:00:00 2001 From: Niko Date: Wed, 24 Jul 2024 16:17:55 +0200 Subject: [PATCH 07/10] feat(bank-sdk): Skonto screen. Fix navigation back PP-648 --- .../bank/sdk/exampleapp/ui/MainActivity.kt | 2 +- bank-sdk/sdk/build.gradle.kts | 1 + .../bank/sdk/capture/skonto/SkontoFragment.kt | 19 +++++++++++++++---- .../src/main/res/navigation/gbs_nav_graph.xml | 10 +++++++--- .../analysis/AnalysisScreenPresenter.java | 1 + gradle/libs.versions.toml | 2 ++ 6 files changed, 27 insertions(+), 8 deletions(-) diff --git a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/MainActivity.kt b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/MainActivity.kt index 3857d3c79..e27f2c63c 100644 --- a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/MainActivity.kt +++ b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/MainActivity.kt @@ -73,7 +73,7 @@ class MainActivity : AppCompatActivity() { cancellationToken?.cancel() } - override fun onNewIntent(intent: Intent?) { + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) if (intent != null && isIntentActionViewOrSend(intent)) { startGiniBankSdkForOpenWith(intent) diff --git a/bank-sdk/sdk/build.gradle.kts b/bank-sdk/sdk/build.gradle.kts index 6057a3719..1adaf5ffd 100644 --- a/bank-sdk/sdk/build.gradle.kts +++ b/bank-sdk/sdk/build.gradle.kts @@ -122,6 +122,7 @@ dependencies { implementation(platform(libs.compose.bom)) implementation(libs.compose.material3) + implementation(libs.compose.activity) implementation(libs.compose.tools.uiToolingPreview) implementation(libs.accompanist.themeAdapter) debugImplementation(libs.compose.tools.uiTooling) diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt index d68a574ba..81a39104a 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt @@ -9,6 +9,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background @@ -67,10 +68,13 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.navigation.NavController +import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import net.gini.android.bank.sdk.GiniBank import net.gini.android.bank.sdk.R +import net.gini.android.bank.sdk.capture.CaptureFlowFragmentListener +import net.gini.android.bank.sdk.capture.CaptureResult import net.gini.android.bank.sdk.capture.skonto.colors.SkontoScreenColors import net.gini.android.bank.sdk.capture.skonto.colors.section.SkontoFooterSectionColors import net.gini.android.bank.sdk.capture.skonto.colors.section.SkontoInfoDialogColors @@ -80,6 +84,7 @@ import net.gini.android.bank.sdk.capture.skonto.colors.section.WithoutSkontoSect import net.gini.android.bank.sdk.capture.skonto.model.SkontoData import net.gini.android.bank.sdk.capture.util.currencyFormatterWithoutSymbol import net.gini.android.capture.GiniCapture +import net.gini.android.capture.internal.util.CancelListener import net.gini.android.capture.ui.components.button.filled.GiniButton import net.gini.android.capture.ui.components.picker.date.GiniDatePickerDialog import net.gini.android.capture.ui.components.switcher.GiniSwitch @@ -115,7 +120,7 @@ class SkontoFragment : Fragment() { val viewModel = ViewModelProvider( factory = ViewModelFactory(args.data), - owner = requireActivity() + owner = this )[SkontoFragmentViewModel::class.java] return ComposeView(requireContext()).apply { @@ -126,7 +131,10 @@ class SkontoFragment : Fragment() { viewModel = viewModel, isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, customBottomNavBarAdapter = customBottomNavBarAdapter, - navController = findNavController(), + navigateBack = { + findNavController() + .navigate(SkontoFragmentDirections.toCaptureFragment()) + } ) } } @@ -146,13 +154,16 @@ class SkontoFragment : Fragment() { @Composable private fun ScreenContent( - navController: NavController, + navigateBack: () -> Unit, viewModel: SkontoFragmentViewModel, modifier: Modifier = Modifier, screenColorScheme: SkontoScreenColors = SkontoScreenColors.colors(), isBottomNavigationBarEnabled: Boolean, customBottomNavBarAdapter: InjectedViewAdapterInstance?, ) { + + BackHandler { navigateBack() } + val state by viewModel.stateFlow.collectAsState() ScreenStateContent( modifier = modifier, @@ -163,7 +174,7 @@ private fun ScreenContent( onDueDateChanged = viewModel::onSkontoDueDateChanged, onFullAmountChange = viewModel::onFullAmountFieldChanged, isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, - onBackClicked = { navController.navigate(SkontoFragmentDirections.toCaptureFragment()) }, + onBackClicked = navigateBack, customBottomNavBarAdapter = customBottomNavBarAdapter, onProceedClicked = {}, onInfoBannerClicked = viewModel::onInfoBannerClicked, diff --git a/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml b/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml index c54a54f24..070dfa13a 100644 --- a/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml +++ b/bank-sdk/sdk/src/main/res/navigation/gbs_nav_graph.xml @@ -42,7 +42,7 @@ app:popEnterAnim="@anim/gc_nav_pop_enter_anim" app:popExitAnim="@anim/gc_nav_pop_exit_anim" app:popUpTo="@id/gbs_destination_capture_fragment" - app:popUpToInclusive="true" /> + app:popUpToInclusive="false" /> + app:enterAnim="@anim/gc_nav_enter_anim" + app:exitAnim="@anim/gc_nav_exit_anim" + app:popEnterAnim="@anim/gc_nav_pop_enter_anim" + app:popExitAnim="@anim/gc_nav_pop_exit_anim" + app:popUpTo="@id/gbs_destination_capture_fragment" + app:popUpToInclusive="true" /> \ No newline at end of file diff --git a/capture-sdk/sdk/src/main/java/net/gini/android/capture/analysis/AnalysisScreenPresenter.java b/capture-sdk/sdk/src/main/java/net/gini/android/capture/analysis/AnalysisScreenPresenter.java index 689fe9168..d87bf5328 100644 --- a/capture-sdk/sdk/src/main/java/net/gini/android/capture/analysis/AnalysisScreenPresenter.java +++ b/capture-sdk/sdk/src/main/java/net/gini/android/capture/analysis/AnalysisScreenPresenter.java @@ -313,6 +313,7 @@ public Void apply(final AnalysisInteractor.ResultHolder resultHolder, .onProceedToNoExtractionsScreen(mMultiPageDocument); return null; } + getAnalysisFragmentListenerOrNoOp() .onExtractionsAvailable(getMapOrEmpty(resultHolder.getExtractions()), getMapOrEmpty(resultHolder.getCompoundExtractions()), diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cec313620..5dd13c6bc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,6 +22,7 @@ hilt = "2.46.1" navigation_component = "2.7.6" compose-bom = "2024.06.00" accompanist-themeAdapter = "1.1.1" +compose-activity = "1.9.0" [libraries] android-gradle = { module = "com.android.tools.build:gradle", version.ref = "android-gradle-plugin" } @@ -115,6 +116,7 @@ tomlj = "org.tomlj:tomlj:1.1.1" json-testing = "org.json:json:20231013" compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" } compose-material3 = { module = "androidx.compose.material3:material3" } +compose-activity = { module = "androidx.activity:activity-compose", version.ref = "compose-activity" } compose-tools-uiTooling = { module = "androidx.compose.ui:ui-tooling" } compose-tools-uiToolingPreview = { module = "androidx.compose.ui:ui-tooling-preview" } accompanist-themeAdapter = { module = "com.google.android.material:compose-theme-adapter-3", version.ref = "accompanist-themeAdapter" } \ No newline at end of file From 8d0be73e6343dd73f9726bf70d8cc53b60799a6b Mon Sep 17 00:00:00 2001 From: Niko Date: Wed, 24 Jul 2024 17:59:29 +0200 Subject: [PATCH 08/10] feat(bank-sdk): Skonto screen. Fix tablet UI PP-648 --- .../CustomSkontoNavigationBarBottomAdapter.kt | 4 - .../layout/custom_skonto_navigation_bar.xml | 9 ++- .../bank/sdk/capture/skonto/SkontoFragment.kt | 80 ++++++++++--------- .../SkontoNavigationBarBottomAdapter.kt | 7 -- .../capture/ui/theme/modifier/Width.kt | 30 +++++++ 5 files changed, 79 insertions(+), 51 deletions(-) create mode 100644 capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/theme/modifier/Width.kt diff --git a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/adapters/CustomSkontoNavigationBarBottomAdapter.kt b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/adapters/CustomSkontoNavigationBarBottomAdapter.kt index 0cba09905..6c3d673b3 100644 --- a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/adapters/CustomSkontoNavigationBarBottomAdapter.kt +++ b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/adapters/CustomSkontoNavigationBarBottomAdapter.kt @@ -11,10 +11,6 @@ class CustomSkontoNavigationBarBottomAdapter : SkontoNavigationBarBottomAdapter private var binding: CustomSkontoNavigationBarBinding? = null - override fun setOnHelpClickListener(onClick: () -> Unit) { - binding?.gbsHelpBtn?.setOnClickListener { onClick() } - } - override fun setOnBackClickListener(onClick: () -> Unit) { binding?.gbsBackBtn?.setOnClickListener { onClick() } } diff --git a/bank-sdk/example-app/src/main/res/layout/custom_skonto_navigation_bar.xml b/bank-sdk/example-app/src/main/res/layout/custom_skonto_navigation_bar.xml index ff2aa4851..e4fe93749 100644 --- a/bank-sdk/example-app/src/main/res/layout/custom_skonto_navigation_bar.xml +++ b/bank-sdk/example-app/src/main/res/layout/custom_skonto_navigation_bar.xml @@ -69,14 +69,15 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/gc_medium" - android:padding="@dimen/gc_medium" - app:tint="?attr/colorOnBackground" - android:contentDescription="@string/gbs_digital_invoice_help_info" android:background="@android:color/transparent" + android:contentDescription="@string/gbs_digital_invoice_help_info" + android:padding="@dimen/gc_medium" + android:visibility="invisible" app:layout_constraintBottom_toBottomOf="@+id/gbs_pay" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/gbs_pay" - app:srcCompat="@drawable/gbs_menu_question_circle_info" /> + app:srcCompat="@drawable/gbs_menu_question_circle_info" + app:tint="?attr/colorOnBackground" /> Unit) - /** * Set the click listener for the back button. * diff --git a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/theme/modifier/Width.kt b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/theme/modifier/Width.kt new file mode 100644 index 000000000..d47168190 --- /dev/null +++ b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/theme/modifier/Width.kt @@ -0,0 +1,30 @@ +package net.gini.android.capture.ui.theme.modifier + +import android.content.res.Configuration +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.widthIn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.unit.max +import net.gini.android.capture.R + +@Composable +fun Modifier.tabletMaxWidth(): Modifier { + return if (isTablet()) { + widthIn(max = dimensionResource(id = R.dimen.gc_tablet_width)) + } else { + fillMaxWidth() + } +} + +@Composable +private fun isTablet(): Boolean { + val configuration = LocalConfiguration.current + return if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { + configuration.screenWidthDp > 840 + } else { + configuration.screenWidthDp > 600 + } +} \ No newline at end of file From ddbbf8ecec83bc3f17939970837514ad6e9d6d3a Mon Sep 17 00:00:00 2001 From: Mahdi Abolfazli Date: Thu, 25 Jul 2024 10:39:11 +0200 Subject: [PATCH 09/10] feat(bank-sdk): Add functionality for proceed button in Skonto PP-648 --- Gemfile.lock | 221 ------------------ .../bank/sdk/capture/CaptureFlowFragment.kt | 31 ++- .../DigitalInvoiceFragmentListener.kt | 7 +- .../sdk/capture/skonto/SkontoDataExtractor.kt | 61 ++++- .../bank/sdk/capture/skonto/SkontoFragment.kt | 23 +- .../capture/skonto/SkontoFragmentContract.kt | 2 +- .../capture/skonto/SkontoFragmentListener.kt | 24 ++ .../capture/skonto/SkontoFragmentViewModel.kt | 34 ++- 8 files changed, 156 insertions(+), 247 deletions(-) delete mode 100644 Gemfile.lock create mode 100644 bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentListener.kt diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index e6d2cb3db..000000000 --- a/Gemfile.lock +++ /dev/null @@ -1,221 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - CFPropertyList (3.0.7) - base64 - nkf - rexml - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) - artifactory (3.0.17) - atomos (0.1.3) - aws-eventstream (1.3.0) - aws-partitions (1.940.0) - aws-sdk-core (3.197.0) - aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) - jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.83.0) - aws-sdk-core (~> 3, >= 3.197.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.152.0) - aws-sdk-core (~> 3, >= 3.197.0) - aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) - aws-sigv4 (1.8.0) - aws-eventstream (~> 1, >= 1.0.2) - babosa (1.0.4) - base64 (0.2.0) - claide (1.1.0) - colored (1.2) - colored2 (3.1.2) - commander (4.6.0) - highline (~> 2.0.0) - declarative (0.0.20) - digest-crc (0.6.5) - rake (>= 12.0.0, < 14.0.0) - domain_name (0.6.20240107) - dotenv (2.8.1) - emoji_regex (3.2.3) - excon (0.110.0) - faraday (1.10.3) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-cookie_jar (0.0.7) - faraday (>= 0.8.0) - http-cookie (~> 1.0.0) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.0) - faraday-excon (1.1.0) - faraday-httpclient (1.0.1) - faraday-multipart (1.0.4) - multipart-post (~> 2) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.0) - faraday (~> 1.0) - fastimage (2.3.1) - fastlane (2.220.0) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.8, < 3.0.0) - artifactory (~> 3.0) - aws-sdk-s3 (~> 1.0) - babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored (~> 1.2) - commander (~> 4.6) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 4.0) - excon (>= 0.71.0, < 1.0.0) - faraday (~> 1.0) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 1.0) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-apis-androidpublisher_v3 (~> 0.3) - google-apis-playcustomapp_v1 (~> 0.1) - google-cloud-env (>= 1.6.0, < 2.0.0) - google-cloud-storage (~> 1.31) - highline (~> 2.0) - http-cookie (~> 1.0.5) - json (< 3.0.0) - jwt (>= 2.1.0, < 3) - mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (>= 2.0.0, < 3.0.0) - naturally (~> 2.2) - optparse (>= 0.1.1, < 1.0.0) - plist (>= 3.1.0, < 4.0.0) - rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.5) - simctl (~> 1.6.3) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (~> 3) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3, < 2.0.0) - gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.54.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.3) - addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.16.2, < 2.a) - httpclient (>= 2.8.1, < 3.a) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.a) - rexml - google-apis-iamcredentials_v1 (0.17.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-playcustomapp_v1 (0.13.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.31.0) - google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.7.0) - google-cloud-env (>= 1.0, < 3.a) - google-cloud-errors (~> 1.0) - google-cloud-env (1.6.0) - faraday (>= 0.17.3, < 3.0) - google-cloud-errors (1.4.0) - google-cloud-storage (1.47.0) - addressable (~> 2.8) - digest-crc (~> 0.4) - google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.31.0) - google-cloud-core (~> 1.6) - googleauth (>= 0.16.2, < 2.a) - mini_mime (~> 1.0) - googleauth (1.8.1) - faraday (>= 0.17.3, < 3.a) - jwt (>= 1.4, < 3.0) - multi_json (~> 1.11) - os (>= 0.9, < 2.0) - signet (>= 0.16, < 2.a) - highline (2.0.3) - http-cookie (1.0.6) - domain_name (~> 0.5) - httpclient (2.8.3) - jmespath (1.6.2) - json (2.7.2) - jwt (2.8.1) - base64 - mini_magick (4.12.0) - mini_mime (1.1.5) - multi_json (1.15.0) - multipart-post (2.4.1) - nanaimo (0.3.0) - naturally (2.2.1) - nkf (0.2.0) - optparse (0.5.0) - os (1.1.4) - plist (3.7.1) - public_suffix (5.0.5) - rake (13.2.1) - representable (3.2.0) - declarative (< 0.1.0) - trailblazer-option (>= 0.1.1, < 0.2.0) - uber (< 0.2.0) - retriable (3.1.2) - rexml (3.2.8) - strscan (>= 3.0.9) - rouge (2.0.7) - ruby2_keywords (0.0.5) - rubyzip (2.3.2) - security (0.1.5) - signet (0.19.0) - addressable (~> 2.8) - faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) - multi_json (~> 1.10) - simctl (1.6.10) - CFPropertyList - naturally - strscan (3.1.0) - terminal-notifier (2.0.0) - terminal-table (3.0.2) - unicode-display_width (>= 1.1.1, < 3) - trailblazer-option (0.1.2) - tty-cursor (0.7.1) - tty-screen (0.8.2) - tty-spinner (0.9.3) - tty-cursor (~> 0.7) - uber (0.1.0) - unicode-display_width (2.5.0) - word_wrap (1.0.0) - xcodeproj (1.24.0) - CFPropertyList (>= 2.3.3, < 4.0) - atomos (~> 0.1.3) - claide (>= 1.0.2, < 2.0) - colored2 (~> 3.1) - nanaimo (~> 0.3.0) - rexml (~> 3.2.4) - xcpretty (0.3.0) - rouge (~> 2.0.7) - xcpretty-travis-formatter (1.0.1) - xcpretty (~> 0.2, >= 0.0.7) - -PLATFORMS - arm64-darwin-21 - x86_64-darwin-20 - x86_64-linux - -DEPENDENCIES - fastlane - -BUNDLED WITH - 2.2.29 diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/CaptureFlowFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/CaptureFlowFragment.kt index 5faaef5d4..af7e7b97b 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/CaptureFlowFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/CaptureFlowFragment.kt @@ -16,6 +16,8 @@ import net.gini.android.bank.sdk.capture.digitalinvoice.DigitalInvoiceFragment import net.gini.android.bank.sdk.capture.digitalinvoice.DigitalInvoiceFragmentListener import net.gini.android.bank.sdk.capture.digitalinvoice.LineItemsValidator import net.gini.android.bank.sdk.capture.skonto.SkontoDataExtractor +import net.gini.android.bank.sdk.capture.skonto.SkontoFragment +import net.gini.android.bank.sdk.capture.skonto.SkontoFragmentListener import net.gini.android.bank.sdk.util.disallowScreenshots import net.gini.android.capture.CaptureSDKResult import net.gini.android.capture.Document @@ -24,16 +26,15 @@ import net.gini.android.capture.GiniCaptureFragment import net.gini.android.capture.GiniCaptureFragmentDirections import net.gini.android.capture.GiniCaptureFragmentListener import net.gini.android.capture.camera.CameraFragmentListener +import net.gini.android.capture.internal.util.CancelListener import net.gini.android.capture.network.model.GiniCaptureCompoundExtraction import net.gini.android.capture.network.model.GiniCaptureSpecificExtraction -import net.gini.android.capture.internal.util.CancelListener -import java.math.BigDecimal -import java.time.LocalDate class CaptureFlowFragment(private val openWithDocument: Document? = null) : Fragment(), GiniCaptureFragmentListener, DigitalInvoiceFragmentListener, + SkontoFragmentListener, CancelListener { private lateinit var navController: NavController @@ -74,7 +75,7 @@ class CaptureFlowFragment(private val openWithDocument: Document? = null) : override fun onCreate(savedInstanceState: Bundle?) { childFragmentManager.fragmentFactory = - CaptureFlowFragmentFactory(this, openWithDocument, this, this) + CaptureFlowFragmentFactory(this, openWithDocument, this, this, this) super.onCreate(savedInstanceState) if (GiniCapture.hasInstance() && !GiniCapture.getInstance().allowScreenshots) { requireActivity().window.disallowScreenshots() @@ -212,6 +213,22 @@ class CaptureFlowFragment(private val openWithDocument: Document? = null) : ) } + + override fun onPayInvoiceWithSkonto( + specificExtractions: Map, + compoundExtractions: Map + ) { + didFinishWithResult = true + captureFlowFragmentListener.onFinishedWithResult( + CaptureResult.Success( + specificExtractions, + compoundExtractions, + emptyList() + ) + ) + } + + override fun onCancelFlow() { val popBackStack = navController.popBackStack() if (!popBackStack) { @@ -242,6 +259,7 @@ class CaptureFlowFragmentFactory( private val giniCaptureFragmentListener: GiniCaptureFragmentListener, private val openWithDocument: Document? = null, private val digitalInvoiceListener: DigitalInvoiceFragmentListener, + private val skontoListener: SkontoFragmentListener, private val cancelCallback: CancelListener ) : FragmentFactory() { override fun instantiate(classLoader: ClassLoader, className: String): Fragment { @@ -258,6 +276,11 @@ class CaptureFlowFragmentFactory( cancelListener = cancelCallback } + SkontoFragment::class.java.name -> SkontoFragment().apply { + skontoFragmentListener = skontoListener + cancelListener = cancelCallback + } + else -> super.instantiate(classLoader, className) } } diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/digitalinvoice/DigitalInvoiceFragmentListener.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/digitalinvoice/DigitalInvoiceFragmentListener.kt index c533ebb92..2b3ac4f48 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/digitalinvoice/DigitalInvoiceFragmentListener.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/digitalinvoice/DigitalInvoiceFragmentListener.kt @@ -2,7 +2,6 @@ package net.gini.android.bank.sdk.capture.digitalinvoice import net.gini.android.capture.network.model.GiniCaptureCompoundExtraction import net.gini.android.capture.network.model.GiniCaptureSpecificExtraction -import net.gini.android.bank.sdk.capture.digitalinvoice.onboarding.DigitalInvoiceOnboardingFragment /** * Created by Alpar Szotyori on 05.12.2019. @@ -25,6 +24,8 @@ interface DigitalInvoiceFragmentListener { * @param specificExtractions - extractions like the "amountToPay", "iban", etc. * @param compoundExtractions - extractions like the "lineItems" */ - fun onPayInvoice(specificExtractions: Map, - compoundExtractions: Map) + fun onPayInvoice( + specificExtractions: Map, + compoundExtractions: Map + ) } \ No newline at end of file diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoDataExtractor.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoDataExtractor.kt index caa6fdf2c..c0eb3d055 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoDataExtractor.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoDataExtractor.kt @@ -4,7 +4,6 @@ import net.gini.android.bank.sdk.capture.skonto.model.SkontoData import net.gini.android.bank.sdk.capture.skonto.model.SkontoData.Amount import net.gini.android.bank.sdk.capture.skonto.model.SkontoData.SkontoPaymentMethod import net.gini.android.capture.network.model.GiniCaptureCompoundExtraction -import net.gini.android.capture.network.model.GiniCaptureExtraction import net.gini.android.capture.network.model.GiniCaptureSpecificExtraction import java.math.BigDecimal import java.time.LocalDate @@ -12,12 +11,64 @@ import java.time.LocalDate internal class SkontoDataExtractor { + companion object { + private var _extractions: MutableMap = extractions + val extractions + get() = _extractions + + private var _compoundExtractions: MutableMap = + compoundExtractions + val compoundExtractions + get() = _compoundExtractions + + + fun updateGiniExtractions(updatedData: SkontoFragmentContract.State.Ready) { + _extractions["amountToPay"]?.value = updatedData.totalAmount.amount.toString() + + val skontoDiscountMaps = compoundExtractions["skontoDiscounts"]?.specificExtractionMaps + skontoDiscountMaps?.map { skontoDiscountData -> + skontoDiscountData.putDataByKeys( + updatedData.skontoPercentage.toString(), + "skontoPercentageDiscounted", + "skontoPercentageDiscountedCalculated", + ) ?: throw NoSuchElementException("Data for `PercentageDiscounted` is missing") + + skontoDiscountData.putDataByKeys( + updatedData.skontoAmount.amount.toString(), + "skontoAmountToPay", + "skontoAmountToPayCalculated" + ) + + skontoDiscountData.putDataByKeys( + updatedData.paymentInDays.toString(), + "skontoRemainingDays", + "skontoRemainingDaysCalculated" + ) + + skontoDiscountData.putDataByKeys( + updatedData.discountDueDate.toString(), + "skontoDueDate", + "skontoDueDateCalculated" + ) + + skontoDiscountData.putDataByKeys( + updatedData.skontoPercentage.toString(), + "skontoAmountDiscounted", + "skontoAmountDiscountedCalculated" + ) + } + } + fun extractSkontoData( - extractions: Map, + extractions: Map, compoundExtractions: Map, ): SkontoData { + + _extractions = extractions.toMutableMap() + _compoundExtractions = compoundExtractions.toMutableMap() + val totalAmountToPay = extractions["amountToPay"] ?: throw NoSuchElementException("Field `extractions.amountToPay` is missing") @@ -70,3 +121,9 @@ internal class SkontoDataExtractor { fun Map.extractDataByKeys(vararg keys: String) = keys.firstNotNullOfOrNull { this[it] } + +fun MutableMap.putDataByKeys( + value: String, + vararg keys: String +) = + keys.firstNotNullOfOrNull { this[it]?.value = value } diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt index 81a39104a..d4c051cfe 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt @@ -67,14 +67,10 @@ import androidx.compose.ui.window.DialogProperties import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import androidx.navigation.NavController import androidx.navigation.findNavController -import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import net.gini.android.bank.sdk.GiniBank import net.gini.android.bank.sdk.R -import net.gini.android.bank.sdk.capture.CaptureFlowFragmentListener -import net.gini.android.bank.sdk.capture.CaptureResult import net.gini.android.bank.sdk.capture.skonto.colors.SkontoScreenColors import net.gini.android.bank.sdk.capture.skonto.colors.section.SkontoFooterSectionColors import net.gini.android.bank.sdk.capture.skonto.colors.section.SkontoInfoDialogColors @@ -104,6 +100,13 @@ class SkontoFragment : Fragment() { private val args: SkontoFragmentArgs by navArgs() + lateinit var cancelListener: CancelListener + + var skontoFragmentListener: SkontoFragmentListener? = null + set(value) { + field = value + } + private val isBottomNavigationBarEnabled = GiniCapture.getInstance().isBottomNavigationBarEnabled @@ -123,6 +126,8 @@ class SkontoFragment : Fragment() { owner = this )[SkontoFragmentViewModel::class.java] + viewModel.setListener(skontoFragmentListener) + return ComposeView(requireContext()).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { @@ -176,7 +181,7 @@ private fun ScreenContent( isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, onBackClicked = navigateBack, customBottomNavBarAdapter = customBottomNavBarAdapter, - onProceedClicked = {}, + onProceedClicked = { viewModel.onProceedClicked() }, onInfoBannerClicked = viewModel::onInfoBannerClicked, onInfoDialogDismissed = viewModel::onInfoDialogDismissed ) @@ -248,7 +253,7 @@ private fun ScreenReadyState( bottomBar = { FooterSection( colors = screenColorScheme.footerSectionColors, - discountValue = state.discountAmount, + discountValue = state.skontoPercentage, totalAmount = state.totalAmount, isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, onBackClicked = onBackClicked, @@ -271,7 +276,7 @@ private fun ScreenReadyState( amountValidation = state.skontoAmountValidation, dueDate = state.discountDueDate, infoPaymentInDays = state.paymentInDays, - infoDiscountValue = state.discountAmount, + infoDiscountValue = state.skontoPercentage, onActiveChange = onDiscountSectionActiveChange, isActive = state.isSkontoSectionActive, onSkontoAmountChange = onDiscountAmountChange, @@ -295,7 +300,7 @@ private fun ScreenReadyState( SkontoFragmentContract.SkontoEdgeCase.SkontoExpired -> stringResource( id = R.string.gbs_skonto_section_info_dialog_date_expired_message, - state.discountAmount.toFloat().formatAsDiscountPercentage() + state.skontoPercentage.toFloat().formatAsDiscountPercentage() ) SkontoFragmentContract.SkontoEdgeCase.SkontoLastDay -> @@ -909,7 +914,7 @@ private fun Float.formatAsDiscountPercentage(): String { private val previewState = SkontoFragmentContract.State.Ready( isSkontoSectionActive = true, paymentInDays = 14, - discountAmount = BigDecimal("3"), + skontoPercentage = BigDecimal("3"), skontoAmount = SkontoData.Amount(BigDecimal("97"), "EUR"), discountDueDate = LocalDate.now(), fullAmount = SkontoData.Amount(BigDecimal("100"), "EUR"), diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt index c43b1a25a..3282158de 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt @@ -10,7 +10,7 @@ internal object SkontoFragmentContract { data class Ready( val isSkontoSectionActive: Boolean, val paymentInDays: Int, - val discountAmount: BigDecimal, + val skontoPercentage: BigDecimal, val skontoAmount: SkontoData.Amount, val skontoAmountValidation: SkontoAmountValidation, val discountDueDate: LocalDate, diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentListener.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentListener.kt new file mode 100644 index 000000000..f5384049b --- /dev/null +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentListener.kt @@ -0,0 +1,24 @@ +package net.gini.android.bank.sdk.capture.skonto + +import net.gini.android.capture.network.model.GiniCaptureCompoundExtraction +import net.gini.android.capture.network.model.GiniCaptureSpecificExtraction + +/** + * Interface used by the [SkontoFragment] to dispatch events to the hosting Activity. + */ +interface SkontoFragmentListener { + + /** + * Called when the user presses the proceed button. + * + * The extractions were updated to contain the user's modifications: + * - "amountToPay" was updated to contain the new amount to pay (with or without skonto) + * + * @param specificExtractions - extractions like the "amountToPay", "iban", etc. + * @param compoundExtractions - extractions like the "skontoAmountToPay", "skontoDueDate", etc. + */ + fun onPayInvoiceWithSkonto( + specificExtractions: Map, + compoundExtractions: Map + ) +} \ No newline at end of file diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt index c75d9f7dd..9ac671a80 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt @@ -18,6 +18,21 @@ internal class SkontoFragmentViewModel( val stateFlow: MutableStateFlow = MutableStateFlow(createInitalState(data)) + private var listener: SkontoFragmentListener? = null + + fun setListener(listener: SkontoFragmentListener?) { + this.listener = listener + } + + fun onProceedClicked() { + val currentState = stateFlow.value as? SkontoFragmentContract.State.Ready ?: return + SkontoDataExtractor.updateGiniExtractions(currentState) + listener?.onPayInvoiceWithSkonto( + SkontoDataExtractor.extractions, + SkontoDataExtractor.compoundExtractions + ) + } + private fun createInitalState( data: SkontoData, ): SkontoFragmentContract.State.Ready { @@ -34,13 +49,14 @@ internal class SkontoFragmentViewModel( val totalAmount = if (isSkontoSectionActive) data.skontoAmountToPay else data.fullAmountToPay - val savedAmountValue = calculateSavedAmount(data.skontoAmountToPay.amount, data.fullAmountToPay.amount) + val savedAmountValue = + calculateSavedAmount(data.skontoAmountToPay.amount, data.fullAmountToPay.amount) val savedAmount = SkontoData.Amount(savedAmountValue, data.fullAmountToPay.currencyCode) return SkontoFragmentContract.State.Ready( isSkontoSectionActive = isSkontoSectionActive, paymentInDays = data.skontoRemainingDays, - discountAmount = discount, + skontoPercentage = discount, skontoAmount = data.skontoAmountToPay, discountDueDate = data.skontoDueDate, fullAmount = data.fullAmountToPay, @@ -66,7 +82,7 @@ internal class SkontoFragmentViewModel( currentState.copy( isSkontoSectionActive = newValue, totalAmount = totalAmount, - discountAmount = discount + skontoPercentage = discount ) ) } @@ -82,15 +98,19 @@ internal class SkontoFragmentViewModel( val newSkontoAmount = currentState.skontoAmount.copy(amount = newValue) val newTotalAmount = currentState.totalAmount.copy(amount = totalAmount) - val savedAmountValue = calculateSavedAmount(newSkontoAmount.amount, currentState.fullAmount.amount) + val savedAmountValue = + calculateSavedAmount(newSkontoAmount.amount, currentState.fullAmount.amount) val savedAmount = SkontoData.Amount(savedAmountValue, currentState.fullAmount.currencyCode) stateFlow.emit( currentState.copy( skontoAmount = newSkontoAmount, - discountAmount = discount, + skontoPercentage = discount, totalAmount = newTotalAmount, - skontoAmountValidation = validateSkontoAmount(newSkontoAmount, currentState.fullAmount), + skontoAmountValidation = validateSkontoAmount( + newSkontoAmount, + currentState.fullAmount + ), savedAmount = savedAmount, ) ) @@ -116,7 +136,7 @@ internal class SkontoFragmentViewModel( val totalAmount = if (currentState.isSkontoSectionActive) currentState.skontoAmount.amount else newValue - val discount = currentState.discountAmount + val discount = currentState.skontoPercentage val skontoAmount = newValue.minus( newValue.multiply( // full_amount - (full_amount * (discount / 100)) From 016bf6d47ca4f146cd2f6122100df570cdfc5057 Mon Sep 17 00:00:00 2001 From: Niko Date: Thu, 25 Jul 2024 10:51:49 +0200 Subject: [PATCH 10/10] feat(bank-sdk): Skonto screen. Remove skonto amount validation PP-648 --- .../bank/sdk/capture/skonto/SkontoFragment.kt | 4 --- .../capture/skonto/SkontoFragmentContract.kt | 11 +------- .../capture/skonto/SkontoFragmentViewModel.kt | 26 ++++++------------- .../textinput/amount/GiniAmountTextInput.kt | 6 ++--- 4 files changed, 11 insertions(+), 36 deletions(-) diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt index 6beb20d80..6fe607e95 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragment.kt @@ -270,7 +270,6 @@ private fun ScreenReadyState( .tabletMaxWidth(), colors = screenColorScheme.skontoSectionColors, amount = state.skontoAmount, - amountValidation = state.skontoAmountValidation, dueDate = state.discountDueDate, infoPaymentInDays = state.paymentInDays, infoDiscountValue = state.discountAmount, @@ -413,7 +412,6 @@ private fun YourInvoiceScanSection( private fun SkontoSection( colors: SkontoSectionColors, amount: SkontoData.Amount, - amountValidation: SkontoFragmentContract.State.Ready.SkontoAmountValidation, dueDate: LocalDate, infoPaymentInDays: Int, infoDiscountValue: BigDecimal, @@ -513,7 +511,6 @@ private fun SkontoSection( .fillMaxWidth() .padding(top = 16.dp), enabled = isActive, - isError = amountValidation != SkontoFragmentContract.State.Ready.SkontoAmountValidation.Valid, colors = colors.amountFieldColors, onValueChange = { onSkontoAmountChange(it) }, label = stringResource(id = R.string.gbs_skonto_section_discount_field_amount_hint), @@ -925,6 +922,5 @@ private val previewState = SkontoFragmentContract.State.Ready( paymentMethod = SkontoData.SkontoPaymentMethod.PayPal, skontoEdgeCase = SkontoFragmentContract.SkontoEdgeCase.PayByCashOnly, edgeCaseInfoDialogVisible = false, - skontoAmountValidation = SkontoFragmentContract.State.Ready.SkontoAmountValidation.Invalid.SkontoAmountGreaterOfFullAmount, savedAmount = SkontoData.Amount(BigDecimal("3"), "EUR") ) \ No newline at end of file diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt index c43b1a25a..07446e751 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentContract.kt @@ -12,7 +12,6 @@ internal object SkontoFragmentContract { val paymentInDays: Int, val discountAmount: BigDecimal, val skontoAmount: SkontoData.Amount, - val skontoAmountValidation: SkontoAmountValidation, val discountDueDate: LocalDate, val fullAmount: SkontoData.Amount, val totalAmount: SkontoData.Amount, @@ -20,15 +19,7 @@ internal object SkontoFragmentContract { val paymentMethod: SkontoData.SkontoPaymentMethod, val skontoEdgeCase: SkontoEdgeCase?, val edgeCaseInfoDialogVisible: Boolean, - ) : State() { - sealed class SkontoAmountValidation { - object Valid : SkontoAmountValidation() - - sealed class Invalid : SkontoAmountValidation() { - object SkontoAmountGreaterOfFullAmount : Invalid() - } - } - } + ) : State() } sealed class SkontoEdgeCase { diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt index c75d9f7dd..9ae9e78a1 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoFragmentViewModel.kt @@ -48,10 +48,6 @@ internal class SkontoFragmentViewModel( paymentMethod = paymentMethod, skontoEdgeCase = edgeCase, edgeCaseInfoDialogVisible = edgeCase != null, - skontoAmountValidation = validateSkontoAmount( - skontoAmount = data.skontoAmountToPay, - fullAmount = data.fullAmountToPay - ), savedAmount = savedAmount ) } @@ -73,6 +69,14 @@ internal class SkontoFragmentViewModel( fun onSkontoAmountFieldChanged(newValue: BigDecimal) = viewModelScope.launch { val currentState = stateFlow.value as? SkontoFragmentContract.State.Ready ?: return@launch + + if (newValue > currentState.fullAmount.amount) { + stateFlow.emit( + currentState.copy(skontoAmount = currentState.skontoAmount) + ) + return@launch + } + val discount = calculateDiscount(newValue, currentState.fullAmount.amount) val totalAmount = if (currentState.isSkontoSectionActive) newValue @@ -90,7 +94,6 @@ internal class SkontoFragmentViewModel( skontoAmount = newSkontoAmount, discountAmount = discount, totalAmount = newTotalAmount, - skontoAmountValidation = validateSkontoAmount(newSkontoAmount, currentState.fullAmount), savedAmount = savedAmount, ) ) @@ -166,19 +169,6 @@ internal class SkontoFragmentViewModel( private fun calculateSavedAmount(skontoAmount: BigDecimal, fullAmount: BigDecimal) = fullAmount.minus(skontoAmount).coerceAtLeast(BigDecimal.ZERO) - private fun validateSkontoAmount( - skontoAmount: SkontoData.Amount, - fullAmount: SkontoData.Amount - ): SkontoFragmentContract.State.Ready.SkontoAmountValidation { - return when { - skontoAmount.amount <= fullAmount.amount -> - SkontoFragmentContract.State.Ready.SkontoAmountValidation.Valid - - else -> - SkontoFragmentContract.State.Ready.SkontoAmountValidation.Invalid.SkontoAmountGreaterOfFullAmount - } - } - private fun extractSkontoEdgeCase( dueDate: LocalDate, paymentMethod: SkontoData.SkontoPaymentMethod, diff --git a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/GiniAmountTextInput.kt b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/GiniAmountTextInput.kt index f1becd33d..229d6a989 100644 --- a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/GiniAmountTextInput.kt +++ b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/textinput/amount/GiniAmountTextInput.kt @@ -43,10 +43,8 @@ fun GiniAmountTextInput( val parsedAmount = decimalFormatter.parseAmount(amount) var text by remember { mutableStateOf(parsedAmount) } - - LaunchedEffect(key1 = parsedAmount) { // we need to reset text if amount was changed only - text = parsedAmount - } + + text = parsedAmount GiniTextInput( modifier = modifier,