stripe / stripe-android

Stripe Android SDK
https://stripe.com/docs/mobile/android
MIT License
1.26k stars 639 forks source link

[BUG] Crash on android compose 1.6.0-alpha03 #7184

Closed Hospes closed 1 year ago

Hospes commented 1 year ago

Summary

Stripe crashes on rememberPaymentSheet.launch(). I'm using android compose 1.6.0-alpha03. If I'm switching back to version 1.5.0 it's working fine. So the problem only with new version. But unfortunately new material3 forces all dependencies to 1.6.0-alpha03 so we can't downgrade android compose. Is it possible to provide hotfix or stripe-android alpha build with supporting newest android compose ?

Code to reproduce

Just launch StripeSheet activity with android compose dependency version 1.6.0-alpha03

Android version

Android 13

Impacted devices

OnePlus 10Pro

Installation method

gradle implementation("com.stripe:stripe-android:20.28.2")

Dependency Versions

kotlin: 1.9.0 stripe-android: 20.28.2 Android Gradle Plugin: 8.1.0 Gradle: 8.3

SDK classes

PaymentSheetScreen

Other information

Here is error stacktrace:

AndroidRuntime          E  FATAL EXCEPTION: main
                           Process: com.whoppah.qa, PID: 12762
                           java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/compose/ui/platform/LocalSoftwareKeyboardController;
                            at com.stripe.android.paymentsheet.ui.PaymentSheetScreenKt.DismissKeyboardOnProcessing(PaymentSheetScreen.kt:74)
                            at com.stripe.android.paymentsheet.ui.PaymentSheetScreenKt.PaymentSheetScreen(PaymentSheetScreen.kt:47)
                            at com.stripe.android.paymentsheet.PaymentSheetActivity$onCreate$2$1.invoke(PaymentSheetActivity.kt:67)
                            at com.stripe.android.paymentsheet.PaymentSheetActivity$onCreate$2$1.invoke(PaymentSheetActivity.kt:66)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                            at androidx.compose.material.MaterialTheme_androidKt.PlatformMaterialTheme(MaterialTheme.android.kt:23)
                            at androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:82)
                            at androidx.compose.material.MaterialThemeKt$MaterialTheme$1$1.invoke(MaterialTheme.kt:81)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                            at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:248)
                            at androidx.compose.material.TextKt.ProvideTextStyle(Text.kt:396)
                            at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:81)
                            at androidx.compose.material.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:80)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                            at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                            at androidx.compose.material.MaterialThemeKt.MaterialTheme(MaterialTheme.kt:72)
                            at com.stripe.android.uicore.StripeThemeKt$StripeTheme$1.invoke(StripeTheme.kt:331)
                            at com.stripe.android.uicore.StripeThemeKt$StripeTheme$1.invoke(StripeTheme.kt:330)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                            at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                            at com.stripe.android.uicore.StripeThemeKt.StripeTheme(StripeTheme.kt:325)
                            at com.stripe.android.paymentsheet.PaymentSheetActivity$onCreate$2.invoke(PaymentSheetActivity.kt:66)
                            at com.stripe.android.paymentsheet.PaymentSheetActivity$onCreate$2.invoke(PaymentSheetActivity.kt:65)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                            at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:428)
                            at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:252)
                            at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:251)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                            at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                            at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:186)
                            at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:119)
                            at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:118)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
AndroidRuntime          E   at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                            at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                            at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:110)
                            at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:160)
                            at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:159)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                            at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:248)
                            at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:159)
                            at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:144)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                            at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                            at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:78)
                            at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3243)
                            at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3176)
                            at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:654)
                            at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1041)
                            at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:562)
                            at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:144)
                            at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:135)
                            at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1220)
                            at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:135)
                            at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:185)
                            at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.kt:322)
                            at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.kt:199)
                            at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:142)
                            at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:135)
                            at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1295)
                            at android.view.View.dispatchAttachedToWindow(View.java:21506)
                            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3541)
                            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3548)
                            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3548)
                            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3548)
                            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3548)
                            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3548)
                            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3548)
                            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3548)
                            at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3548)
                            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2981)
                            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2458)
                            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9478)
                            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1412)
                            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1420)
                            at android.view.Choreographer.doCallbacks(Choreographer.java:1047)
                            at android.view.ChoreographerExtImpl.checkScrollOptSceneEnable(ChoreographerExtImpl.java:451)
                            at android.view.Choreographer.doFrame(Choreographer.java:918)
AndroidRuntime          E   at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1395)
                            at android.os.Handler.handleCallback(Handler.java:942)
                            at android.os.Handler.dispatchMessage(Handler.java:99)
                            at android.os.Looper.loopOnce(Looper.java:240)
                            at android.os.Looper.loop(Looper.java:351)
                            at android.app.ActivityThread.main(ActivityThread.java:8416)
                            at java.lang.reflect.Method.invoke(Native Method)
                            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584)
                            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)
                           Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.compose.ui.platform.LocalSoftwareKeyboardController" on path: DexPathList[[zip file "/data/app/~~Mrp_akYfAb8pbKhbKIMIRQ==/com.whoppah.qa-ibSB-qne7_BIArXbw4WmGg==/base.apk"],nativeLibraryDirectories=[/data/app/~~Mrp_akYfAb8pbKhbKIMIRQ==/com.whoppah.qa-ibSB-qne7_BIArXbw4WmGg==/lib/arm64, /system/lib64, /system_ext/lib64]]
                            at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:259)
                            at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
                            at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
                            ... 94 more
