From b614a967931bed98a6280dff85ad3767330d16f5 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 4 Feb 2025 10:19:29 +0100 Subject: [PATCH 1/4] Add extra test data for address lookup COAND-1064 --- example-app/src/main/assets/lookup_options.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/example-app/src/main/assets/lookup_options.json b/example-app/src/main/assets/lookup_options.json index 362e3fd303..bd9e32a131 100644 --- a/example-app/src/main/assets/lookup_options.json +++ b/example-app/src/main/assets/lookup_options.json @@ -11,6 +11,17 @@ "city": "Amsterdam" } }, + { + "id": "5", + "address": { + "country": "NL", + "postalCode": "1012KK", + "houseNumberOrName": "49", + "street": "Rokin", + "stateOrProvince": "Noord-Holland", + "city": "Amsterdam" + } + }, { "id": "1", "address": { From afbc750e32347191f9f78dedfda0b42297a44c3d Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 4 Feb 2025 15:48:50 +0100 Subject: [PATCH 2/4] Split form initialization and updating input fields This solves an issue where the input fields were not updated if you an address with address lookup from the same country. COAND-1064 --- .../core/internal/ui/view/AddressFormInput.kt | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt index 897b23f7fc..564414f471 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt @@ -24,6 +24,7 @@ import com.adyen.checkout.ui.core.internal.ui.AddressDelegate import com.adyen.checkout.ui.core.internal.ui.AddressSpecification import com.adyen.checkout.ui.core.internal.ui.SimpleTextListAdapter import com.adyen.checkout.ui.core.internal.ui.model.AddressListItem +import com.adyen.checkout.ui.core.internal.ui.model.AddressOutputData import com.adyen.checkout.ui.core.internal.util.hideError import com.adyen.checkout.ui.core.internal.util.setLocalizedHintFromStyle import com.adyen.checkout.ui.core.internal.util.setLocalizedTextFromStyle @@ -220,6 +221,8 @@ class AddressFormInput @JvmOverloads constructor( currentSpec = selectedSpecification autoCompleteTextViewCountry?.setText(selectedCountry?.name) populateFormFields(selectedSpecification) + } else { + updateInputFields(delegate.addressOutputData) } } @@ -248,6 +251,7 @@ class AddressFormInput @JvmOverloads constructor( val hadFocus = hasFocus() formContainer?.removeAllViews() LayoutInflater.from(context).inflate(layoutResId, formContainer, true) + updateInputFields(delegate.addressOutputData) initForm(specification) if (hadFocus) requestFocus() } @@ -314,7 +318,7 @@ class AddressFormInput @JvmOverloads constructor( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { setAutofillHints(HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_STREET_ADDRESS) } - setText(delegate.addressOutputData.street.value) + setOnChangeListener { delegate.updateAddressInputData { street = it.toString() } textInputLayoutStreet?.hideError() @@ -333,7 +337,7 @@ class AddressFormInput @JvmOverloads constructor( private fun initHouseNumberInput(styleResId: Int?) { styleResId?.let { textInputLayoutHouseNumber?.setLocalizedHintFromStyle(it, localizedContext) } editTextHouseNumber?.apply { - setText(delegate.addressOutputData.houseNumberOrName.value) + setOnChangeListener { delegate.updateAddressInputData { houseNumberOrName = it.toString() } textInputLayoutHouseNumber?.hideError() @@ -355,7 +359,7 @@ class AddressFormInput @JvmOverloads constructor( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { setAutofillHints(HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_APT_NUMBER) } - setText(delegate.addressOutputData.apartmentSuite.value) + setOnChangeListener { delegate.updateAddressInputData { apartmentSuite = it.toString() } } @@ -376,7 +380,7 @@ class AddressFormInput @JvmOverloads constructor( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { setAutofillHints(HintConstants.AUTOFILL_HINT_POSTAL_CODE) } - setText(delegate.addressOutputData.postalCode.value) + setOnChangeListener { delegate.updateAddressInputData { postalCode = it.toString() } textInputLayoutPostalCode?.hideError() @@ -398,7 +402,7 @@ class AddressFormInput @JvmOverloads constructor( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { setAutofillHints(HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_LOCALITY) } - setText(delegate.addressOutputData.city.value) + setOnChangeListener { delegate.updateAddressInputData { city = it.toString() } textInputLayoutCity?.hideError() @@ -420,7 +424,7 @@ class AddressFormInput @JvmOverloads constructor( if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { setAutofillHints(HintConstants.AUTOFILL_HINT_POSTAL_ADDRESS_REGION) } - setText(delegate.addressOutputData.stateOrProvince.value) + setOnChangeListener { delegate.updateAddressInputData { stateOrProvince = it.toString() } textInputLayoutProvinceTerritory?.hideError() @@ -452,6 +456,21 @@ class AddressFormInput @JvmOverloads constructor( } } + private fun updateInputFields(outputData: AddressOutputData) { + editTextStreet?.setTextIfChanged(outputData.street.value) + editTextHouseNumber?.setTextIfChanged(outputData.houseNumberOrName.value) + editTextApartmentSuite?.setTextIfChanged(outputData.apartmentSuite.value) + editTextPostalCode?.setTextIfChanged(outputData.postalCode.value) + editTextCity?.setTextIfChanged(outputData.city.value) + editTextProvinceTerritory?.setTextIfChanged(outputData.stateOrProvince.value) + } + + private fun TextView.setTextIfChanged(newText: String?) { + if (newText != text.toString()) { + text = newText + } + } + fun updateAddressHint(isOptional: Boolean) { val spec = AddressSpecification.fromString(delegate.addressOutputData.country.value) From 16a21962f4210100e11d6665dbd1bc3305c33e71 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 4 Feb 2025 15:51:49 +0100 Subject: [PATCH 3/4] Don't reset all input when changing country This will retain all the filled in data, so that a shopper doesn't have to start over when changing the country. COAND-1064 --- .../checkout/ui/core/internal/ui/view/AddressFormInput.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt index 564414f471..a861537055 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt @@ -122,7 +122,8 @@ class AddressFormInput @JvmOverloads constructor( val selectedCountryCode = countryAdapter.getItem(position).code if (delegate.addressOutputData.country.value != selectedCountryCode) { delegate.updateAddressInputData { - reset() + // Only reset state/province, so filled in data is retained. + stateOrProvince = "" country = selectedCountryCode } populateFormFields(AddressSpecification.fromString(selectedCountryCode)) From 401792aebc7cfc7a1dca9e3e1afbc63a16485996 Mon Sep 17 00:00:00 2001 From: Oscar Spruit Date: Tue, 4 Feb 2025 15:52:57 +0100 Subject: [PATCH 4/4] Reset form input data after navigating back from address lookup This will make sure the data is not retained when navigating back without confirming. COAND-1064 --- .../card/internal/ui/DefaultCardDelegate.kt | 1 + .../card/internal/ui/DefaultCardDelegateTest.kt | 15 +++++++++++++++ .../ui/core/internal/ui/view/AddressFormInput.kt | 1 - 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt index da82dceb2b..61118b7997 100644 --- a/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt +++ b/card/src/main/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegate.kt @@ -507,6 +507,7 @@ class DefaultCardDelegate( override fun handleBackPress(): Boolean { return if (_viewFlow.value == CardComponentViewType.AddressLookup) { + addressDelegate.updateAddressInputData { reset() } _viewFlow.tryEmit(CardComponentViewType.DefaultCardView) true } else { diff --git a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt index 36a26ca18b..c12ac74a12 100644 --- a/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt +++ b/card/src/test/java/com/adyen/checkout/card/internal/ui/DefaultCardDelegateTest.kt @@ -64,6 +64,7 @@ import com.adyen.checkout.test.TestDispatcherExtension import com.adyen.checkout.test.extensions.test import com.adyen.checkout.ui.core.internal.data.api.AddressRepository import com.adyen.checkout.ui.core.internal.data.api.TestAddressRepository +import com.adyen.checkout.ui.core.internal.ui.AddressDelegate import com.adyen.checkout.ui.core.internal.ui.AddressFormUIState import com.adyen.checkout.ui.core.internal.ui.AddressLookupDelegate import com.adyen.checkout.ui.core.internal.ui.SubmitHandler @@ -1231,6 +1232,7 @@ internal class DefaultCardDelegateTest( @Test fun `when view type is AddressLookup and handleBackPress() is called DefaultCardView should be emitted`() = runTest { + whenever(addressLookupDelegate.addressDelegate) doReturn mock() delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) delegate.startAddressLookup() assertTrue(delegate.handleBackPress()) @@ -1240,6 +1242,19 @@ internal class DefaultCardDelegateTest( } } + @Test + fun `when view type is AddressLookup and handleBackPress() is called, then address form data should be reset`() = + runTest { + val addressDelegate = mock() + whenever(addressLookupDelegate.addressDelegate) doReturn addressDelegate + delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) + delegate.startAddressLookup() + + delegate.handleBackPress() + + verify(addressDelegate).updateAddressInputData(any()) + } + @Test fun `when view type is DefaultCardView and handleBackPress() is called it should return false`() = runTest { delegate.initialize(CoroutineScope(UnconfinedTestDispatcher())) diff --git a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt index a861537055..9502410455 100644 --- a/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt +++ b/ui-core/src/main/java/com/adyen/checkout/ui/core/internal/ui/view/AddressFormInput.kt @@ -338,7 +338,6 @@ class AddressFormInput @JvmOverloads constructor( private fun initHouseNumberInput(styleResId: Int?) { styleResId?.let { textInputLayoutHouseNumber?.setLocalizedHintFromStyle(it, localizedContext) } editTextHouseNumber?.apply { - setOnChangeListener { delegate.updateAddressInputData { houseNumberOrName = it.toString() } textInputLayoutHouseNumber?.hideError()