Skip to content

Commit

Permalink
Fix the bug when the navigation icon was wrong for secondary screens
Browse files Browse the repository at this point in the history
COAND-868
  • Loading branch information
araratthehero committed Apr 26, 2024
1 parent 3a73389 commit cd11e33
Show file tree
Hide file tree
Showing 13 changed files with 243 additions and 67 deletions.
Expand Up @@ -91,6 +91,15 @@ class BacsDirectDebitComponent internal constructor(
return (delegate as? BacsDirectDebitDelegate)?.setMode(BacsDirectDebitMode.INPUT) ?: false
}

/**
* Check if [BacsDirectDebitComponent] will handle back press when [handleBackPress] is called.
*
* @return Whether back press can been handled or not.
*/
fun canHandleBackPress(): Boolean {
return (delegate as? BacsDirectDebitDelegate)?.canHandleBackPress() ?: false
}

/**
* Handle back press in [BacsDirectDebitComponent] if necessary.
*
Expand Down
Expand Up @@ -37,6 +37,8 @@ internal interface BacsDirectDebitDelegate :

fun updateInputData(update: BacsDirectDebitInputData.() -> Unit)

fun canHandleBackPress(): Boolean

fun handleBackPress(): Boolean

fun setInteractionBlocked(isInteractionBlocked: Boolean)
Expand Down
Expand Up @@ -139,22 +139,24 @@ internal class DefaultBacsDirectDebitDelegate(
}
}

override fun canHandleBackPress() = isModeConfirmation()

override fun handleBackPress(): Boolean {
val isConfirmationMode = _componentStateFlow.value.mode == BacsDirectDebitMode.CONFIRMATION
return if (isConfirmationMode) {
return if (isModeConfirmation()) {
setMode(BacsDirectDebitMode.INPUT)
true
} else {
false
}
}

private fun onInputDataChanged() {
updateViewType(inputData.mode)
private fun isModeConfirmation() = _componentStateFlow.value.mode == BacsDirectDebitMode.CONFIRMATION

private fun onInputDataChanged() {
val outputData = createOutputData()
_outputDataFlow.tryEmit(outputData)
updateComponentState(outputData)
updateViewType(inputData.mode)
}

private fun updateViewType(mode: BacsDirectDebitMode) {
Expand Down
Expand Up @@ -553,6 +553,62 @@ internal class DefaultBacsDirectDebitDelegateTest(
}
}

@Nested
inner class BackPressHandlingTest {

@Test
fun `when mode is Confirmation and canHandleBackPress() is called then true should be returned`() =
runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
delegate.updateInputData {
holderName = "test"
bankAccountNumber = "12345678"
sortCode = "123456"
shopperEmail = "test@adyen.com"
isAmountConsentChecked = true
isAccountConsentChecked = true
}

delegate.setMode(BacsDirectDebitMode.CONFIRMATION)

assertTrue(delegate.canHandleBackPress())
}

@Test
fun `when mode is is Input and canHandleBackPress() is called then false should be returned`() =
runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))

assertFalse(delegate.canHandleBackPress())
}

@Test
fun `when mode is Confirmation and handleBackPress() is called then mode should be set to Input`() =
runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
delegate.updateInputData {
holderName = "test"
bankAccountNumber = "12345678"
sortCode = "123456"
shopperEmail = "test@adyen.com"
isAmountConsentChecked = true
isAccountConsentChecked = true
}

delegate.setMode(BacsDirectDebitMode.CONFIRMATION)

assertTrue(delegate.handleBackPress())
assertEquals(BacsDirectDebitMode.INPUT, delegate.outputData.mode)
}

@Test
fun `when mode is is Input and handleBackPress() is called then false should be returned`() = runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))

assertFalse(delegate.handleBackPress())
}
}

@Nested
inner class AnalyticsTest {

Expand Down
4 changes: 4 additions & 0 deletions card/src/main/java/com/adyen/checkout/card/CardComponent.kt
Expand Up @@ -133,6 +133,10 @@ constructor(
cardDelegate.setAddressLookupResult(addressLookupResult)
}

fun canHandleBackPress(): Boolean {
return (delegate as? CardDelegate)?.canHandleBackPress() ?: false
}

fun handleBackPress(): Boolean {
return (delegate as? CardDelegate)?.handleBackPress() ?: false
}
Expand Down
Expand Up @@ -55,6 +55,8 @@ interface CardDelegate :

fun setAddressLookupResult(addressLookupResult: AddressLookupResult)

fun canHandleBackPress(): Boolean

fun handleBackPress(): Boolean

fun startAddressLookup()
Expand Down
Expand Up @@ -485,15 +485,19 @@ class DefaultCardDelegate(
addressLookupDelegate.initialize(coroutineScope, inputData.address)
}

override fun canHandleBackPress() = isViewTypeAddressLookup()

override fun handleBackPress(): Boolean {
return if (_viewFlow.value == CardComponentViewType.AddressLookup) {
return if (isViewTypeAddressLookup()) {
_viewFlow.tryEmit(CardComponentViewType.DefaultCardView)
true
} else {
false
}
}

private fun isViewTypeAddressLookup() = _viewFlow.value == CardComponentViewType.AddressLookup

// Validation
private fun validateCardNumber(
cardNumber: String,
Expand Down
Expand Up @@ -310,9 +310,9 @@ internal class StoredCardDelegate(

override fun startAddressLookup() = Unit

override fun handleBackPress(): Boolean {
return false
}
override fun canHandleBackPress() = false

override fun handleBackPress() = false

override fun getPaymentMethodType(): String {
return storedPaymentMethod.type ?: PaymentMethodTypes.UNKNOWN
Expand Down
Expand Up @@ -1174,22 +1174,48 @@ internal class DefaultCardDelegateTest(
}
}

@Test
fun `when view type is AddressLookup and handleBackPress() is called DefaultCardView should be emitted`() =
runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
delegate.startAddressLookup()
assertTrue(delegate.handleBackPress())
delegate.viewFlow.test {
assertEquals(CardComponentViewType.DefaultCardView, awaitItem())
expectNoEvents()
@Nested
inner class BackPressHandlingTest {

@Test
fun `when view type is AddressLookup and canHandleBackPress() is called then true should be returned`() =
runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))

delegate.startAddressLookup()

assertTrue(delegate.canHandleBackPress())
}
}

@Test
fun `when view type is DefaultCardView and handleBackPress() is called it should return false`() = runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))
assertFalse(delegate.handleBackPress())
@Test
fun `when view type is DefaultCardView and canHandleBackPress() is called then false should be returned`() =
runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))

assertFalse(delegate.canHandleBackPress())
}

@Test
fun `when view type is AddressLookup and handleBackPress() is called then DefaultCardView should be emitted`() =
runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))

delegate.startAddressLookup()

assertTrue(delegate.handleBackPress())
delegate.viewFlow.test {
assertEquals(CardComponentViewType.DefaultCardView, awaitItem())
expectNoEvents()
}
}

@Test
fun `when view type is DefaultCardView and handleBackPress() is called then false should be returned`() =
runTest {
delegate.initialize(CoroutineScope(UnconfinedTestDispatcher()))

assertFalse(delegate.handleBackPress())
}
}

@Test
Expand Down
Expand Up @@ -15,12 +15,15 @@ import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
import androidx.lifecycle.lifecycleScope
import com.adyen.checkout.bacs.BacsDirectDebitComponent
import com.adyen.checkout.core.AdyenLogLevel
import com.adyen.checkout.core.internal.util.adyenLog
import com.adyen.checkout.dropin.databinding.FragmentBacsDirectDebitComponentBinding
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

internal class BacsDirectDebitDialogFragment : BaseComponentDialogFragment() {

Expand All @@ -29,6 +32,14 @@ internal class BacsDirectDebitDialogFragment : BaseComponentDialogFragment() {

private val bacsDirectDebitComponent: BacsDirectDebitComponent by lazy { component as BacsDirectDebitComponent }

private val navigationSource: NavigationSource
get() = when {
bacsDirectDebitComponent.canHandleBackPress() -> NavigationSource.HANDLED_BY_COMPONENT
navigatedFromPreselected -> NavigationSource.PRESELECTED_PAYMENT_METHOD
dropInViewModel.shouldSkipToSinglePaymentMethod() -> NavigationSource.NO_SOURCE
else -> NavigationSource.PAYMENT_METHOD_LIST
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentBacsDirectDebitComponentBinding.inflate(inflater, container, false)
return binding.root
Expand All @@ -45,14 +56,28 @@ internal class BacsDirectDebitDialogFragment : BaseComponentDialogFragment() {
if (bacsDirectDebitComponent.isConfirmationRequired()) {
binding.bacsView.requestFocus()
}

bacsDirectDebitComponent.viewFlow
.onEach { updateToolbarMode() }
.launchIn(viewLifecycleOwner.lifecycleScope)
}

private fun initToolbar() = with(binding.bottomSheetToolbar) {
setTitle(paymentMethod.name)
setOnButtonClickListener {
onBackPressed()
}
setMode(toolbarMode)
updateToolbarMode()
}

private fun updateToolbarMode() {
val toolbarMode = when (navigationSource) {
NavigationSource.HANDLED_BY_COMPONENT -> DropInBottomSheetToolbarMode.BACK_BUTTON
NavigationSource.PRESELECTED_PAYMENT_METHOD -> DropInBottomSheetToolbarMode.BACK_BUTTON
NavigationSource.PAYMENT_METHOD_LIST -> DropInBottomSheetToolbarMode.BACK_BUTTON
NavigationSource.NO_SOURCE -> DropInBottomSheetToolbarMode.CLOSE_BUTTON
}
binding.bottomSheetToolbar.setMode(toolbarMode)
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
Expand All @@ -62,10 +87,16 @@ internal class BacsDirectDebitDialogFragment : BaseComponentDialogFragment() {
return dialog
}

override fun onBackPressed() = if (bacsDirectDebitComponent.handleBackPress()) {
true
} else {
super.onBackPressed()
override fun onBackPressed() = performBackAction()

private fun performBackAction(): Boolean {
when (navigationSource) {
NavigationSource.HANDLED_BY_COMPONENT -> bacsDirectDebitComponent.handleBackPress()
NavigationSource.PRESELECTED_PAYMENT_METHOD -> protocol.showPreselectedDialog()
NavigationSource.NO_SOURCE -> protocol.terminateDropIn()
NavigationSource.PAYMENT_METHOD_LIST -> protocol.showPaymentMethodsDialog()
}
return true
}

private fun setDialogToFullScreen(dialog: Dialog) {
Expand Down Expand Up @@ -93,5 +124,12 @@ internal class BacsDirectDebitDialogFragment : BaseComponentDialogFragment() {
super.onDestroyView()
}

internal enum class NavigationSource {
HANDLED_BY_COMPONENT,
PRESELECTED_PAYMENT_METHOD,
PAYMENT_METHOD_LIST,
NO_SOURCE,
}

companion object : BaseCompanion<BacsDirectDebitDialogFragment>(BacsDirectDebitDialogFragment::class.java)
}
Expand Up @@ -39,21 +39,7 @@ internal abstract class BaseComponentDialogFragment :
var storedPaymentMethod: StoredPaymentMethod = StoredPaymentMethod()
lateinit var component: PaymentComponent
protected var isStoredPayment = false
private var navigatedFromPreselected = false

private val navigationSource: NavigationSource
get() = when {
navigatedFromPreselected -> NavigationSource.PRESELECTED_PAYMENT_METHOD
dropInViewModel.shouldSkipToSinglePaymentMethod() -> NavigationSource.NO_SOURCE
else -> NavigationSource.PAYMENT_METHOD_LIST
}

protected val toolbarMode: DropInBottomSheetToolbarMode
get() = when (navigationSource) {
NavigationSource.PRESELECTED_PAYMENT_METHOD -> DropInBottomSheetToolbarMode.BACK_BUTTON
NavigationSource.PAYMENT_METHOD_LIST -> DropInBottomSheetToolbarMode.BACK_BUTTON
NavigationSource.NO_SOURCE -> DropInBottomSheetToolbarMode.CLOSE_BUTTON
}
protected var navigatedFromPreselected = false

open class BaseCompanion<T : BaseComponentDialogFragment>(private var classes: Class<T>) {

Expand Down Expand Up @@ -98,19 +84,6 @@ internal abstract class BaseComponentDialogFragment :
savedInstanceState: Bundle?
): View?

override fun onBackPressed() = performBackAction()

private fun performBackAction(): Boolean {
adyenLog(AdyenLogLevel.DEBUG) { "onBackPressed - $navigatedFromPreselected" }

when (navigationSource) {
NavigationSource.PRESELECTED_PAYMENT_METHOD -> protocol.showPreselectedDialog()
NavigationSource.NO_SOURCE -> protocol.terminateDropIn()
NavigationSource.PAYMENT_METHOD_LIST -> protocol.showPaymentMethodsDialog()
}
return true
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
loadComponent()
Expand Down Expand Up @@ -181,10 +154,4 @@ internal abstract class BaseComponentDialogFragment :
adyenLog(AdyenLogLevel.ERROR) { componentError.errorMessage }
protocol.showError(null, getString(R.string.component_error), componentError.errorMessage, true)
}

internal enum class NavigationSource {
PRESELECTED_PAYMENT_METHOD,
PAYMENT_METHOD_LIST,
NO_SOURCE,
}
}

0 comments on commit cd11e33

Please sign in to comment.