how to call a method in startActivityForResult in android using kotlin?

248 Views Asked by At

I'm writing a native module in order to adding a generic pass to google wallet in React Native using kotlin. according to google wallet api documentation for android (https://developers.google.com/wallet/generic/android), I have to implement a onActivityResult function to get the result after user tap on the add to google wallet button.

According to React Native documentation about how to listen to a onActivityResult (https://reactnative.dev/docs/0.71/native-modules-android?android-language=kotlin#getting-activity-result-from-startactivityforresult) I'm trying to call walletClient.savePassesJwt(jwtSignedToken, this, addToGoogleWalletRequestCode) method to get it's result on onActivityResult function. I don't know what Intent to use for this matter and i get the followin error in JavaScript side of my React Native project.

{"info": "{
  \"nativeStackAndroid\": [
    {
      \"lineNumber\": 2174,
      \"file\": \"Instrumentation.java\",
      \"methodName\": \"checkStartActivityResult\",
      \"class\": \"android.app.Instrumentation\"
    },
    {
      \"lineNumber\": 1805,
      \"file\": \"Instrumentation.java\",
      \"methodName\": \"execStartActivity\",
      \"class\": \"android.app.Instrumentation\"
    },
    {
      \"lineNumber\": 5596,
      \"file\": \"Activity.java\",
      \"methodName\": \"startActivityForResult\",
      \"class\": \"android.app.Activity\"
    },
    {
      \"lineNumber\": 597,
      \"file\": \"ComponentActivity.java\",
      \"methodName\": \"startActivityForResult\",
      \"class\": \"androidx.activity.ComponentActivity\"
    },
    {
      \"lineNumber\": 5554,
      \"file\": \"Activity.java\",
      \"methodName\": \"startActivityForResult\",
      \"class\": \"android.app.Activity\"
    },
    {
      \"lineNumber\": 583,
      \"file\": \"ComponentActivity.java\",
      \"methodName\": \"startActivityForResult\",
      \"class\": \"androidx.activity.ComponentActivity\"
    },
    {
      \"lineNumber\": 112,
      \"file\": \"GoogleWalletModule.kt\",
      \"methodName\": \"savePassToGoogleWallet\",
      \"class\": \"com.peaktowertech.digivcard.GoogleWalletModule\"
    },
    {
      \"lineNumber\": -2,
      \"file\": \"Method.java\",
      \"methodName\": \"invoke\",
      \"class\": \"java.lang.reflect.Method\"
    },
    {
      \"lineNumber\": 372,
      \"file\": \"JavaMethodWrapper.java\",
      \"methodName\": \"invoke\",
      \"class\": \"com.facebook.react.bridge.JavaMethodWrapper\"
    },
    {
      \"lineNumber\": 188,
      \"file\": \"JavaModuleWrapper.java\",
      \"methodName\": \"invoke\",
      \"class\": \"com.facebook.react.bridge.JavaModuleWrapper\"
    },
    {
      \"lineNumber\": -2,
      \"file\": \"NativeRunnable.java\",
      \"methodName\": \"run\",
      \"class\": \"com.facebook.jni.NativeRunnable\"
    },
    {
      \"lineNumber\": 942,
      \"file\": \"Handler.java\",
      \"methodName\": \"handleCallback\",
      \"class\": \"android.os.Handler\"
    },
    {
      \"lineNumber\": 99,
      \"file\": \"Handler.java\",
      \"methodName\": \"dispatchMessage\",
      \"class\": \"android.os.Handler\"
    },
    {
      \"lineNumber\": 27,
      \"file\": \"MessageQueueThreadHandler.java\",
      \"methodName\": \"dispatchMessage\",
      \"class\": \"com.facebook.react.bridge.queue.MessageQueueThreadHandler\"
    },
    {
      \"lineNumber\": 226,
      \"file\": \"Looper.java\",
      \"methodName\": \"loopOnce\",
      \"class\": \"android.os.Looper\"
    },
    {
      \"lineNumber\": 313,
      \"file\": \"Looper.java\",
      \"methodName\": \"loop\",
      \"class\": \"android.os.Looper\"
    },
    {
      \"lineNumber\": 228,
      \"file\": \"MessageQueueThreadImpl.java\",
      \"methodName\": \"run\",
      \"class\": \"com.facebook.react.bridge.queue.MessageQueueThreadImpl$4\"
    },
    {
      \"lineNumber\": 1012,
      \"file\": \"Thread.java\",
      \"methodName\": \"run\",
      \"class\": \"java.lang.Thread\"
    }
  ],
  \"userInfo\": null,
  \"message\": \"No Activity found to handle Intent {  }\",
  \"code\": \"E_FAILED_TO_SHOW_WALLET_FLOW\"
}", "message": "Add to google wallet failed"}

