From 4bc80e72f5c74017214b2ccc517e06c9d25830a6 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 2 May 2024 16:01:35 +0200 Subject: [PATCH 01/33] Create UPIAppsAdapter which supports a list of upi apps COAND-866 --- ui-core/src/main/res/values/styles.xml | 8 ++ .../upi/internal/ui/model/UPICollectItem.kt | 56 ++++++++++ .../upi/internal/ui/view/UPIAppsAdapter.kt | 100 ++++++++++++++++++ .../ui/view/UPICollectGenericAppViewHolder.kt | 39 +++++++ .../ui/view/UPICollectItemViewHolder.kt | 20 ++++ .../view/UPICollectManualAddressViewHolder.kt | 85 +++++++++++++++ .../ui/view/UPICollectPaymentAppViewHolder.kt | 55 ++++++++++ .../main/res/drawable/ic_upi_generic_app.xml | 14 +++ .../main/res/drawable/ic_upi_manual_input.xml | 33 ++++++ upi/src/main/res/layout/upi_app.xml | 55 ++++++++++ upi/src/main/res/layout/upi_app_generic.xml | 55 ++++++++++ .../res/layout/upi_app_manual_address.xml | 86 +++++++++++++++ .../main/res/template/values/strings.xml.tt | 4 + upi/src/main/res/values/strings.xml | 6 +- upi/src/main/res/values/styles.xml | 51 +++++++++ 15 files changed, 666 insertions(+), 1 deletion(-) create mode 100644 upi/src/main/java/com/adyen/checkout/upi/internal/ui/model/UPICollectItem.kt create mode 100644 upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIAppsAdapter.kt create mode 100644 upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPICollectGenericAppViewHolder.kt create mode 100644 upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPICollectItemViewHolder.kt create mode 100644 upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPICollectManualAddressViewHolder.kt create mode 100644 upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPICollectPaymentAppViewHolder.kt create mode 100644 upi/src/main/res/drawable/ic_upi_generic_app.xml create mode 100644 upi/src/main/res/drawable/ic_upi_manual_input.xml create mode 100644 upi/src/main/res/layout/upi_app.xml create mode 100644 upi/src/main/res/layout/upi_app_generic.xml create mode 100644 upi/src/main/res/layout/upi_app_manual_address.xml diff --git a/ui-core/src/main/res/values/styles.xml b/ui-core/src/main/res/values/styles.xml index f1afd8eb11..d38d79cf3e 100644 --- a/ui-core/src/main/res/values/styles.xml +++ b/ui-core/src/main/res/values/styles.xml @@ -150,6 +150,14 @@ ?attr/colorPrimary + + + + + + + + + + + + + + + + + + + + + + + + + From 7da9ee62014eb9438f0a9b2101115a4f18a17252 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 2 May 2024 16:08:43 +0200 Subject: [PATCH 02/33] Add new Collect option in UPIView COAND-866 --- .../checkout/upi/internal/ui/view/UPIView.kt | 52 +++++++++++++------ upi/src/main/res/layout/upi_view.xml | 22 ++++++-- upi/src/main/res/values/styles.xml | 5 ++ 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIView.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIView.kt index 76a817c4ca..0bcf55780d 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIView.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIView.kt @@ -59,8 +59,7 @@ internal class UPIView @JvmOverloads constructor( this.localizedContext = localizedContext initLocalizedStrings(localizedContext) - - initPicker(delegate) + initModeToggle() initVpaInput(delegate, localizedContext) } @@ -69,6 +68,10 @@ internal class UPIView @JvmOverloads constructor( R.style.AdyenCheckout_UPI_ModeSelectionTextView, localizedContext, ) + binding.buttonCollect.setLocalizedTextFromStyle( + R.style.AdyenCheckout_UPI_CollectButton, + localizedContext, + ) binding.buttonVpa.setLocalizedTextFromStyle( R.style.AdyenCheckout_UPI_VPAButton, localizedContext, @@ -87,21 +90,32 @@ internal class UPIView @JvmOverloads constructor( ) } - private fun initPicker(delegate: UPIDelegate) { - binding.toggleButtonChoice.check(R.id.button_vpa) + private fun initModeToggle() { binding.toggleButtonChoice.addOnButtonCheckedListener { _, checkedId, isChecked -> - when (checkedId) { - R.id.button_vpa -> { - binding.textInputLayoutVpa.isVisible = isChecked - binding.textViewQrCodeDescription.isVisible = !isChecked - binding.editTextVpa.isFocusableInTouchMode = isChecked - binding.editTextVpa.isFocusable = isChecked - if (isChecked) { - binding.editTextVpa.requestFocus() - binding.editTextVpa.showKeyboard() - delegate.updateInputData { mode = UPIMode.VPA } - } - } + when(checkedId) { + R.id.button_collect -> updateUpiCollectViews(isChecked) + R.id.button_vpa -> updateUpiVpaViews(isChecked) + R.id.button_qrCode -> updateUpiQrCodeViews(isChecked) + } + } + } + + private fun updateUpiCollectViews(isChecked: Boolean) { + binding.recyclerViewUpiCollect.isVisible = isChecked + if (isChecked) { + binding.editTextVpa.clearFocus() + hideKeyboard() + } + } + private fun updateUpiVpaViews(isChecked: Boolean) { + binding.textInputLayoutVpa.isVisible = isChecked + binding.editTextVpa.isFocusableInTouchMode = isChecked + binding.editTextVpa.isFocusable = isChecked + if (isChecked) { + binding.editTextVpa.requestFocus() + binding.editTextVpa.showKeyboard() + } + } R.id.button_qrCode -> { binding.textInputLayoutVpa.isVisible = !isChecked @@ -115,6 +129,12 @@ internal class UPIView @JvmOverloads constructor( } } } + + private fun updateUpiQrCodeViews(isChecked: Boolean) { + binding.textViewQrCodeDescription.isVisible = isChecked + if (isChecked) { + binding.editTextVpa.clearFocus() + hideKeyboard() } } diff --git a/upi/src/main/res/layout/upi_view.xml b/upi/src/main/res/layout/upi_view.xml index 776d09c509..545ba143d0 100644 --- a/upi/src/main/res/layout/upi_view.xml +++ b/upi/src/main/res/layout/upi_view.xml @@ -6,6 +6,7 @@ ~ Created by oscars on 7/2/2023. --> + + + android:layout_weight="1" + android:visibility="gone" + tools:visibility="visible" /> + android:layout_weight="1" + android:visibility="gone" + tools:visibility="visible" /> @@ -45,7 +59,9 @@ style="@style/AdyenCheckout.TextInputLayout" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="16dp"> + android:layout_marginTop="16dp" + android:visibility="gone" + tools:visibility="visible"> true + + - @@ -40,7 +40,7 @@ @@ -54,40 +54,40 @@ @dimen/standard_half_margin - - - - - - - - @@ -40,7 +40,7 @@ @@ -64,11 +64,11 @@ diff --git a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataIdUtilsTest.kt b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataIdUtilsTest.kt index 2a0d254f7f..5ac9cdfbca 100644 --- a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataIdUtilsTest.kt +++ b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataIdUtilsTest.kt @@ -23,24 +23,50 @@ internal class AppDataIdUtilsTest { fun `when mapToPaymentApp is called, then a mapped list is returned`( sourceList: List, environment: Environment, + selectedItemId: String?, expectedList: List ) { - assertEquals(expectedList, sourceList.mapToPaymentApp(environment)) + assertEquals(expectedList, sourceList.mapToPaymentApp(environment, selectedItemId)) } companion object { @JvmStatic fun appListSource() = listOf( - // sourceList, environment, expectedList + // sourceList, environment, selectedItemId, expectedList Arguments.arguments( listOf( AppData("id1", "name1"), AppData("id2", "name2"), ), Environment.TEST, + "id1", listOf( - UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST), - UPIIntentItem.PaymentApp("id2", "name2", Environment.TEST), + UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST, true), + UPIIntentItem.PaymentApp("id2", "name2", Environment.TEST, false), + ), + ), + Arguments.arguments( + listOf( + AppData("id1", "name1"), + AppData("id2", "name2"), + ), + Environment.TEST, + null, + listOf( + UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST, false), + UPIIntentItem.PaymentApp("id2", "name2", Environment.TEST, false), + ), + ), + Arguments.arguments( + listOf( + AppData("id1", "name1"), + AppData("id2", "name2"), + ), + Environment.TEST, + null, + listOf( + UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST, false), + UPIIntentItem.PaymentApp("id2", "name2", Environment.TEST, false), ), ), Arguments.arguments( @@ -49,8 +75,9 @@ internal class AppDataIdUtilsTest { AppData("", ""), ), Environment.TEST, + null, listOf( - UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST), + UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST, false), ), ), Arguments.arguments( @@ -60,8 +87,9 @@ internal class AppDataIdUtilsTest { AppData("id3", null), ), Environment.TEST, + null, listOf( - UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST), + UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST, false), ), ), Arguments.arguments( @@ -73,6 +101,7 @@ internal class AppDataIdUtilsTest { AppData(null, null), ), Environment.TEST, + null, listOf(), ), ) diff --git a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt index 5eda24c544..fdd5013447 100644 --- a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt +++ b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt @@ -85,13 +85,13 @@ internal class DefaultUPIDelegateTest( ), ) val intentItemList = listOf( - UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST), + UPIIntentItem.PaymentApp("id1", "name1", Environment.TEST, true), UPIIntentItem.PaymentApp("id2", "name2", Environment.TEST), - UPIIntentItem.GenericApp, + UPIIntentItem.GenericApp(), UPIIntentItem.ManualInput(null), ) val expectedAvailableModes = listOf( - UPIMode.Intent(intentItemList, intentItemList.firstOrNull()), + UPIMode.Intent(intentItemList), UPIMode.Qr, ) val delegate = createUPIDelegate(paymentMethod = paymentMethod) @@ -155,7 +155,7 @@ internal class DefaultUPIDelegateTest( delegate.updateInputData { selectedMode = UPISelectedMode.INTENT - selectedUPIIntentItem = UPIIntentItem.GenericApp + selectedUPIIntentItem = UPIIntentItem.GenericApp() } assertTrue(outputTestFlow.latestValue.isValid) @@ -283,7 +283,7 @@ internal class DefaultUPIDelegateTest( val componentStateTestFlow = delegate.componentStateFlow.test(testScheduler) val outputData = createOutputData( selectedMode = UPISelectedMode.INTENT, - selectedUPIIntentItem = UPIIntentItem.GenericApp, + selectedUPIIntentItem = UPIIntentItem.GenericApp(), ) delegate.updateComponentState(outputData) @@ -450,7 +450,7 @@ internal class DefaultUPIDelegateTest( fun `when selected mode is INTENT and there is selected upi intent item, then submit button should be enabled`() { delegate.updateInputData { selectedMode = UPISelectedMode.INTENT - selectedUPIIntentItem = UPIIntentItem.GenericApp + selectedUPIIntentItem = UPIIntentItem.GenericApp() } assertTrue(delegate.shouldEnableSubmitButton()) diff --git a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/model/UPISelectedModeTest.kt b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/model/UPISelectedModeTest.kt index 8350e55160..c7e4b47ef8 100644 --- a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/model/UPISelectedModeTest.kt +++ b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/model/UPISelectedModeTest.kt @@ -18,7 +18,7 @@ internal class UPISelectedModeTest { @ParameterizedTest @MethodSource("upiModeSource") - fun `when upiMode is Intent, then Intent selected mode is returned`( + fun `when selected upiMode is Intent, then Intent selected mode is returned`( upiMode: UPIMode, selectedMode: UPISelectedMode ) { @@ -30,15 +30,15 @@ internal class UPISelectedModeTest { fun upiModeSource() = listOf( // upiMode, selectedMode Arguments.arguments( - UPIMode.Intent(listOf(), UPIIntentItem.PaymentApp("", "", Environment.TEST)), + UPIMode.Intent(listOf(UPIIntentItem.PaymentApp("", "", Environment.TEST, true))), UPISelectedMode.INTENT, ), Arguments.arguments( - UPIMode.Intent(listOf(), UPIIntentItem.GenericApp), + UPIMode.Intent(listOf(UPIIntentItem.GenericApp(true))), UPISelectedMode.INTENT, ), Arguments.arguments( - UPIMode.Intent(listOf(), UPIIntentItem.ManualInput(null)), + UPIMode.Intent(listOf(UPIIntentItem.ManualInput(null, true))), UPISelectedMode.INTENT, ), Arguments.arguments( From 2866091fb5cd568bee7d2566f07275e9cfdb04b2 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 11 Jun 2024 16:04:39 +0200 Subject: [PATCH 25/33] Move onClickListener to the bind function COAND-866 --- .../checkout/upi/internal/ui/view/UPIAppsAdapter.kt | 6 +----- .../ui/view/UPIIntentGenericAppViewHolder.kt | 13 +++++-------- .../upi/internal/ui/view/UPIIntentItemViewHolder.kt | 4 +--- .../ui/view/UPIIntentManualAddressViewHolder.kt | 13 +++++-------- .../ui/view/UPIIntentPaymentAppViewHolder.kt | 13 +++++-------- 5 files changed, 17 insertions(+), 32 deletions(-) diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIAppsAdapter.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIAppsAdapter.kt index 9caaa16c46..2c96193358 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIAppsAdapter.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIAppsAdapter.kt @@ -60,11 +60,7 @@ internal class UPIAppsAdapter( } override fun onBindViewHolder(holder: UPIIntentItemViewHolder, position: Int) = with(holder) { - val item = getItem(position) - bind(item) - setOnClickListener { - onItemClickListener.invoke(item) - } + bind(getItem(position), onItemClickListener) } object UPIAppsDiffCallback : DiffUtil.ItemCallback() { diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentGenericAppViewHolder.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentGenericAppViewHolder.kt index d940ff505f..5a235d78b6 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentGenericAppViewHolder.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentGenericAppViewHolder.kt @@ -17,23 +17,20 @@ internal class UPIIntentGenericAppViewHolder( private val binding: UpiAppGenericBinding, ) : UPIIntentItemViewHolder(binding) { - override fun bind(item: UPIIntentItem) { + override fun bind(item: UPIIntentItem, onClickListener: (UPIIntentItem) -> Unit) { (item as? UPIIntentItem.GenericApp) ?: run { adyenLog(AdyenLogLevel.DEBUG) { "Item type is not recognized, thus the item can not be bound" } return } + itemView.setOnClickListener { + onClickListener.invoke(item) + } + bindItem(item.isSelected) } private fun bindItem(isChecked: Boolean) = with(binding) { radioButtonUpiApp.isChecked = isChecked } - - override fun setOnClickListener(onClickListener: (Int) -> Unit) { - itemView.setOnClickListener { - val position = getBindingAdapterPosition() - onClickListener.invoke(position) - } - } } diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentItemViewHolder.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentItemViewHolder.kt index fd67858699..8be51b3721 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentItemViewHolder.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentItemViewHolder.kt @@ -14,7 +14,5 @@ import com.adyen.checkout.upi.internal.ui.model.UPIIntentItem internal abstract class UPIIntentItemViewHolder(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) { - abstract fun bind(item: UPIIntentItem) - - abstract fun setOnClickListener(onClickListener: (Int) -> Unit) + abstract fun bind(item: UPIIntentItem, onClickListener: (UPIIntentItem) -> Unit) } diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentManualAddressViewHolder.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentManualAddressViewHolder.kt index bc840fb503..4d6e9b590d 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentManualAddressViewHolder.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentManualAddressViewHolder.kt @@ -32,12 +32,16 @@ internal class UPIIntentManualAddressViewHolder( } } - override fun bind(item: UPIIntentItem) { + override fun bind(item: UPIIntentItem, onClickListener: (UPIIntentItem) -> Unit) { (item as? UPIIntentItem.ManualInput) ?: run { adyenLog(AdyenLogLevel.DEBUG) { "Item type is not recognized, thus the item can not be bound" } return } + itemView.setOnClickListener { + onClickListener.invoke(item) + } + val errorMessage = item.errorMessageResource?.let { messageResource -> localizedContext.getString(messageResource) } @@ -75,11 +79,4 @@ internal class UPIIntentManualAddressViewHolder( textInputLayoutManualAddress.hideError() } } - - override fun setOnClickListener(onClickListener: (Int) -> Unit) { - itemView.setOnClickListener { - val position = getBindingAdapterPosition() - onClickListener.invoke(position) - } - } } diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentPaymentAppViewHolder.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentPaymentAppViewHolder.kt index d5671cbeff..e76b9e7c38 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentPaymentAppViewHolder.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIIntentPaymentAppViewHolder.kt @@ -19,12 +19,16 @@ internal class UPIIntentPaymentAppViewHolder( private val paymentMethod: String, ) : UPIIntentItemViewHolder(binding) { - override fun bind(item: UPIIntentItem) { + override fun bind(item: UPIIntentItem, onClickListener: (UPIIntentItem) -> Unit) { val app = (item as? UPIIntentItem.PaymentApp) ?: run { adyenLog(AdyenLogLevel.DEBUG) { "Item type is not recognized, thus the item can not be bound" } return } + itemView.setOnClickListener { + onClickListener.invoke(item) + } + bindItem( paymentMethod = paymentMethod, paymentApp = app, @@ -45,11 +49,4 @@ internal class UPIIntentPaymentAppViewHolder( txSubVariant = paymentApp.id, ) } - - override fun setOnClickListener(onClickListener: (Int) -> Unit) { - itemView.setOnClickListener { - val position = getBindingAdapterPosition() - onClickListener.invoke(position) - } - } } From 22f5ac4ca126217e4feded9ec17ea810c9cbb1d0 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Tue, 11 Jun 2024 16:08:03 +0200 Subject: [PATCH 26/33] Clean up code COAND-866 --- .../adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt | 1 - .../upi/internal/ui/{UPIAppIdUtils.kt => AppDataUtils.kt} | 0 .../internal/ui/{AppDataIdUtilsTest.kt => AppDataUtilsTest.kt} | 2 +- 3 files changed, 1 insertion(+), 2 deletions(-) rename upi/src/main/java/com/adyen/checkout/upi/internal/ui/{UPIAppIdUtils.kt => AppDataUtils.kt} (100%) rename upi/src/test/java/com/adyen/checkout/upi/internal/ui/{AppDataIdUtilsTest.kt => AppDataUtilsTest.kt} (99%) diff --git a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt index 640666ee7a..ce5830e4f5 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt @@ -122,7 +122,6 @@ internal class DefaultAwaitDelegate( } this.action = action - paymentDataRepository.paymentData = action.paymentData val event = GenericEvents.action( component = action.paymentMethodType.orEmpty(), diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/UPIAppIdUtils.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/AppDataUtils.kt similarity index 100% rename from upi/src/main/java/com/adyen/checkout/upi/internal/ui/UPIAppIdUtils.kt rename to upi/src/main/java/com/adyen/checkout/upi/internal/ui/AppDataUtils.kt diff --git a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataIdUtilsTest.kt b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataUtilsTest.kt similarity index 99% rename from upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataIdUtilsTest.kt rename to upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataUtilsTest.kt index 5ac9cdfbca..5c1b6109c1 100644 --- a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataIdUtilsTest.kt +++ b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/AppDataUtilsTest.kt @@ -16,7 +16,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource -internal class AppDataIdUtilsTest { +internal class AppDataUtilsTest { @ParameterizedTest @MethodSource("appListSource") From e81135dcc2c4b1af507014db773df7daf7547b29 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Wed, 12 Jun 2024 16:01:38 +0200 Subject: [PATCH 27/33] Optimise validation logic COAND-866 --- .../upi/internal/ui/DefaultUPIDelegate.kt | 76 ++++++++------- .../checkout/upi/internal/ui/UPIDelegate.kt | 2 +- .../upi/internal/ui/model/UPIInputData.kt | 2 +- .../checkout/upi/internal/ui/view/UPIView.kt | 6 +- .../upi/internal/ui/DefaultUPIDelegateTest.kt | 92 +++++++++++++------ 5 files changed, 109 insertions(+), 69 deletions(-) diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt index b4c51613ef..8d09fc66db 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegate.kt @@ -72,10 +72,6 @@ internal class DefaultUPIDelegate( override val uiEventFlow: Flow = submitHandler.uiEventFlow - // This allows moving validation to the delegate without changing the communication structure between delegate and - // the view. After we refactor state handling and validation logic, this can be improved. - private var cachedIntentVirtualPaymentAddress: String = "" - override fun initialize(coroutineScope: CoroutineScope) { submitHandler.initialize(coroutineScope, componentStateFlow) initializeAnalytics(coroutineScope) @@ -120,35 +116,49 @@ internal class DefaultUPIDelegate( updateComponentState(outputData) } - private fun createOutputData() = with(inputData) { - val appIds = paymentMethod.apps + private fun createOutputData(includeValidationErrors: Boolean = false) = with(inputData) { + val availableModes = createAvailableModes(this, paymentMethod, includeValidationErrors) val intentVirtualPaymentAddressFieldState = validateVirtualPaymentAddress(intentVirtualPaymentAddress) - val availableModes = if (!appIds.isNullOrEmpty()) { + + UPIOutputData( + selectedMode = selectedMode ?: availableModes.first().mapToSelectedMode(), + selectedUPIIntentItem = selectedUPIIntentItem, + availableModes = availableModes, + virtualPaymentAddressFieldState = validateVirtualPaymentAddress(vpaVirtualPaymentAddress), + intentVirtualPaymentAddressFieldState = intentVirtualPaymentAddressFieldState, + ) + } + + private fun createAvailableModes( + inputData: UPIInputData, + paymentMethod: PaymentMethod, + includeValidation: Boolean + ) = with(inputData) { + val appIds = paymentMethod.apps + if (!appIds.isNullOrEmpty()) { + val paymentAddressFieldState = if (includeValidation) { + validateVirtualPaymentAddress(intentVirtualPaymentAddress) + } else { + null + } val intentItemList = createIntentItems( appIds, componentParams.environment, - intentVirtualPaymentAddressFieldState, selectedUPIIntentItem, + paymentAddressFieldState, ) + listOf(UPIMode.Intent(intentItemList), UPIMode.Qr) } else { listOf(UPIMode.Vpa, UPIMode.Qr) } - - UPIOutputData( - selectedMode = selectedMode ?: availableModes.first().mapToSelectedMode(), - selectedUPIIntentItem = selectedUPIIntentItem, - availableModes = availableModes, - virtualPaymentAddressFieldState = validateVirtualPaymentAddress(vpaVirtualPaymentAddress), - intentVirtualPaymentAddressFieldState = intentVirtualPaymentAddressFieldState, - ) } private fun createIntentItems( upiApps: List, environment: Environment, - intentVirtualPaymentAddressFieldState: FieldState, - selectedUPIIntentItem: UPIIntentItem? + selectedUPIIntentItem: UPIIntentItem?, + paymentAddressFieldState: FieldState?, ): List { val paymentApps = upiApps.mapToPaymentApp( environment = environment, @@ -159,8 +169,9 @@ internal class DefaultUPIDelegate( isSelected = selectedUPIIntentItem is UPIIntentItem.GenericApp, ) - val manualInputErrorMessageId = - getValidationErrorResourceIdOrNull(intentVirtualPaymentAddressFieldState.validation) + val manualInputErrorMessageId = paymentAddressFieldState?.let { + getValidationErrorResourceIdOrNull(paymentAddressFieldState.validation) + } val manualInput = UPIIntentItem.ManualInput( errorMessageResource = manualInputErrorMessageId, isSelected = selectedUPIIntentItem is UPIIntentItem.ManualInput, @@ -176,10 +187,8 @@ internal class DefaultUPIDelegate( private fun getValidationErrorResourceIdOrNull(validation: Validation?): Int? = (validation as? Validation.Invalid)?.reason - private fun validateVirtualPaymentAddress(virtualPaymentAddress: String?): FieldState = - if (virtualPaymentAddress == null) { - FieldState("", Validation.Valid) - } else if (virtualPaymentAddress.isNotBlank()) { + private fun validateVirtualPaymentAddress(virtualPaymentAddress: String): FieldState = + if (virtualPaymentAddress.isNotBlank()) { FieldState(virtualPaymentAddress, Validation.Valid) } else { FieldState(virtualPaymentAddress, Validation.Invalid(R.string.checkout_upi_vpa_validation)) @@ -275,15 +284,10 @@ internal class DefaultUPIDelegate( _componentStateFlow.tryEmit(componentState) } - override fun updateIntentVirtualPaymentAddress(value: String) { - cachedIntentVirtualPaymentAddress = value - - // This makes sure that the field validation gets updated for the delegate too and not only for the input field - if (inputData.intentVirtualPaymentAddress != null) { - updateInputData { - intentVirtualPaymentAddress = null - } - } + override fun highlightValidationErrors() { + adyenLog(AdyenLogLevel.VERBOSE) { "updating outputData to include validation" } + val outputData = createOutputData(includeValidationErrors = true) + outputDataChanged(outputData) } override fun setInteractionBlocked(isInteractionBlocked: Boolean) { @@ -295,12 +299,6 @@ internal class DefaultUPIDelegate( } override fun onSubmit() { - // This allows moving validation to the delegate without changing the communication structure between delegate - // and the view. After we refactor state handling and validation logic, this can be improved. - updateInputData { - intentVirtualPaymentAddress = cachedIntentVirtualPaymentAddress - } - val event = GenericEvents.submit(paymentMethod.type.orEmpty()) analyticsManager.trackEvent(event) diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/UPIDelegate.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/UPIDelegate.kt index d1bc4f4f32..693b2d8068 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/UPIDelegate.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/UPIDelegate.kt @@ -31,7 +31,7 @@ internal interface UPIDelegate : fun updateInputData(update: UPIInputData.() -> Unit) - fun updateIntentVirtualPaymentAddress(value: String) + fun highlightValidationErrors() fun setInteractionBlocked(isInteractionBlocked: Boolean) } diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/model/UPIInputData.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/model/UPIInputData.kt index fccf635376..a9645d3f31 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/model/UPIInputData.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/model/UPIInputData.kt @@ -14,5 +14,5 @@ internal data class UPIInputData( var selectedMode: UPISelectedMode? = null, var selectedUPIIntentItem: UPIIntentItem? = null, var vpaVirtualPaymentAddress: String = "", - var intentVirtualPaymentAddress: String? = null, + var intentVirtualPaymentAddress: String = "", ) : InputData diff --git a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIView.kt b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIView.kt index ec16018f80..39b0d80afc 100644 --- a/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIView.kt +++ b/upi/src/main/java/com/adyen/checkout/upi/internal/ui/view/UPIView.kt @@ -177,7 +177,9 @@ internal class UPIView @JvmOverloads constructor( } private fun onIntentItemInputChanged(value: String) { - delegate.updateIntentVirtualPaymentAddress(value) + delegate.updateInputData { + intentVirtualPaymentAddress = value + } } private fun initViewsForVpa(isChecked: Boolean) = with(binding) { @@ -238,6 +240,8 @@ internal class UPIView @JvmOverloads constructor( if (vpaValidation is Validation.Invalid) { binding.textInputLayoutVpa.showError(localizedContext.getString(vpaValidation.reason)) } + + delegate.highlightValidationErrors() } override fun getView(): View = this diff --git a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt index fdd5013447..2d7f3e8ad5 100644 --- a/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt +++ b/upi/src/test/java/com/adyen/checkout/upi/internal/ui/DefaultUPIDelegateTest.kt @@ -380,33 +380,6 @@ internal class DefaultUPIDelegateTest( } } - @Nested - inner class VirtualPaymentAddressCacheTest { - - @Test - fun `when updateIntentVirtualPaymentAddress is called, then payment address is cleared`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - - delegate.updateIntentVirtualPaymentAddress("test_address") - - delegate.outputDataFlow.test { - assertEquals("", expectMostRecentItem().intentVirtualPaymentAddressFieldState.value) - } - } - - @Test - fun `when onSubmit is called, then payment address is being updated with the cache`() = runTest { - delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) - delegate.updateIntentVirtualPaymentAddress("test_address") - - delegate.onSubmit() - - delegate.outputDataFlow.test { - assertEquals("test_address", expectMostRecentItem().intentVirtualPaymentAddressFieldState.value) - } - } - } - @Nested inner class SubmitButtonVisibilityTest { @@ -502,6 +475,71 @@ internal class DefaultUPIDelegateTest( } } + @Nested + @DisplayName("when highlightValidationErrors is called for ") + inner class HighlightValidationErrorsTest { + + @Test + fun `invalid payment address, output data includes validation errors`() = runTest { + val paymentMethod = PaymentMethod( + apps = listOf( + AppData("id1", "name1"), + ), + ) + val delegate = createUPIDelegate(paymentMethod = paymentMethod) + val outputTestFlow = delegate.outputDataFlow.test(testScheduler) + delegate.updateInputData { + selectedMode = UPISelectedMode.INTENT + selectedUPIIntentItem = UPIIntentItem.ManualInput(null) + intentVirtualPaymentAddress = " " + } + + delegate.highlightValidationErrors() + + with(outputTestFlow.latestValue) { + assertTrue( + availableModes.any { mode -> + mode is UPIMode.Intent && mode.intentItems.any { item -> + item is UPIIntentItem.ManualInput && item.errorMessageResource != null + } + }, + ) + } + + outputTestFlow.cancel() + } + + @Test + fun `valid payment address, output data does not include validation errors`() = runTest { + val paymentMethod = PaymentMethod( + apps = listOf( + AppData("id1", "name1"), + ), + ) + val delegate = createUPIDelegate(paymentMethod = paymentMethod) + val outputTestFlow = delegate.outputDataFlow.test(testScheduler) + delegate.updateInputData { + selectedMode = UPISelectedMode.INTENT + selectedUPIIntentItem = UPIIntentItem.ManualInput(null) + intentVirtualPaymentAddress = "test" + } + + delegate.highlightValidationErrors() + + with(outputTestFlow.latestValue) { + assertTrue( + availableModes.any { mode -> + mode is UPIMode.Intent && mode.intentItems.any { item -> + item is UPIIntentItem.ManualInput && item.errorMessageResource == null + } + }, + ) + } + + outputTestFlow.cancel() + } + } + @Nested inner class AnalyticsTest { From b1b33ac8c051e70034d54f0341d2845c53f946d6 Mon Sep 17 00:00:00 2001 From: Ararat Mnatsakanyan Date: Thu, 13 Jun 2024 14:43:00 +0200 Subject: [PATCH 28/33] Fix UI alignment COAND-866 --- upi/src/main/res/layout/upi_app.xml | 18 +++++++++++++----- upi/src/main/res/layout/upi_app_generic.xml | 18 +++++++++++++----- .../main/res/layout/upi_app_manual_address.xml | 3 +-- upi/src/main/res/layout/upi_view.xml | 7 ++++--- upi/src/main/res/values/styles.xml | 5 ----- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/upi/src/main/res/layout/upi_app.xml b/upi/src/main/res/layout/upi_app.xml index 64bf5ca45b..301368a9e6 100644 --- a/upi/src/main/res/layout/upi_app.xml +++ b/upi/src/main/res/layout/upi_app.xml @@ -10,14 +10,13 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/layout_upi_app" - style="@style/AdyenCheckout.UPI.RecyclerListItem" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -26,7 +25,7 @@ style="@style/AdyenCheckout.UPI.Intent.AppIdTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" - app:layout_constraintBottom_toTopOf="@+id/divider_upi_app" + app:layout_constraintBottom_toTopOf="@+id/barrier_divider" app:layout_constraintEnd_toStartOf="@+id/radioButton_upi_app" app:layout_constraintHorizontal_bias="0" app:layout_constraintStart_toEndOf="@+id/imageView_upi_logo" @@ -39,10 +38,18 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:clickable="false" - app:layout_constraintBottom_toTopOf="@+id/divider_upi_app" + app:layout_constraintBottom_toTopOf="@+id/barrier_divider" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/barrier_divider" /> diff --git a/upi/src/main/res/layout/upi_app_generic.xml b/upi/src/main/res/layout/upi_app_generic.xml index 5cf45ebd7d..fd42094bfb 100644 --- a/upi/src/main/res/layout/upi_app_generic.xml +++ b/upi/src/main/res/layout/upi_app_generic.xml @@ -10,14 +10,13 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/layout_upi_app" - style="@style/AdyenCheckout.UPI.RecyclerListItem" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -26,7 +25,7 @@ style="@style/AdyenCheckout.UPI.Intent.AppIdTextView.Generic" android:layout_width="wrap_content" android:layout_height="wrap_content" - app:layout_constraintBottom_toTopOf="@+id/divider_upi_app" + app:layout_constraintBottom_toTopOf="@+id/barrier_divider" app:layout_constraintEnd_toStartOf="@+id/radioButton_upi_app" app:layout_constraintHorizontal_bias="0" app:layout_constraintStart_toEndOf="@+id/imageView_upi_logo" @@ -39,10 +38,18 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:clickable="false" - app:layout_constraintBottom_toTopOf="@+id/divider_upi_app" + app:layout_constraintBottom_toTopOf="@+id/barrier_divider" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/barrier_divider" /> diff --git a/upi/src/main/res/layout/upi_app_manual_address.xml b/upi/src/main/res/layout/upi_app_manual_address.xml index 55a9b3c737..b765a9f5ef 100644 --- a/upi/src/main/res/layout/upi_app_manual_address.xml +++ b/upi/src/main/res/layout/upi_app_manual_address.xml @@ -10,7 +10,6 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/layout_upi_manual_address" - style="@style/AdyenCheckout.UPI.RecyclerListItem" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -48,6 +47,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" app:barrierDirection="bottom" + app:barrierMargin="@dimen/standard_half_margin" app:constraint_referenced_ids="imageView_upi_logo, textView_upi_app_name, radioButton_upi_manual_address" /> diff --git a/upi/src/main/res/values/styles.xml b/upi/src/main/res/values/styles.xml index 3cd3f45241..ea5d85067c 100644 --- a/upi/src/main/res/values/styles.xml +++ b/upi/src/main/res/values/styles.xml @@ -49,11 +49,6 @@ @dimen/input_layout_height - - From 4c31a3e0092278ed3e6d4a4768b7a0452dde622f Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 19 Jun 2024 10:05:20 +0200 Subject: [PATCH 30/33] Make await-redirect flow more explicit This also makes sure the logo of the payment method will be loaded regardless of the redirect. COAND-866 --- .../await/internal/ui/DefaultAwaitDelegate.kt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt index ce5830e4f5..497b3fc491 100644 --- a/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt +++ b/await/src/main/java/com/adyen/checkout/await/internal/ui/DefaultAwaitDelegate.kt @@ -122,6 +122,7 @@ internal class DefaultAwaitDelegate( } this.action = action + paymentDataRepository.paymentData = action.paymentData val event = GenericEvents.action( component = action.paymentMethodType.orEmpty(), @@ -129,10 +130,13 @@ internal class DefaultAwaitDelegate( ) analyticsManager?.trackEvent(event) + launchAction(action, activity) + initState(action) + } + + private fun launchAction(action: AwaitAction, activity: Activity) { if (shouldLaunchRedirect(action)) { makeRedirect(action, activity) - } else { - initState(action) } } @@ -143,7 +147,9 @@ internal class DefaultAwaitDelegate( try { adyenLog(AdyenLogLevel.DEBUG) { "makeRedirect - $url" } redirectHandler.launchUriRedirect(activity, url) - initState(action) + val paymentData = paymentDataRepository.paymentData + ?: throw CheckoutException("Payment data should not be null") + startStatusPolling(paymentData, action) } catch (exception: CheckoutException) { emitError(exception) } @@ -151,7 +157,6 @@ internal class DefaultAwaitDelegate( private fun initState(action: AwaitAction) { val paymentData = action.paymentData - paymentDataRepository.paymentData = paymentData if (paymentData == null) { adyenLog(AdyenLogLevel.ERROR) { "Payment data is null" } emitError(ComponentException("Payment data is null")) @@ -159,7 +164,10 @@ internal class DefaultAwaitDelegate( } createOutputData(null, action) - startStatusPolling(paymentData, action) + // Redirect flow starts polling after it launched a redirect + if (!shouldLaunchRedirect(action)) { + startStatusPolling(paymentData, action) + } } private fun startStatusPolling(paymentData: String, action: Action) { From 53d534fe117c481d55fb3ecec89b479d96950554 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 19 Jun 2024 10:48:19 +0200 Subject: [PATCH 31/33] Move new fields to bottom of existing data classes This makes sure the api doesn't break. COAND-866 --- .../main/java/com/adyen/checkout/components/core/AppData.kt | 4 ++-- .../com/adyen/checkout/components/core/PaymentMethod.kt | 6 +++--- .../components/core/paymentmethod/UPIPaymentMethod.kt | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/AppData.kt b/components-core/src/main/java/com/adyen/checkout/components/core/AppData.kt index 9e7490ff20..54377ab924 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/AppData.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/AppData.kt @@ -17,8 +17,8 @@ import org.json.JSONObject @Parcelize data class AppData( - var id: String? = null, - var name: String? = null, + val id: String? = null, + val name: String? = null, ) : ModelObject() { companion object { diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethod.kt b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethod.kt index f16cb7ed07..80ae6820f7 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethod.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/PaymentMethod.kt @@ -28,9 +28,9 @@ data class PaymentMethod( var brand: String? = null, var fundingSource: String? = null, var issuers: List? = null, - var apps: List? = null, var configuration: Configuration? = null, var details: List? = null, + var apps: List? = null, ) : ModelObject() { companion object { @@ -61,9 +61,9 @@ data class PaymentMethod( putOpt(BRAND, modelObject.brand) putOpt(FUNDING_SOURCE, modelObject.fundingSource) putOpt(ISSUERS, serializeOptList(modelObject.issuers, Issuer.SERIALIZER)) - putOpt(APPS, serializeOptList(modelObject.apps, AppData.SERIALIZER)) putOpt(CONFIGURATION, serializeOpt(modelObject.configuration, Configuration.SERIALIZER)) putOpt(DETAILS, serializeOptList(modelObject.details, InputDetail.SERIALIZER)) + putOpt(APPS, serializeOptList(modelObject.apps, AppData.SERIALIZER)) } } catch (e: JSONException) { throw ModelSerializationException(PaymentMethod::class.java, e) @@ -78,9 +78,9 @@ data class PaymentMethod( brand = jsonObject.getStringOrNull(BRAND), fundingSource = jsonObject.getStringOrNull(FUNDING_SOURCE), issuers = deserializeOptList(jsonObject.optJSONArray(ISSUERS), Issuer.SERIALIZER), - apps = deserializeOptList(jsonObject.optJSONArray(APPS), AppData.SERIALIZER), configuration = deserializeOpt(jsonObject.optJSONObject(CONFIGURATION), Configuration.SERIALIZER), details = deserializeOptList(jsonObject.optJSONArray(DETAILS), InputDetail.SERIALIZER), + apps = deserializeOptList(jsonObject.optJSONArray(APPS), AppData.SERIALIZER), ) } } diff --git a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod.kt b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod.kt index e240d68883..92ad452c0c 100644 --- a/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod.kt +++ b/components-core/src/main/java/com/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod.kt @@ -18,8 +18,8 @@ import org.json.JSONObject data class UPIPaymentMethod( override var type: String?, override var checkoutAttemptId: String?, - var appId: String?, var virtualPaymentAddress: String?, + var appId: String?, ) : PaymentMethodDetails() { companion object { @@ -33,8 +33,8 @@ data class UPIPaymentMethod( JSONObject().apply { putOpt(TYPE, modelObject.type) putOpt(CHECKOUT_ATTEMPT_ID, modelObject.checkoutAttemptId) - putOpt(APP_ID, modelObject.appId) putOpt(VIRTUAL_PAYMENT_ADDRESS, modelObject.virtualPaymentAddress) + putOpt(APP_ID, modelObject.appId) } } catch (e: JSONException) { throw ModelSerializationException(UPIPaymentMethod::class.java, e) @@ -45,8 +45,8 @@ data class UPIPaymentMethod( return UPIPaymentMethod( type = jsonObject.getStringOrNull(TYPE), checkoutAttemptId = jsonObject.getStringOrNull(CHECKOUT_ATTEMPT_ID), - appId = jsonObject.getStringOrNull(APP_ID), virtualPaymentAddress = jsonObject.getStringOrNull(VIRTUAL_PAYMENT_ADDRESS), + appId = jsonObject.getStringOrNull(APP_ID), ) } } From 426ab9500c0d1f498eb7d3952d9f0ae0851d1b02 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 19 Jun 2024 14:11:47 +0200 Subject: [PATCH 32/33] Update api change formatting to split it in sections COAND-926 --- scripts/process_api_changes.sh | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/scripts/process_api_changes.sh b/scripts/process_api_changes.sh index 8f41a86f51..d6e5582c91 100644 --- a/scripts/process_api_changes.sh +++ b/scripts/process_api_changes.sh @@ -27,7 +27,7 @@ api_files=($(find . -name '*.api')) api_files_size=${#api_files[@]} for ((i = 0 ; i < $api_files_size ; i+= 2 )); do - git_diff=$(git diff --no-index --unified=0 "${api_files[i]}" "${api_files[i + 1]}") + git_diff=$(git diff --no-index "${api_files[i]}" "${api_files[i + 1]}") if [ -n "$git_diff" ] then # Add module name as title @@ -39,21 +39,33 @@ for ((i = 0 ; i < $api_files_size ; i+= 2 )); do title=${title%????} echo "## $title" >> "$output_file" - # Open code block - echo "\`\`\`diff" >> "$output_file" - diff_index=0 + is_block_open=false # Dump git diff output while read -r line; do # Skip first 4 lines as they are verbose if (( $diff_index > 3 )) then + + if [[ "$line" == "@@"* ]] + then + if $is_block_open + then + # Close code block + echo "\`\`\`" >> "$output_file" + is_block_open=false + fi + + # Open code block + echo "\`\`\`diff" >> "$output_file" + is_block_open=true + fi echo "$line" >> "$output_file" fi ((diff_index++)) done <<< "$git_diff" - # Close code block + # Always close last code block echo "\`\`\`" >> "$output_file" fi done From d6b39c7699bbe8e958bd5436e933896f7059dad7 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Wed, 19 Jun 2024 14:12:39 +0200 Subject: [PATCH 33/33] Run apiDump COAND-866 --- await/api/await.api | 3 +- components-core/api/components-core.api | 57 +++++++++++++++++++++---- ui-core/api/ui-core.api | 4 ++ 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/await/api/await.api b/await/api/await.api index 1ed785a119..ba2ce65fb4 100644 --- a/await/api/await.api +++ b/await/api/await.api @@ -1,4 +1,4 @@ -public final class com/adyen/checkout/await/AwaitComponent : androidx/lifecycle/ViewModel, com/adyen/checkout/components/core/internal/ActionComponent, com/adyen/checkout/ui/core/internal/ui/ViewableComponent { +public final class com/adyen/checkout/await/AwaitComponent : androidx/lifecycle/ViewModel, com/adyen/checkout/components/core/RedirectableActionComponent, com/adyen/checkout/components/core/internal/ActionComponent, com/adyen/checkout/ui/core/internal/ui/ViewableComponent { public static final field Companion Lcom/adyen/checkout/await/AwaitComponent$Companion; public static final field PROVIDER Lcom/adyen/checkout/components/core/internal/provider/ActionComponentProvider; public fun canHandleAction (Lcom/adyen/checkout/components/core/action/Action;)Z @@ -6,6 +6,7 @@ public final class com/adyen/checkout/await/AwaitComponent : androidx/lifecycle/ public synthetic fun getDelegate ()Lcom/adyen/checkout/components/core/internal/ui/ComponentDelegate; public fun getViewFlow ()Lkotlinx/coroutines/flow/Flow; public fun handleAction (Lcom/adyen/checkout/components/core/action/Action;Landroid/app/Activity;)V + public fun setOnRedirectListener (Lkotlin/jvm/functions/Function0;)V } public final class com/adyen/checkout/await/AwaitComponent$Companion { diff --git a/components-core/api/components-core.api b/components-core/api/components-core.api index 7fc6a96a5a..3b4cf25598 100644 --- a/components-core/api/components-core.api +++ b/components-core/api/components-core.api @@ -209,6 +209,36 @@ public final class com/adyen/checkout/components/core/AnalyticsLevel : java/lang public static fun values ()[Lcom/adyen/checkout/components/core/AnalyticsLevel; } +public final class com/adyen/checkout/components/core/AppData : com/adyen/checkout/core/internal/data/model/ModelObject { + public static final field CREATOR Landroid/os/Parcelable$Creator; + public static final field Companion Lcom/adyen/checkout/components/core/AppData$Companion; + public static final field SERIALIZER Lcom/adyen/checkout/core/internal/data/model/ModelObject$Serializer; + public fun ()V + public fun (Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lcom/adyen/checkout/components/core/AppData; + public static synthetic fun copy$default (Lcom/adyen/checkout/components/core/AppData;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/adyen/checkout/components/core/AppData; + public fun equals (Ljava/lang/Object;)Z + public final fun getId ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public fun writeToParcel (Landroid/os/Parcel;I)V +} + +public final class com/adyen/checkout/components/core/AppData$Companion { +} + +public final class com/adyen/checkout/components/core/AppData$Creator : android/os/Parcelable$Creator { + public fun ()V + public final fun createFromParcel (Landroid/os/Parcel;)Lcom/adyen/checkout/components/core/AppData; + public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object; + public final fun newArray (I)[Lcom/adyen/checkout/components/core/AppData; + public synthetic fun newArray (I)[Ljava/lang/Object; +} + public final class com/adyen/checkout/components/core/BalanceResult : com/adyen/checkout/core/internal/data/model/ModelObject { public static final field CREATOR Landroid/os/Parcelable$Creator; public static final field Companion Lcom/adyen/checkout/components/core/BalanceResult$Companion; @@ -769,8 +799,8 @@ public final class com/adyen/checkout/components/core/PaymentMethod : com/adyen/ public static final field Companion Lcom/adyen/checkout/components/core/PaymentMethod$Companion; public static final field SERIALIZER Lcom/adyen/checkout/core/internal/data/model/ModelObject$Serializer; public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lcom/adyen/checkout/components/core/Configuration;Ljava/util/List;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lcom/adyen/checkout/components/core/Configuration;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lcom/adyen/checkout/components/core/Configuration;Ljava/util/List;Ljava/util/List;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lcom/adyen/checkout/components/core/Configuration;Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ljava/util/List; @@ -779,9 +809,11 @@ public final class com/adyen/checkout/components/core/PaymentMethod : com/adyen/ public final fun component6 ()Ljava/util/List; public final fun component7 ()Lcom/adyen/checkout/components/core/Configuration; public final fun component8 ()Ljava/util/List; - public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lcom/adyen/checkout/components/core/Configuration;Ljava/util/List;)Lcom/adyen/checkout/components/core/PaymentMethod; - public static synthetic fun copy$default (Lcom/adyen/checkout/components/core/PaymentMethod;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lcom/adyen/checkout/components/core/Configuration;Ljava/util/List;ILjava/lang/Object;)Lcom/adyen/checkout/components/core/PaymentMethod; + public final fun component9 ()Ljava/util/List; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lcom/adyen/checkout/components/core/Configuration;Ljava/util/List;Ljava/util/List;)Lcom/adyen/checkout/components/core/PaymentMethod; + public static synthetic fun copy$default (Lcom/adyen/checkout/components/core/PaymentMethod;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lcom/adyen/checkout/components/core/Configuration;Ljava/util/List;Ljava/util/List;ILjava/lang/Object;)Lcom/adyen/checkout/components/core/PaymentMethod; public fun equals (Ljava/lang/Object;)Z + public final fun getApps ()Ljava/util/List; public final fun getBrand ()Ljava/lang/String; public final fun getBrands ()Ljava/util/List; public final fun getConfiguration ()Lcom/adyen/checkout/components/core/Configuration; @@ -791,6 +823,7 @@ public final class com/adyen/checkout/components/core/PaymentMethod : com/adyen/ public final fun getName ()Ljava/lang/String; public final fun getType ()Ljava/lang/String; public fun hashCode ()I + public final fun setApps (Ljava/util/List;)V public final fun setBrand (Ljava/lang/String;)V public final fun setBrands (Ljava/util/List;)V public final fun setConfiguration (Lcom/adyen/checkout/components/core/Configuration;)V @@ -879,6 +912,7 @@ public final class com/adyen/checkout/components/core/PaymentMethodTypes { public static final field UNKNOWN Ljava/lang/String; public static final field UPI Ljava/lang/String; public static final field UPI_COLLECT Ljava/lang/String; + public static final field UPI_INTENT Ljava/lang/String; public static final field UPI_QR Ljava/lang/String; public static final field WECHAT_PAY_MINI_PROGRAM Ljava/lang/String; public static final field WECHAT_PAY_QR Ljava/lang/String; @@ -1064,15 +1098,17 @@ public final class com/adyen/checkout/components/core/action/AwaitAction : com/a public static final field Companion Lcom/adyen/checkout/components/core/action/AwaitAction$Companion; public static final field SERIALIZER Lcom/adyen/checkout/core/internal/data/model/ModelObject$Serializer; public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun describeContents ()I public fun getPaymentData ()Ljava/lang/String; public fun getPaymentMethodType ()Ljava/lang/String; public fun getType ()Ljava/lang/String; + public final fun getUrl ()Ljava/lang/String; public fun setPaymentData (Ljava/lang/String;)V public fun setPaymentMethodType (Ljava/lang/String;)V public fun setType (Ljava/lang/String;)V + public final fun setUrl (Ljava/lang/String;)V public fun writeToParcel (Landroid/os/Parcel;I)V } @@ -2819,18 +2855,21 @@ public final class com/adyen/checkout/components/core/paymentmethod/UPIPaymentMe public static final field CREATOR Landroid/os/Parcelable$Creator; public static final field Companion Lcom/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod$Companion; public static final field SERIALIZER Lcom/adyen/checkout/core/internal/data/model/ModelObject$Serializer; - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ljava/lang/String; - public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod; - public static synthetic fun copy$default (Lcom/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod; + public final fun component4 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod; + public static synthetic fun copy$default (Lcom/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/adyen/checkout/components/core/paymentmethod/UPIPaymentMethod; public fun describeContents ()I public fun equals (Ljava/lang/Object;)Z + public final fun getAppId ()Ljava/lang/String; public fun getCheckoutAttemptId ()Ljava/lang/String; public fun getType ()Ljava/lang/String; public final fun getVirtualPaymentAddress ()Ljava/lang/String; public fun hashCode ()I + public final fun setAppId (Ljava/lang/String;)V public fun setCheckoutAttemptId (Ljava/lang/String;)V public fun setType (Ljava/lang/String;)V public final fun setVirtualPaymentAddress (Ljava/lang/String;)V diff --git a/ui-core/api/ui-core.api b/ui-core/api/ui-core.api index 45030c84f3..669d9ed39d 100644 --- a/ui-core/api/ui-core.api +++ b/ui-core/api/ui-core.api @@ -62,6 +62,10 @@ public final class com/adyen/checkout/ui/core/internal/ui/PaymentComponentUIEven public static final field INSTANCE Lcom/adyen/checkout/ui/core/internal/ui/PaymentComponentUIEvent$InvalidUI; } +public final class com/adyen/checkout/ui/core/internal/ui/PaymentComponentUIEvent$StateUpdated : com/adyen/checkout/ui/core/internal/ui/PaymentComponentUIEvent { + public static final field INSTANCE Lcom/adyen/checkout/ui/core/internal/ui/PaymentComponentUIEvent$StateUpdated; +} + public final class com/adyen/checkout/ui/core/internal/ui/PaymentComponentUIState$Blocked : com/adyen/checkout/ui/core/internal/ui/PaymentComponentUIState { public static final field INSTANCE Lcom/adyen/checkout/ui/core/internal/ui/PaymentComponentUIState$Blocked; }