> For the complete documentation index, see [llms.txt](https://docs.caf.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.caf.io/caf-sdk/react-native/standalone-modules/cafsmartauth/expo-modules/native-module-android.md).

# Native Module Android

## How to Create a Native Module in Android

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](https://docs.expo.dev/modules/native-module-tutorial/#android-native-module).

### 1. Open the Android Project in Android Studio

You can include this in your package.json under the scripts section:

```json
"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

```kotlin
class CafSmartAuthBridgeModule : Module() {
    private val context
        get() = requireNotNull(appContext.reactContext)
    private val activity
        get() = 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.
    override fun definition() = 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) {
                    throw IllegalStateException("$PERMISSION_ERROR_DESCRIPTION ${e.message}")
                }
            }
        }
    }

    private fun build(mfaToken: String, faceAuthToken: String, settings: String): CafSmartAuth {
        val smartAuthSettings = CafSmartAuthBridgeSettings(settings = settings)

        return CafSmartAuth.CafBuilder(mfaToken, context).apply {
            setSdkPlatform(CafSdkPlatform.REACT_NATIVE)
            smartAuthSettings.cafStage?.let { setStage(it) }
            smartAuthSettings.emailUrl?.let { setEmailUrl(it) }
            smartAuthSettings.phoneUrl?.let { setPhoneUrl(it) }
            setFaceAuthenticatorSettings(
                CafFaceAuthenticatorSettings(
                    faceAuthToken,
                    smartAuthSettings.faceAuthenticatorSettings?.loadingScreen,
                    smartAuthSettings.faceAuthenticatorSettings?.enableScreenCapture,
                    smartAuthSettings.faceAuthenticatorSettings?.filter
                )
            )
            setThemeConfigurator(
                CafThemeConfigurator(
                    lightTheme = smartAuthSettings.theme?.lightTheme ?: CafTheme(),
                    darkTheme = smartAuthSettings.theme?.darkTheme ?: CafTheme()
                )
            )
        }.build()
    }

    private fun setupListener() = object : CafVerifyPolicyListener {
        override fun onSuccess(
            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 ?: "")
            ))
        }

        override fun onPending(isAuthorized: Boolean, attestation: String?) {
            sendEvent(CAF_SMART_AUTH_PENDING_EVENT, mapOf(
                CAF_MAP_KEY_IS_AUTHORIZED to isAuthorized,
                CAF_MAP_KEY_ATTESTATION to (attestation ?: "")
            ))
        }

        override fun onError(failure: CafFailure, message: String?) {
            sendEvent(CAF_SMART_AUTH_ERROR_EVENT, mapOf(
                CAF_MAP_KEY_ERROR_MESSAGE to failure.message
            ))
        }

        override fun onCancel() {
            sendEvent(CAF_SMART_AUTH_CANCEL_EVENT, mapOf(CAF_MAP_KEY_IS_CANCELLED to true))
        }

        override fun onLoading() {
            sendEvent(CAF_SMART_AUTH_LOADING_EVENT, mapOf(CAF_MAP_KEY_IS_LOADING to true))
        }

        override fun onLoaded() {
            sendEvent(CAF_SMART_AUTH_LOADED_EVENT, mapOf(CAF_MAP_KEY_IS_LOADED to true))
        }
    }

    private fun requestPermission(permission: String, activity: Activity) {
        if (ContextCompat.checkSelfPermission(
                context,
                permission
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            ActivityCompat.requestPermissions(activity, arrayOf(permission), REQUEST_CODE)
        }
    }

    companion object {
        private const val REQUEST_CODE = 1234

        private const val CAF_SMART_AUTH_MODULE_NAME = "CafSmartAuthBridgeModule"
        private const val CAF_SMART_AUTH_FUNCTION_START_SMART_AUTH = "startSmartAuth"
        private const val CAF_SMART_AUTH_FUNCTION_REQUEST_LOCATION_PERMISSIONS = "requestLocationPermissions"

        private const val CAF_SMART_AUTH_SUCCESS_EVENT = "CafSmartAuth_Success"
        private const val CAF_SMART_AUTH_PENDING_EVENT = "CafSmartAuth_Pending"
        private const val CAF_SMART_AUTH_ERROR_EVENT = "CafSmartAuth_Error"
        private const val CAF_SMART_AUTH_CANCEL_EVENT = "CafSmartAuth_Cancel"
        private const val CAF_SMART_AUTH_LOADING_EVENT = "CafSmartAuth_Loading"
        private const val CAF_SMART_AUTH_LOADED_EVENT = "CafSmartAuth_Loaded"

        private const val CAF_MAP_KEY_IS_AUTHORIZED = "isAuthorized"
        private const val CAF_MAP_KEY_ATTEMPT_ID = "attemptId"
        private const val CAF_MAP_KEY_ATTESTATION = "attestation"
        private const val CAF_MAP_KEY_ERROR_MESSAGE = "errorMessage"
        private const val CAF_MAP_KEY_IS_CANCELLED = "isCancelled"
        private const val CAF_MAP_KEY_IS_LOADING = "isLoading"
        private const val CAF_MAP_KEY_IS_LOADED = "isLoaded"

        private const val 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.

#### Example Implementation:

```kotlin
internal data class CafFaceAuthenticationSettingsModel(
    val loadingScreen: Boolean?,
    val enableScreenCapture: Boolean?,
    val filter: CafFilterStyle?
)

internal data class CafSmartAuthBridgeTheme(
    val lightTheme: CafTheme,
    val darkTheme: CafTheme
)

internal class CafSmartAuthBridgeSettings(settings: String) : Serializable {
    val cafStage: CafStage?
    val emailUrl: String?
    val phoneUrl: String?
    val faceAuthenticatorSettings: CafFaceAuthenticationSettingsModel?
    val theme: CafSmartAuthBridgeTheme?

    init {
        val jsonObject = JSONObject(settings)

        cafStage = jsonObject.takeIf {
            it.has(STAGE)
        }?.let {
            CafStage.values()[it.getInt(STAGE)]
        }

        emailUrl = jsonObject.takeIf {
            it.has(EMAIL)
        }?.getString(EMAIL)

        phoneUrl = jsonObject.takeIf {
            it.has(PHONE)
        }?.getString(PHONE)

        faceAuthenticatorSettings = jsonObject.takeIf {
            it.has(FACE_AUTHENTICATION_SETTINGS)
        }?.let {
            val faceAuthenticatorSettings = it.getJSONObject(FACE_AUTHENTICATION_SETTINGS)

            val filterStyle = faceAuthenticatorSettings.takeIf { settings ->
                settings.has(FILTER)
            }?.let {
                CafFilterStyle.values()[faceAuthenticatorSettings.getInt(FILTER)]
            }

            CafFaceAuthenticationSettingsModel(
                faceAuthenticatorSettings.getBoolean(LOADING_SCREEN),
                faceAuthenticatorSettings.optBoolean(ENABLE_SCREEN_CAPTURE),
                filterStyle ?: CafFilterStyle.LINE_DRAWING
            )
        }

        theme = jsonObject.takeIf {
            it.has(THEME)
        }?.let {
            val theme = it.getJSONObject(THEME)

            val lightTheme = theme.optJSONObject(LIGHT_THEME)
            val darkTheme = theme.optJSONObject(DARK_THEME)

            CafSmartAuthBridgeTheme(
                lightTheme = parseTheme(lightTheme),
                darkTheme = parseTheme(darkTheme)
            )
        }
    }

    private companion object {
        const val STAGE = "stage"
        const val EMAIL = "emailUrl"
        const val PHONE = "phoneUrl"
        const val FACE_AUTHENTICATION_SETTINGS = "faceAuthenticationSettings"
        const val LOADING_SCREEN = "loadingScreen"
        const val ENABLE_SCREEN_CAPTURE = "enableScreenCapture"
        const val FILTER = "filter"
        const val THEME = "theme"
        const val LIGHT_THEME = "lightTheme"
        const val DARK_THEME = "darkTheme"
        const val BACKGROUND_COLOR = "backgroundColor"
        const val TEXT_COLOR = "textColor"
        const val PROGRESS_COLOR = "progressColor"
        const val LINK_COLOR = "linkColor"
        const val BOX_BACKGROUND_COLOR = "boxBackgroundColor"
        const val BOX_BORDER_COLOR = "boxBorderColor"
        const val BOX_TEXT_COLOR = "boxTextColor"

        const val BACKGROUND_COLOR_HEX = "#FFFFFF"
        const val TEXT_COLOR_HEX = "#FF000000"
        const val PRIMARY_COLOR_HEX = "#004AF7"
        const val BOX_BACKGROUND_COLOR_HEX = "#0A004AF7"
    }
}
```

With this, your native module is configured and ready to be used in React Native.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.caf.io/caf-sdk/react-native/standalone-modules/cafsmartauth/expo-modules/native-module-android.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