My native kotlin module:

package com.example.something;
import android.app.Activity
import android.content.Intent
import android.util.Log
import com.facebook.react.bridge.BaseActivityEventListener
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.LifecycleEventListener
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.google.android.gms.pay.Pay
import com.google.android.gms.pay.PayClient
import com.google.android.gms.pay.PayApiAvailabilityStatus

class GoogleWalletModule(reactContext: ReactApplicationContext, private var walletClient: PayClient=Pay.getClient(reactContext)) : ReactContextBaseJavaModule(reactContext) {


    private val addToGoogleWalletRequestCode = 1000

    private var saveToWalletPromise: Promise? = null

    private val activityEventListener =
        object : BaseActivityEventListener() {
            override fun onActivityResult(
                activity: Activity?,
                requestCode: Int,
                resultCode: Int,
                data: Intent?
            ) {
                if (requestCode == addToGoogleWalletRequestCode) {

                    saveToWalletPromise?.let { promise ->

                        when (resultCode) {
                            Activity.RESULT_OK -> {
                                // Pass saved successfully
                            }

                            Activity.RESULT_CANCELED -> {
                                // Save operation canceled
                                promise.reject(E_ADD_TO_WALLET_CANCELLED, "Add to wallet was cancelled")
                            }

                            PayClient.SavePassesResult.SAVE_ERROR -> data?.let { intentData ->
                                val errorMessage =
                                    intentData.getStringExtra(PayClient.EXTRA_API_ERROR_MESSAGE)
                                // Handle error
                                promise.reject(E_FAILED_TO_SAVE_PASS,errorMessage)
                            }

                            else -> {
                                // Handle unexpected (non-API) exception
                            }
                        }
                    }
                }
            }
        }

    init {
        reactContext.addActivityEventListener(activityEventListener)
    }


    override fun getName() = "GoogleWalletModule"

    @ReactMethod fun isGoogleWalletApiAvailable(myFailureCallback: Callback, mySuccessCallback: Callback) {
        walletClient
            .getPayApiAvailabilityStatus(PayClient.RequestType.SAVE_PASSES)
            .addOnSuccessListener { status ->
                if (status == PayApiAvailabilityStatus.AVAILABLE) {
                    // The API is available, show the button in your UI
                    mySuccessCallback.invoke(true)
                } else {
                    // The user or device is not eligible for using the Pay API
                    mySuccessCallback.invoke(false)
                }
            }
            .addOnFailureListener {
                // Hide the button and optionally show an error message
                myFailureCallback.invoke("E_WRONG")
            }
    }


    @ReactMethod fun savePassToGoogleWallet(jwtSignedToken: String,promise: Promise) {
        val activity = currentActivity

        if (activity == null) {
            promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist")
            return
        }

        saveToWalletPromise = promise

        try {

            val igniterIntent = Intent().apply {
                () -> walletClient.savePassesJwt(jwtSignedToken, activity, addToGoogleWalletRequestCode)
            }

            activity.startActivityForResult(igniterIntent,addToGoogleWalletRequestCode)
        } catch (t: Throwable) {
            saveToWalletPromise?.reject(E_FAILED_TO_SHOW_ADD_TO_WALLET, t)
            saveToWalletPromise = null
        }

    }


    companion object {
        const val E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST"
        const val E_ADD_TO_WALLET_CANCELLED = "E_PICKER_CANCELLED"
        const val E_FAILED_TO_SHOW_ADD_TO_WALLET = "E_FAILED_TO_SHOW_WALLET_FLOW"
        const val E_FAILED_TO_SAVE_PASS = "E_FAILED_TO_SAVE_PASS"
    }

}

I am not familiar with android development. Could anyone help me how can I fix this?

1

There are 1 best solutions below

3
Nitesh Tiwari On
  • Pre-requistic

    • GoogleWalletModule is added into React Package
    • React Package is added into MainApplication.kt (your Main class under android/src/main/java/com/example/something)
  • For startActivityForResult to give result in the listener event. You would need to send the result back.

    • Within your function savePassesJwt(..) send the result by calling below functions for success and failure case

      • Success case

        • activity.sendResult(Activity.RESULT_OK, addToGoogleWalletRequestCode)
      • Failure or cancel case

        • activity.sendResult(Activity.RESULT_CANCELED, addToGoogleWalletRequestCode)
    • Passing addToGoogleWalletRequestCode within sendResult(..) will help map the correct requester.

Further Read: https://developer.android.com/training/basics/intents/result