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?
Pre-requistic
GoogleWalletModuleis added into React PackageMainApplication.kt(your Main class underandroid/src/main/java/com/example/something)For
startActivityForResultto 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 caseSuccess case
activity.sendResult(Activity.RESULT_OK, addToGoogleWalletRequestCode)Failure or cancel case
activity.sendResult(Activity.RESULT_CANCELED, addToGoogleWalletRequestCode)Passing
addToGoogleWalletRequestCodewithinsendResult(..)will help map the correct requester.Further Read: https://developer.android.com/training/basics/intents/result