tillh-stripe commented 1 year ago

Hi @Hospes 👋 It seems like LocalSoftwareKeyboardController is being removed in the alpha version of Compose UI 1.6.0. Therefore, you’re seeing the ClassNotFoundException.

Are you able to downgrade to a non-alpha version of Compose Material3? It seems like version 1.1.1 doesn’t depend on the alpha version of Compose UI.

Once Compose UI 1.6.0 is stable, we’ll be looking to upgrade ourselves and address the issue then.

Hospes commented 1 year ago

Thank you for your answer. Well reverting back to material2 gonna be not an easy task, but we don't have any other options. So will do it.

tillh-stripe commented 1 year ago

I don’t think you’ll need to downgrade from Material 3 to Material 2. It looks to me like version 1.1.1 of Material 3 doesn’t depend on any alpha versions, so that should be good enough.

I’m going to close this issue now, but feel free to reach out with any other issues you encounter.

davwheat commented 11 months ago

This is a blocker for our integration of Stripe while migrating away from Braintree in our org.

We use LocalSoftwareKeyboardController within our code to dismiss the software keyboard in Material 3, so I feel like this could be a red herring.

tillh-stripe commented 11 months ago

Hi David 👋 We can’t depend on Compose 1.6.0 just yet, as it’s an alpha version.

A temporary workaround could be for you to use LocalTextInputService instead of LocalSoftwareKeyboardController. That’s what the latter uses internally (despite its method being marked as deprecated).

Is that feasible for you?

davwheat commented 11 months ago

I don't think so... We heavily depend on Material 3 alpha at the moment due to its DatePicker composable, etc.

Would there be any issues with locally modifying the library to depend on 1.6.0-alpha? Or potentially an alpha version of the SDK to match? I get this isn't ideal for anyone, though.

whatiamdoing commented 11 months ago

is it possible to exclude compose dependencies by exclude(group = "com.stripe", module = "paymentsheet")?

davwheat commented 11 months ago

Hi David 👋 We can’t depend on Compose 1.6.0 just yet, as it’s an alpha version.

A temporary workaround could be for you to use LocalTextInputService instead of LocalSoftwareKeyboardController. That’s what the latter uses internally (despite its method being marked as deprecated).

Is that feasible for you?

Looking back at this again this week, I don't really understand what's causing this issue.

Stripe uses LocalSoftwareKeyboardController in its SDK code, and so do we within our code.

LocalSoftwareKeyboardController hasn't been removed in Compose 1.6.0-alpha (else obviously we couldn't be using it in our app which depends on this).

I think there's some other issue I'm missing here.

yogurtearl commented 10 months ago

The problem here is that Stripe Android SDK is calling an ExperimentalComposeUiApi API, which is subject to change.

https://github.com/stripe/stripe-android/blob/65289496bca8dc41f02faf6a19996ed4ef6fa86d/paymentsheet/src/main/java/com/stripe/android/common/ui/BottomSheetKeyboardHandler.kt#L33-L38

LocalSoftwareKeyboardController changed in compose.ui 1.6.0-alpha02 See here: https://android-review.googlesource.com/c/platform/frameworks/support/+/2628449

In Compose 1.5.x it was:

@ExperimentalComposeUiApi
public object LocalSoftwareKeyboardController {

After 1.6.0-alpha02 it is this:

val LocalSoftwareKeyboardController = staticCompositionLocalOf<SoftwareKeyboardController?> { null }

Compose made a binary incompatible change, which is to be expected for anything marked with @ExperimentalComposeUiApi.

Ideally, the Stripe SDK would not use any @OptIn APIs from it's dependencies, as it will create binary compatibility problems.

One possible workaround for apps that are facing this issue:

in app/src/main/kotlin/androidx/compose/ui/platform/LocalSoftwareKeyboardController.kt put this bridge:

package androidx.compose.ui.platform

import androidx.compose.runtime.Composable

// Used by Stripe Android SDK
// Work around for https://github.com/stripe/stripe-android/issues/7184
// This existed as Experimental in Compose 1.5, but was moved to a val in in Compose 1.6
@Suppress("unused")
object LocalSoftwareKeyboardController {
    val current
        @Composable
        get() = LocalSoftwareKeyboardController.current
}
tillh-stripe commented 10 months ago

Thanks for posting the workaround here, @yogurtearl 🙏 And you’re right, I should have been more careful around using this experimental API. The team and I will be more conscious of this going forward.

eldenskloss commented 9 months ago

Just for the next developer who comes along as lost as I was:

Step 1: Create a path in your project -> app/src/main/kotlin/androidx/compose/ui/platform (For me I had to create "/kotlin/androidx/compose/ui/platform"

Step 2: Create the LocalSoftwareKeyboardController class in that directory and copy the above code into it.

Step 3: You did it! Have a drink and stop hitting your head on the table.

jvanderwee commented 7 months ago

Presumably no longer an issue after #7840 is merged and released 🙏

hotellinawebmaster commented 6 months ago

Hi @Hospes 👋 It seems like LocalSoftwareKeyboardController is being removed in the alpha version of Compose UI 1.6.0. Therefore, you’re seeing the ClassNotFoundException.

Are you able to downgrade to a non-alpha version of Compose Material3? It seems like version 1.1.1 doesn’t depend on the alpha version of Compose UI.

Once Compose UI 1.6.0 is stable, we’ll be looking to upgrade ourselves and address the issue then.

Last working Compose UI version I tried within the 1.5.x span is

implementation 'androidx.compose.ui:ui-android:1.5.4'