From 2d11c5dd43ae4d81becdefb307d96254b4bba651 Mon Sep 17 00:00:00 2001 From: Michael Shafrir Date: Thu, 24 Sep 2020 09:41:05 -0400 Subject: [PATCH] Add support for configuring a footer layout in payment methods screen Summary The footer layout id can be specified via - `PaymentSessionConfig.Builder#setPaymentMethodsFooter()` - `PaymentMethodsActivityStarter.Args.Builder#setPaymentMethodsFooter()` Motivation ANDROID-576 --- .../activity/PaymentSessionActivity.kt | 1 + .../res/layout/payment_methods_activity.xml | 11 +++++- stripe/res/values/ids.xml | 1 + .../java/com/stripe/android/PaymentSession.kt | 1 + .../stripe/android/PaymentSessionConfig.kt | 20 ++++++++++ .../android/view/PaymentMethodsActivity.kt | 38 +++++++++++++++++++ .../view/PaymentMethodsActivityStarter.kt | 15 ++++++++ 7 files changed, 86 insertions(+), 1 deletion(-) 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