Skip to content

Commit

Permalink
Migrate PaymentMethodsActivity to use Activity Result API (#3205)
Browse files Browse the repository at this point in the history
Simplify API for receiving results from `AddPaymentMethodActivity`

This change requires refactoring `AddPaymentMethodRowView`

https://developer.android.com/training/basics/intents/result
  • Loading branch information
mshafrir-stripe authored Dec 18, 2020
1 parent 76ecafd commit 97c3ac3
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 168 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ ext {
ktlintVersion = '0.40.0'
materialVersion = '1.2.1'

fragmentVersion = '1.2.5'
fragmentVersion = '1.3.0-rc01'

androidTestVersion = '1.3.0'
}
Expand Down
3 changes: 2 additions & 1 deletion stripe/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dependencies {
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
implementation "androidx.fragment:fragment-ktx:$fragmentVersion"
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation "androidx.activity:activity-ktx:1.2.0-rc01"

implementation 'com.google.android.gms:play-services-wallet:18.1.2'

Expand Down Expand Up @@ -58,7 +59,7 @@ dependencies {
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlinCoroutinesVersion"
testImplementation 'com.google.truth:truth:1.1'
testImplementation "androidx.arch.core:core-testing:2.1.0"
testImplementation "androidx.fragment:fragment-testing:$fragmentVersion"
testImplementation "androidx.fragment:fragment-testing:1.2.5"

androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
androidTestImplementation "androidx.test:rules:$androidTestVersion"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ internal abstract class SheetViewModel<TransitionTargetType, ViewStateType>(
protected val mutablePaymentMethods = MutableLiveData<List<PaymentMethod>>()
internal val paymentMethods: LiveData<List<PaymentMethod>> = mutablePaymentMethods

private val mutableTransition = MutableLiveData<TransitionTargetType>()
internal val transition: LiveData<TransitionTargetType> = mutableTransition
private val mutableTransition = MutableLiveData<TransitionTargetType?>(null)
internal val transition: LiveData<TransitionTargetType?> = mutableTransition

private val mutableSelection = MutableLiveData<PaymentSelection?>()
internal val selection: LiveData<PaymentSelection?> = mutableSelection
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.stripe.android.view

import android.content.Context
import android.content.Intent
import androidx.activity.result.contract.ActivityResultContract

internal class AddPaymentMethodContract :
ActivityResultContract<AddPaymentMethodActivityStarter.Args, AddPaymentMethodActivityStarter.Result>() {

override fun createIntent(
context: Context,
input: AddPaymentMethodActivityStarter.Args?
): Intent {
return Intent(context, AddPaymentMethodActivity::class.java)
.putExtra(ActivityStarter.Args.EXTRA, input)
}

override fun parseResult(
resultCode: Int,
intent: Intent?
): AddPaymentMethodActivityStarter.Result {
return AddPaymentMethodActivityStarter.Result.fromIntent(intent)
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
package com.stripe.android.view

import android.app.Activity
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import com.stripe.android.R
import com.stripe.android.databinding.AddPaymentMethodRowBinding
import com.stripe.android.model.PaymentMethod

@SuppressWarnings("ViewConstructor")
internal class AddPaymentMethodRowView private constructor(
context: Context,
@IdRes idRes: Int,
@StringRes private val labelRes: Int,
private val args: AddPaymentMethodActivityStarter.Args
context: Context
) : FrameLayout(context) {

private val viewBinding = AddPaymentMethodRowBinding.inflate(
Expand All @@ -25,62 +16,11 @@ internal class AddPaymentMethodRowView private constructor(
true
)

internal val label = viewBinding.label

init {
id = idRes
layoutParams = ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)

isFocusable = true
isClickable = true

contentDescription = context.getString(labelRes)
}

override fun onAttachedToWindow() {
super.onAttachedToWindow()
viewBinding.label.setText(labelRes)

(context as? Activity)?.let { activity ->
setOnClickListener {
AddPaymentMethodActivityStarter(activity).startForResult(args)
}
}
}

internal companion object {
internal fun createCard(
activity: Activity,
args: PaymentMethodsActivityStarter.Args
): AddPaymentMethodRowView {
return AddPaymentMethodRowView(
activity,
R.id.stripe_payment_methods_add_card,
R.string.payment_method_add_new_card,
AddPaymentMethodActivityStarter.Args.Builder()
.setBillingAddressFields(args.billingAddressFields)
.setShouldAttachToCustomer(true)
.setIsPaymentSessionActive(args.isPaymentSessionActive)
.setPaymentMethodType(PaymentMethod.Type.Card)
.setAddPaymentMethodFooter(args.addPaymentMethodFooterLayoutId)
.setPaymentConfiguration(args.paymentConfiguration)
.setWindowFlags(args.windowFlags)
.build()
)
}

internal fun createFpx(
activity: Activity,
args: PaymentMethodsActivityStarter.Args
): AddPaymentMethodRowView {
return AddPaymentMethodRowView(
activity,
R.id.stripe_payment_methods_add_fpx,
R.string.payment_method_add_new_fpx,
AddPaymentMethodActivityStarter.Args.Builder()
.setIsPaymentSessionActive(args.isPaymentSessionActive)
.setPaymentMethodType(PaymentMethod.Type.Fpx)
.setPaymentConfiguration(args.paymentConfiguration)
.build()
)
}
}
}
110 changes: 50 additions & 60 deletions stripe/src/main/java/com/stripe/android/view/PaymentMethodsActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.activity.viewModels
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AppCompatActivity
import androidx.core.text.util.LinkifyCompat
import androidx.core.view.ViewCompat
Expand Down Expand Up @@ -91,27 +92,31 @@ class PaymentMethodsActivity : AppCompatActivity() {
window.addFlags(it)
}

viewModel.snackbarData.observe(
this,
{ snackbarText ->
snackbarText?.let {
Snackbar.make(viewBinding.coordinator, it, Snackbar.LENGTH_SHORT).show()
}
viewModel.snackbarData.observe(this) { snackbarText ->
snackbarText?.let {
Snackbar.make(viewBinding.coordinator, it, Snackbar.LENGTH_SHORT).show()
}
)
viewModel.progressData.observe(
this,
{
viewBinding.progressBar.visibility = if (it) {
View.VISIBLE
} else {
View.GONE
}
}
viewModel.progressData.observe(this) {
viewBinding.progressBar.visibility = if (it) {
View.VISIBLE
} else {
View.GONE
}
)
}

setupRecyclerView()

val addPaymentMethodLauncher = registerForActivityResult(
AddPaymentMethodContract(),
::onAddPaymentMethodResult
)
adapter.addPaymentMethodArgs.observe(this) { args ->
if (args != null) {
addPaymentMethodLauncher.launch(args)
}
}

setSupportActionBar(viewBinding.toolbar)

supportActionBar?.apply {
Expand Down Expand Up @@ -171,36 +176,24 @@ class PaymentMethodsActivity : AppCompatActivity() {
}
}

public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == AddPaymentMethodActivityStarter.REQUEST_CODE &&
resultCode == Activity.RESULT_OK
) {
onPaymentMethodCreated(data)
}
}

override fun onSupportNavigateUp(): Boolean {
finishWithResult(adapter.selectedPaymentMethod, Activity.RESULT_CANCELED)
return true
}

private fun onPaymentMethodCreated(data: Intent?) {
data?.let {
val result =
AddPaymentMethodActivityStarter.Result.fromIntent(data)
when (result) {
is AddPaymentMethodActivityStarter.Result.Success -> {
onAddedPaymentMethod(result.paymentMethod)
}
is AddPaymentMethodActivityStarter.Result.Failure -> {
// TODO(mshafrir-stripe): notify user that payment method can not be added at this time
}
else -> {
// no-op
}
@VisibleForTesting
internal fun onAddPaymentMethodResult(result: AddPaymentMethodActivityStarter.Result) {
when (result) {
is AddPaymentMethodActivityStarter.Result.Success -> {
onAddedPaymentMethod(result.paymentMethod)
}
is AddPaymentMethodActivityStarter.Result.Failure -> {
// TODO(mshafrir-stripe): notify user that payment method can not be added at this time
}
} ?: fetchCustomerPaymentMethods()
else -> {
// no-op
}
}
}

private fun onAddedPaymentMethod(paymentMethod: PaymentMethod) {
Expand All @@ -221,27 +214,24 @@ class PaymentMethodsActivity : AppCompatActivity() {
}

private fun fetchCustomerPaymentMethods() {
viewModel.getPaymentMethods().observe(
this,
{ result ->
result.fold(
onSuccess = { adapter.setPaymentMethods(it) },
onFailure = {
alertDisplayer.show(
when (it) {
is StripeException -> {
TranslatorManager.getErrorMessageTranslator()
.translate(it.statusCode, it.message, it.stripeError)
}
else -> {
it.message.orEmpty()
}
viewModel.getPaymentMethods().observe(this) { result ->
result.fold(
onSuccess = { adapter.setPaymentMethods(it) },
onFailure = {
alertDisplayer.show(
when (it) {
is StripeException -> {
TranslatorManager.getErrorMessageTranslator()
.translate(it.statusCode, it.message, it.stripeError)
}
)
}
)
}
)
else -> {
it.message.orEmpty()
}
}
)
}
)
}
}

private fun finishWithGooglePay() {
Expand Down
Loading

0 comments on commit 97c3ac3

Please sign in to comment.