[fenix] For https://github.com/mozilla-mobile/fenix/issues/24855: Allow updating info for an existing address.

pull/600/head
mcarare 2 years ago committed by mergify[bot]
parent 81e4534da1
commit 02fb70abf9

@ -8,6 +8,7 @@ import android.os.Bundle
import android.view.View import android.view.View
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.SecureFragment import org.mozilla.fenix.SecureFragment
@ -27,6 +28,14 @@ class AddressEditorFragment : SecureFragment(R.layout.fragment_address_editor) {
private lateinit var addressEditorView: AddressEditorView private lateinit var addressEditorView: AddressEditorView
private lateinit var interactor: AddressEditorInteractor private lateinit var interactor: AddressEditorInteractor
private val args by navArgs<AddressEditorFragmentArgs>()
/**
* Returns true if an existing address is being edited, and false otherwise.
*/
private val isEditing: Boolean
get() = args.address != null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -42,13 +51,17 @@ class AddressEditorFragment : SecureFragment(R.layout.fragment_address_editor) {
val binding = FragmentAddressEditorBinding.bind(view) val binding = FragmentAddressEditorBinding.bind(view)
addressEditorView = AddressEditorView(binding, interactor) addressEditorView = AddressEditorView(binding, interactor, args.address)
addressEditorView.bind() addressEditorView.bind()
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
showToolbar(getString(R.string.addresses_add_address)) if (isEditing) {
showToolbar(getString(R.string.addresses_edit_address))
} else {
showToolbar(getString(R.string.addresses_add_address))
}
} }
override fun onStop() { override fun onStop() {

@ -28,6 +28,11 @@ interface AddressEditorController {
* @see [AddressEditorInteractor.onSaveAddress] * @see [AddressEditorInteractor.onSaveAddress]
*/ */
fun handleSaveAddress(addressFields: UpdatableAddressFields) fun handleSaveAddress(addressFields: UpdatableAddressFields)
/**
* @see [AddressEditorInteractor.onUpdateAddress]
*/
fun handleUpdateAddress(guid: String, addressFields: UpdatableAddressFields)
} }
/** /**
@ -57,4 +62,14 @@ class DefaultAddressEditorController(
} }
} }
} }
override fun handleUpdateAddress(guid: String, addressFields: UpdatableAddressFields) {
lifecycleScope.launch {
storage.updateAddress(guid, addressFields)
lifecycleScope.launch(Dispatchers.Main) {
navController.popBackStack()
}
}
}
} }

@ -37,17 +37,19 @@ class DefaultAddressManagementController(
) : AddressManagementController { ) : AddressManagementController {
override fun handleAddressClicked(address: Address) { override fun handleAddressClicked(address: Address) {
navigateToAddressEditor() navigateToAddressEditor(address)
} }
override fun handleAddAddressButtonClicked() { override fun handleAddAddressButtonClicked() {
navigateToAddressEditor() navigateToAddressEditor()
} }
private fun navigateToAddressEditor() { private fun navigateToAddressEditor(address: Address? = null) {
navController.navigate( navController.navigate(
AddressManagementFragmentDirections AddressManagementFragmentDirections
.actionAddressManagementFragmentToAddressEditorFragment() .actionAddressManagementFragmentToAddressEditorFragment(
address = address
)
) )
} }
} }

@ -25,6 +25,14 @@ interface AddressEditorInteractor {
* @param addressFields A [UpdatableAddressFields] record to add. * @param addressFields A [UpdatableAddressFields] record to add.
*/ */
fun onSaveAddress(addressFields: UpdatableAddressFields) fun onSaveAddress(addressFields: UpdatableAddressFields)
/**
* Updates the provided address in the autofill storage. Called when a user
* taps on the update menu item or "Update" button.
*
* @param addressFields A [UpdatableAddressFields] record to add.
*/
fun onUpdateAddress(guid: String, addressFields: UpdatableAddressFields)
} }
/** /**
@ -44,4 +52,8 @@ class DefaultAddressEditorInteractor(
override fun onSaveAddress(addressFields: UpdatableAddressFields) { override fun onSaveAddress(addressFields: UpdatableAddressFields) {
controller.handleSaveAddress(addressFields) controller.handleSaveAddress(addressFields)
} }
override fun onUpdateAddress(guid: String, addressFields: UpdatableAddressFields) {
controller.handleUpdateAddress(guid, addressFields)
}
} }

@ -4,9 +4,15 @@
package org.mozilla.fenix.settings.address.view package org.mozilla.fenix.settings.address.view
import android.content.Context
import android.content.DialogInterface
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import mozilla.components.concept.storage.Address
import mozilla.components.concept.storage.UpdatableAddressFields import mozilla.components.concept.storage.UpdatableAddressFields
import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.components.support.ktx.android.view.hideKeyboard
import mozilla.components.support.ktx.android.view.showKeyboard import mozilla.components.support.ktx.android.view.showKeyboard
import org.mozilla.fenix.R
import org.mozilla.fenix.databinding.FragmentAddressEditorBinding import org.mozilla.fenix.databinding.FragmentAddressEditorBinding
import org.mozilla.fenix.ext.placeCursorAtEnd import org.mozilla.fenix.ext.placeCursorAtEnd
import org.mozilla.fenix.settings.address.interactor.AddressEditorInteractor import org.mozilla.fenix.settings.address.interactor.AddressEditorInteractor
@ -16,7 +22,8 @@ import org.mozilla.fenix.settings.address.interactor.AddressEditorInteractor
*/ */
class AddressEditorView( class AddressEditorView(
private val binding: FragmentAddressEditorBinding, private val binding: FragmentAddressEditorBinding,
private val interactor: AddressEditorInteractor private val interactor: AddressEditorInteractor,
private val address: Address? = null
) { ) {
/** /**
@ -36,26 +43,44 @@ class AddressEditorView(
binding.saveButton.setOnClickListener { binding.saveButton.setOnClickListener {
saveAddress() saveAddress()
} }
address?.let { address ->
binding.emailInput.setText(address.email)
binding.phoneInput.setText(address.tel)
binding.firstNameInput.setText(address.givenName)
binding.middleNameInput.setText(address.additionalName)
binding.lastNameInput.setText(address.familyName)
binding.streetAddressInput.setText(address.streetAddress)
binding.cityInput.setText(address.addressLevel2)
binding.stateInput.setText(address.addressLevel1)
binding.zipInput.setText(address.postalCode)
}
} }
internal fun saveAddress() { internal fun saveAddress() {
binding.root.hideKeyboard() binding.root.hideKeyboard()
interactor.onSaveAddress( val addressFields = UpdatableAddressFields(
UpdatableAddressFields( givenName = binding.firstNameInput.text.toString(),
givenName = binding.firstNameInput.text.toString(), additionalName = binding.middleNameInput.text.toString(),
additionalName = binding.middleNameInput.text.toString(), familyName = binding.lastNameInput.text.toString(),
familyName = binding.lastNameInput.text.toString(), organization = "",
organization = "", streetAddress = binding.streetAddressInput.text.toString(),
streetAddress = binding.streetAddressInput.text.toString(), addressLevel3 = "",
addressLevel3 = "", addressLevel2 = "",
addressLevel2 = "", addressLevel1 = "",
addressLevel1 = "", postalCode = binding.zipInput.text.toString(),
postalCode = binding.zipInput.text.toString(), country = "",
country = "", tel = binding.phoneInput.text.toString(),
tel = binding.phoneInput.text.toString(), email = binding.emailInput.text.toString()
email = binding.emailInput.text.toString()
)
) )
if (address != null) {
interactor.onUpdateAddress(address.guid, addressFields)
} else {
interactor.onSaveAddress(addressFields)
}
} }
} }

@ -1258,7 +1258,13 @@
<fragment <fragment
android:id="@+id/addressEditorFragment" android:id="@+id/addressEditorFragment"
android:name="org.mozilla.fenix.settings.address.AddressEditorFragment" android:name="org.mozilla.fenix.settings.address.AddressEditorFragment"
android:label="@string/addresses_add_address" /> android:label="@string/addresses_add_address">
<argument
android:name="address"
android:defaultValue="@null"
app:argType="mozilla.components.concept.storage.Address"
app:nullable="true" />
</fragment>
<fragment <fragment
android:id="@+id/addressManagementFragment" android:id="@+id/addressManagementFragment"
android:name="org.mozilla.fenix.settings.address.AddressManagementFragment" android:name="org.mozilla.fenix.settings.address.AddressManagementFragment"

@ -1546,6 +1546,8 @@
<string name="credit_cards_biometric_prompt_unlock_message">Unlock to use stored credit card information</string> <string name="credit_cards_biometric_prompt_unlock_message">Unlock to use stored credit card information</string>
<!-- Title of the "Add address" screen --> <!-- Title of the "Add address" screen -->
<string name="addresses_add_address">Add address</string> <string name="addresses_add_address">Add address</string>
<!-- Title of the "Edit address" screen -->
<string name="addresses_edit_address">Edit address</string>
<!-- Title of the "Manage addresses" screen --> <!-- Title of the "Manage addresses" screen -->
<string name="addresses_manage_addresses">Manage addresses</string> <string name="addresses_manage_addresses">Manage addresses</string>
<!-- The header for the full name of an address --> <!-- The header for the full name of an address -->

@ -6,10 +6,13 @@ package org.mozilla.fenix.settings.address
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.spyk import io.mockk.spyk
import io.mockk.verify import io.mockk.verify
import mozilla.components.concept.storage.Address
import mozilla.components.support.test.robolectric.testContext import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -26,12 +29,15 @@ class AddressEditorViewTest {
private lateinit var interactor: AddressEditorInteractor private lateinit var interactor: AddressEditorInteractor
private lateinit var addressEditorView: AddressEditorView private lateinit var addressEditorView: AddressEditorView
private lateinit var binding: FragmentAddressEditorBinding private lateinit var binding: FragmentAddressEditorBinding
private lateinit var address: Address
@Before @Before
fun setup() { fun setup() {
view = LayoutInflater.from(testContext).inflate(R.layout.fragment_address_editor, null) view = LayoutInflater.from(testContext).inflate(R.layout.fragment_address_editor, null)
binding = FragmentAddressEditorBinding.bind(view) binding = FragmentAddressEditorBinding.bind(view)
interactor = mockk(relaxed = true) interactor = mockk(relaxed = true)
address = mockk(relaxed = true)
every { address.guid } returns "123"
addressEditorView = spyk(AddressEditorView(binding, interactor)) addressEditorView = spyk(AddressEditorView(binding, interactor))
} }
@ -44,4 +50,40 @@ class AddressEditorViewTest {
verify { interactor.onCancelButtonClicked() } verify { interactor.onCancelButtonClicked() }
} }
@Test
fun `GIVEN an existing address WHEN editor is opened THEN the form fields are correctly mapped to the address fields`() {
val address = Address(
guid = "123",
givenName = "Given",
additionalName = "Additional",
familyName = "Family",
organization = "Organization",
streetAddress = "Street",
addressLevel3 = "Suburb",
addressLevel2 = "City",
addressLevel1 = "State",
postalCode = "PostalCode",
country = "Country",
tel = "Telephone",
email = "email@mozilla.com",
timeCreated = 0L,
timeLastUsed = 1L,
timeLastModified = 1L,
timesUsed = 2L
)
val addressEditorView = spyk(AddressEditorView(binding, interactor, address))
addressEditorView.bind()
assertEquals("PostalCode", binding.zipInput.text.toString())
assertEquals("State", binding.stateInput.text.toString())
assertEquals("City", binding.cityInput.text.toString())
assertEquals("Street", binding.streetAddressInput.text.toString())
assertEquals("Family", binding.lastNameInput.text.toString())
assertEquals("Given", binding.firstNameInput.text.toString())
assertEquals("Additional", binding.middleNameInput.text.toString())
assertEquals("email@mozilla.com", binding.emailInput.text.toString())
assertEquals("Telephone", binding.phoneInput.text.toString())
}
} }

@ -6,11 +6,14 @@ package org.mozilla.fenix.settings.address.controller
import androidx.navigation.NavController import androidx.navigation.NavController
import io.mockk.coVerify import io.mockk.coVerify
import io.mockk.coVerifySequence
import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.spyk import io.mockk.spyk
import io.mockk.verify import io.mockk.verify
import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest import kotlinx.coroutines.test.runBlockingTest
import mozilla.components.concept.storage.Address
import mozilla.components.concept.storage.UpdatableAddressFields import mozilla.components.concept.storage.UpdatableAddressFields
import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage
import mozilla.components.support.test.rule.MainCoroutineRule import mozilla.components.support.test.rule.MainCoroutineRule
@ -73,4 +76,18 @@ class DefaultAddressEditorControllerTest {
navController.popBackStack() navController.popBackStack()
} }
} }
@Test
fun `GIVEN an existing address record WHEN save address is called THEN update the address record to storage`() = runBlockingTest {
val address: Address = mockk()
val addressFields: UpdatableAddressFields = mockk()
every { address.guid } returns "123"
controller.handleUpdateAddress(address.guid, addressFields)
coVerifySequence {
storage.updateAddress("123", addressFields)
navController.popBackStack()
}
}
} }

@ -37,7 +37,9 @@ class DefaultAddressManagementControllerTest {
verify { verify {
navController.navigate( navController.navigate(
AddressManagementFragmentDirections AddressManagementFragmentDirections
.actionAddressManagementFragmentToAddressEditorFragment() .actionAddressManagementFragmentToAddressEditorFragment(
address = address
)
) )
} }
} }

Loading…
Cancel
Save