diff --git a/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.kt b/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.kt
index 3a3d6964901..81b5278e230 100644
--- a/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.kt
+++ b/example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.kt
@@ -119,6 +119,7 @@ class PaymentSessionActivity : AppCompatActivity() {
return PaymentSession(
activity = this,
config = PaymentSessionConfig.Builder()
+ .setPaymentMethodsFooter(R.layout.add_payment_method_footer)
.setAddPaymentMethodFooter(R.layout.add_payment_method_footer)
.setPrepopulatedShippingInfo(EXAMPLE_SHIPPING_INFO)
.setHiddenShippingInfoFields()
diff --git a/stripe/res/layout/payment_methods_activity.xml b/stripe/res/layout/payment_methods_activity.xml
index a920c274fd7..d10eb907938 100644
--- a/stripe/res/layout/payment_methods_activity.xml
+++ b/stripe/res/layout/payment_methods_activity.xml
@@ -28,12 +28,21 @@
android:layout_below="@id/toolbar"
android:visibility="gone" />
+
+
+ android:layout_above="@+id/footer_container"
+ android:layout_marginTop="@dimen/stripe_list_top_margin"
+ android:layout_marginBottom="@dimen/stripe_list_top_margin"/>
diff --git a/stripe/res/values/ids.xml b/stripe/res/values/ids.xml
index b5c78b2fcea..c74e5882a1f 100644
--- a/stripe/res/values/ids.xml
+++ b/stripe/res/values/ids.xml
@@ -6,4 +6,5 @@
+
diff --git a/stripe/src/main/java/com/stripe/android/PaymentSession.kt b/stripe/src/main/java/com/stripe/android/PaymentSession.kt
index 0cb5ff2366c..d3ce3c69a83 100644
--- a/stripe/src/main/java/com/stripe/android/PaymentSession.kt
+++ b/stripe/src/main/java/com/stripe/android/PaymentSession.kt
@@ -233,6 +233,7 @@ class PaymentSession @VisibleForTesting internal constructor(
.setInitialPaymentMethodId(
viewModel.getSelectedPaymentMethodId(selectedPaymentMethodId)
)
+ .setPaymentMethodsFooter(config.paymentMethodsFooterLayoutId)
.setAddPaymentMethodFooter(config.addPaymentMethodFooterLayoutId)
.setIsPaymentSessionActive(true)
.setPaymentConfiguration(PaymentConfiguration.getInstance(context))
diff --git a/stripe/src/main/java/com/stripe/android/PaymentSessionConfig.kt b/stripe/src/main/java/com/stripe/android/PaymentSessionConfig.kt
index ca7b9a487da..9be4e139ddd 100644
--- a/stripe/src/main/java/com/stripe/android/PaymentSessionConfig.kt
+++ b/stripe/src/main/java/com/stripe/android/PaymentSessionConfig.kt
@@ -28,9 +28,15 @@ data class PaymentSessionConfig internal constructor(
val prepopulatedShippingInfo: ShippingInformation? = null,
val isShippingInfoRequired: Boolean = false,
val isShippingMethodRequired: Boolean = false,
+
+ @LayoutRes
+ @get:LayoutRes
+ val paymentMethodsFooterLayoutId: Int = 0,
+
@LayoutRes
@get:LayoutRes
val addPaymentMethodFooterLayoutId: Int = 0,
+
val paymentMethodTypes: List = listOf(PaymentMethod.Type.Card),
val shouldShowGooglePay: Boolean = false,
val allowedShippingCountryCodes: Set = emptySet(),
@@ -104,6 +110,9 @@ data class PaymentSessionConfig internal constructor(
private var shouldPrefetchCustomer: Boolean = true
private var canDeletePaymentMethods: Boolean = true
+ @LayoutRes
+ private var paymentMethodsFooterLayoutId: Int = 0
+
@LayoutRes
private var addPaymentMethodFooterLayoutId: Int = 0
@@ -164,6 +173,16 @@ data class PaymentSessionConfig internal constructor(
this.shippingMethodsRequired = shippingMethodsRequired
}
+ /**
+ * @param paymentMethodsFooterLayoutId optional layout id that will be inflated and
+ * displayed beneath the payment method selection list on [PaymentMethodsActivity]
+ */
+ fun setPaymentMethodsFooter(
+ @LayoutRes paymentMethodsFooterLayoutId: Int
+ ): Builder = apply {
+ this.paymentMethodsFooterLayoutId = paymentMethodsFooterLayoutId
+ }
+
/**
* @param addPaymentMethodFooterLayoutId optional layout id that will be inflated and
* displayed beneath the payment details collection form on [AddPaymentMethodActivity]
@@ -266,6 +285,7 @@ data class PaymentSessionConfig internal constructor(
prepopulatedShippingInfo = shippingInformation,
isShippingInfoRequired = shippingInfoRequired,
isShippingMethodRequired = shippingMethodsRequired,
+ paymentMethodsFooterLayoutId = paymentMethodsFooterLayoutId,
addPaymentMethodFooterLayoutId = addPaymentMethodFooterLayoutId,
paymentMethodTypes = paymentMethodTypes,
shouldShowGooglePay = shouldShowGooglePay,
diff --git a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.kt b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.kt
index e1aa5cdef60..d57123851c7 100644
--- a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.kt
+++ b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.kt
@@ -2,12 +2,20 @@ package com.stripe.android.view
import android.app.Activity
import android.content.Intent
+import android.os.Build
import android.os.Bundle
+import android.text.method.LinkMovementMethod
+import android.text.util.Linkify
import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.text.util.LinkifyCompat
+import androidx.core.view.ViewCompat
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.snackbar.Snackbar
import com.stripe.android.CustomerSession
+import com.stripe.android.R
import com.stripe.android.databinding.PaymentMethodsActivityBinding
import com.stripe.android.exception.StripeException
import com.stripe.android.model.PaymentMethod
@@ -114,6 +122,15 @@ class PaymentMethodsActivity : AppCompatActivity() {
setDisplayShowHomeEnabled(true)
}
+ createFooterView(viewBinding.footerContainer)?.let { footer ->
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ viewBinding.recycler.accessibilityTraversalBefore = footer.id
+ footer.accessibilityTraversalAfter = viewBinding.recycler.id
+ }
+ viewBinding.footerContainer.addView(footer)
+ viewBinding.footerContainer.visibility = View.VISIBLE
+ }
+
fetchCustomerPaymentMethods()
// This prevents the first click from being eaten by the focus.
@@ -260,6 +277,27 @@ class PaymentMethodsActivity : AppCompatActivity() {
finish()
}
+ private fun createFooterView(
+ contentRoot: ViewGroup
+ ): View? {
+ return if (args.paymentMethodsFooterLayoutId > 0) {
+ val footerView = layoutInflater.inflate(
+ args.paymentMethodsFooterLayoutId,
+ contentRoot,
+ false
+ )
+ footerView.id = R.id.stripe_payment_methods_footer
+ if (footerView is TextView) {
+ LinkifyCompat.addLinks(footerView, Linkify.ALL)
+ ViewCompat.enableAccessibleClickableSpanSupport(footerView)
+ footerView.movementMethod = LinkMovementMethod.getInstance()
+ }
+ footerView
+ } else {
+ null
+ }
+ }
+
override fun onDestroy() {
viewModel.selectedPaymentMethodId = adapter.selectedPaymentMethod?.id
super.onDestroy()
diff --git a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivityStarter.kt b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivityStarter.kt
index a2ba7349820..b3507a46fbb 100644
--- a/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivityStarter.kt
+++ b/stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivityStarter.kt
@@ -39,6 +39,7 @@ class PaymentMethodsActivityStarter : ActivityStarter,
@@ -61,6 +62,9 @@ class PaymentMethodsActivityStarter : ActivityStarter