This detailed guide explains how to create a expo module for the Android platform within a Expo application. The process follows the step-by-step instructions from the official documentation.
1. Open the Android Project in Android Studio
You can include this in your package.json under the scripts section:
"scripts": {"open:android":"open -a \"Android Studio\" android"},
This script ensures that the Expo module opens correctly in Android Studio.
2. Create the Native Module
Create a file named CafSmartAuthBridgeModule.kt in the directory modules/caf-smart-auth-react-native/android/app/src/main/java/com/your-app-name/. This file will contain the class that implements the native module.
Key Functions of the CafSmartAuthBridgeModule Class
build: The build function is responsible for configuring and instantiating the CafSmartAuth object using the provided parameters. This function applies various settings, such as defining stages and facial authentication, to prepare the SDK for use.
setupListener: Sets up a listener to monitor the status of verification operations, such as success, pending, error, cancellation, and others.
requestPermission: Requests the necessary permissions, such as location permissions, for the proper functioning of the SDK.
startSmartAuth: Exposed to JavaScript, this function starts the smart authentication process using the received parameters.
requestLocationPermissions: Exposed to JavaScript, allows the application to request location permissions from the user.
Example Implementation of the CafSmartAuthBridgeModule.kt File
classCafSmartAuthBridgeModule : Module() {privateval contextget() =requireNotNull(appContext.reactContext)privateval activityget() =requireNotNull(appContext.currentActivity)// Each module class must implement the definition function. The definition consists of components// that describes the module's functionality and behavior.// See https://docs.expo.dev/modules/module-api for more details about available components.overridefundefinition() =ModuleDefinition {// Sets the name of the module that JavaScript code will use to refer to the module. Takes a string as an argument.// Can be inferred from module's class name, but it's recommended to set it explicitly for clarity.// The module will be accessible from `requireNativeModule('CafSmartAuthBridgeModule')` in JavaScript.Name(CAF_SMART_AUTH_MODULE_NAME)// Defines event names that the module can send to JavaScript.Events( CAF_SMART_AUTH_SUCCESS_EVENT, CAF_SMART_AUTH_PENDING_EVENT, CAF_SMART_AUTH_ERROR_EVENT, CAF_SMART_AUTH_CANCEL_EVENT, CAF_SMART_AUTH_LOADING_EVENT, CAF_SMART_AUTH_LOADED_EVENT )// Defines a JavaScript synchronous function that runs the native code on the JavaScript thread.Function(CAF_SMART_AUTH_FUNCTION_START_SMART_AUTH) { mfaToken: String, faceAuthToken: String, personId: String, policyId: String, jsonString: String ->Handler(Looper.getMainLooper()).post {val smartAuth =build( mfaToken = mfaToken, faceAuthToken = faceAuthToken, settings = jsonString ) smartAuth.verifyPolicy(personId, policyId, setupListener()) } }// Defines a JavaScript asynchronous function that runs the native code on the JavaScript thread.AsyncFunction(CAF_SMART_AUTH_FUNCTION_REQUEST_LOCATION_PERMISSIONS) {Handler(Looper.getMainLooper()).post {try {requestPermission( permission = Manifest.permission.ACCESS_FINE_LOCATION, activity = activity )requestPermission( permission = Manifest.permission.ACCESS_COARSE_LOCATION, activity = activity ) } catch (e: Exception) {throwIllegalStateException("$PERMISSION_ERROR_DESCRIPTION ${e.message}") } } } }privatefunbuild(mfaToken: String, faceAuthToken: String, settings: String): CafSmartAuth {val faceAuthenticationSettings =CafSmartAuthBridgeSettings(settings = settings)return CafSmartAuth.CafBuilder(mfaToken, context) .apply {setSdkPlatform(CafSdkPlatform.REACT_NATIVE) faceAuthenticationSettings.cafStage?.let { setStage(it) }setFaceAuthenticatorSettings(CafFaceAuthenticatorSettings( faceAuthToken, faceAuthenticationSettings.faceAuthenticatorSettings?.loadingScreen, faceAuthenticationSettings.faceAuthenticatorSettings?.enableScreenCapture, faceAuthenticationSettings.faceAuthenticatorSettings?.filter ) ) } .build() }privatefunsetupListener() =object : CafVerifyPolicyListener {overridefunonSuccess( isAuthorized: Boolean, attemptId: String?, attestation: String? ) {sendEvent(CAF_SMART_AUTH_SUCCESS_EVENT, mapOf( CAF_MAP_KEY_IS_AUTHORIZED to isAuthorized, CAF_MAP_KEY_ATTEMPT_ID to (attemptId ?: ""), CAF_MAP_KEY_ATTESTATION to (attestation ?: "") )) }overridefunonPending(isAuthorized: Boolean, attestation: String?) {sendEvent(CAF_SMART_AUTH_PENDING_EVENT, mapOf( CAF_MAP_KEY_IS_AUTHORIZED to isAuthorized, CAF_MAP_KEY_ATTESTATION to (attestation ?: "") )) }overridefunonError(failure: CafFailure) {sendEvent(CAF_SMART_AUTH_ERROR_EVENT, mapOf( CAF_MAP_KEY_ERROR_MESSAGE to failure.message )) }overridefunonCancel() {sendEvent(CAF_SMART_AUTH_CANCEL_EVENT, mapOf(CAF_MAP_KEY_IS_CANCELLED to true)) }overridefunonLoading() {sendEvent(CAF_SMART_AUTH_LOADING_EVENT, mapOf(CAF_MAP_KEY_IS_LOADING to true)) }overridefunonLoaded() {sendEvent(CAF_SMART_AUTH_LOADED_EVENT, mapOf(CAF_MAP_KEY_IS_LOADED to true)) } }privatefunrequestPermission(permission: String, activity: Activity) {if (ContextCompat.checkSelfPermission( context, permission ) != PackageManager.PERMISSION_GRANTED ) { ActivityCompat.requestPermissions(activity, arrayOf(permission), REQUEST_CODE) } }companionobject {privateconstval REQUEST_CODE =1234privateconstval CAF_SMART_AUTH_MODULE_NAME ="CafSmartAuthBridgeModule"privateconstval CAF_SMART_AUTH_FUNCTION_START_SMART_AUTH ="startSmartAuth"privateconstval CAF_SMART_AUTH_FUNCTION_REQUEST_LOCATION_PERMISSIONS ="requestLocationPermissions"privateconstval CAF_SMART_AUTH_SUCCESS_EVENT ="CafSmartAuth_Success"privateconstval CAF_SMART_AUTH_PENDING_EVENT ="CafSmartAuth_Pending"privateconstval CAF_SMART_AUTH_ERROR_EVENT ="CafSmartAuth_Error"privateconstval CAF_SMART_AUTH_CANCEL_EVENT ="CafSmartAuth_Cancel"privateconstval CAF_SMART_AUTH_LOADING_EVENT ="CafSmartAuth_Loading"privateconstval CAF_SMART_AUTH_LOADED_EVENT ="CafSmartAuth_Loaded"privateconstval CAF_MAP_KEY_IS_AUTHORIZED ="isAuthorized"privateconstval CAF_MAP_KEY_ATTEMPT_ID ="attemptId"privateconstval CAF_MAP_KEY_ATTESTATION ="attestation"privateconstval CAF_MAP_KEY_ERROR_MESSAGE ="errorMessage"privateconstval CAF_MAP_KEY_IS_CANCELLED ="isCancelled"privateconstval CAF_MAP_KEY_IS_LOADING ="isLoading"privateconstval CAF_MAP_KEY_IS_LOADED ="isLoaded"privateconstval PERMISSION_ERROR_DESCRIPTION ="Error checking location permissions:" }}
3. Create the CafSmartAuthBridgeSettings.kt File
This file will handle the interpretation of data sent from React Native to the native module.