Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent text change race condition when moving the focus #10255

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

cttsai-stripe
Copy link
Contributor

Summary

https://jira.corp.stripe.com/browse/RUN_MOBILESDK-3861
Delay the focus change when completing each card input field.

Motivation

#10178
A rare race condition where TextWatcher removal during active text change callback iteration causes IndexOutOfBoundsException, affecting our legacy CardInputWidget, CardMultilineWidget, and CardFormView

Testing

  • Added tests
  • Modified tests
  • Manually verified

Copy link
Contributor

github-actions bot commented Feb 21, 2025

Diffuse output:

OLD: paymentsheet-example-release-master.apk (signature: V1, V2)
NEW: paymentsheet-example-release-pr.apk (signature: V1, V2)

          │           compressed           │         uncompressed         
          ├───────────┬───────────┬────────┼──────────┬──────────┬────────
 APK      │ old       │ new       │ diff   │ old      │ new      │ diff   
──────────┼───────────┼───────────┼────────┼──────────┼──────────┼────────
      dex │   4.1 MiB │   4.1 MiB │ +178 B │    9 MiB │    9 MiB │ +264 B 
     arsc │   2.4 MiB │   2.4 MiB │    0 B │  2.4 MiB │  2.4 MiB │    0 B 
 manifest │   5.1 KiB │   5.1 KiB │    0 B │ 25.7 KiB │ 25.7 KiB │    0 B 
      res │ 910.9 KiB │ 910.9 KiB │    0 B │  1.4 MiB │  1.4 MiB │    0 B 
   native │   2.6 MiB │   2.6 MiB │    0 B │    6 MiB │    6 MiB │    0 B 
    asset │   1.6 MiB │   1.6 MiB │  -15 B │  1.6 MiB │  1.6 MiB │  -15 B 
    other │   1.4 MiB │   1.4 MiB │   +6 B │  1.6 MiB │  1.6 MiB │    0 B 
──────────┼───────────┼───────────┼────────┼──────────┼──────────┼────────
    total │  12.9 MiB │  12.9 MiB │ +169 B │ 22.1 MiB │ 22.1 MiB │ +249 B 

 DEX     │ old   │ new   │ diff           
─────────┼───────┼───────┼────────────────
   files │     1 │     1 │  0             
 strings │ 42781 │ 42782 │ +1 (+8 -7)     
   types │ 15351 │ 15352 │ +1 (+7 -6)     
 classes │ 12958 │ 12959 │ +1 (+1 -0)     
 methods │ 62691 │ 62693 │ +2 (+167 -165) 
  fields │ 41697 │ 41699 │ +2 (+163 -161) 

 ARSC    │ old  │ new  │ diff 
─────────┼──────┼──────┼──────
 configs │  243 │  243 │  0   
 entries │ 6273 │ 6273 │  0
APK
    compressed     │    uncompressed    │                                           
──────────┬────────┼───────────┬────────┤                                           
 size     │ diff   │ size      │ diff   │ path                                      
──────────┼────────┼───────────┼────────┼───────────────────────────────────────────
  4.1 MiB │ +178 B │     9 MiB │ +264 B │ ∆ classes.dex                             
  8.1 KiB │  -15 B │     8 KiB │  -15 B │ ∆ assets/dexopt/baseline.prof             
 50.6 KiB │  +10 B │ 119.9 KiB │    0 B │ ∆ META-INF/MANIFEST.MF                    
    270 B │   -2 B │     120 B │    0 B │ ∆ META-INF/version-control-info.textproto 
  1.2 KiB │   -2 B │   1.2 KiB │    0 B │ ∆ META-INF/CERT.RSA                       
──────────┼────────┼───────────┼────────┼───────────────────────────────────────────
  4.2 MiB │ +169 B │   9.2 MiB │ +249 B │ (total)
