diff --git a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ConfigurationActivity.kt b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ConfigurationActivity.kt index bba1f063eb..cd78ff0ecc 100644 --- a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ConfigurationActivity.kt +++ b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ConfigurationActivity.kt @@ -139,6 +139,9 @@ class ConfigurationActivity : AppCompatActivity() { binding.switchReviewScreenCustomBottomNavbar.isChecked = configuration.isReviewScreenCustomBottomNavBarEnabled + binding.switchSkontoCustomBottomNavbar.isChecked = + configuration.isSkontoCustomNavBarEnabled + // 12 enable image picker screens custom bottom navigation bar -> was implemented on iOS, not needed for Android // 13 enable onboarding screens at first launch @@ -369,6 +372,14 @@ class ConfigurationActivity : AppCompatActivity() { ) } + binding.switchSkontoCustomBottomNavbar.setOnCheckedChangeListener { _, isChecked -> + configurationViewModel.setConfiguration( + configurationViewModel.configurationFlow.value.copy( + isSkontoCustomNavBarEnabled = isChecked + ) + ) + } + // 12 enable image picker screens custom bottom navigation bar -> was implemented on iOS, not needed for Android // 13 enable onboarding screens at first launch @@ -421,6 +432,7 @@ class ConfigurationActivity : AppCompatActivity() { ) ) } + // 19 enable multi page in custom onboarding pages binding.switchCustomOnboardingMultiPage.setOnCheckedChangeListener { _, isChecked -> configurationViewModel.setConfiguration( diff --git a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ConfigurationViewModel.kt b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ConfigurationViewModel.kt index e2abeb843b..cc9dea3200 100644 --- a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ConfigurationViewModel.kt +++ b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/ConfigurationViewModel.kt @@ -26,6 +26,7 @@ import net.gini.android.bank.sdk.exampleapp.ui.adapters.CustomOnButtonLoadingInd import net.gini.android.bank.sdk.exampleapp.ui.adapters.CustomOnboardingIllustrationAdapter import net.gini.android.bank.sdk.exampleapp.ui.adapters.CustomOnboardingNavigationBarBottomAdapter import net.gini.android.bank.sdk.exampleapp.ui.adapters.CustomReviewNavigationBarBottomAdapter +import net.gini.android.bank.sdk.exampleapp.ui.adapters.CustomSkontoNavigationBarBottomAdapter import net.gini.android.bank.sdk.exampleapp.ui.data.Configuration import net.gini.android.capture.GiniCaptureDebug import net.gini.android.capture.help.HelpItem @@ -275,6 +276,12 @@ class ConfigurationViewModel @Inject constructor( CustomDigitalInvoiceHelpNavigationBarBottomAdapter() } + if (configuration.isSkontoCustomNavBarEnabled) { + GiniBank.skontoNavigationBarBottomAdapter = CustomSkontoNavigationBarBottomAdapter() + } else { + GiniBank.skontoNavigationBarBottomAdapter = null + } + // 35 Digital invoice onboarding bottom navigation bar if (configuration.isDigitalInvoiceOnboardingBottomNavigationBarEnabled) { GiniBank.digitalInvoiceOnboardingNavigationBarBottomAdapter = 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 new file mode 100644 index 0000000000..0cba099059 --- /dev/null +++ b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/adapters/CustomSkontoNavigationBarBottomAdapter.kt @@ -0,0 +1,54 @@ +package net.gini.android.bank.sdk.exampleapp.ui.adapters + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import net.gini.android.bank.sdk.capture.skonto.SkontoNavigationBarBottomAdapter +import net.gini.android.bank.sdk.exampleapp.databinding.CustomSkontoNavigationBarBinding + +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() } + } + + override fun setOnProceedClickListener(onClick: () -> Unit) { + binding?.gbsPay?.setOnClickListener { onClick() } + } + + override fun setProceedButtonEnabled(enabled: Boolean) { + binding?.gbsPay?.isEnabled = enabled + } + + override fun setTotalPriceText(text: String) { + binding?.priceTotal?.text = text + } + + override fun setDiscountLabelText(text: String) { + binding?.discountInfo?.text = text + } + + override fun onCreateView(container: ViewGroup): View { + binding = CustomSkontoNavigationBarBinding.inflate( + LayoutInflater.from(container.context), + container, + false + ) + return binding!!.root + } + + override fun setDiscountLabelVisible(visible: Boolean) { + binding?.discountInfo?.isVisible = visible + } + + override fun onDestroy() { + binding = null + } +} \ No newline at end of file diff --git a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/data/Configuration.kt b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/data/Configuration.kt index cc40c366fa..64c04492db 100644 --- a/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/data/Configuration.kt +++ b/bank-sdk/example-app/src/main/java/net/gini/android/bank/sdk/exampleapp/ui/data/Configuration.kt @@ -154,8 +154,12 @@ data class Configuration( // 37 Debug mode val isDebugModeEnabled: Boolean = true, + // 38 Is Allow Screenshots val isAllowScreenshotsEnabled: Boolean = true, + // 39 Skonto Custom bottom navigation + val isSkontoCustomNavBarEnabled: Boolean = false, + ) : Parcelable { companion object { diff --git a/bank-sdk/example-app/src/main/res/layout/activity_configuration.xml b/bank-sdk/example-app/src/main/res/layout/activity_configuration.xml index 845396ef52..b26e83aa42 100644 --- a/bank-sdk/example-app/src/main/res/layout/activity_configuration.xml +++ b/bank-sdk/example-app/src/main/res/layout/activity_configuration.xml @@ -208,13 +208,27 @@ android:text="@string/review_screen_custom_bottom_navbar_switch_label" /> + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bank-sdk/example-app/src/main/res/values/strings.xml b/bank-sdk/example-app/src/main/res/values/strings.xml index 5ed91dbac2..1b59b68ffa 100644 --- a/bank-sdk/example-app/src/main/res/values/strings.xml +++ b/bank-sdk/example-app/src/main/res/values/strings.xml @@ -67,6 +67,7 @@ The custom bottom navigation bar is shown if \'Bottom navigation bar\' is also enabled. Camera screen custom bottom navigation bar Review screen custom bottom navigation bar + Skonto screen custom bottom navigation bar Onboarding screens at every launch Onboarding screens at first launch Overwrites \'Onboarding screens at first launch\'. diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/GiniBank.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/GiniBank.kt index d86a1a582f..4b158e9a23 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/GiniBank.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/GiniBank.kt @@ -24,6 +24,7 @@ import net.gini.android.bank.sdk.capture.digitalinvoice.view.DefaultDigitalInvoi import net.gini.android.bank.sdk.capture.digitalinvoice.view.DefaultDigitalInvoiceOnboardingNavigationBarBottomAdapter import net.gini.android.bank.sdk.capture.digitalinvoice.view.DigitalInvoiceNavigationBarBottomAdapter import net.gini.android.bank.sdk.capture.digitalinvoice.view.DigitalInvoiceOnboardingNavigationBarBottomAdapter +import net.gini.android.bank.sdk.capture.skonto.SkontoNavigationBarBottomAdapter import net.gini.android.bank.sdk.error.AmountParsingException import net.gini.android.bank.sdk.pay.getBusinessIntent import net.gini.android.bank.sdk.pay.getRequestId @@ -101,6 +102,18 @@ object GiniBank { } get() = digitalInvoiceNavigationBarBottomAdapterInstance.viewAdapter + + internal var skontoNavigationBarBottomAdapterInstance: InjectedViewAdapterInstance? = + null + + var skontoNavigationBarBottomAdapter: SkontoNavigationBarBottomAdapter? + set(value) { + skontoNavigationBarBottomAdapterInstance = + value?.let { InjectedViewAdapterInstance(it) } + } + get() = skontoNavigationBarBottomAdapterInstance?.viewAdapter + + internal fun getCaptureConfiguration() = captureConfiguration /** @@ -316,18 +329,26 @@ object GiniBank { ): CancellationToken { giniCapture.let { capture -> check(capture != null) { "Capture feature is not configured. Call setCaptureConfiguration before starting the flow." } - return capture.createDocumentForImportedFiles(intent, context, object : AsyncCallback { - override fun onSuccess(result: Document) { - resultLauncher.launch(CaptureImportInput.Forward(result)) - } - - override fun onError(exception: ImportedFileValidationException?) { - resultLauncher.launch(CaptureImportInput.Error(exception?.validationError, exception?.message)) - } + return capture.createDocumentForImportedFiles( + intent, + context, + object : AsyncCallback { + override fun onSuccess(result: Document) { + resultLauncher.launch(CaptureImportInput.Forward(result)) + } + + override fun onError(exception: ImportedFileValidationException?) { + resultLauncher.launch( + CaptureImportInput.Error( + exception?.validationError, + exception?.message + ) + ) + } - override fun onCancelled() { - } - }) + override fun onCancelled() { + } + }) } } @@ -413,17 +434,18 @@ object GiniBank { /** * The document was processed successfully. */ - data class Success(val document: Document?): CreateDocumentFromImportedFileResult() + data class Success(val document: Document?) : CreateDocumentFromImportedFileResult() /** * Document processing returned an error. */ - data class Error(val error: ImportedFileValidationException?): CreateDocumentFromImportedFileResult() + data class Error(val error: ImportedFileValidationException?) : + CreateDocumentFromImportedFileResult() /** * Document processing was cancelled. */ - object Cancelled: CreateDocumentFromImportedFileResult() + object Cancelled : CreateDocumentFromImportedFileResult() } /** @@ -442,7 +464,7 @@ object GiniBank { return giniCapture?.createDocumentForImportedFiles( intent, context, - object: AsyncCallback { + object : AsyncCallback { override fun onSuccess(result: Document?) { callback(CreateDocumentFromImportedFileResult.Success(result)) } diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/Configuration.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/Configuration.kt index f5f3821e12..1d921d7287 100644 --- a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/Configuration.kt +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/Configuration.kt @@ -1,5 +1,6 @@ package net.gini.android.bank.sdk.capture +import net.gini.android.bank.sdk.capture.skonto.SkontoNavigationBarBottomAdapter import net.gini.android.capture.DocumentImportEnabledFileTypes import net.gini.android.capture.EntryPoint import net.gini.android.capture.GiniCapture @@ -181,6 +182,11 @@ data class CaptureConfiguration( */ val reviewNavigationBarBottomAdapter: ReviewNavigationBarBottomAdapter? = null, + /** + * Set an adapter implementation to show a custom bottom navigation bar on the Skonto screen. + */ + val skontoNavigationBarBottomAdapter: SkontoNavigationBarBottomAdapter? = null, + /** * Set an adapter implementation to show a custom bottom navigation bar on the help screen. */ @@ -200,7 +206,7 @@ data class CaptureConfiguration( * * IMPORTANT: If you disallow screenshots and use the [CaptureFlowFragment] for launching the SDK in your activity, please clear the [android.view.WindowManager.LayoutParams.FLAG_SECURE] * on your activity's window after the SDK has finished to allow users to take screenshots of your app again. - */ + */ val allowScreenshots: Boolean = true, /** @@ -236,15 +242,47 @@ internal fun GiniCapture.Builder.applyConfiguration(configuration: CaptureConfig }) } configuration.navigationBarTopAdapter?.let { setNavigationBarTopAdapter(it) } - configuration.onboardingAlignCornersIllustrationAdapter?.let { setOnboardingAlignCornersIllustrationAdapter(it) } - configuration.onboardingLightingIllustrationAdapter?.let { setOnboardingLightingIllustrationAdapter(it) } - configuration.onboardingMultiPageIllustrationAdapter?.let { setOnboardingMultiPageIllustrationAdapter(it) } - configuration.onboardingQRCodeIllustrationAdapter?.let { setOnboardingQRCodeIllustrationAdapter(it) } + configuration.onboardingAlignCornersIllustrationAdapter?.let { + setOnboardingAlignCornersIllustrationAdapter( + it + ) + } + configuration.onboardingLightingIllustrationAdapter?.let { + setOnboardingLightingIllustrationAdapter( + it + ) + } + configuration.onboardingMultiPageIllustrationAdapter?.let { + setOnboardingMultiPageIllustrationAdapter( + it + ) + } + configuration.onboardingQRCodeIllustrationAdapter?.let { + setOnboardingQRCodeIllustrationAdapter( + it + ) + } configuration.customLoadingIndicatorAdapter?.let { setLoadingIndicatorAdapter(it) } - configuration.onButtonLoadingIndicatorAdapter?.let { setOnButtonLoadingIndicatorAdapter(it) } - configuration.onboardingNavigationBarBottomAdapter?.let { setOnboardingNavigationBarBottomAdapter(it) } - configuration.cameraNavigationBarBottomAdapter?.let { setCameraNavigationBarBottomAdapter(it) } - configuration.reviewNavigationBarBottomAdapter?.let { setReviewBottomBarNavigationAdapter(it) } + configuration.onButtonLoadingIndicatorAdapter?.let { + setOnButtonLoadingIndicatorAdapter( + it + ) + } + configuration.onboardingNavigationBarBottomAdapter?.let { + setOnboardingNavigationBarBottomAdapter( + it + ) + } + configuration.cameraNavigationBarBottomAdapter?.let { + setCameraNavigationBarBottomAdapter( + it + ) + } + configuration.reviewNavigationBarBottomAdapter?.let { + setReviewBottomBarNavigationAdapter( + it + ) + } configuration.helpNavigationBarBottomAdapter?.let { setHelpNavigationBarBottomAdapter(it) } } } 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 df86a072a6..bf5a7fefe7 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 @@ -2,10 +2,10 @@ package net.gini.android.bank.sdk.capture.skonto import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.FrameLayout import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background @@ -28,6 +28,7 @@ import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -44,6 +45,7 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.RectangleShape 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 @@ -52,8 +54,10 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider +import net.gini.android.bank.sdk.GiniBank import net.gini.android.bank.sdk.R import net.gini.android.bank.sdk.capture.skonto.colors.SkontoScreenColors import net.gini.android.bank.sdk.capture.skonto.colors.section.SkontoFooterSectionColors @@ -70,19 +74,27 @@ 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.view.InjectedViewAdapterInstance import java.math.BigDecimal import java.math.RoundingMode -import java.text.DecimalFormat import java.time.LocalDate import java.time.format.DateTimeFormatter class SkontoFragment : Fragment() { - val bottomNavBar = GiniCapture.getInstance().internal().navigationBarTopAdapterInstance + private val isBottomNavigationBarEnabled = + GiniCapture.getInstance().isBottomNavigationBarEnabled + + private val customBottomNavBarAdapter: InjectedViewAdapterInstance? = + GiniBank.skontoNavigationBarBottomAdapterInstance + private var customBottomNavigationBarView: View? = null + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { + customBottomNavigationBarView = + container?.let { customBottomNavBarAdapter?.viewAdapter?.onCreateView(it) } val viewModel = ViewModelProvider(requireActivity())[SkontoFragmentViewModel::class.java] @@ -92,6 +104,8 @@ class SkontoFragment : Fragment() { GiniTheme { ScreenContent( viewModel = viewModel, + isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, + customBottomNavBarAdapter = customBottomNavBarAdapter, ) } } @@ -103,7 +117,9 @@ class SkontoFragment : Fragment() { private fun ScreenContent( viewModel: SkontoFragmentViewModel, modifier: Modifier = Modifier, - screenColorScheme: SkontoScreenColors = SkontoScreenColors.colors() + screenColorScheme: SkontoScreenColors = SkontoScreenColors.colors(), + isBottomNavigationBarEnabled: Boolean, + customBottomNavBarAdapter: InjectedViewAdapterInstance?, ) { val state by viewModel.stateFlow.collectAsState() ScreenStateContent( @@ -113,7 +129,12 @@ private fun ScreenContent( onDiscountSectionActiveChange = viewModel::onSkontoActiveChanged, onSkontoAmountChange = viewModel::onSkontoAmountFieldChanged, onDueDateChanged = viewModel::onSkontoDueDateChanged, - onFullAmountChange = viewModel::onFullAmountFieldChanged + onFullAmountChange = viewModel::onFullAmountFieldChanged, + isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, + onBackClicked = {}, + onHelpClicked = {}, + customBottomNavBarAdapter = customBottomNavBarAdapter, + onProceedClicked = {} ) } @@ -124,6 +145,11 @@ private fun ScreenStateContent( onSkontoAmountChange: (BigDecimal) -> Unit, onFullAmountChange: (BigDecimal) -> Unit, onDueDateChanged: (LocalDate) -> Unit, + onBackClicked: () -> Unit, + onHelpClicked: () -> Unit, + onProceedClicked: () -> Unit, + isBottomNavigationBarEnabled: Boolean, + customBottomNavBarAdapter: InjectedViewAdapterInstance?, modifier: Modifier = Modifier, screenColorScheme: SkontoScreenColors = SkontoScreenColors.colors() ) { @@ -136,7 +162,12 @@ private fun ScreenStateContent( onDiscountSectionActiveChange = onDiscountSectionActiveChange, onDiscountAmountChange = onSkontoAmountChange, onDueDateChanged = onDueDateChanged, - onFullAmountChange = onFullAmountChange + onFullAmountChange = onFullAmountChange, + onBackClicked = onBackClicked, + onHelpClicked = onHelpClicked, + isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, + customBottomNavBarAdapter = customBottomNavBarAdapter, + onProceedClicked = onProceedClicked, ) } @@ -144,11 +175,16 @@ private fun ScreenStateContent( @Composable private fun ScreenReadyState( + onBackClicked: () -> Unit, + onHelpClicked: () -> Unit, + onProceedClicked: () -> Unit, state: SkontoFragmentContract.State.Ready, onDiscountSectionActiveChange: (Boolean) -> Unit, onDiscountAmountChange: (BigDecimal) -> Unit, onDueDateChanged: (LocalDate) -> Unit, onFullAmountChange: (BigDecimal) -> Unit, + isBottomNavigationBarEnabled: Boolean, + customBottomNavBarAdapter: InjectedViewAdapterInstance?, modifier: Modifier = Modifier, screenColorScheme: SkontoScreenColors = SkontoScreenColors.colors(), ) { @@ -156,13 +192,26 @@ private fun ScreenReadyState( val scrollState = rememberScrollState() Scaffold(modifier = modifier, containerColor = screenColorScheme.backgroundColor, - topBar = { TopAppBar(colors = screenColorScheme.topAppBarColors) }, + topBar = { + TopAppBar( + isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, + colors = screenColorScheme.topAppBarColors, + onBackClicked = onBackClicked, + onHelpClicked = onHelpClicked, + ) + }, bottomBar = { FooterSection( colors = screenColorScheme.footerSectionColors, discountValue = state.discountValue, totalAmount = state.totalAmount, - currency = state.currency + currency = state.currency, + isBottomNavigationBarEnabled = isBottomNavigationBarEnabled, + onBackClicked = onBackClicked, + onHelpClicked = onHelpClicked, + customBottomNavBarAdapter = customBottomNavBarAdapter, + onProceedClicked = onProceedClicked, + isSkontoSectionActive = state.isSkontoSectionActive ) }) { Column( @@ -200,7 +249,10 @@ private fun ScreenReadyState( @Composable private fun TopAppBar( + onBackClicked: () -> Unit, + onHelpClicked: () -> Unit, modifier: Modifier = Modifier, + isBottomNavigationBarEnabled: Boolean, colors: GiniTopBarColors, ) { GiniTopBar( @@ -208,21 +260,50 @@ private fun TopAppBar( colors = colors, title = stringResource(id = R.string.gbs_skonto_screen_title), navigationIcon = { - Icon( - modifier = Modifier.padding(16.dp), - painter = rememberVectorPainter(image = Icons.AutoMirrored.Default.ArrowBack), - contentDescription = null, - ) + AnimatedVisibility(visible = !isBottomNavigationBarEnabled) { + NavigationActionBack(onClick = onBackClicked) + } }, actions = { - Icon( - modifier = Modifier.padding(16.dp), - painter = painterResource(net.gini.android.capture.R.drawable.gc_help_icon), - contentDescription = null, - ) + AnimatedVisibility(visible = !isBottomNavigationBarEnabled) { + NavigationActionHelp(onClick = onHelpClicked) + } }) } +@Composable +private fun NavigationActionBack( + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + IconButton( + modifier = modifier, + onClick = onClick + ) { + Icon( + painter = rememberVectorPainter(image = Icons.AutoMirrored.Default.ArrowBack), + contentDescription = null, + ) + } +} + +@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, @@ -390,10 +471,12 @@ private fun SkontoSection( } if (isDatePickerVisible) { - GiniDatePickerDialog(onDismissRequest = { isDatePickerVisible = false }, onSaved = { - isDatePickerVisible = false - onDueDateChanged(it) - }) + GiniDatePickerDialog( + onDismissRequest = { isDatePickerVisible = false }, + onSaved = { + isDatePickerVisible = false + onDueDateChanged(it) + }) } } @@ -496,66 +579,117 @@ private fun FooterSection( discountValue: BigDecimal, currency: String, colors: SkontoFooterSectionColors, + isBottomNavigationBarEnabled: Boolean, + isSkontoSectionActive: Boolean, + onBackClicked: () -> Unit, + onHelpClicked: () -> Unit, + onProceedClicked: () -> Unit, modifier: Modifier = Modifier, + customBottomNavBarAdapter: InjectedViewAdapterInstance?, ) { - Card( - modifier = modifier.fillMaxWidth(), - shape = RectangleShape, - colors = CardDefaults.cardColors(containerColor = colors.cardBackgroundColor) - ) { - Column( - modifier = Modifier.padding(16.dp) + val animatedTotalAmount by animateFloatAsState( + targetValue = totalAmount.toFloat(), label = "totalAmount" + ) + val animatedDiscountAmount by animateFloatAsState( + targetValue = discountValue.toFloat(), label = "discountAmount" + ) + val totalPriceText = "${currencyFormatterWithoutSymbol().format(animatedTotalAmount)} $currency" + val discountLabelText = stringResource( + id = R.string.gbs_skonto_section_footer_label_discount, + animatedDiscountAmount.formatAsDiscountPercentage() + ) + val proceedEnabled by remember { mutableStateOf(true) } + + if (customBottomNavBarAdapter != null) { + val ctx = LocalContext.current + AndroidView(factory = { + customBottomNavBarAdapter.viewAdapter.onCreateView(FrameLayout(ctx)) + }, update = { + with(customBottomNavBarAdapter.viewAdapter) { + setTotalPriceText(totalPriceText) + setProceedButtonEnabled(proceedEnabled) // TODO Integrate validation + setOnHelpClickListener(onHelpClicked) + setOnBackClickListener(onBackClicked) + setDiscountLabelText(discountLabelText) + setDiscountLabelVisible(isSkontoSectionActive) + setOnProceedClickListener(onProceedClicked) + } + }) + } else { + Card( + modifier = modifier.fillMaxWidth(), + shape = RectangleShape, + colors = CardDefaults.cardColors(containerColor = colors.cardBackgroundColor) ) { - 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, - ) { - val animatedTotalAmount by animateFloatAsState( - targetValue = totalAmount.toFloat(), label = "totalAmount" - ) - Text( - text = "${currencyFormatterWithoutSymbol().format(animatedTotalAmount)} $currency", - style = GiniTheme.typography.headline5, - color = colors.amountTextColor, - ) - Box( - modifier = Modifier - .height(IntrinsicSize.Min) - .padding(horizontal = 12.dp) - .background( - colors.discountLabelColorScheme.backgroundColor, - RoundedCornerShape(4.dp) - ), + Column { + Column( + modifier = Modifier.padding(start = 20.dp, end = 20.dp, top = 20.dp) ) { - val animatedDiscountAmount by animateFloatAsState( - targetValue = discountValue.toFloat(), label = "discountAmount" - ) - Text( - modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp), - text = stringResource( - id = R.string.gbs_skonto_section_footer_label_discount, - animatedDiscountAmount.formatAsDiscountPercentage() - ), - style = GiniTheme.typography.caption1, - color = colors.discountLabelColorScheme.textColor, + 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, + ) { + Text( + text = totalPriceText, + style = GiniTheme.typography.headline5, + color = colors.amountTextColor, + ) + AnimatedVisibility(visible = isSkontoSectionActive) { + Box( + modifier = Modifier + .height(IntrinsicSize.Min) + .padding(horizontal = 12.dp) + .background( + colors.discountLabelColorScheme.backgroundColor, + RoundedCornerShape(4.dp) + ), + ) { + Text( + modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp), + text = discountLabelText, + style = GiniTheme.typography.caption1, + color = colors.discountLabelColorScheme.textColor, + ) + } + } + } + } + val buttonPaddingHorizontal = if (isBottomNavigationBarEnabled) 0.dp else 20.dp + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly + ) { + AnimatedVisibility(visible = isBottomNavigationBarEnabled) { + NavigationActionBack( + modifier = Modifier.padding(horizontal = 4.dp), + onClick = onBackClicked + ) + } + GiniButton( + modifier = Modifier + .weight(0.1f) + .padding(horizontal = buttonPaddingHorizontal), + 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 + ) + } } } - GiniButton( - modifier = Modifier.fillMaxWidth(), - text = stringResource(id = R.string.gbs_skonto_section_footer_continue_button_text), - onClick = { /*TODO*/ }, - giniButtonColors = colors.continueButtonColors - ) } } } @@ -585,6 +719,11 @@ private fun ScreenReadyStatePreview() { onDiscountAmountChange = {}, onDueDateChanged = {}, onFullAmountChange = {}, + onHelpClicked = {}, + onBackClicked = {}, + isBottomNavigationBarEnabled = false, + onProceedClicked = {}, + customBottomNavBarAdapter = null ) } } diff --git a/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoNavigationBarBottomAdapter.kt b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoNavigationBarBottomAdapter.kt new file mode 100644 index 0000000000..52a95e7b3c --- /dev/null +++ b/bank-sdk/sdk/src/main/java/net/gini/android/bank/sdk/capture/skonto/SkontoNavigationBarBottomAdapter.kt @@ -0,0 +1,45 @@ +package net.gini.android.bank.sdk.capture.skonto + +import net.gini.android.capture.view.InjectedViewAdapter + +interface SkontoNavigationBarBottomAdapter : InjectedViewAdapter { + + /** + * Set the click listener for the help button. + * + * @param onClick the click function for the help button + */ + fun setOnHelpClickListener(onClick: () -> Unit) + + /** + * Set the click listener for the back button. + * + * @param onClick the click function for the back button + */ + fun setOnBackClickListener(onClick: () -> Unit) + + /** + * Set the click listener for the proceed button. + * + * @param listener the click listener for the button + */ + fun setOnProceedClickListener(onClick: () -> Unit) + + /** + * Enable or disable the proceed button. + * + * @param enabled for enabling or disabling the button + */ + fun setProceedButtonEnabled(enabled: Boolean) + + /** + * Set the total price. + * + * @param text price string with currency symbol + */ + fun setTotalPriceText(text: String) + + fun setDiscountLabelVisible(visible: Boolean) + + fun setDiscountLabelText(text: String) +} \ No newline at end of file diff --git a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/topbar/GiniTopBar.kt b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/topbar/GiniTopBar.kt index 1b3bdc2fda..5ec05f9af2 100644 --- a/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/topbar/GiniTopBar.kt +++ b/capture-sdk/sdk/src/main/java/net/gini/android/capture/ui/components/topbar/GiniTopBar.kt @@ -31,7 +31,6 @@ fun GiniTopBar( GiniTopBar( modifier = modifier, colors = colors, title = { Text( - modifier = Modifier.padding(16.dp), text = title, style = GiniTheme.typography.headline6, )