diff --git a/example/src/main/kotlin/com/processout/example/ui/screen/card/payment/CardPaymentFragment.kt b/example/src/main/kotlin/com/processout/example/ui/screen/card/payment/CardPaymentFragment.kt index 93672dbd9..835cfbe87 100644 --- a/example/src/main/kotlin/com/processout/example/ui/screen/card/payment/CardPaymentFragment.kt +++ b/example/src/main/kotlin/com/processout/example/ui/screen/card/payment/CardPaymentFragment.kt @@ -27,6 +27,7 @@ import com.processout.sdk.core.ProcessOutActivityResult import com.processout.sdk.core.onFailure import com.processout.sdk.core.onSuccess import com.processout.sdk.ui.card.tokenization.POCardTokenizationConfiguration +import com.processout.sdk.ui.card.tokenization.POCardTokenizationConfiguration.SubmitButton import com.processout.sdk.ui.card.tokenization.POCardTokenizationLauncher import com.processout.sdk.ui.shared.view.dialog.POAlertDialog import com.processout.sdk.ui.threeds.PO3DSRedirectCustomTabLauncher @@ -98,7 +99,8 @@ class CardPaymentFragment : BaseFragment( viewModel.onTokenizing() launcher.launch( POCardTokenizationConfiguration( - savingAllowed = true + savingAllowed = true, + submitButton = SubmitButton() ) ) } diff --git a/example/src/main/kotlin/com/processout/example/ui/screen/checkout/DynamicCheckoutFragment.kt b/example/src/main/kotlin/com/processout/example/ui/screen/checkout/DynamicCheckoutFragment.kt index 7bb84ff96..ade773923 100644 --- a/example/src/main/kotlin/com/processout/example/ui/screen/checkout/DynamicCheckoutFragment.kt +++ b/example/src/main/kotlin/com/processout/example/ui/screen/checkout/DynamicCheckoutFragment.kt @@ -26,7 +26,7 @@ import com.processout.sdk.core.onSuccess import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.AlternativePaymentConfiguration import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.AlternativePaymentConfiguration.PaymentConfirmationConfiguration -import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.AlternativePaymentConfiguration.PaymentConfirmationConfiguration.ConfirmButton +import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.SubmitButton import com.processout.sdk.ui.checkout.PODynamicCheckoutLauncher import com.processout.sdk.ui.shared.view.dialog.POAlertDialog import com.processout.sdk.ui.threeds.PO3DSRedirectCustomTabLauncher @@ -99,7 +99,7 @@ class DynamicCheckoutFragment : BaseFragment( alternativePayment = AlternativePaymentConfiguration( returnUrl = Constants.RETURN_URL, paymentConfirmation = PaymentConfirmationConfiguration( - confirmButton = ConfirmButton() + confirmButton = SubmitButton() ) ) ) diff --git a/example/src/main/kotlin/com/processout/example/ui/screen/features/FeaturesFragment.kt b/example/src/main/kotlin/com/processout/example/ui/screen/features/FeaturesFragment.kt index 8ae77ded1..9b75c0711 100644 --- a/example/src/main/kotlin/com/processout/example/ui/screen/features/FeaturesFragment.kt +++ b/example/src/main/kotlin/com/processout/example/ui/screen/features/FeaturesFragment.kt @@ -20,6 +20,7 @@ import com.processout.sdk.api.model.response.POCard import com.processout.sdk.api.model.response.POGooglePayCardTokenizationData import com.processout.sdk.core.* import com.processout.sdk.ui.card.update.POCardUpdateConfiguration +import com.processout.sdk.ui.card.update.POCardUpdateConfiguration.* import com.processout.sdk.ui.card.update.POCardUpdateLauncher import com.processout.sdk.ui.googlepay.POGooglePayCardTokenizationLauncher import com.processout.sdk.ui.shared.configuration.POCancellationConfiguration @@ -90,15 +91,15 @@ class FeaturesFragment : BaseFragment( cardUpdateLauncher.launch( POCardUpdateConfiguration( cardId = card?.id ?: String(), - options = POCardUpdateConfiguration.Options( - cardInformation = POCardUpdateConfiguration.CardInformation( + options = Options( + cardInformation = CardInformation( maskedNumber = maskedNumber, iin = card?.iin, scheme = card?.scheme, preferredScheme = card?.coScheme ), + submitButton = SubmitButton(), cancellation = POCancellationConfiguration( - secondaryAction = true, backPressed = true, dragDown = true, touchOutside = false diff --git a/sdk/src/main/res/values-ar/strings.xml b/sdk/src/main/res/values-ar/strings.xml index 832a99ca2..b0b99cf7c 100644 --- a/sdk/src/main/res/values-ar/strings.xml +++ b/sdk/src/main/res/values-ar/strings.xml @@ -92,4 +92,9 @@ إلغاء الدفع ليس الآن + + إلغاء؟ + إلغاء + ليس الآن + diff --git a/sdk/src/main/res/values-fr/strings.xml b/sdk/src/main/res/values-fr/strings.xml index ce36959bf..775d57145 100644 --- a/sdk/src/main/res/values-fr/strings.xml +++ b/sdk/src/main/res/values-fr/strings.xml @@ -90,4 +90,9 @@ Annuler le paiement Pas maintenant + + Annuler ? + Annuler + Pas maintenant + diff --git a/sdk/src/main/res/values-pl/strings.xml b/sdk/src/main/res/values-pl/strings.xml index 42c08035f..4db7d47a2 100644 --- a/sdk/src/main/res/values-pl/strings.xml +++ b/sdk/src/main/res/values-pl/strings.xml @@ -91,4 +91,9 @@ Anuluj płatność Nie teraz + + Anulować? + Anuluj + Nie teraz + diff --git a/sdk/src/main/res/values-pt/strings.xml b/sdk/src/main/res/values-pt/strings.xml index 2adabbfdd..3f133113f 100644 --- a/sdk/src/main/res/values-pt/strings.xml +++ b/sdk/src/main/res/values-pt/strings.xml @@ -90,4 +90,9 @@ Cancelar pagamento Agora não + + Cancelar? + Cancelar + Agora não + diff --git a/sdk/src/main/res/values/strings.xml b/sdk/src/main/res/values/strings.xml index 0656094f6..3c9b94560 100644 --- a/sdk/src/main/res/values/strings.xml +++ b/sdk/src/main/res/values/strings.xml @@ -89,4 +89,9 @@ Cancel payment Not now + + Cancel? + Cancel + Not now + diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POActionsContainer.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POActionsContainer.kt index 2cbe65cde..b0f0be12e 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POActionsContainer.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POActionsContainer.kt @@ -109,7 +109,8 @@ private fun Actions( modifier = modifier.fillMaxWidth(), style = if (primary) primaryActionStyle else secondaryActionStyle, enabled = enabled, - loading = loading + loading = loading, + iconResId = iconResId ) if (requestConfirmation) { confirmation?.run { diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt index eb0c1d8b5..8b5cd9776 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/component/POButton.kt @@ -1,6 +1,8 @@ package com.processout.sdk.ui.core.component +import androidx.annotation.DrawableRes import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.Image import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsPressedAsState import androidx.compose.foundation.layout.* @@ -10,8 +12,10 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp @@ -21,10 +25,12 @@ import com.processout.sdk.ui.core.component.POButton.border import com.processout.sdk.ui.core.component.POButton.colors import com.processout.sdk.ui.core.component.POButton.contentPadding import com.processout.sdk.ui.core.component.POButton.elevation +import com.processout.sdk.ui.core.extension.conditional import com.processout.sdk.ui.core.style.POButtonDefaults import com.processout.sdk.ui.core.style.POButtonStateStyle import com.processout.sdk.ui.core.style.POButtonStyle import com.processout.sdk.ui.core.theme.ProcessOutTheme +import com.processout.sdk.ui.core.theme.ProcessOutTheme.spacing /** @suppress */ @ProcessOutInternalApi @@ -37,9 +43,11 @@ fun POButton( enabled: Boolean = true, loading: Boolean = false, leadingContent: @Composable RowScope.() -> Unit = {}, + @DrawableRes iconResId: Int? = null, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) { val pressed by interactionSource.collectIsPressedAsState() + val colors = colors(style = style, enabled = enabled, loading = loading, pressed = pressed) CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides Dp.Unspecified) { Button( onClick = onClick, @@ -47,7 +55,7 @@ fun POButton( min = ProcessOutTheme.dimensions.interactiveComponentMinSize ), enabled = enabled && !loading, - colors = colors(style = style, enabled = enabled, loading = loading, pressed = pressed), + colors = colors, shape = if (enabled) style.normal.shape else style.disabled.shape, border = border(style = style, enabled = enabled, pressed = pressed), elevation = elevation(style = style, enabled = enabled, loading = loading), @@ -65,6 +73,19 @@ fun POButton( } } else { leadingContent() + iconResId?.let { + val iconColor = if (enabled) colors.contentColor else colors.disabledContentColor + Image( + painter = painterResource(it), + contentDescription = null, + modifier = Modifier + .conditional(text.isNotBlank()) { + padding(end = spacing.small) + } + .requiredSize(20.dp), + colorFilter = ColorFilter.tint(color = iconColor) + ) + } POText( text = text, style = if (enabled) style.normal.text.textStyle else style.disabled.text.textStyle, diff --git a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POActionState.kt b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POActionState.kt index 940a36256..cae5fa472 100644 --- a/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POActionState.kt +++ b/ui-core/src/main/kotlin/com/processout/sdk/ui/core/state/POActionState.kt @@ -1,5 +1,6 @@ package com.processout.sdk.ui.core.state +import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable import com.processout.sdk.ui.core.annotation.ProcessOutInternalApi @@ -12,6 +13,7 @@ data class POActionState( val primary: Boolean, val enabled: Boolean = true, val loading: Boolean = false, + @DrawableRes val iconResId: Int? = null, val confirmation: Confirmation? = null ) { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationBottomSheet.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationBottomSheet.kt index 360c3532f..0f54f1f2d 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationBottomSheet.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/tokenization/CardTokenizationBottomSheet.kt @@ -23,6 +23,7 @@ import com.processout.sdk.ui.card.tokenization.CardTokenizationActivityContract. import com.processout.sdk.ui.card.tokenization.CardTokenizationCompletion.Failure import com.processout.sdk.ui.card.tokenization.CardTokenizationCompletion.Success import com.processout.sdk.ui.card.tokenization.CardTokenizationEvent.Dismiss +import com.processout.sdk.ui.card.tokenization.POCardTokenizationConfiguration.SubmitButton import com.processout.sdk.ui.core.theme.ProcessOutTheme import com.processout.sdk.ui.shared.component.displayCutoutHeight import com.processout.sdk.ui.shared.component.screenModeAsState @@ -41,7 +42,7 @@ internal class CardTokenizationBottomSheet : BaseBottomSheetDialogFragment? = null, val style: Style? = null ) : Parcelable { + /** + * Defines card tokenization configuration. + * + * @param[title] Custom title. + * @param[cvcRequired] Specifies whether the card CVC should be collected. Default value is _true_. + * @param[isCardholderNameFieldVisible] Specifies whether the cardholder name field should be displayed. Default value is _true_. + * @param[billingAddress] Allows to customize the collection of billing address. + * @param[savingAllowed] Displays checkbox that allows to save the card details for future payments. + * @param[primaryActionText] Custom primary action text (e.g. "Submit"). + * @param[secondaryActionText] Custom secondary action text (e.g. "Cancel"). + * @param[cancellation] Specifies cancellation behaviour. + * @param[metadata] Metadata related to the card. + * @param[style] Allows to customize the look and feel. + */ + @Deprecated(message = "Use alternative constructor.") + constructor( + title: String? = null, + cvcRequired: Boolean = true, + isCardholderNameFieldVisible: Boolean = true, + billingAddress: BillingAddressConfiguration = BillingAddressConfiguration(), + savingAllowed: Boolean = false, + primaryActionText: String? = null, + secondaryActionText: String? = null, + cancellation: POCancellationConfiguration = POCancellationConfiguration(), + metadata: Map? = null, + style: Style? = null + ) : this( + title = title, + cvcRequired = cvcRequired, + isCardholderNameFieldVisible = isCardholderNameFieldVisible, + billingAddress = billingAddress, + savingAllowed = savingAllowed, + submitButton = SubmitButton(text = primaryActionText), + cancelButton = if (cancellation.secondaryAction) + CancelButton(text = secondaryActionText) else null, + cancellation = cancellation, + metadata = metadata, + style = style + ) + /** * Defines billing address configuration. * @@ -69,6 +111,35 @@ data class POCardTokenizationConfiguration( } } + /** + * Submit button configuration. + * + * @param[text] Button text. Pass _null_ to use default text. + * @param[iconResId] Button icon drawable resource ID. Pass _null_ to hide. + */ + @Parcelize + data class SubmitButton( + val text: String? = null, + @DrawableRes + val iconResId: Int? = null + ) : Parcelable + + /** + * Cancel button configuration. + * + * @param[text] Button text. Pass _null_ to use default text. + * @param[iconResId] Button icon drawable resource ID. Pass _null_ to hide. + * @param[confirmation] Specifies action confirmation configuration (e.g. dialog). + * Use _null_ to disable, this is a default behaviour. + */ + @Parcelize + data class CancelButton( + val text: String? = null, + @DrawableRes + val iconResId: Int? = null, + val confirmation: POActionConfirmationConfiguration? = null + ) : Parcelable + /** * Allows to customize the look and feel. * diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateBottomSheet.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateBottomSheet.kt index ee9848edd..3a156750e 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateBottomSheet.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateBottomSheet.kt @@ -21,6 +21,8 @@ import com.processout.sdk.ui.base.BaseBottomSheetDialogFragment import com.processout.sdk.ui.card.update.CardUpdateCompletion.Failure import com.processout.sdk.ui.card.update.CardUpdateCompletion.Success import com.processout.sdk.ui.card.update.CardUpdateEvent.Dismiss +import com.processout.sdk.ui.card.update.POCardUpdateConfiguration.Options +import com.processout.sdk.ui.card.update.POCardUpdateConfiguration.SubmitButton import com.processout.sdk.ui.core.theme.ProcessOutTheme import com.processout.sdk.ui.shared.component.screenModeAsState import com.processout.sdk.ui.shared.extension.dpToPx @@ -40,7 +42,7 @@ internal class CardUpdateBottomSheet : BaseBottomSheetDialogFragment() { CardUpdateViewModel.Factory( app = requireActivity().application, cardId = configuration?.cardId ?: String(), - options = configuration?.options ?: POCardUpdateConfiguration.Options() + options = configuration?.options ?: Options(submitButton = SubmitButton()) ) } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateViewModel.kt index 184fcc67a..57d3e7bf0 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/CardUpdateViewModel.kt @@ -28,6 +28,7 @@ import com.processout.sdk.core.onSuccess import com.processout.sdk.ui.card.update.CardUpdateCompletion.* import com.processout.sdk.ui.card.update.CardUpdateEvent.* import com.processout.sdk.ui.core.state.POActionState +import com.processout.sdk.ui.core.state.POActionState.Confirmation import com.processout.sdk.ui.core.state.POImmutableList import com.processout.sdk.ui.shared.extension.orElse import com.processout.sdk.ui.shared.filter.CardSecurityCodeInputFilter @@ -100,14 +101,28 @@ internal class CardUpdateViewModel private constructor( focusedFieldId = CardFieldId.CVC, primaryAction = POActionState( id = ActionId.SUBMIT, - text = primaryActionText ?: app.getString(R.string.po_card_update_button_submit), - primary = true + text = submitButton.text ?: app.getString(R.string.po_card_update_button_submit), + primary = true, + iconResId = submitButton.iconResId ), - secondaryAction = if (cancellation.secondaryAction) POActionState( - id = ActionId.CANCEL, - text = secondaryActionText ?: app.getString(R.string.po_card_update_button_cancel), - primary = false - ) else null, + secondaryAction = cancelButton?.let { + POActionState( + id = ActionId.CANCEL, + text = it.text ?: app.getString(R.string.po_card_update_button_cancel), + primary = false, + iconResId = it.iconResId, + confirmation = it.confirmation?.run { + Confirmation( + title = title ?: app.getString(R.string.po_cancel_confirmation_title), + message = message, + confirmActionText = confirmActionText + ?: app.getString(R.string.po_cancel_confirmation_confirm), + dismissActionText = dismissActionText + ?: app.getString(R.string.po_cancel_confirmation_dismiss) + ) + } + ) + }, draggable = cancellation.dragDown ) } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/POCardUpdateConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/POCardUpdateConfiguration.kt index 8b6fe9360..df74777c4 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/update/POCardUpdateConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/update/POCardUpdateConfiguration.kt @@ -2,9 +2,11 @@ package com.processout.sdk.ui.card.update import android.os.Parcelable import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes import com.processout.sdk.ui.core.style.POActionsContainerStyle import com.processout.sdk.ui.core.style.POFieldStyle import com.processout.sdk.ui.core.style.POTextStyle +import com.processout.sdk.ui.shared.configuration.POActionConfirmationConfiguration import com.processout.sdk.ui.shared.configuration.POCancellationConfiguration import kotlinx.parcelize.Parcelize @@ -18,7 +20,7 @@ import kotlinx.parcelize.Parcelize @Parcelize data class POCardUpdateConfiguration( val cardId: String, - val options: Options = Options(), + val options: Options = Options(submitButton = SubmitButton()), val style: Style? = null ) : Parcelable { @@ -27,17 +29,72 @@ data class POCardUpdateConfiguration( * * @param[title] Custom title. * @param[cardInformation] Allows to provide card information that will be visible in UI. - * @param[primaryActionText] Custom primary action text (e.g. "Submit"). - * @param[secondaryActionText] Custom secondary action text (e.g. "Cancel"). + * @param[submitButton] Submit button configuration. + * @param[cancelButton] Cancel button configuration. Use _null_ to hide. * @param[cancellation] Specifies cancellation behaviour. */ @Parcelize data class Options( val title: String? = null, val cardInformation: CardInformation? = null, - val primaryActionText: String? = null, - val secondaryActionText: String? = null, + val submitButton: SubmitButton = SubmitButton(), + val cancelButton: CancelButton? = CancelButton(), val cancellation: POCancellationConfiguration = POCancellationConfiguration() + ) : Parcelable { + + /** + * Allows to customize behaviour and pre-define the values. + * + * @param[title] Custom title. + * @param[cardInformation] Allows to provide card information that will be visible in UI. + * @param[primaryActionText] Custom primary action text (e.g. "Submit"). + * @param[secondaryActionText] Custom secondary action text (e.g. "Cancel"). + * @param[cancellation] Specifies cancellation behaviour. + */ + @Deprecated(message = "Use alternative constructor.") + constructor( + title: String? = null, + cardInformation: CardInformation? = null, + primaryActionText: String? = null, + secondaryActionText: String? = null, + cancellation: POCancellationConfiguration = POCancellationConfiguration() + ) : this( + title = title, + cardInformation = cardInformation, + submitButton = SubmitButton(text = primaryActionText), + cancelButton = if (cancellation.secondaryAction) + CancelButton(text = secondaryActionText) else null, + cancellation = cancellation + ) + } + + /** + * Submit button configuration. + * + * @param[text] Button text. Pass _null_ to use default text. + * @param[iconResId] Button icon drawable resource ID. Pass _null_ to hide. + */ + @Parcelize + data class SubmitButton( + val text: String? = null, + @DrawableRes + val iconResId: Int? = null + ) : Parcelable + + /** + * Cancel button configuration. + * + * @param[text] Button text. Pass _null_ to use default text. + * @param[iconResId] Button icon drawable resource ID. Pass _null_ to hide. + * @param[confirmation] Specifies action confirmation configuration (e.g. dialog). + * Use _null_ to disable, this is a default behaviour. + */ + @Parcelize + data class CancelButton( + val text: String? = null, + @DrawableRes + val iconResId: Int? = null, + val confirmation: POActionConfirmationConfiguration? = null ) : Parcelable /** diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutActivity.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutActivity.kt index 9f435299f..407af91f7 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutActivity.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutActivity.kt @@ -47,14 +47,16 @@ import com.processout.sdk.ui.checkout.DynamicCheckoutCompletion.Success import com.processout.sdk.ui.checkout.DynamicCheckoutEvent.* import com.processout.sdk.ui.checkout.DynamicCheckoutSideEffect.* import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.CancelButton +import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.SubmitButton import com.processout.sdk.ui.checkout.screen.DynamicCheckoutScreen import com.processout.sdk.ui.core.theme.ProcessOutTheme import com.processout.sdk.ui.googlepay.POGooglePayCardTokenizationLauncher import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModel -import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.* +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.Options +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.PaymentConfirmationConfiguration import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.PaymentConfirmationConfiguration.Companion.DEFAULT_TIMEOUT_SECONDS import com.processout.sdk.ui.shared.configuration.POBarcodeConfiguration -import com.processout.sdk.ui.shared.configuration.POCancellationConfiguration import com.processout.sdk.ui.shared.extension.collectImmediately import com.processout.sdk.ui.web.customtab.POCustomTabAuthorizationActivity import com.processout.sdk.ui.web.customtab.POCustomTabAuthorizationActivityContract @@ -103,11 +105,19 @@ internal class DynamicCheckoutActivity : BaseTransparentPortraitActivity() { defaultAddress = billingAddress?.defaultAddress, attachDefaultsToPaymentMethod = billingAddress?.attachDefaultsToPaymentMethod ?: false ), - primaryActionText = configuration?.submitButtonText, - secondaryActionText = configuration?.cancelButton?.text, - cancellation = POCancellationConfiguration( - secondaryAction = configuration?.cancelButton != null - ), + submitButton = configuration?.submitButton?.let { + POCardTokenizationConfiguration.SubmitButton( + text = it.text, + iconResId = it.iconResId + ) + } ?: POCardTokenizationConfiguration.SubmitButton(), + cancelButton = configuration?.cancelButton?.let { + POCardTokenizationConfiguration.CancelButton( + text = it.text, + iconResId = it.iconResId, + confirmation = it.confirmation + ) + }, metadata = configuration?.card?.metadata ) } @@ -115,24 +125,31 @@ internal class DynamicCheckoutActivity : BaseTransparentPortraitActivity() { private fun nativeAlternativePaymentConfiguration(): Options { val paymentConfirmation = configuration?.alternativePayment?.paymentConfirmation return Options( - primaryActionText = configuration?.submitButtonText, - secondaryAction = configuration?.cancelButton?.toSecondaryAction(), + submitButton = configuration?.submitButton?.map() ?: PONativeAlternativePaymentConfiguration.SubmitButton(), + cancelButton = configuration?.cancelButton?.map(), paymentConfirmation = PaymentConfirmationConfiguration( waitsConfirmation = true, timeoutSeconds = paymentConfirmation?.timeoutSeconds ?: DEFAULT_TIMEOUT_SECONDS, showProgressIndicatorAfterSeconds = paymentConfirmation?.showProgressIndicatorAfterSeconds, hideGatewayDetails = true, - primaryAction = paymentConfirmation?.confirmButton?.let { ConfirmAction(text = it.text) }, - secondaryAction = paymentConfirmation?.cancelButton?.toSecondaryAction() + confirmButton = paymentConfirmation?.confirmButton?.map(), + cancelButton = paymentConfirmation?.cancelButton?.map() ), - barcode = configuration?.alternativePayment?.barcode ?: POBarcodeConfiguration(), + barcode = configuration?.alternativePayment?.barcode + ?: POBarcodeConfiguration(saveButton = POBarcodeConfiguration.Button()), inlineSingleSelectValuesLimit = configuration?.alternativePayment?.inlineSingleSelectValuesLimit ?: 5, skipSuccessScreen = true ) } - private fun CancelButton.toSecondaryAction() = SecondaryAction.Cancel( + private fun SubmitButton.map() = PONativeAlternativePaymentConfiguration.SubmitButton( text = text, + iconResId = iconResId + ) + + private fun CancelButton.map() = PONativeAlternativePaymentConfiguration.CancelButton( + text = text, + iconResId = iconResId, disabledForSeconds = disabledForSeconds, confirmation = confirmation ) @@ -208,14 +225,16 @@ internal class DynamicCheckoutActivity : BaseTransparentPortraitActivity() { private fun dispatchBackPressed() { onBackPressedDispatcher.addCallback(this) { - viewModel.onEvent( - Dismiss( - ProcessOutResult.Failure( - code = Cancelled, - message = "Cancelled by the user with back press or gesture." + if (configuration?.cancelOnBackPressed == true) { + viewModel.onEvent( + Dismiss( + ProcessOutResult.Failure( + code = Cancelled, + message = "Cancelled by the user with back press or gesture." + ) ) ) - ) + } } } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutViewModel.kt index 101963354..b002cbd61 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/DynamicCheckoutViewModel.kt @@ -146,7 +146,10 @@ internal class DynamicCheckoutViewModel private constructor( } } else -> defaultCancelAction - }?.copy(id = interactorState.cancelActionId) + }?.copy( + id = interactorState.cancelActionId, + iconResId = configuration.cancelButton?.iconResId + ) } private fun CancelButton.toActionState( @@ -233,7 +236,7 @@ internal class DynamicCheckoutViewModel private constructor( interactorState.paymentMethods.mapNotNull { paymentMethod -> val id = paymentMethod.id val selected = id == interactorState.selectedPaymentMethod?.id - val submitButtonText = configuration.submitButtonText ?: app.getString(R.string.po_dynamic_checkout_button_pay) + val submitButtonText = configuration.submitButton.text ?: app.getString(R.string.po_dynamic_checkout_button_pay) when (paymentMethod) { is Card -> RegularPayment( id = id, @@ -243,7 +246,11 @@ internal class DynamicCheckoutViewModel private constructor( selected = selected ), content = if (selected) Content.Card(cardTokenizationState) else null, - submitAction = if (selected) cardTokenizationState.primaryAction.copy(text = submitButtonText) else null + submitAction = if (selected) + cardTokenizationState.primaryAction.copy( + text = submitButtonText, + iconResId = configuration.submitButton.iconResId + ) else null ) is AlternativePayment -> if (!paymentMethod.isExpress) RegularPayment( @@ -260,7 +267,8 @@ internal class DynamicCheckoutViewModel private constructor( text = submitButtonText, primary = true, loading = id == interactorState.processingPaymentMethod?.id || - interactorState.invoice == null + interactorState.invoice == null, + iconResId = configuration.submitButton.iconResId ) ) else null is NativeAlternativePayment -> RegularPayment( @@ -273,7 +281,10 @@ internal class DynamicCheckoutViewModel private constructor( ), content = if (selected) Content.NativeAlternativePayment(nativeAlternativePaymentState) else null, submitAction = if (selected && nativeAlternativePaymentState is UserInput) - nativeAlternativePaymentState.primaryAction.copy(text = submitButtonText) else null + nativeAlternativePaymentState.primaryAction.copy( + text = submitButtonText, + iconResId = configuration.submitButton.iconResId + ) else null ) else -> null } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutConfiguration.kt index 135601857..371fdbc80 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/PODynamicCheckoutConfiguration.kt @@ -20,9 +20,10 @@ data class PODynamicCheckoutConfiguration( val card: CardConfiguration = CardConfiguration(), val googlePay: GooglePayConfiguration = GooglePayConfiguration(), val alternativePayment: AlternativePaymentConfiguration = AlternativePaymentConfiguration(), - val preselectSinglePaymentMethod: Boolean = true, - val submitButtonText: String? = null, + val submitButton: SubmitButton = SubmitButton(), val cancelButton: CancelButton? = CancelButton(), + val cancelOnBackPressed: Boolean = true, + val preselectSinglePaymentMethod: Boolean = true, val paymentSuccess: PaymentSuccess? = PaymentSuccess(), val style: Style? = null ) : Parcelable { @@ -91,7 +92,7 @@ data class PODynamicCheckoutConfiguration( data class AlternativePaymentConfiguration( val returnUrl: String? = null, val inlineSingleSelectValuesLimit: Int = 5, - val barcode: POBarcodeConfiguration = POBarcodeConfiguration(), + val barcode: POBarcodeConfiguration = POBarcodeConfiguration(saveButton = POBarcodeConfiguration.Button()), val paymentConfirmation: PaymentConfirmationConfiguration = PaymentConfirmationConfiguration() ) : Parcelable { @@ -99,20 +100,23 @@ data class PODynamicCheckoutConfiguration( data class PaymentConfirmationConfiguration( val timeoutSeconds: Int = 3 * 60, val showProgressIndicatorAfterSeconds: Int? = null, - val confirmButton: ConfirmButton? = null, + val confirmButton: SubmitButton? = null, val cancelButton: CancelButton? = CancelButton() - ) : Parcelable { - - @Parcelize - data class ConfirmButton( - val text: String? = null - ) : Parcelable - } + ) : Parcelable } + @Parcelize + data class SubmitButton( + val text: String? = null, + @DrawableRes + val iconResId: Int? = null + ) : Parcelable + @Parcelize data class CancelButton( val text: String? = null, + @DrawableRes + val iconResId: Int? = null, val disabledForSeconds: Int = 0, val confirmation: POActionConfirmationConfiguration? = null ) : Parcelable diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt index eda31def8..413a963a9 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/DynamicCheckoutScreen.kt @@ -443,7 +443,8 @@ private fun RegularPaymentContent( .padding(top = spacing.extraLarge), style = style.actionsContainer.primary, enabled = enabled, - loading = loading + loading = loading, + iconResId = iconResId ) } } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt index 4cfc5437b..c03d0ad46 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/checkout/screen/NativeAlternativePayment.kt @@ -403,7 +403,8 @@ private fun Capture( ) }, modifier = Modifier.fillMaxWidth(), - style = style.actionsContainer.primary + style = style.actionsContainer.primary, + iconResId = action.iconResId ) } state.saveBarcodeAction?.let { action -> @@ -418,7 +419,8 @@ private fun Capture( ) }, modifier = Modifier.fillMaxWidth(), - style = style.actionsContainer.secondary + style = style.actionsContainer.secondary, + iconResId = action.iconResId ) } } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentBottomSheet.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentBottomSheet.kt index 937d41df1..a8dd3664b 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentBottomSheet.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentBottomSheet.kt @@ -29,9 +29,9 @@ import com.processout.sdk.ui.napm.NativeAlternativePaymentScreen.AnimationDurati import com.processout.sdk.ui.napm.NativeAlternativePaymentSideEffect.PermissionRequest import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModelState.Capture import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.Options +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.SubmitButton import com.processout.sdk.ui.shared.component.isImeVisibleAsState import com.processout.sdk.ui.shared.component.screenModeAsState -import com.processout.sdk.ui.shared.configuration.POCancellationConfiguration import com.processout.sdk.ui.shared.extension.collectImmediately import com.processout.sdk.ui.shared.extension.dpToPx import kotlinx.coroutines.delay @@ -54,7 +54,7 @@ internal class NativeAlternativePaymentBottomSheet : BaseBottomSheetDialogFragme app = requireActivity().application, invoiceId = configuration?.invoiceId ?: String(), gatewayConfigurationId = configuration?.gatewayConfigurationId ?: String(), - options = configuration?.options ?: Options(), + options = configuration?.options ?: Options(submitButton = SubmitButton()), eventDispatcher = PODefaultEventDispatchers.defaultNativeAlternativePaymentMethod ) } @@ -132,15 +132,7 @@ internal class NativeAlternativePaymentBottomSheet : BaseBottomSheetDialogFragme override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - configuration?.options?.cancellation?.let { - apply( - POCancellationConfiguration( - backPressed = it.backPressed, - dragDown = it.dragDown, - touchOutside = it.touchOutside - ) - ) - } + configuration?.let { apply(it.options.cancellation) } } private fun handle(sideEffect: NativeAlternativePaymentSideEffect) { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt index 92d5e5692..fa35d033b 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentInteractor.kt @@ -45,9 +45,8 @@ import com.processout.sdk.ui.napm.NativeAlternativePaymentEvent.* import com.processout.sdk.ui.napm.NativeAlternativePaymentEvent.Action import com.processout.sdk.ui.napm.NativeAlternativePaymentInteractorState.* import com.processout.sdk.ui.napm.NativeAlternativePaymentSideEffect.PermissionRequest +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.CancelButton import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.Options -import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.SecondaryAction -import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.SecondaryAction.Cancel import com.processout.sdk.ui.shared.extension.dpToPx import com.processout.sdk.ui.shared.provider.BarcodeBitmapProvider import com.processout.sdk.ui.shared.provider.MediaStorageProvider @@ -714,7 +713,7 @@ internal class NativeAlternativePaymentInteractor( dispatch(WillWaitForCaptureConfirmation(additionalActionExpected = additionalActionExpected)) _state.update { Capturing(captureStateValue) } enableCapturingSecondaryAction() - if (!additionalActionExpected || options.paymentConfirmation.primaryAction == null) { + if (!additionalActionExpected || options.paymentConfirmation.confirmButton == null) { capture() } } @@ -829,14 +828,11 @@ internal class NativeAlternativePaymentInteractor( //region Features - private val SecondaryAction?.disabledForMillis: Long - get() = when (this) { - is Cancel -> disabledForSeconds * 1000L - null -> 0 - } + private val CancelButton?.disabledForMillis: Long + get() = this?.disabledForSeconds?.let { it * 1000L } ?: 0 private fun enableUserInputSecondaryAction() { - handler.postDelayed(delayInMillis = options.secondaryAction.disabledForMillis) { + handler.postDelayed(delayInMillis = options.cancelButton.disabledForMillis) { _state.whenUserInput { stateValue -> _state.update { with(stateValue) { @@ -848,7 +844,7 @@ internal class NativeAlternativePaymentInteractor( } private fun enableCapturingSecondaryAction() { - handler.postDelayed(delayInMillis = options.paymentConfirmation.secondaryAction.disabledForMillis) { + handler.postDelayed(delayInMillis = options.paymentConfirmation.cancelButton.disabledForMillis) { _state.whenCapturing { stateValue -> _state.update { with(stateValue) { diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt index 465b10045..baba4382c 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/NativeAlternativePaymentViewModel.kt @@ -22,11 +22,10 @@ import com.processout.sdk.ui.core.state.POImmutableList import com.processout.sdk.ui.napm.NativeAlternativePaymentInteractorState.* import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModelState.Field.* import com.processout.sdk.ui.napm.NativeAlternativePaymentViewModelState.Image +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.CancelButton import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.Options import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.PaymentConfirmationConfiguration.Companion.DEFAULT_TIMEOUT_SECONDS import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.PaymentConfirmationConfiguration.Companion.MAX_TIMEOUT_SECONDS -import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.SecondaryAction -import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.SecondaryAction.Cancel import com.processout.sdk.ui.shared.extension.map import com.processout.sdk.ui.shared.filter.PhoneNumberInputFilter import com.processout.sdk.ui.shared.provider.BarcodeBitmapProvider @@ -139,12 +138,13 @@ internal class NativeAlternativePaymentViewModel private constructor( focusedFieldId = focusedFieldId, primaryAction = POActionState( id = primaryActionId, - text = options.primaryActionText ?: invoice.formatPrimaryActionText(), + text = options.submitButton.text ?: invoice.formatPrimaryActionText(), primary = true, enabled = submitAllowed, - loading = submitting + loading = submitting, + iconResId = options.submitButton.iconResId ), - secondaryAction = options.secondaryAction?.toActionState( + secondaryAction = options.cancelButton?.toActionState( id = secondaryAction.id, enabled = secondaryAction.enabled && !submitting ) @@ -152,7 +152,7 @@ internal class NativeAlternativePaymentViewModel private constructor( } private fun Capturing.map() = with(value) { - val secondaryAction = options.paymentConfirmation.secondaryAction?.toActionState( + val secondaryAction = options.paymentConfirmation.cancelButton?.toActionState( id = secondaryAction.id, enabled = secondaryAction.enabled ) @@ -162,12 +162,13 @@ internal class NativeAlternativePaymentViewModel private constructor( secondaryAction = secondaryAction ) } else { - val primaryAction = options.paymentConfirmation.primaryAction?.let { + val primaryAction = options.paymentConfirmation.confirmButton?.let { primaryActionId?.let { id -> POActionState( id = id, text = it.text ?: app.getString(R.string.po_native_apm_confirm_payment_button_text), - primary = true + primary = true, + iconResId = it.iconResId ) } } @@ -182,12 +183,13 @@ internal class NativeAlternativePaymentViewModel private constructor( saveBarcodeAction = customerAction?.barcode?.let { POActionState( id = it.actionId, - text = options.barcode.saveActionText + text = options.barcode.saveButton.text ?: app.getString( R.string.po_native_apm_save_barcode_button_text_format, it.type.rawType.uppercase() ), - primary = false + primary = false, + iconResId = options.barcode.saveButton.iconResId ) }, confirmationDialog = confirmationDialog(), @@ -359,27 +361,26 @@ internal class NativeAlternativePaymentViewModel private constructor( app.getString(R.string.po_native_apm_submit_button_text) } - private fun SecondaryAction.toActionState( + private fun CancelButton.toActionState( id: String, enabled: Boolean - ): POActionState = when (this) { - is Cancel -> POActionState( - id = id, - text = text ?: app.getString(R.string.po_native_apm_cancel_button_text), - primary = false, - enabled = enabled, - confirmation = confirmation?.run { - Confirmation( - title = title ?: app.getString(R.string.po_cancel_payment_confirmation_title), - message = message, - confirmActionText = confirmActionText - ?: app.getString(R.string.po_cancel_payment_confirmation_confirm), - dismissActionText = dismissActionText - ?: app.getString(R.string.po_cancel_payment_confirmation_dismiss) - ) - } - ) - } + ) = POActionState( + id = id, + text = text ?: app.getString(R.string.po_native_apm_cancel_button_text), + primary = false, + enabled = enabled, + iconResId = iconResId, + confirmation = confirmation?.run { + Confirmation( + title = title ?: app.getString(R.string.po_cancel_payment_confirmation_title), + message = message, + confirmActionText = confirmActionText + ?: app.getString(R.string.po_cancel_payment_confirmation_confirm), + dismissActionText = dismissActionText + ?: app.getString(R.string.po_cancel_payment_confirmation_dismiss) + ) + } + ) private fun CaptureStateValue.confirmationDialog(): ConfirmationDialogState? = customerAction?.barcode?.let { barcode -> diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt index a76e65e1b..8191bfc4e 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt @@ -4,8 +4,11 @@ import android.os.Parcelable import androidx.annotation.ColorRes import androidx.annotation.DrawableRes import com.processout.sdk.ui.core.style.* +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.CancelButton +import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.SecondaryAction import com.processout.sdk.ui.shared.configuration.POActionConfirmationConfiguration import com.processout.sdk.ui.shared.configuration.POBarcodeConfiguration +import com.processout.sdk.ui.shared.configuration.POCancellationConfiguration import kotlinx.parcelize.Parcelize /** @@ -20,7 +23,7 @@ import kotlinx.parcelize.Parcelize data class PONativeAlternativePaymentConfiguration( val invoiceId: String, val gatewayConfigurationId: String, - val options: Options = Options(), + val options: Options = Options(submitButton = SubmitButton()), val style: Style? = null ) : Parcelable { @@ -28,8 +31,8 @@ data class PONativeAlternativePaymentConfiguration( * Allows to customize behaviour and pre-define the values. * * @param[title] Custom title. - * @param[primaryActionText] Custom primary action text (e.g. "Pay"). - * @param[secondaryAction] Secondary action (e.g. "Cancel"). Use _null_ to hide, this is a default behaviour. + * @param[submitButton] Submit button configuration. + * @param[cancelButton] Cancel button configuration. Use _null_ to hide, this is a default behaviour. * @param[cancellation] Specifies cancellation behaviour. * @param[paymentConfirmation] Specifies payment confirmation behaviour. * @param[barcode] Specifies barcode configuration. @@ -42,22 +45,100 @@ data class PONativeAlternativePaymentConfiguration( @Parcelize data class Options( val title: String? = null, - val primaryActionText: String? = null, - val secondaryAction: SecondaryAction? = null, - val cancellation: CancellationConfiguration = CancellationConfiguration(), - val paymentConfirmation: PaymentConfirmationConfiguration = PaymentConfirmationConfiguration(), - val barcode: POBarcodeConfiguration = POBarcodeConfiguration(), + val submitButton: SubmitButton = SubmitButton(), + val cancelButton: CancelButton? = null, + val cancellation: POCancellationConfiguration = POCancellationConfiguration(), + val paymentConfirmation: PaymentConfirmationConfiguration = PaymentConfirmationConfiguration(confirmButton = null), + val barcode: POBarcodeConfiguration = POBarcodeConfiguration(saveButton = POBarcodeConfiguration.Button()), val inlineSingleSelectValuesLimit: Int = 5, val skipSuccessScreen: Boolean = false, val successMessage: String? = null + ) : Parcelable { + + /** + * Allows to customize behaviour and pre-define the values. + * + * @param[title] Custom title. + * @param[primaryActionText] Custom primary action text (e.g. "Pay"). + * @param[secondaryAction] Secondary action (e.g. "Cancel"). Use _null_ to hide, this is a default behaviour. + * @param[cancellation] Specifies cancellation behaviour. + * @param[paymentConfirmation] Specifies payment confirmation behaviour. + * @param[barcode] Specifies barcode configuration. + * @param[inlineSingleSelectValuesLimit] Defines maximum number of options that will be + * displayed inline for parameters where user should select single option (e.g. radio buttons). + * Default value is _5_. + * @param[skipSuccessScreen] Only applies when [PaymentConfirmationConfiguration.waitsConfirmation] is _true_. + * @param[successMessage] Custom success message when payment is completed. + */ + @Deprecated(message = "Use alternative constructor.") + constructor( + title: String? = null, + primaryActionText: String? = null, + secondaryAction: SecondaryAction? = null, + cancellation: CancellationConfiguration = CancellationConfiguration(), + paymentConfirmation: PaymentConfirmationConfiguration = PaymentConfirmationConfiguration(confirmButton = null), + barcode: POBarcodeConfiguration = POBarcodeConfiguration(saveButton = POBarcodeConfiguration.Button()), + inlineSingleSelectValuesLimit: Int = 5, + skipSuccessScreen: Boolean = false, + successMessage: String? = null + ) : this( + title = title, + submitButton = SubmitButton(text = primaryActionText), + cancelButton = secondaryAction?.toCancelButton(), + cancellation = with(cancellation) { + POCancellationConfiguration( + backPressed = backPressed, + dragDown = dragDown, + touchOutside = touchOutside + ) + }, + paymentConfirmation = paymentConfirmation, + barcode = barcode, + inlineSingleSelectValuesLimit = inlineSingleSelectValuesLimit, + skipSuccessScreen = skipSuccessScreen, + successMessage = successMessage + ) + } + + /** + * Submit button configuration. + * + * @param[text] Button text. Pass _null_ to use default text. + * @param[iconResId] Button icon drawable resource ID. Pass _null_ to hide. + */ + @Parcelize + data class SubmitButton( + val text: String? = null, + @DrawableRes + val iconResId: Int? = null ) : Parcelable /** - * Action for confirmation. + * Cancel button configuration. + * + * @param[text] Button text. Pass _null_ to use default text. + * @param[iconResId] Button icon drawable resource ID. Pass _null_ to hide. + * @param[disabledForSeconds] Initially disables the button for the given amount of time in seconds. + * By default user can interact with the button immediately when it's visible. + * @param[confirmation] Specifies action confirmation configuration (e.g. dialog). + * Use _null_ to disable, this is a default behaviour. + */ + @Parcelize + data class CancelButton( + val text: String? = null, + @DrawableRes + val iconResId: Int? = null, + val disabledForSeconds: Int = 0, + val confirmation: POActionConfirmationConfiguration? = null + ) : Parcelable + + /** + * Confirmation action. * * @param[text] Action text. Pass _null_ to use default text. */ @Parcelize + @Deprecated(message = "Use 'SubmitButton' instead.") data class ConfirmAction( val text: String? = null ) : Parcelable @@ -65,17 +146,19 @@ data class PONativeAlternativePaymentConfiguration( /** * Supported secondary actions. */ + @Deprecated(message = "Use 'CancelButton' instead.") sealed class SecondaryAction : Parcelable { /** * Action for cancellation. * * @param[text] Action text. Pass _null_ to use default text. - * @param[disabledForSeconds] Initially disables action for the given amount of time in seconds. - * By default user can interact with action immediately when it's visible. + * @param[disabledForSeconds] Initially disables the action for the given amount of time in seconds. + * By default user can interact with the action immediately when it's visible. * @param[confirmation] Specifies action confirmation configuration (e.g. dialog). * Use _null_ to disable, this is a default behaviour. */ @Parcelize + @Deprecated(message = "Use 'CancelButton' instead.") data class Cancel( val text: String? = null, val disabledForSeconds: Int = 0, @@ -91,6 +174,7 @@ data class PONativeAlternativePaymentConfiguration( * @param[touchOutside] Cancel on touch of the outside dimmed area of the bottom sheet. Default value is _true_. */ @Parcelize + @Deprecated(message = "Use 'POCancellationConfiguration' instead.") data class CancellationConfiguration( val backPressed: Boolean = true, val dragDown: Boolean = true, @@ -108,10 +192,8 @@ data class PONativeAlternativePaymentConfiguration( * Use _null_ to hide, this is a default behaviour. * @param[hideGatewayDetails] Specifies whether gateway information (such as name/logo) should be hidden during payment confirmation * even when specific payment provider details are not available. Default value is _false_. - * @param[primaryAction] Optional primary action for payment confirmation. - * To hide action use _null_, this is a default behaviour. - * @param[secondaryAction] Secondary action (e.g. "Cancel") that could be optionally presented to user during payment confirmation stage. - * Use _null_ to hide, this is a default behaviour. + * @param[confirmButton] Confirm button configuration. + * @param[cancelButton] Cancel button configuration. */ @Parcelize data class PaymentConfirmationConfiguration( @@ -119,13 +201,47 @@ data class PONativeAlternativePaymentConfiguration( val timeoutSeconds: Int = DEFAULT_TIMEOUT_SECONDS, val showProgressIndicatorAfterSeconds: Int? = null, val hideGatewayDetails: Boolean = false, - val primaryAction: ConfirmAction? = null, - val secondaryAction: SecondaryAction? = null + val confirmButton: SubmitButton? = null, + val cancelButton: CancelButton? = null ) : Parcelable { + companion object { const val MAX_TIMEOUT_SECONDS = 15 * 60 const val DEFAULT_TIMEOUT_SECONDS = 3 * 60 } + + /** + * Specifies payment confirmation behaviour. + * + * @param[waitsConfirmation] Specifies whether flow should wait for payment confirmation from PSP + * or will complete right after all user input is submitted. Default value is _true_. + * @param[timeoutSeconds] Amount of time (in seconds) to wait for final payment confirmation. + * Default value is 3 minutes, while maximum value is 15 minutes. + * @param[showProgressIndicatorAfterSeconds] Show progress indicator during payment confirmation after provided delay (in seconds). + * Use _null_ to hide, this is a default behaviour. + * @param[hideGatewayDetails] Specifies whether gateway information (such as name/logo) should be hidden during payment confirmation + * even when specific payment provider details are not available. Default value is _false_. + * @param[primaryAction] Optional primary action for payment confirmation. + * To hide action use _null_, this is a default behaviour. + * @param[secondaryAction] Secondary action (e.g. "Cancel") that could be optionally presented to user during payment confirmation stage. + * Use _null_ to hide, this is a default behaviour. + */ + @Deprecated(message = "Use alternative constructor.") + constructor( + waitsConfirmation: Boolean = true, + timeoutSeconds: Int = DEFAULT_TIMEOUT_SECONDS, + showProgressIndicatorAfterSeconds: Int? = null, + hideGatewayDetails: Boolean = false, + primaryAction: ConfirmAction? = null, + secondaryAction: SecondaryAction? = null + ) : this( + waitsConfirmation = waitsConfirmation, + timeoutSeconds = timeoutSeconds, + showProgressIndicatorAfterSeconds = showProgressIndicatorAfterSeconds, + hideGatewayDetails = hideGatewayDetails, + confirmButton = primaryAction?.let { SubmitButton(text = it.text) }, + cancelButton = secondaryAction?.toCancelButton() + ) } /** @@ -175,3 +291,12 @@ data class PONativeAlternativePaymentConfiguration( val dragHandleColorResId: Int? = null ) : Parcelable } + +private fun SecondaryAction.toCancelButton(): CancelButton = + when (this) { + is SecondaryAction.Cancel -> CancelButton( + text = text, + disabledForSeconds = disabledForSeconds, + confirmation = confirmation + ) + } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/shared/configuration/POBarcodeConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/shared/configuration/POBarcodeConfiguration.kt index 0d2da6911..277936248 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/shared/configuration/POBarcodeConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/shared/configuration/POBarcodeConfiguration.kt @@ -1,16 +1,46 @@ package com.processout.sdk.ui.shared.configuration import android.os.Parcelable +import androidx.annotation.DrawableRes import kotlinx.parcelize.Parcelize /** * Specifies barcode configuration. * - * @param[saveActionText] Text on the button that saves barcode. - * @param[saveErrorConfirmation] Requests user confirmation (e.g. dialog) when saving barcode has failed. Use _null_ to disable. + * @param[saveButton] Save button configuration. + * @param[saveErrorConfirmation] Requests user confirmation (e.g. dialog) when barcode saving has failed. Use _null_ to disable. */ @Parcelize data class POBarcodeConfiguration( - val saveActionText: String? = null, + val saveButton: Button = Button(), val saveErrorConfirmation: POActionConfirmationConfiguration? = POActionConfirmationConfiguration() -) : Parcelable +) : Parcelable { + + /** + * Specifies barcode configuration. + * + * @param[saveActionText] Save button text. + * @param[saveErrorConfirmation] Requests user confirmation (e.g. dialog) when barcode saving has failed. Use _null_ to disable. + */ + @Deprecated(message = "Use alternative constructor.") + constructor( + saveActionText: String? = null, + saveErrorConfirmation: POActionConfirmationConfiguration? = POActionConfirmationConfiguration() + ) : this( + saveButton = Button(text = saveActionText), + saveErrorConfirmation = saveErrorConfirmation + ) + + /** + * Button configuration. + * + * @param[text] Button text. Pass _null_ to use default text. + * @param[iconResId] Button icon drawable resource ID. Pass _null_ to hide. + */ + @Parcelize + data class Button( + val text: String? = null, + @DrawableRes + val iconResId: Int? = null + ) : Parcelable +} diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/shared/configuration/POCancellationConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/shared/configuration/POCancellationConfiguration.kt index 29f24f7cb..dbdd4f569 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/shared/configuration/POCancellationConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/shared/configuration/POCancellationConfiguration.kt @@ -12,9 +12,29 @@ import kotlinx.parcelize.Parcelize * @param[touchOutside] Cancel on touch of the outside dimmed area of the bottom sheet. Default value is _true_. */ @Parcelize -data class POCancellationConfiguration( +data class POCancellationConfiguration @Deprecated(message = "Use alternative constructor.") constructor( + @Deprecated(message = "Use configuration of specific button.") val secondaryAction: Boolean = true, val backPressed: Boolean = true, val dragDown: Boolean = true, val touchOutside: Boolean = true -) : Parcelable +) : Parcelable { + + /** + * Specifies cancellation behaviour. + * + * @param[backPressed] Cancel on back button press or back gesture. Default value is _true_. + * @param[dragDown] Cancel when bottom sheet is dragged down out of the screen. Default value is _true_. + * @param[touchOutside] Cancel on touch of the outside dimmed area of the bottom sheet. Default value is _true_. + */ + constructor( + backPressed: Boolean = true, + dragDown: Boolean = true, + touchOutside: Boolean = true + ) : this( + secondaryAction = false, + backPressed = backPressed, + dragDown = dragDown, + touchOutside = touchOutside + ) +}