DEX
STRINGS:

   old   │ new   │ diff       
  ───────┼───────┼────────────
   42781 │ 42782 │ +1 (+8 -7) 
  
  + Lq9/r0;
  + [Lq9/E;
  + [Lq9/N;
  + [Lq9/e0;
  + [Lq9/g0;
  + [Lq9/p0;
  + [Lq9/x;
  + ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"cf74da0","r8-mode":"full","version":"8.8.27"}
  
  - [Lq9/D;
  - [Lq9/M;
  - [Lq9/d0;
  - [Lq9/f0;
  - [Lq9/o0;
  - [Lq9/w;
  - ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":21,"pg-map-id":"7bc847a","r8-mode":"full","version":"8.8.27"}
  

TYPES:

   old   │ new   │ diff       
  ───────┼───────┼────────────
   15351 │ 15352 │ +1 (+7 -6) 
  
  + Lq9/r0;
  + [Lq9/E;
  + [Lq9/N;
  + [Lq9/e0;
  + [Lq9/g0;
  + [Lq9/p0;
  + [Lq9/x;
  
  - [Lq9/D;
  - [Lq9/M;
  - [Lq9/d0;
  - [Lq9/f0;
  - [Lq9/o0;
  - [Lq9/w;
  

METHODS:

   old   │ new   │ diff           
  ───────┼───────┼────────────────
   62691 │ 62693 │ +2 (+167 -165) 
  
  + M1.e <init>(b, n0)
  + Z4.j b(a, long, W, ContinuationImpl) → Object
  + com.stripe.android.view.CardMultilineWidget getCardNumberErrorListener_payments_core_release() → o0
  + com.stripe.android.view.CardMultilineWidget getCvcErrorListener_payments_core_release() → o0
  + com.stripe.android.view.CardMultilineWidget getExpirationDateErrorListener_payments_core_release() → o0
  + com.stripe.android.view.CardMultilineWidget getPostalCodeErrorListener_payments_core_release() → o0
  + com.stripe.android.view.CardMultilineWidget setCardNumberErrorListener(o0)
  + com.stripe.android.view.CardMultilineWidget setCardNumberErrorListener_payments_core_release(o0)
  + com.stripe.android.view.CardMultilineWidget setCardValidCallback(F)
  + com.stripe.android.view.CardMultilineWidget setCvcErrorListener(o0)
  + com.stripe.android.view.CardMultilineWidget setCvcErrorListener_payments_core_release(o0)
  + com.stripe.android.view.CardMultilineWidget setExpirationDateErrorListener(o0)
  + com.stripe.android.view.CardMultilineWidget setExpirationDateErrorListener_payments_core_release(o0)
  + com.stripe.android.view.CardMultilineWidget setPostalCodeErrorListener(o0)
  + com.stripe.android.view.CardMultilineWidget setPostalCodeErrorListener_payments_core_release(o0)
  + com.stripe.android.view.PaymentAuthWebViewActivity l() → c0
  + com.stripe.android.view.PostalCodeEditText getConfig_payments_core_release() → e0
  + com.stripe.android.view.PostalCodeEditText setConfig_payments_core_release(e0)
  + com.stripe.android.view.ShippingInfoWidget a(g0) → boolean
  + com.stripe.android.view.StripeEditText setAfterTextChangedListener(m0)
  + com.stripe.android.view.StripeEditText setDeleteEmptyListener(n0)
  + com.stripe.android.view.StripeEditText setErrorMessageListener(o0)
  + e6.j <init>(CollectBankAccountActivity, Z)
  + q9.A <init>(CardNumberEditText, Continuation)
  + q9.B <init>(h0, Continuation, CardNumberEditText)
  + q9.C <init>(E, h0, Continuation, CardNumberEditText)
  + q9.C h(Object, Object) → Object
  + q9.C n(Object, Continuation) → Continuation
  + q9.C q(Object) → Object
  + q9.D <init>(Object, int)
  + q9.D onLayoutChange(View, int, int, int, int, int, int, int, int)
  + q9.E <clinit>()
  + q9.E valueOf(String) → E
  + q9.E values() → E[]
  + q9.G <init>(View, int)
  + q9.G a(Animation)
  + q9.G b(Animation)
  + q9.G c(Animation)
  + q9.G d(Animation)
  + q9.G onAnimationEnd(Animation)
  + q9.G onAnimationRepeat(Animation)
  + q9.G onAnimationStart(Animation)
  + q9.H <init>(Context, AttributeSet)
  + q9.H onAttachedToWindow()
  + q9.I <init>(K, ContinuationImpl)
  + q9.J <init>(K, Continuation)
  + q9.J h(Object, Object) → Object
  + q9.J n(Object, Continuation) → Continuation
  + q9.J q(Object) → Object
  + q9.K <init>(E, T)
  + q9.K h(K, ContinuationImpl) → Object
  + q9.L <init>(List, h, Activity)
  + q9.L performFiltering(CharSequence) → Filter_FilterResults
  + q9.L publishResults(CharSequence, Filter_FilterResults)
  + q9.M <init>(h, a)
  + q9.M fixText(CharSequence) → CharSequence
  + q9.M isValid(CharSequence) → boolean
  + q9.N <clinit>()
  + q9.N <init>(f, Parcelable)
  + q9.N describeContents() → int
  + q9.N equals(Object) → boolean
  + q9.N hashCode() → int
  + q9.N toString() → String
  + q9.N writeToParcel(Parcel, int)
  + q9.O <init>(CountryTextInputLayout, boolean)
  + q9.O onLayoutChange(View, int, int, int, int, int, int, int, int)
  + q9.P <clinit>()
  + q9.Q <init>(TextInputLayout)
  + q9.S <init>(ExpiryDateEditText)
  + q9.S afterTextChanged(Editable)
  
...✂

@tjclawson-stripe
Copy link
Collaborator

@cttsai-stripe just a heads up, this won't be pulled into stripe-react-native until we upgrade the react native version so we can update to the latest stripe-android version OR we backport the fix

@cttsai-stripe
Copy link
Contributor Author

@cttsai-stripe just a heads up, this won't be pulled into stripe-react-native until we upgrade the react native version so we can update to the latest stripe-android version OR we backport the fix

Do we do that in the rn repo or the android repo?
I think the fix is not urgent since a. the third party module also fixes this on their side b. the broken part is rare and legacy.

@tjclawson-stripe
Copy link
Collaborator

We'd cherry-pick this commit to the release/20.52 branch and then do a release from there which would then be automatically picked up by stripe-react-native. I agree this doesn't seem high enough priority to do so, but I wouldn't respond on the issue that it's fixed in stripe-react-native

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants