mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-19 09:25:34 +00:00
[fenix] fixes https://github.com/mozilla-mobile/fenix/issues/24918: add subregion dropdown to address editor
This commit is contained in:
parent
e175c84def
commit
d081128be7
@ -0,0 +1,158 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.settings.address
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import mozilla.components.concept.storage.Address
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
|
||||||
|
internal const val DEFAULT_COUNTRY = "US"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value type representing properties determined by the country used in an [Address].
|
||||||
|
* This data is meant to mirror the data currently represented on desktop here:
|
||||||
|
* https://searchfox.org/mozilla-central/source/toolkit/components/formautofill/addressmetadata/addressReferences.js
|
||||||
|
*
|
||||||
|
* This can be expanded to included things like a list of applicable states/provinces per country
|
||||||
|
* or the names that should be used for each form field.
|
||||||
|
*
|
||||||
|
* Note: Most properties here need to be kept in sync with the data in the above desktop
|
||||||
|
* address reference file in order to prevent duplications when sync is enabled. There are
|
||||||
|
* ongoing conversations about how best to share that data cross-platform, if at all.
|
||||||
|
* Some more detail: https://bugzilla.mozilla.org/show_bug.cgi?id=1769809
|
||||||
|
*
|
||||||
|
* Exceptions: [displayName] is a local property and stop-gap to a more robust solution.
|
||||||
|
*
|
||||||
|
* @property countryCode The country code used to lookup the address data. Should match desktop entries.
|
||||||
|
* @property displayName The name to display when selected.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
||||||
|
internal data class Country(
|
||||||
|
val countryCode: String,
|
||||||
|
val displayName: String,
|
||||||
|
@StringRes val subregionTitleResource: Int,
|
||||||
|
val subregions: List<String>,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal object AddressUtils {
|
||||||
|
/**
|
||||||
|
* The current list of supported countries.
|
||||||
|
*/
|
||||||
|
val countries = mapOf(
|
||||||
|
"CA" to Country(
|
||||||
|
countryCode = "CA",
|
||||||
|
displayName = "Canada",
|
||||||
|
subregionTitleResource = R.string.addresses_province,
|
||||||
|
subregions = Subregions.CA,
|
||||||
|
),
|
||||||
|
"US" to Country(
|
||||||
|
countryCode = "US",
|
||||||
|
displayName = "United States",
|
||||||
|
subregionTitleResource = R.string.addresses_state,
|
||||||
|
subregions = Subregions.US,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the country code associated with a [Country.displayName], or the [DEFAULT_COUNTRY] code
|
||||||
|
* if the display name is not supported.
|
||||||
|
*/
|
||||||
|
fun getCountryCode(displayName: String) = countries.values.find {
|
||||||
|
it.displayName == displayName
|
||||||
|
}?.countryCode ?: DEFAULT_COUNTRY
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a [Country.displayName] to the associated country code.
|
||||||
|
*/
|
||||||
|
fun String.toCountryCode() = AddressUtils.getCountryCode(this)
|
||||||
|
|
||||||
|
private object Subregions {
|
||||||
|
// This data is meant to mirror the data currently represented on desktop here:
|
||||||
|
// https://searchfox.org/mozilla-central/source/toolkit/components/formautofill/addressmetadata/addressReferences.js
|
||||||
|
val CA = listOf(
|
||||||
|
"Alberta",
|
||||||
|
"British Columbia",
|
||||||
|
"Manitoba",
|
||||||
|
"New Brunswick",
|
||||||
|
"Newfoundland and Labrador",
|
||||||
|
"Northwest Territories",
|
||||||
|
"Nova Scotia",
|
||||||
|
"Nunavut",
|
||||||
|
"Ontario",
|
||||||
|
"Prince Edward Island",
|
||||||
|
"Quebec",
|
||||||
|
"Saskatchewan",
|
||||||
|
"Yukon",
|
||||||
|
)
|
||||||
|
|
||||||
|
// This data is meant to mirror the data currently represented on desktop here:
|
||||||
|
// https://searchfox.org/mozilla-central/source/toolkit/components/formautofill/addressmetadata/addressReferences.js
|
||||||
|
val US = listOf(
|
||||||
|
"Alabama",
|
||||||
|
"Alaska",
|
||||||
|
"American Samoa",
|
||||||
|
"Arizona",
|
||||||
|
"Arkansas",
|
||||||
|
"Armed Forces (AA)",
|
||||||
|
"Armed Forces (AE)",
|
||||||
|
"Armed Forces (AP)",
|
||||||
|
"California",
|
||||||
|
"Colorado",
|
||||||
|
"Connecticut",
|
||||||
|
"Delaware",
|
||||||
|
"District of Columbia",
|
||||||
|
"Florida",
|
||||||
|
"Georgia",
|
||||||
|
"Guam",
|
||||||
|
"Hawaii",
|
||||||
|
"Idaho",
|
||||||
|
"Illinois",
|
||||||
|
"Indiana",
|
||||||
|
"Iowa",
|
||||||
|
"Kansas",
|
||||||
|
"Kentucky",
|
||||||
|
"Louisiana",
|
||||||
|
"Maine",
|
||||||
|
"Marshall Islands",
|
||||||
|
"Maryland",
|
||||||
|
"Massachusetts",
|
||||||
|
"Michigan",
|
||||||
|
"Micronesia",
|
||||||
|
"Minnesota",
|
||||||
|
"Mississippi",
|
||||||
|
"Missouri",
|
||||||
|
"Montana",
|
||||||
|
"Nebraska",
|
||||||
|
"Nevada",
|
||||||
|
"New Hampshire",
|
||||||
|
"New Jersey",
|
||||||
|
"New Mexico",
|
||||||
|
"New York",
|
||||||
|
"North Carolina",
|
||||||
|
"North Dakota",
|
||||||
|
"Northern Mariana Islands",
|
||||||
|
"Ohio",
|
||||||
|
"Oklahoma",
|
||||||
|
"Oregon",
|
||||||
|
"Palau",
|
||||||
|
"Pennsylvania",
|
||||||
|
"Puerto Rico",
|
||||||
|
"Rhode Island",
|
||||||
|
"South Carolina",
|
||||||
|
"South Dakota",
|
||||||
|
"Tennessee",
|
||||||
|
"Texas",
|
||||||
|
"Utah",
|
||||||
|
"Vermont",
|
||||||
|
"Virgin Islands",
|
||||||
|
"Virginia",
|
||||||
|
"Washington",
|
||||||
|
"West Virginia",
|
||||||
|
"Wisconsin",
|
||||||
|
"Wyoming",
|
||||||
|
)
|
||||||
|
}
|
@ -6,11 +6,12 @@ package org.mozilla.fenix.settings.address.view
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.AdapterView
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import mozilla.components.concept.storage.Address
|
import mozilla.components.concept.storage.Address
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import androidx.annotation.VisibleForTesting
|
|
||||||
import mozilla.components.browser.state.search.RegionState
|
import mozilla.components.browser.state.search.RegionState
|
||||||
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
|
||||||
@ -19,12 +20,19 @@ 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.AddressEditorFragment
|
import org.mozilla.fenix.settings.address.AddressEditorFragment
|
||||||
|
import org.mozilla.fenix.settings.address.AddressUtils.countries
|
||||||
|
import org.mozilla.fenix.settings.address.Country
|
||||||
|
import org.mozilla.fenix.settings.address.DEFAULT_COUNTRY
|
||||||
import org.mozilla.fenix.settings.address.interactor.AddressEditorInteractor
|
import org.mozilla.fenix.settings.address.interactor.AddressEditorInteractor
|
||||||
|
import org.mozilla.fenix.settings.address.toCountryCode
|
||||||
internal const val DEFAULT_COUNTRY = "US"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows an address editor for adding or updating an address.
|
* An address editor for adding or updating an address.
|
||||||
|
*
|
||||||
|
* @param binding The binding used to display the view.
|
||||||
|
* @param interactor [AddressEditorInteractor] used to respond to any user interactions.
|
||||||
|
* @param region If the [RegionState] is available, it will be used to set the country when adding a new address.
|
||||||
|
* @param address An [Address] to edit.
|
||||||
*/
|
*/
|
||||||
class AddressEditorView(
|
class AddressEditorView(
|
||||||
private val binding: FragmentAddressEditorBinding,
|
private val binding: FragmentAddressEditorBinding,
|
||||||
@ -33,36 +41,6 @@ class AddressEditorView(
|
|||||||
private val address: Address? = null
|
private val address: Address? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
|
||||||
* Value type representing properties determined by the country used in an [Address].
|
|
||||||
* This data is meant to mirror the data currently represented on desktop here:
|
|
||||||
* https://searchfox.org/mozilla-central/source/toolkit/components/formautofill/addressmetadata/addressReferences.js
|
|
||||||
*
|
|
||||||
* This can be expanded to included things like a list of applicable states/provinces per country
|
|
||||||
* or the names that should be used for each form field.
|
|
||||||
*
|
|
||||||
* Note: Most properties here need to be kept in sync with the data in the above desktop
|
|
||||||
* address reference file in order to prevent duplications when sync is enabled. There are
|
|
||||||
* ongoing conversations about how best to share that data cross-platform, if at all.
|
|
||||||
* Some more detail: https://bugzilla.mozilla.org/show_bug.cgi?id=1769809
|
|
||||||
*
|
|
||||||
* Exceptions: [displayName] is a local property and stop-gap to a more robust solution.
|
|
||||||
*
|
|
||||||
* @property key The country code used to lookup the address data. Should match desktop entries.
|
|
||||||
* @property displayName The name to display when selected.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
|
||||||
internal data class Country(
|
|
||||||
val key: String,
|
|
||||||
val displayName: String,
|
|
||||||
)
|
|
||||||
|
|
||||||
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
|
|
||||||
internal val countries = mapOf(
|
|
||||||
"CA" to Country("CA", "Canada"),
|
|
||||||
"US" to Country("US", "United States"),
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Binds the view in the [AddressEditorFragment], using the current [Address] if available.
|
* Binds the view in the [AddressEditorFragment], using the current [Address] if available.
|
||||||
*/
|
*/
|
||||||
@ -91,7 +69,6 @@ class AddressEditorView(
|
|||||||
|
|
||||||
binding.streetAddressInput.setText(address.streetAddress)
|
binding.streetAddressInput.setText(address.streetAddress)
|
||||||
binding.cityInput.setText(address.addressLevel2)
|
binding.cityInput.setText(address.addressLevel2)
|
||||||
binding.stateInput.setText(address.addressLevel1)
|
|
||||||
binding.zipInput.setText(address.postalCode)
|
binding.zipInput.setText(address.postalCode)
|
||||||
|
|
||||||
binding.deleteButton.apply {
|
binding.deleteButton.apply {
|
||||||
@ -102,7 +79,7 @@ class AddressEditorView(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bindCountryDropdown()
|
bindDropdowns()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun saveAddress() {
|
internal fun saveAddress() {
|
||||||
@ -116,7 +93,7 @@ class AddressEditorView(
|
|||||||
streetAddress = binding.streetAddressInput.text.toString(),
|
streetAddress = binding.streetAddressInput.text.toString(),
|
||||||
addressLevel3 = "",
|
addressLevel3 = "",
|
||||||
addressLevel2 = "",
|
addressLevel2 = "",
|
||||||
addressLevel1 = "",
|
addressLevel1 = binding.subregionDropDown.selectedItem.toString(),
|
||||||
postalCode = binding.zipInput.text.toString(),
|
postalCode = binding.zipInput.text.toString(),
|
||||||
country = binding.countryDropDown.selectedItem.toString().toCountryCode(),
|
country = binding.countryDropDown.selectedItem.toString().toCountryCode(),
|
||||||
tel = binding.phoneInput.text.toString(),
|
tel = binding.phoneInput.text.toString(),
|
||||||
@ -143,26 +120,59 @@ class AddressEditorView(
|
|||||||
}.show()
|
}.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindCountryDropdown() {
|
private fun bindDropdowns() {
|
||||||
val adapter = ArrayAdapter<String>(
|
val adapter = ArrayAdapter(
|
||||||
binding.root.context,
|
binding.root.context,
|
||||||
android.R.layout.simple_spinner_dropdown_item,
|
android.R.layout.simple_spinner_dropdown_item,
|
||||||
|
countries.values.map { it.displayName }
|
||||||
)
|
)
|
||||||
|
|
||||||
val selectedCountryKey = (address?.country ?: region?.home).takeIf {
|
val selectedCountryKey = (address?.country ?: region?.home).takeIf {
|
||||||
it in countries.keys
|
it in countries.keys
|
||||||
} ?: DEFAULT_COUNTRY
|
} ?: DEFAULT_COUNTRY
|
||||||
var selectedPosition = -1
|
|
||||||
countries.values.forEachIndexed { index, country ->
|
val selectedPosition = countries.values
|
||||||
if (country.key == selectedCountryKey) selectedPosition = index
|
.indexOfFirst { it.countryCode == selectedCountryKey }
|
||||||
adapter.add(country.displayName)
|
.takeIf { it > 0 }
|
||||||
}
|
?: 0
|
||||||
|
|
||||||
binding.countryDropDown.adapter = adapter
|
binding.countryDropDown.adapter = adapter
|
||||||
binding.countryDropDown.setSelection(selectedPosition)
|
binding.countryDropDown.setSelection(selectedPosition)
|
||||||
|
binding.countryDropDown.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(
|
||||||
|
parent: AdapterView<*>?,
|
||||||
|
view: View?,
|
||||||
|
position: Int,
|
||||||
|
id: Long
|
||||||
|
) {
|
||||||
|
val newCountryKey = binding.countryDropDown.selectedItem.toString().toCountryCode()
|
||||||
|
countries[newCountryKey]?.let { country ->
|
||||||
|
bindSubregionDropdown(country)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNothingSelected(p0: AdapterView<*>?) = Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
countries[selectedCountryKey]?.let { country ->
|
||||||
|
bindSubregionDropdown(country)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun String.toCountryCode() = countries.values.find {
|
private fun bindSubregionDropdown(country: Country) {
|
||||||
it.displayName == this
|
val subregions = country.subregions
|
||||||
}?.key ?: DEFAULT_COUNTRY
|
val selectedSubregion = address?.addressLevel1?.takeIf { it in subregions }
|
||||||
|
?: subregions.first()
|
||||||
|
|
||||||
|
val adapter = ArrayAdapter(
|
||||||
|
binding.root.context,
|
||||||
|
android.R.layout.simple_spinner_dropdown_item,
|
||||||
|
country.subregions
|
||||||
|
)
|
||||||
|
|
||||||
|
val selectedPosition = subregions.indexOf(selectedSubregion).takeIf { it > 0 } ?: 0
|
||||||
|
binding.subregionDropDown.adapter = adapter
|
||||||
|
binding.subregionDropDown.setSelection(selectedPosition)
|
||||||
|
binding.subregionTitle.setText(country.subregionTitleResource)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,50 +234,40 @@
|
|||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<!-- State -->
|
<!-- Subregion -->
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/state_title"
|
android:id="@+id/subregion_layout"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:letterSpacing="0.05"
|
|
||||||
android:paddingStart="3dp"
|
android:paddingStart="3dp"
|
||||||
android:paddingEnd="0dp"
|
android:paddingEnd="3dp"
|
||||||
android:text="@string/addresses_state"
|
android:orientation="vertical"
|
||||||
android:textColor="?attr/textPrimary"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:labelFor="@id/state_input"
|
|
||||||
app:fontFamily="@font/metropolis_semibold"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/city_layout" />
|
app:layout_constraintTop_toBottomOf="@id/city_layout"
|
||||||
|
app:layout_constraintBottom_toTopOf="@id/zip_title">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<TextView
|
||||||
android:id="@+id/state_layout"
|
android:id="@+id/subregion_title"
|
||||||
android:layout_width="0dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textColor="?attr/textPrimary"
|
|
||||||
app:hintEnabled="false"
|
|
||||||
app:layout_constraintHorizontal_bias="0.8"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/zip_layout"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/state_title">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/state_input"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:ellipsize="end"
|
android:layout_marginTop="10dp"
|
||||||
android:fontFamily="sans-serif"
|
|
||||||
android:imeOptions="flagNoExtractUi"
|
|
||||||
android:letterSpacing="0.01"
|
|
||||||
android:lineSpacingExtra="8sp"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:singleLine="true"
|
|
||||||
android:textColor="?attr/textPrimary"
|
android:textColor="?attr/textPrimary"
|
||||||
android:textSize="16sp" />
|
android:textSize="12sp"
|
||||||
|
android:labelFor="@id/subregion_drop_down"
|
||||||
|
app:fontFamily="@font/metropolis_semibold"
|
||||||
|
android:text="@string/addresses_state" />
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
<androidx.appcompat.widget.AppCompatSpinner
|
||||||
|
android:id="@+id/subregion_drop_down"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="?attr/textPrimary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Zip -->
|
<!-- Zip -->
|
||||||
<TextView
|
<TextView
|
||||||
@ -295,17 +285,16 @@
|
|||||||
android:labelFor="@id/zip_input"
|
android:labelFor="@id/zip_input"
|
||||||
app:fontFamily="@font/metropolis_semibold"
|
app:fontFamily="@font/metropolis_semibold"
|
||||||
app:layout_constraintStart_toStartOf="@+id/zip_layout"
|
app:layout_constraintStart_toStartOf="@+id/zip_layout"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/city_layout" />
|
app:layout_constraintTop_toBottomOf="@+id/subregion_layout" />
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/zip_layout"
|
android:id="@+id/zip_layout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:textColor="?attr/textPrimary"
|
android:textColor="?attr/textPrimary"
|
||||||
app:hintEnabled="false"
|
app:hintEnabled="false"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toEndOf="@+id/state_layout"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/zip_title">
|
app:layout_constraintTop_toBottomOf="@+id/zip_title">
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
@ -331,7 +320,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
android:paddingStart="3dp"
|
android:paddingStart="3dp"
|
||||||
android:paddingEnd="0dp"
|
android:paddingEnd="3dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
@ -341,6 +330,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
android:textColor="?attr/textPrimary"
|
android:textColor="?attr/textPrimary"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
android:labelFor="@id/country_drop_down"
|
android:labelFor="@id/country_drop_down"
|
||||||
@ -487,7 +477,7 @@
|
|||||||
android:paddingEnd="12dp"
|
android:paddingEnd="12dp"
|
||||||
android:text="@string/addresses_save_button"
|
android:text="@string/addresses_save_button"
|
||||||
app:layout_constraintTop_toTopOf="@+id/cancel_button"
|
app:layout_constraintTop_toTopOf="@+id/cancel_button"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/cancel_button"
|
app:layout_constraintBottom_toBottomOf="@id/cancel_button"
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -1567,8 +1567,10 @@
|
|||||||
<string name="addresses_street_address">Street Address</string>
|
<string name="addresses_street_address">Street Address</string>
|
||||||
<!-- The header for the city of an address -->
|
<!-- The header for the city of an address -->
|
||||||
<string name="addresses_city">City</string>
|
<string name="addresses_city">City</string>
|
||||||
<!-- The header for the state of an address -->
|
<!-- The header for the subregion of an address when "state" should be used -->
|
||||||
<string name="addresses_state">State</string>
|
<string name="addresses_state">State</string>
|
||||||
|
<!-- The header for the subregion of an address when "province" should be used -->
|
||||||
|
<string name="addresses_province">Province</string>
|
||||||
<!-- The header for the zip code of an address -->
|
<!-- The header for the zip code of an address -->
|
||||||
<string name="addresses_zip">Zip</string>
|
<string name="addresses_zip">Zip</string>
|
||||||
<!-- The header for the country or region of an address -->
|
<!-- The header for the country or region of an address -->
|
||||||
|
@ -13,8 +13,10 @@ import io.mockk.verify
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import mozilla.components.browser.state.search.RegionState
|
import mozilla.components.browser.state.search.RegionState
|
||||||
import mozilla.components.concept.storage.Address
|
import mozilla.components.concept.storage.Address
|
||||||
|
import mozilla.components.concept.storage.UpdatableAddressFields
|
||||||
import mozilla.components.support.test.robolectric.testContext
|
import mozilla.components.support.test.robolectric.testContext
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNotEquals
|
||||||
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
|
||||||
@ -23,7 +25,6 @@ import org.mozilla.fenix.databinding.FragmentAddressEditorBinding
|
|||||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||||
import org.mozilla.fenix.settings.address.interactor.AddressEditorInteractor
|
import org.mozilla.fenix.settings.address.interactor.AddressEditorInteractor
|
||||||
import org.mozilla.fenix.settings.address.view.AddressEditorView
|
import org.mozilla.fenix.settings.address.view.AddressEditorView
|
||||||
import org.mozilla.fenix.settings.address.view.DEFAULT_COUNTRY
|
|
||||||
|
|
||||||
@RunWith(FenixRobolectricTestRunner::class)
|
@RunWith(FenixRobolectricTestRunner::class)
|
||||||
class AddressEditorViewTest {
|
class AddressEditorViewTest {
|
||||||
@ -45,6 +46,63 @@ class AddressEditorViewTest {
|
|||||||
addressEditorView = spyk(AddressEditorView(binding, interactor))
|
addressEditorView = spyk(AddressEditorView(binding, interactor))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN an existing address WHEN the save button is clicked THEN interactor updates address`() {
|
||||||
|
val country = AddressUtils.countries["US"]!!
|
||||||
|
val address = generateAddress(country = country.countryCode, addressLevel1 = country.subregions[0])
|
||||||
|
val addressEditorView = AddressEditorView(
|
||||||
|
binding = binding,
|
||||||
|
interactor = interactor,
|
||||||
|
address = address,
|
||||||
|
)
|
||||||
|
|
||||||
|
addressEditorView.bind()
|
||||||
|
addressEditorView.saveAddress()
|
||||||
|
|
||||||
|
val expected = UpdatableAddressFields(
|
||||||
|
givenName = address.givenName,
|
||||||
|
additionalName = address.additionalName,
|
||||||
|
familyName = address.familyName,
|
||||||
|
organization = "",
|
||||||
|
streetAddress = address.streetAddress,
|
||||||
|
addressLevel3 = "",
|
||||||
|
addressLevel2 = "",
|
||||||
|
addressLevel1 = address.addressLevel1,
|
||||||
|
postalCode = address.postalCode,
|
||||||
|
country = address.country,
|
||||||
|
tel = address.tel,
|
||||||
|
email = address.email,
|
||||||
|
)
|
||||||
|
verify { interactor.onUpdateAddress(address.guid, expected) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN a new address WHEN the save button is clicked THEN interactor saves new address`() {
|
||||||
|
val addressEditorView = AddressEditorView(
|
||||||
|
binding = binding,
|
||||||
|
interactor = interactor,
|
||||||
|
)
|
||||||
|
|
||||||
|
addressEditorView.bind()
|
||||||
|
addressEditorView.saveAddress()
|
||||||
|
|
||||||
|
val expected = UpdatableAddressFields(
|
||||||
|
givenName = "",
|
||||||
|
additionalName = "",
|
||||||
|
familyName = "",
|
||||||
|
organization = "",
|
||||||
|
streetAddress = "",
|
||||||
|
addressLevel3 = "",
|
||||||
|
addressLevel2 = "",
|
||||||
|
addressLevel1 = "Alabama",
|
||||||
|
postalCode = "",
|
||||||
|
country = "US",
|
||||||
|
tel = "",
|
||||||
|
email = "",
|
||||||
|
)
|
||||||
|
verify { interactor.onSaveAddress(expected) }
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `WHEN the cancel button is clicked THEN interactor is called`() {
|
fun `WHEN the cancel button is clicked THEN interactor is called`() {
|
||||||
addressEditorView.bind()
|
addressEditorView.bind()
|
||||||
@ -68,7 +126,7 @@ class AddressEditorViewTest {
|
|||||||
addressEditorView.bind()
|
addressEditorView.bind()
|
||||||
|
|
||||||
assertEquals("PostalCode", binding.zipInput.text.toString())
|
assertEquals("PostalCode", binding.zipInput.text.toString())
|
||||||
assertEquals("State", binding.stateInput.text.toString())
|
assertEquals(address.addressLevel1, binding.subregionDropDown.selectedItem.toString())
|
||||||
assertEquals("City", binding.cityInput.text.toString())
|
assertEquals("City", binding.cityInput.text.toString())
|
||||||
assertEquals("Street", binding.streetAddressInput.text.toString())
|
assertEquals("Street", binding.streetAddressInput.text.toString())
|
||||||
assertEquals("Family", binding.lastNameInput.text.toString())
|
assertEquals("Family", binding.lastNameInput.text.toString())
|
||||||
@ -108,6 +166,72 @@ class AddressEditorViewTest {
|
|||||||
verify { addressEditorView.showConfirmDeleteAddressDialog(view.context, "123") }
|
verify { addressEditorView.showConfirmDeleteAddressDialog(view.context, "123") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN existing address with correct subregion and country WHEN subregion dropdown is bound THEN adapter sets subregion dropdown to address`() {
|
||||||
|
val address = generateAddress(country = "US", addressLevel1 = "Oregon")
|
||||||
|
|
||||||
|
val addressEditorView = AddressEditorView(
|
||||||
|
binding = binding,
|
||||||
|
interactor = interactor,
|
||||||
|
address = address,
|
||||||
|
)
|
||||||
|
addressEditorView.bind()
|
||||||
|
|
||||||
|
assertEquals("Oregon", binding.subregionDropDown.selectedItem.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN existing address subregion outside of country WHEN subregion dropdown is bound THEN dropdown defaults to first subregion entry for country`() {
|
||||||
|
val address = generateAddress(country = "CA", addressLevel1 = "Alabama")
|
||||||
|
|
||||||
|
val addressEditorView = AddressEditorView(
|
||||||
|
binding = binding,
|
||||||
|
interactor = interactor,
|
||||||
|
address = address,
|
||||||
|
)
|
||||||
|
addressEditorView.bind()
|
||||||
|
|
||||||
|
assertEquals("Alberta", binding.subregionDropDown.selectedItem.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN no existing address WHEN subregion dropdown is bound THEN dropdown defaults to first subregion of default country`() {
|
||||||
|
val addressEditorView = AddressEditorView(
|
||||||
|
binding = binding,
|
||||||
|
interactor = interactor,
|
||||||
|
)
|
||||||
|
addressEditorView.bind()
|
||||||
|
|
||||||
|
assertEquals("Alabama", binding.subregionDropDown.selectedItem.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN country is changed THEN available subregions are updated`() {
|
||||||
|
val addressEditorView = AddressEditorView(
|
||||||
|
binding = binding,
|
||||||
|
interactor = interactor,
|
||||||
|
)
|
||||||
|
addressEditorView.bind()
|
||||||
|
|
||||||
|
assertEquals("Alabama", binding.subregionDropDown.selectedItem.toString())
|
||||||
|
binding.countryDropDown.setSelection(0)
|
||||||
|
assertNotEquals("Alabama", binding.subregionDropDown.selectedItem.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN existing address not in available countries WHEN view is bound THEN country and subregion dropdowns are set to default `() {
|
||||||
|
val address = generateAddress(country = "I AM NOT A COUNTRY", addressLevel1 = "I AM NOT A STATE")
|
||||||
|
val addressEditorView = AddressEditorView(
|
||||||
|
binding = binding,
|
||||||
|
interactor = interactor,
|
||||||
|
address = address,
|
||||||
|
)
|
||||||
|
addressEditorView.bind()
|
||||||
|
|
||||||
|
assertEquals("United States", binding.countryDropDown.selectedItem.toString())
|
||||||
|
assertEquals("Alabama", binding.subregionDropDown.selectedItem.toString())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `GIVEN existing address WHEN country dropdown is bound THEN adapter sets country dropdown to address`() {
|
fun `GIVEN existing address WHEN country dropdown is bound THEN adapter sets country dropdown to address`() {
|
||||||
val addressEditorView = spyk(
|
val addressEditorView = spyk(
|
||||||
@ -119,7 +243,7 @@ class AddressEditorViewTest {
|
|||||||
)
|
)
|
||||||
addressEditorView.bind()
|
addressEditorView.bind()
|
||||||
|
|
||||||
assertEquals(addressEditorView.countries["CA"]?.displayName, binding.countryDropDown.selectedItem.toString())
|
assertEquals(AddressUtils.countries["CA"]?.displayName, binding.countryDropDown.selectedItem.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -134,7 +258,7 @@ class AddressEditorViewTest {
|
|||||||
)
|
)
|
||||||
addressEditorView.bind()
|
addressEditorView.bind()
|
||||||
|
|
||||||
assertEquals(addressEditorView.countries[DEFAULT_COUNTRY]!!.displayName, binding.countryDropDown.selectedItem.toString())
|
assertEquals(AddressUtils.countries[DEFAULT_COUNTRY]!!.displayName, binding.countryDropDown.selectedItem.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -147,7 +271,7 @@ class AddressEditorViewTest {
|
|||||||
)
|
)
|
||||||
addressEditorView.bind()
|
addressEditorView.bind()
|
||||||
|
|
||||||
assertEquals(addressEditorView.countries["CA"]?.displayName, binding.countryDropDown.selectedItem.toString())
|
assertEquals(AddressUtils.countries["CA"]?.displayName, binding.countryDropDown.selectedItem.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -160,7 +284,7 @@ class AddressEditorViewTest {
|
|||||||
)
|
)
|
||||||
addressEditorView.bind()
|
addressEditorView.bind()
|
||||||
|
|
||||||
assertEquals(addressEditorView.countries[DEFAULT_COUNTRY]!!.displayName, binding.countryDropDown.selectedItem.toString())
|
assertEquals(AddressUtils.countries[DEFAULT_COUNTRY]!!.displayName, binding.countryDropDown.selectedItem.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -173,10 +297,10 @@ class AddressEditorViewTest {
|
|||||||
)
|
)
|
||||||
addressEditorView.bind()
|
addressEditorView.bind()
|
||||||
|
|
||||||
assertEquals(addressEditorView.countries[DEFAULT_COUNTRY]!!.displayName, binding.countryDropDown.selectedItem.toString())
|
assertEquals(AddressUtils.countries[DEFAULT_COUNTRY]!!.displayName, binding.countryDropDown.selectedItem.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun generateAddress(country: String = "US") = Address(
|
private fun generateAddress(country: String = "US", addressLevel1: String = "Oregon") = Address(
|
||||||
guid = "123",
|
guid = "123",
|
||||||
givenName = "Given",
|
givenName = "Given",
|
||||||
additionalName = "Additional",
|
additionalName = "Additional",
|
||||||
@ -185,7 +309,7 @@ class AddressEditorViewTest {
|
|||||||
streetAddress = "Street",
|
streetAddress = "Street",
|
||||||
addressLevel3 = "Suburb",
|
addressLevel3 = "Suburb",
|
||||||
addressLevel2 = "City",
|
addressLevel2 = "City",
|
||||||
addressLevel1 = "State",
|
addressLevel1 = addressLevel1,
|
||||||
postalCode = "PostalCode",
|
postalCode = "PostalCode",
|
||||||
country = country,
|
country = country,
|
||||||
tel = "Telephone",
|
tel = "Telephone",
|
||||||
|
Loading…
Reference in New Issue
Block a user