Search code examples
androidreact-nativeexpobraintree

Starting custom activity in React Native with Expo


I am currently working at a React Native application built with Expo and I wish to integrate the Braintree SDK Drop-in UI component to create a check-out section. Unfortunately, I can not figure out a way to start on button click an activity that opens the native drop-in UI with parameters passed from the React Native application.

I have tried multiple alternatives, first of which was writing an Expo module. I managed to pass data to the Expo module, but I was unable to start an activity from within the Expo module. Another alternative I tried was writing an activity and adding it to the AndroidManifest.xml file, and trying to then start said activity using the startAsyncActivity method from the Expo Intent-Launcher, but I couldn't get that to work either.


Solution

  • I actually managed to solve this by creating a local Expo module (using npx create-expo-module@latest --local), adding a view with a button, and when the button is clicked, I create a new intent using the given context, and start the custom activity. Below you can find some code samples:

    MyModule.kt

    package expo.modules.mymodule
    
    import expo.modules.kotlin.modules.Module
    import expo.modules.kotlin.modules.ModuleDefinition
    
    class MyModule : Module() {
      override fun definition() = ModuleDefinition {
        Name("MyModule")
    
        View(MyModuleView::class) {}
      }
    }
    

    MyModuleView.kt

    package expo.modules.mymodule
    
    import android.util.Log
    import android.content.Context
    import android.content.Intent
    import android.util.TypedValue
    import android.view.Gravity
    import android.widget.Button
    import expo.modules.mymodule.MyActivity
    import expo.modules.kotlin.AppContext
    import expo.modules.kotlin.views.ExpoView
    
    class MyModuleView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
      internal val helloButton = Button(context).also {
        addView(it)
        it.text = "Checkout"
        it.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
        it.setOnClickListener {
          val myIntent = Intent(context, MyActivity::class.java)
          context.startActivity(myIntent);
        }
      }
    }
    

    MyActivity.kt

    package expo.modules.mymodule
    
    import android.os.Bundle
    import android.util.Log
    import android.widget.Toast
    import androidx.fragment.app.FragmentActivity
    import com.braintreepayments.api.DropInClient
    import com.braintreepayments.api.DropInListener
    import com.braintreepayments.api.DropInRequest
    import com.braintreepayments.api.DropInResult
    
    import java.lang.Exception
    
    class MyActivity : FragmentActivity(), DropInListener {
        private var dropInClient: DropInClient? = null;
        private var dropInRequest: DropInRequest? = null;
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            dropInRequest = DropInRequest();
            dropInClient = DropInClient(this@MyActivity,"authorization_token");
            dropInClient!!.setListener(this@MyActivity);
        }
    
        override fun onStart() {
            super.onStart()
            dropInClient!!.launchDropIn(dropInRequest)
        }
    
        override fun onDropInSuccess(dropInResult: DropInResult) {
            val token = dropInResult.paymentMethodNonce!!.string;
            Log.d("TestApplication",token);
        }
    
        override fun onDropInFailure(error: Exception) {
            Log.d("TestApplication",error.toString());
        }
    }
    

    Also, in order to integrate the local module in the React Native application, I added the following lines to my package.json:

      "private": true,
      "expo": {
        "autolinking": {
          "nativeModulesDir": "./modules"
        }
      }