From f11f8dc6c50b184009834f6eb13d3096ce1f8a56 Mon Sep 17 00:00:00 2001
From: charliecruzan-stripe
<97612659+charliecruzan-stripe@users.noreply.github.com>
Date: Mon, 18 Apr 2022 17:29:53 -0400
Subject: [PATCH] fix: onBlur callback on CardField (#894)
---
CHANGELOG.md | 1 +
.../AuBECSDebitFormViewManager.kt | 2 -
.../com/reactnativestripesdk/CardFieldView.kt | 82 +++++++++++--------
.../CardFieldViewManager.kt | 5 --
.../CardFormViewManager.kt | 4 -
.../GooglePayButtonManager.kt | 1 -
.../PaymentLauncherFragment.kt | 9 +-
.../StripeContainerManager.kt | 1 -
.../reactnativestripesdk/StripeSdkModule.kt | 18 ----
9 files changed, 53 insertions(+), 70 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 992461853..cb935330d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
# CHANGELOG
+- [#894](https://github.com/stripe/stripe-react-native/pull/894) Fix: `` `onBlur` callback will now be called appropriately on Android
- [#846](https://github.com/stripe/stripe-react-native/pull/846) Fix: Avoid crashes when `currentActivity` is null
- [#879](https://github.com/stripe/stripe-react-native/pull/879) Feat: Add support for ACHv2 payments on Android (already existed on iOS).
- [#879](https://github.com/stripe/stripe-react-native/pull/879) Chore: Upgraded `stripe-android` from v19.3.+ to v20.0.+
diff --git a/android/src/main/java/com/reactnativestripesdk/AuBECSDebitFormViewManager.kt b/android/src/main/java/com/reactnativestripesdk/AuBECSDebitFormViewManager.kt
index 7012c5e99..ea5a98bcf 100644
--- a/android/src/main/java/com/reactnativestripesdk/AuBECSDebitFormViewManager.kt
+++ b/android/src/main/java/com/reactnativestripesdk/AuBECSDebitFormViewManager.kt
@@ -15,13 +15,11 @@ class AuBECSDebitFormViewManager : SimpleViewManager() {
}
@ReactProp(name = "companyName")
- @SuppressWarnings("unused")
fun setCompanyName(view: AuBECSDebitFormView, name: String?) {
view.setCompanyName(name)
}
@ReactProp(name = "formStyle")
- @SuppressWarnings("unused")
fun setFormStyle(view: AuBECSDebitFormView, style: ReadableMap) {
view.setFormStyle(style)
}
diff --git a/android/src/main/java/com/reactnativestripesdk/CardFieldView.kt b/android/src/main/java/com/reactnativestripesdk/CardFieldView.kt
index 7641deabb..9a49b48fd 100644
--- a/android/src/main/java/com/reactnativestripesdk/CardFieldView.kt
+++ b/android/src/main/java/com/reactnativestripesdk/CardFieldView.kt
@@ -23,18 +23,18 @@ import com.stripe.android.view.CardValidCallback
class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
private var mCardWidget: CardInputWidget = CardInputWidget(context)
+ private val cardInputWidgetBinding = CardInputWidgetBinding.bind(mCardWidget)
val cardDetails: MutableMap = mutableMapOf("brand" to "", "last4" to "", "expiryMonth" to null, "expiryYear" to null, "postalCode" to "", "validNumber" to "Unknown", "validCVC" to "Unknown", "validExpiryDate" to "Unknown")
var cardParams: PaymentMethodCreateParams.Card? = null
var cardAddress: Address? = null
private var mEventDispatcher: EventDispatcher? = context.getNativeModule(UIManagerModule::class.java)?.eventDispatcher
private var dangerouslyGetFullCardDetails: Boolean = false
+ private var currentFocusedField: String? = null
init {
-
- val binding = CardInputWidgetBinding.bind(mCardWidget)
- binding.container.isFocusable = true
- binding.container.isFocusableInTouchMode = true
- binding.container.requestFocus()
+ cardInputWidgetBinding.container.isFocusable = true
+ cardInputWidgetBinding.container.isFocusableInTouchMode = true
+ cardInputWidgetBinding.container.requestFocus()
addView(mCardWidget)
setListeners()
@@ -44,37 +44,37 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
fun setAutofocus(value: Boolean) {
if (value) {
- val binding = CardInputWidgetBinding.bind(mCardWidget)
- binding.cardNumberEditText.requestFocus()
- binding.cardNumberEditText.showSoftKeyboard()
+ cardInputWidgetBinding.cardNumberEditText.requestFocus()
+ cardInputWidgetBinding.cardNumberEditText.showSoftKeyboard()
}
}
fun requestFocusFromJS() {
- val binding = CardInputWidgetBinding.bind(mCardWidget)
- binding.cardNumberEditText.requestFocus()
- binding.cardNumberEditText.showSoftKeyboard()
+ cardInputWidgetBinding.cardNumberEditText.requestFocus()
+ cardInputWidgetBinding.cardNumberEditText.showSoftKeyboard()
}
fun requestBlurFromJS() {
- val binding = CardInputWidgetBinding.bind(mCardWidget)
- binding.cardNumberEditText.hideSoftKeyboard()
- binding.cardNumberEditText.clearFocus()
- binding.container.requestFocus()
+ cardInputWidgetBinding.cardNumberEditText.hideSoftKeyboard()
+ cardInputWidgetBinding.cardNumberEditText.clearFocus()
+ cardInputWidgetBinding.container.requestFocus()
}
fun requestClearFromJS() {
- val binding = CardInputWidgetBinding.bind(mCardWidget)
- binding.cardNumberEditText.setText("")
- binding.cvcEditText.setText("")
- binding.expiryDateEditText.setText("")
+ cardInputWidgetBinding.cardNumberEditText.setText("")
+ cardInputWidgetBinding.cvcEditText.setText("")
+ cardInputWidgetBinding.expiryDateEditText.setText("")
if (mCardWidget.postalCodeEnabled) {
- binding.postalCodeEditText.setText("")
+ cardInputWidgetBinding.postalCodeEditText.setText("")
}
}
+ private fun onChangeFocus() {
+ mEventDispatcher?.dispatchEvent(
+ CardFocusEvent(id, currentFocusedField))
+ }
+
fun setCardStyle(value: ReadableMap) {
- val binding = CardInputWidgetBinding.bind(mCardWidget)
val borderWidth = getIntOrNull(value, "borderWidth")
val backgroundColor = getValOr(value, "backgroundColor", null)
val borderColor = getValOr(value, "borderColor", null)
@@ -85,7 +85,11 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
val placeholderColor = getValOr(value, "placeholderColor", null)
val textErrorColor = getValOr(value, "textErrorColor", null)
val cursorColor = getValOr(value, "cursorColor", null)
- val bindings = setOf(binding.cardNumberEditText, binding.cvcEditText, binding.expiryDateEditText, binding.postalCodeEditText)
+ val bindings = setOf(
+ cardInputWidgetBinding.cardNumberEditText,
+ cardInputWidgetBinding.cvcEditText,
+ cardInputWidgetBinding.expiryDateEditText,
+ cardInputWidgetBinding.postalCodeEditText)
textColor?.let {
for (editTextBinding in bindings) {
@@ -148,23 +152,22 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
}
fun setPlaceHolders(value: ReadableMap) {
- val binding = CardInputWidgetBinding.bind(mCardWidget)
val numberPlaceholder = getValOr(value, "number", null)
val expirationPlaceholder = getValOr(value, "expiration", null)
val cvcPlaceholder = getValOr(value, "cvc", null)
val postalCodePlaceholder = getValOr(value, "postalCode", null)
numberPlaceholder?.let {
- binding.cardNumberEditText.hint = it
+ cardInputWidgetBinding.cardNumberEditText.hint = it
}
expirationPlaceholder?.let {
- binding.expiryDateEditText.hint = it
+ cardInputWidgetBinding.expiryDateEditText.hint = it
}
cvcPlaceholder?.let {
mCardWidget.setCvcLabel(it)
}
postalCodePlaceholder?.let {
- binding.postalCodeEditText.hint = it
+ cardInputWidgetBinding.postalCodeEditText.hint = it
}
}
@@ -180,7 +183,7 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
return cardDetails
}
- fun onValidCardChange() {
+ private fun onValidCardChange() {
mCardWidget.paymentMethodCard?.let {
cardParams = it
cardAddress = Address.Builder()
@@ -207,6 +210,23 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
}
private fun setListeners() {
+ cardInputWidgetBinding.cardNumberEditText.setOnFocusChangeListener { _, hasFocus ->
+ currentFocusedField = if (hasFocus) CardInputListener.FocusField.CardNumber.name else null
+ onChangeFocus()
+ }
+ cardInputWidgetBinding.expiryDateEditText.setOnFocusChangeListener { _, hasFocus ->
+ currentFocusedField = if (hasFocus) CardInputListener.FocusField.ExpiryDate.name else null
+ onChangeFocus()
+ }
+ cardInputWidgetBinding.cvcEditText.setOnFocusChangeListener { _, hasFocus ->
+ currentFocusedField = if (hasFocus) CardInputListener.FocusField.Cvc.name else null
+ onChangeFocus()
+ }
+ cardInputWidgetBinding.postalCodeEditText.setOnFocusChangeListener { _, hasFocus ->
+ currentFocusedField = if (hasFocus) CardInputListener.FocusField.PostalCode.name else null
+ onChangeFocus()
+ }
+
mCardWidget.setCardValidCallback { isValid, invalidFields ->
cardDetails["validNumber"] = if (invalidFields.contains(CardValidCallback.Fields.Number)) "Invalid" else "Valid"
cardDetails["validCVC"] = if (invalidFields.contains(CardValidCallback.Fields.Cvc)) "Invalid" else "Valid"
@@ -224,13 +244,7 @@ class CardFieldView(context: ThemedReactContext) : FrameLayout(context) {
override fun onExpirationComplete() {}
override fun onCvcComplete() {}
override fun onPostalCodeComplete() {}
-
- override fun onFocusChange(focusField: CardInputListener.FocusField) {
- if (mEventDispatcher != null) {
- mEventDispatcher?.dispatchEvent(
- CardFocusEvent(id, focusField.name))
- }
- }
+ override fun onFocusChange(focusField: CardInputListener.FocusField) {}
})
mCardWidget.setExpiryDateTextWatcher(object : TextWatcher {
diff --git a/android/src/main/java/com/reactnativestripesdk/CardFieldViewManager.kt b/android/src/main/java/com/reactnativestripesdk/CardFieldViewManager.kt
index beeff2d19..5952a38ea 100644
--- a/android/src/main/java/com/reactnativestripesdk/CardFieldViewManager.kt
+++ b/android/src/main/java/com/reactnativestripesdk/CardFieldViewManager.kt
@@ -27,31 +27,26 @@ class CardFieldViewManager : SimpleViewManager() {
}
@ReactProp(name = "dangerouslyGetFullCardDetails")
- @SuppressWarnings("unused")
fun setDangerouslyGetFullCardDetails(view: CardFieldView, dangerouslyGetFullCardDetails: Boolean = false) {
view.setDangerouslyGetFullCardDetails(dangerouslyGetFullCardDetails)
}
@ReactProp(name = "postalCodeEnabled")
- @SuppressWarnings("unused")
fun setPostalCodeEnabled(view: CardFieldView, postalCodeEnabled: Boolean = true) {
view.setPostalCodeEnabled(postalCodeEnabled)
}
@ReactProp(name = "autofocus")
- @SuppressWarnings("unused")
fun setAutofocus(view: CardFieldView, autofocus: Boolean = false) {
view.setAutofocus(autofocus)
}
@ReactProp(name = "cardStyle")
- @SuppressWarnings("unused")
fun setCardStyle(view: CardFieldView, cardStyle: ReadableMap) {
view.setCardStyle(cardStyle)
}
@ReactProp(name = "placeholder")
- @SuppressWarnings("unused")
fun setPlaceHolders(view: CardFieldView, placeholder: ReadableMap) {
view.setPlaceHolders(placeholder)
}
diff --git a/android/src/main/java/com/reactnativestripesdk/CardFormViewManager.kt b/android/src/main/java/com/reactnativestripesdk/CardFormViewManager.kt
index bbcf15306..96c59e663 100644
--- a/android/src/main/java/com/reactnativestripesdk/CardFormViewManager.kt
+++ b/android/src/main/java/com/reactnativestripesdk/CardFormViewManager.kt
@@ -27,13 +27,11 @@ class CardFormViewManager : SimpleViewManager() {
}
@ReactProp(name = "dangerouslyGetFullCardDetails")
- @SuppressWarnings("unused")
fun setDangerouslyGetFullCardDetails(view: CardFormView, dangerouslyGetFullCardDetails: Boolean = false) {
view.setDangerouslyGetFullCardDetails(dangerouslyGetFullCardDetails)
}
@ReactProp(name = "postalCodeEnabled")
- @SuppressWarnings("unused")
fun setPostalCodeEnabled(view: CardFormView, postalCodeEnabled: Boolean = false) {
view.setPostalCodeEnabled(postalCodeEnabled)
}
@@ -44,13 +42,11 @@ class CardFormViewManager : SimpleViewManager() {
// }
@ReactProp(name = "autofocus")
- @SuppressWarnings("unused")
fun setAutofocus(view: CardFormView, autofocus: Boolean = false) {
view.setAutofocus(autofocus)
}
@ReactProp(name = "cardStyle")
- @SuppressWarnings("unused")
fun setCardStyle(view: CardFormView, cardStyle: ReadableMap) {
view.setCardStyle(cardStyle)
}
diff --git a/android/src/main/java/com/reactnativestripesdk/GooglePayButtonManager.kt b/android/src/main/java/com/reactnativestripesdk/GooglePayButtonManager.kt
index 89c9bf65b..19d87e49d 100644
--- a/android/src/main/java/com/reactnativestripesdk/GooglePayButtonManager.kt
+++ b/android/src/main/java/com/reactnativestripesdk/GooglePayButtonManager.kt
@@ -16,7 +16,6 @@ class GooglePayButtonManager : SimpleViewManager() {
}
@ReactProp(name = "buttonType")
- @SuppressWarnings("unused")
fun buttonType(view: GooglePayButtonView, buttonType: String) {
view.setType(buttonType)
}
diff --git a/android/src/main/java/com/reactnativestripesdk/PaymentLauncherFragment.kt b/android/src/main/java/com/reactnativestripesdk/PaymentLauncherFragment.kt
index 2ccf4142a..ce990a409 100644
--- a/android/src/main/java/com/reactnativestripesdk/PaymentLauncherFragment.kt
+++ b/android/src/main/java/com/reactnativestripesdk/PaymentLauncherFragment.kt
@@ -18,11 +18,10 @@ class PaymentLauncherFragment(
private val publishableKey: String,
private val stripeAccountId: String?,
) : Fragment() {
- lateinit var paymentLauncher: PaymentLauncher
-
- var clientSecret: String? = null
- var promise: Promise? = null
- var isPaymentIntent: Boolean = true
+ private lateinit var paymentLauncher: PaymentLauncher
+ private var clientSecret: String? = null
+ private var promise: Promise? = null
+ private var isPaymentIntent: Boolean = true
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
diff --git a/android/src/main/java/com/reactnativestripesdk/StripeContainerManager.kt b/android/src/main/java/com/reactnativestripesdk/StripeContainerManager.kt
index d297ca376..8d9efcef3 100644
--- a/android/src/main/java/com/reactnativestripesdk/StripeContainerManager.kt
+++ b/android/src/main/java/com/reactnativestripesdk/StripeContainerManager.kt
@@ -8,7 +8,6 @@ class StripeContainerManager : ViewGroupManager() {
override fun getName() = "StripeContainer"
@ReactProp(name = "keyboardShouldPersistTaps")
- @SuppressWarnings("unused")
fun setKeyboardShouldPersistTaps(view: StripeContainerView, keyboardShouldPersistTaps: Boolean) {
view.setKeyboardShouldPersistTaps(keyboardShouldPersistTaps)
}
diff --git a/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt b/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt
index aa30463bd..1d9820dbf 100644
--- a/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt
+++ b/android/src/main/java/com/reactnativestripesdk/StripeSdkModule.kt
@@ -190,7 +190,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun initialise(params: ReadableMap, promise: Promise) {
val publishableKey = getValOr(params, "publishableKey", null) as String
val appInfo = getMapOrNull(params, "appInfo") as ReadableMap
@@ -236,7 +235,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun initPaymentSheet(params: ReadableMap, promise: Promise) {
getCurrentActivityOrResolveWithError(promise)?.let { activity ->
this.initPaymentSheetPromise = promise
@@ -252,14 +250,12 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun presentPaymentSheet(promise: Promise) {
this.presentPaymentSheetPromise = promise
paymentSheetFragment?.present()
}
@ReactMethod
- @SuppressWarnings("unused")
fun confirmPaymentSheetPayment(promise: Promise) {
this.confirmPaymentSheetPaymentPromise = promise
paymentSheetFragment?.confirmPayment()
@@ -304,7 +300,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun createPaymentMethod(data: ReadableMap, options: ReadableMap, promise: Promise) {
val cardParams = (cardFieldView?.cardParams ?: cardFormView?.cardParams) ?: run {
promise.resolve(createError("Failed", "Card details not complete"))
@@ -330,7 +325,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun createToken(params: ReadableMap, promise: Promise) {
val type = getValOr(params, "type", null)
if (type == null) {
@@ -411,7 +405,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun createTokenForCVCUpdate(cvc: String, promise: Promise) {
stripe.createCvcUpdateToken(
cvc,
@@ -431,7 +424,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun handleNextAction(paymentIntentClientSecret: String, promise: Promise) {
paymentLauncherFragment.handleNextActionForPaymentIntent(
paymentIntentClientSecret,
@@ -459,7 +451,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
// }
@ReactMethod
- @SuppressWarnings("unused")
fun confirmPayment(paymentIntentClientSecret: String, params: ReadableMap, options: ReadableMap, promise: Promise) {
val paymentMethodType = getValOr(params, "type")?.let { mapToPaymentMethodType(it) } ?: run {
promise.resolve(createError(ConfirmPaymentErrorType.Failed.toString(), "You must provide paymentMethodType"))
@@ -504,7 +495,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun retrievePaymentIntent(clientSecret: String, promise: Promise) {
CoroutineScope(Dispatchers.IO).launch {
val paymentIntent = stripe.retrievePaymentIntentSynchronous(clientSecret)
@@ -517,7 +507,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun retrieveSetupIntent(clientSecret: String, promise: Promise) {
CoroutineScope(Dispatchers.IO).launch {
val setupIntent = stripe.retrieveSetupIntentSynchronous(clientSecret)
@@ -530,7 +519,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun confirmSetupIntent(setupIntentClientSecret: String, params: ReadableMap, options: ReadableMap, promise: Promise) {
val paymentMethodType = getValOr(params, "type")?.let { mapToPaymentMethodType(it) } ?: run {
promise.resolve(createError(ConfirmPaymentErrorType.Failed.toString(), "You must provide paymentMethodType"))
@@ -555,7 +543,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun isGooglePaySupported(params: ReadableMap?, promise: Promise) {
val fragment = GooglePayPaymentMethodLauncherFragment(
reactContext,
@@ -572,7 +559,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun initGooglePay(params: ReadableMap, promise: Promise) {
googlePayFragment = GooglePayFragment().also {
val bundle = toBundleObject(params)
@@ -589,7 +575,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun presentGooglePay(params: ReadableMap, promise: Promise) {
val clientSecret = getValOr(params, "clientSecret") ?: run {
promise.resolve(createError(GooglePayErrorType.Failed.toString(), "you must provide clientSecret"))
@@ -608,7 +593,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun createGooglePayPaymentMethod(params: ReadableMap, promise: Promise) {
val currencyCode = getValOr(params, "currencyCode", null) ?: run {
promise.resolve(createError(GooglePayErrorType.Failed.toString(), "you must provide currencyCode"))
@@ -623,7 +607,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun collectBankAccount(isPaymentIntent: Boolean, clientSecret: String, params: ReadableMap, promise: Promise) {
val paymentMethodType = mapToPaymentMethodType(getValOr(params, "type", null))
if (paymentMethodType != PaymentMethod.Type.USBankAccount) {
@@ -660,7 +643,6 @@ class StripeSdkModule(private val reactContext: ReactApplicationContext) : React
}
@ReactMethod
- @SuppressWarnings("unused")
fun verifyMicrodeposits(isPaymentIntent: Boolean, clientSecret: String, params: ReadableMap, promise: Promise) {
val amounts = params.getArray("amounts")
val descriptorCode = params.getString("descriptorCode")