Skip to content

Commit

Permalink
Merge pull request #1560 from Adyen/feature/top_bar_navigation
Browse files Browse the repository at this point in the history
Feature/top bar navigation
  • Loading branch information
araratthehero authored Jun 6, 2024
2 parents 53e67b8 + 991da0b commit 494d2d8
Show file tree
Hide file tree
Showing 33 changed files with 463 additions and 247 deletions.
45 changes: 4 additions & 41 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,8 @@
[//]: # (## Deprecated)
[//]: # ( - Configurations public constructor are deprecated, please use each Configuration's builder to make a Configuration object)

## New
- For external redirects, you can now [customize the colors of the toolbar and navigation bar](docs/UI_CUSTOMIZATION.md#styling-custom-tabs) displayed in [Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs).
- TWINT is now supported with a native flow, and you no longer need to redirect shoppers through the browser. To use the redirect flow, set the following configuration:
```kotlin
CheckoutConfiguration(
environment = environment,
clientKey = clientKey,
..
) {
// Optionally pass the payment method type to only configure it for the specific payment method.
instantPayment(PaymentMethodTypes.TWINT) {
setActionHandlingMethod(ActionHandlingMethod.PREFER_WEB)
}
}
```

## Fixed
- Fixed some memory leaks.
- In case of a debug build, Drop-in no longer overrides the log level.
- For cards, when a shopper does not select an address, the address lookup function now displays a validation error.
- Actions no longer crash when your app uses obfuscation.
- When handling a 3D Secure 2 challenge using Checkout API v66 or earlier, Drop-in no longer throws an error.
- If the app process unexpectedly terminates when handling actions, the state is now restored and you can proceed with the payment flow.
- For `/sessions`, fixed an issue where the `setEnableRemovingStoredPaymentMethods` flag in the [Drop-in configuration](https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/?platform=Android&integration=Drop-in#3-optional-add-a-configuration-object) was ignored.

## Changed
- The phone number input field in the payment form now shows ISO codes instead of flags.
- The UI elements that were previously labelled **Country** are now **Country/Region**.
- Dependency versions:
| Name | Version |
|--------------------------------------------------------------------------------------------------------------|-------------------------------|
| [Adyen 3DS2](https://github.com/Adyen/adyen-3ds2-android/releases/tag/2.2.18) | **2.2.18** |
| [Android Gradle plugin](https://developer.android.com/build/releases/gradle-plugin) | **8.3.2** |
| [AndroidX Browser](https://developer.android.com/jetpack/androidx/releases/browser#1.8.0) | **1.8.0** |
| [AndroidX Compose Activity](https://developer.android.com/jetpack/androidx/releases/activity#1.9.0) | **1.9.0** |
| [AndroidX Compose BoM](https://developer.android.com/develop/ui/compose/bom/bom-mapping) | **2024.04.01** |
| [AndroidX Compose Compiler](https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.12) | **1.5.12** |
| [AndroidX Lifecycle](https://developer.android.com/jetpack/androidx/releases/lifecycle#2.7.0) | **2.7.0** |
| [Google Pay](https://developers.google.com/pay/api/android/support/release-notes#feb-24) | **19.3.0** |
| [Google Pay Compose Button](https://github.com/google-pay/compose-pay-button/releases/tag/v1.0.0) | **1.0.0** |
| [Kotlin](https://github.com/JetBrains/kotlin/releases/tag/v1.9.24) | **1.9.24** |
| [Kotlin coroutines](https://github.com/Kotlin/kotlinx.coroutines/releases/tag/1.8.0) | **1.8.0** |
- Drop-in navigation improvements:
- Top navigation has been added
- Dragging gesture has been disabled which caused Drop-in to dismiss
- Going back from actions dismisses Drop-in
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,9 @@ internal class BacsDirectDebitInputView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) :
LinearLayout(
context,
attrs,
defStyleAttr,
),
ComponentView {
) : LinearLayout(context, attrs, defStyleAttr), ComponentView {

private val binding: BacsDirectDebitInputViewBinding =
BacsDirectDebitInputViewBinding.inflate(LayoutInflater.from(context), this)
private val binding = BacsDirectDebitInputViewBinding.inflate(LayoutInflater.from(context), this)

private lateinit var localizedContext: Context

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class GiftCardPaymentMethod(
checkoutAttemptId = jsonObject.getStringOrNull(CHECKOUT_ATTEMPT_ID),
encryptedCardNumber = jsonObject.getStringOrNull(ENCRYPTED_CARD_NUMBER),
encryptedSecurityCode = jsonObject.getStringOrNull(ENCRYPTED_SECURITY_CODE),
brand = jsonObject.getStringOrNull(BRAND)
brand = jsonObject.getStringOrNull(BRAND),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import com.adyen.checkout.core.internal.util.adyenLog
import com.adyen.checkout.dropin.R
import com.adyen.checkout.dropin.databinding.FragmentGenericActionComponentBinding
import com.adyen.checkout.dropin.internal.util.arguments
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import com.adyen.checkout.ui.core.R as UICoreR
Expand Down Expand Up @@ -72,14 +71,19 @@ internal class ActionComponentDialogFragment :
}
}

private val navigationSource: NavigationSource
get() = when {
shouldFinishWithAction() -> NavigationSource.ACTION
else -> NavigationSource.NO_SOURCE
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adyenLog(AdyenLogLevel.DEBUG) { "onCreate" }
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentGenericActionComponentBinding.inflate(inflater)
setInitViewState(BottomSheetBehavior.STATE_EXPANDED)
return binding.root
}

Expand All @@ -91,7 +95,7 @@ internal class ActionComponentDialogFragment :
super.onViewCreated(view, savedInstanceState)
adyenLog(AdyenLogLevel.DEBUG) { "onViewCreated" }
initObservers()
binding.header.isVisible = false
initToolbar()

try {
val analyticsManager = dropInViewModel.analyticsManager
Expand All @@ -117,6 +121,18 @@ internal class ActionComponentDialogFragment :
}
}

private fun initToolbar() = with(binding.bottomSheetToolbar) {
setOnButtonClickListener {
performBackAction()
}

val toolbarMode = when (navigationSource) {
NavigationSource.ACTION -> DropInBottomSheetToolbarMode.CLOSE_BUTTON
NavigationSource.NO_SOURCE -> DropInBottomSheetToolbarMode.CLOSE_BUTTON
}
setMode(toolbarMode)
}

override fun onAdditionalDetails(actionComponentData: ActionComponentData) {
onActionComponentDataChanged(actionComponentData)
}
Expand Down Expand Up @@ -157,20 +173,13 @@ internal class ActionComponentDialogFragment :
actionComponent.handleAction(action, requireActivity())
}

override fun onBackPressed(): Boolean {
// polling will be canceled by lifecycle event
when {
shouldFinishWithAction() -> {
protocol.finishWithAction()
}

dropInViewModel.shouldSkipToSinglePaymentMethod() -> {
protocol.terminateDropIn()
}
override fun onBackPressed() = performBackAction()

else -> {
protocol.showPaymentMethodsDialog()
}
private fun performBackAction(): Boolean {
// polling will be canceled by lifecycle event
when (navigationSource) {
NavigationSource.ACTION -> protocol.finishWithAction()
NavigationSource.NO_SOURCE -> protocol.terminateDropIn()
}
return true
}
Expand All @@ -195,7 +204,7 @@ internal class ActionComponentDialogFragment :
when (componentError.exception) {
is CancellationException -> {
adyenLog(AdyenLogLevel.DEBUG) { "Flow was cancelled by user" }
onBackPressed()
performBackAction()
}

else -> {
Expand All @@ -219,6 +228,11 @@ internal class ActionComponentDialogFragment :
super.onDestroyView()
}

internal enum class NavigationSource {
ACTION,
NO_SOURCE,
}

companion object {
private const val ACTION = "ACTION"
private const val CHECKOUT_CONFIGURATION = "CHECKOUT_CONFIGURATION"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ internal class BacsDirectDebitDialogFragment : BaseComponentDialogFragment() {

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

companion object : BaseCompanion<BacsDirectDebitDialogFragment>(BacsDirectDebitDialogFragment::class.java)

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentBacsDirectDebitComponentBinding.inflate(inflater, container, false)
return binding.root
Expand All @@ -40,47 +38,61 @@ internal class BacsDirectDebitDialogFragment : BaseComponentDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adyenLog(AdyenLogLevel.DEBUG) { "onViewCreated" }
binding.header.text = paymentMethod.name

initToolbar()

binding.bacsView.attach(bacsDirectDebitComponent, viewLifecycleOwner)

if (bacsDirectDebitComponent.isConfirmationRequired()) {
setInitViewState(BottomSheetBehavior.STATE_EXPANDED)
binding.bacsView.requestFocus()
}
}

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

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
adyenLog(AdyenLogLevel.DEBUG) { "onCreateDialog" }
val dialog = super.onCreateDialog(savedInstanceState)
setDialogToFullScreen(dialog)
return dialog
}

override fun onBackPressed(): Boolean {
return if (bacsDirectDebitComponent.handleBackPress()) {
true
} else {
super.onBackPressed()
}
override fun onBackPressed() = if (bacsDirectDebitComponent.handleBackPress()) {
true
} else {
super.onBackPressed()
}

private fun setDialogToFullScreen(dialog: Dialog) {
dialog.setOnShowListener {
val bottomSheetDialog = dialog as BottomSheetDialog
val bottomSheet =
bottomSheetDialog.findViewById<FrameLayout>(MaterialR.id.design_bottom_sheet)
val layoutParams = bottomSheet?.layoutParams
val behavior = bottomSheet?.let { BottomSheetBehavior.from(it) }
behavior?.isDraggable = false
layoutParams?.height = WindowManager.LayoutParams.MATCH_PARENT
bottomSheet?.layoutParams = layoutParams
behavior?.state = BottomSheetBehavior.STATE_EXPANDED

bottomSheet?.let {
val layoutParams = it.layoutParams
layoutParams?.height = WindowManager.LayoutParams.MATCH_PARENT
it.layoutParams = layoutParams

BottomSheetBehavior.from(it).apply {
state = BottomSheetBehavior.STATE_EXPANDED
isHideable = false
isDraggable = false
}
}
}
}

override fun onDestroyView() {
_binding = null
super.onDestroyView()
}

companion object : BaseCompanion<BacsDirectDebitDialogFragment>(BacsDirectDebitDialogFragment::class.java)
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ internal abstract class BaseComponentDialogFragment :
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
}

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

fun newInstance(
Expand Down Expand Up @@ -84,13 +98,15 @@ internal abstract class BaseComponentDialogFragment :
savedInstanceState: Bundle?
): View?

override fun onBackPressed(): Boolean {
override fun onBackPressed() = performBackAction()

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

when {
navigatedFromPreselected -> protocol.showPreselectedDialog()
dropInViewModel.shouldSkipToSinglePaymentMethod() -> protocol.terminateDropIn()
else -> protocol.showPaymentMethodsDialog()
when (navigationSource) {
NavigationSource.PRESELECTED_PAYMENT_METHOD -> protocol.showPreselectedDialog()
NavigationSource.NO_SOURCE -> protocol.terminateDropIn()
NavigationSource.PAYMENT_METHOD_LIST -> protocol.showPaymentMethodsDialog()
}
return true
}
Expand Down Expand Up @@ -165,4 +181,10 @@ internal abstract class BaseComponentDialogFragment :
adyenLog(AdyenLogLevel.ERROR) { componentError.errorMessage }
protocol.showError(null, getString(UICoreR.string.component_error), componentError.errorMessage, true)
}

internal enum class NavigationSource {
PRESELECTED_PAYMENT_METHOD,
PAYMENT_METHOD_LIST,
NO_SOURCE,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import com.adyen.checkout.components.core.LookupAddress
import com.adyen.checkout.core.AdyenLogLevel
import com.adyen.checkout.core.internal.util.adyenLog
import com.adyen.checkout.dropin.databinding.FragmentCardComponentBinding
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

Expand All @@ -39,11 +38,7 @@ internal class CardComponentDialogFragment : BaseComponentDialogFragment(), Addr
super.onViewCreated(view, savedInstanceState)
adyenLog(AdyenLogLevel.DEBUG) { "onViewCreated" }

binding.header.text = if (isStoredPayment) {
storedPaymentMethod.name
} else {
paymentMethod.name
}
initToolbar()

cardComponent.setOnBinValueListener(protocol::onBinValue)
cardComponent.setOnBinLookupListener(protocol::onBinLookup)
Expand All @@ -52,7 +47,6 @@ internal class CardComponentDialogFragment : BaseComponentDialogFragment(), Addr
binding.cardView.attach(cardComponent, viewLifecycleOwner)

if (cardComponent.isConfirmationRequired()) {
setInitViewState(BottomSheetBehavior.STATE_EXPANDED)
binding.cardView.requestFocus()
}

Expand All @@ -65,6 +59,20 @@ internal class CardComponentDialogFragment : BaseComponentDialogFragment(), Addr
.launchIn(viewLifecycleOwner.lifecycleScope)
}

private fun initToolbar() = with(binding.bottomSheetToolbar) {
val title = if (isStoredPayment) {
storedPaymentMethod.name
} else {
paymentMethod.name
}
setTitle(title)
setMode(toolbarMode)

setOnButtonClickListener {
onBackPressed()
}
}

override fun onQueryChanged(query: String) {
protocol.onAddressLookupQuery(query)
}
Expand All @@ -73,11 +81,10 @@ internal class CardComponentDialogFragment : BaseComponentDialogFragment(), Addr
return protocol.onAddressLookupCompletion(lookupAddress)
}

override fun onBackPressed(): Boolean {
if (cardComponent.handleBackPress()) {
return true
}
return super.onBackPressed()
override fun onBackPressed() = if (cardComponent.handleBackPress()) {
true
} else {
super.onBackPressed()
}

override fun onDestroyView() {
Expand Down
Loading

0 comments on commit 494d2d8

Please sign in to comment.