For #18265 - [Add card] Integrate the credit card storage and handle adding a new credit card (#18719)

upstream-sync
Gabriel Luong 3 years ago committed by GitHub
parent 6e5b4b3ce6
commit 1008ca2b4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,13 +5,22 @@
package org.mozilla.fenix.settings.creditcards package org.mozilla.fenix.settings.creditcards
import android.os.Bundle import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import kotlinx.android.synthetic.main.fragment_credit_card_editor.view.* import kotlinx.android.synthetic.main.fragment_credit_card_editor.*
import mozilla.components.concept.storage.UpdatableCreditCardFields
import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.settings.creditcards.controller.CreditCardEditorController
import org.mozilla.fenix.settings.creditcards.controller.DefaultCreditCardEditorController
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Locale import java.util.Locale
@ -21,23 +30,49 @@ import java.util.Locale
*/ */
class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor) { class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor) {
private lateinit var controller: CreditCardEditorController
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
showToolbar(getString(R.string.credit_cards_add_card)) showToolbar(getString(R.string.credit_cards_add_card))
setupButtonClickListeners(view) setHasOptionsMenu(true)
setupButtonClickListeners()
setupExpiryMonthDropDown(view) setupExpiryMonthDropDown(view)
setupExpiryYearDropDown(view) setupExpiryYearDropDown(view)
controller = DefaultCreditCardEditorController(
storage = requireContext().components.core.autofillStorage,
lifecycleScope = lifecycleScope,
navController = findNavController()
)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.credit_card_editor, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
R.id.save_credit_card_button -> {
saveCreditCard()
true
}
else -> false
} }
/** /**
* Setup the all button click listeners in the credit card editor. * Setup the all button click listeners in the credit card editor.
*/ */
private fun setupButtonClickListeners(view: View) { private fun setupButtonClickListeners() {
view.cancel_button.setOnClickListener { cancel_button.setOnClickListener {
findNavController().popBackStack() findNavController().popBackStack()
} }
save_button.setOnClickListener {
saveCreditCard()
}
} }
/** /**
@ -57,7 +92,7 @@ class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor)
adapter.add(dateFormat.format(calendar.time)) adapter.add(dateFormat.format(calendar.time))
} }
view.expiry_month_drop_down.adapter = adapter expiry_month_drop_down.adapter = adapter
} }
/** /**
@ -74,7 +109,25 @@ class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor)
adapter.add(year.toString()) adapter.add(year.toString())
} }
view.expiry_year_drop_down.adapter = adapter expiry_year_drop_down.adapter = adapter
}
/**
* Helper function called by the the "Save" button and menu item to save a new credit card
* from the entered credit card fields.
*/
private fun saveCreditCard() {
view?.hideKeyboard()
controller.handleSaveCreditCard(
UpdatableCreditCardFields(
billingName = name_on_card_input.text.toString(),
cardNumber = card_number_input.text.toString(),
expiryMonth = (expiry_month_drop_down.selectedItemPosition + 1).toLong(),
expiryYear = expiry_year_drop_down.selectedItem.toString().toLong(),
cardType = "amex"
)
)
} }
companion object { companion object {

@ -0,0 +1,55 @@
/* 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.creditcards.controller
import androidx.navigation.NavController
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.concept.storage.UpdatableCreditCardFields
import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage
/**
* [CreditCardEditorFragment] controller. An interface that handles the view manipulation of the
* credit card editor.
*/
interface CreditCardEditorController {
/**
* Saves the provided credit card field into the credit card storage. Called when a user
* taps on the save menu item or "Save" button.
*
* @param creditCardFields A [UpdatableCreditCardFields] record to add.
*/
fun handleSaveCreditCard(creditCardFields: UpdatableCreditCardFields)
}
/**
* The default implementation of [CreditCardEditorController].
*
* @param storage An instance of the [AutofillCreditCardsAddressesStorage] for adding and retrieving
* credit cards.
* @param lifecycleScope [CoroutineScope] scope to launch coroutines.
* @param navController [NavController] used for navigation.
* @param ioDispatcher [CoroutineDispatcher] used for executing async tasks. Defaults to [Dispatchers.IO].
*/
class DefaultCreditCardEditorController(
private val storage: AutofillCreditCardsAddressesStorage,
private val lifecycleScope: CoroutineScope,
private val navController: NavController,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : CreditCardEditorController {
override fun handleSaveCreditCard(creditCardFields: UpdatableCreditCardFields) {
lifecycleScope.launch(ioDispatcher) {
storage.addCreditCard(creditCardFields)
lifecycleScope.launch(Dispatchers.Main) {
navController.popBackStack()
}
}
}
}

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/save_credit_card_button"
android:icon="@drawable/mozac_ic_check"
android:title="@string/save"
app:iconTint="?primaryText"
app:showAsAction="ifRoom" />
</menu>

@ -0,0 +1,73 @@
/* 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.creditcards
import androidx.navigation.NavController
import io.mockk.coVerify
import io.mockk.mockk
import io.mockk.spyk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest
import mozilla.components.concept.storage.UpdatableCreditCardFields
import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage
import mozilla.components.support.test.rule.MainCoroutineRule
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mozilla.fenix.settings.creditcards.controller.DefaultCreditCardEditorController
@ExperimentalCoroutinesApi
class DefaultCreditCardEditorControllerTest {
private val storage: AutofillCreditCardsAddressesStorage = mockk(relaxed = true)
private val navController: NavController = mockk(relaxed = true)
private val testCoroutineScope = TestCoroutineScope()
private val testDispatcher = TestCoroutineDispatcher()
private lateinit var controller: DefaultCreditCardEditorController
@get:Rule
val coroutinesTestRule = MainCoroutineRule(testDispatcher)
@Before
fun setup() {
controller = spyk(
DefaultCreditCardEditorController(
storage = storage,
lifecycleScope = testCoroutineScope,
navController = navController,
ioDispatcher = testDispatcher
)
)
}
@After
fun cleanUp() {
testCoroutineScope.cleanupTestCoroutines()
testDispatcher.cleanupTestCoroutines()
}
@Test
fun handleSaveCreditCard() = testCoroutineScope.runBlockingTest {
val creditCardFields = UpdatableCreditCardFields(
billingName = "Banana Apple",
cardNumber = "4111111111111112",
expiryMonth = 1,
expiryYear = 2030,
cardType = "discover"
)
controller.handleSaveCreditCard(creditCardFields)
coVerify {
storage.addCreditCard(creditCardFields)
navController.popBackStack()
}
}
}
Loading…
Cancel
Save