LogoLogo
Useful links
  • Home
  • Product guides
  • API
  • SDKs
  • Overview
  • First steps
  • ANDROID
    • Getting Started with the SDK
    • Standalone Modules
      • Document Detector
        • Release Notes
        • Current Version
        • Requirements
        • Gradle Dependencies
        • Gradle Source Code
        • Setting up the SDK
          • Setting properties
          • Capture Stages
          • Messages Settings
          • Customization
          • Security Settings
          • Detection Steps
          • Upload Mode
          • Advanced Settings
            • Proxy configuration
            • Preview configuration
        • Start Document Detection
        • Source Code
        • Google security form
        • Reduce SDKs size
        • SDKs response
        • References
        • FAQ
      • Face Liveness
        • Release Notes
        • Current Version
        • Requirements
        • Gradle Dependencies
        • Gradle Source Code
        • SDK Lifecycle
        • Building the SDK
        • Start Liveness Verification
        • Source Code
        • References
        • Advanced Features
        • FAQ
      • Face Authenticator
        • Release Notes
      • Smart Auth
        • Release Notes
        • Current Version
        • Requirements
        • Gradle Dependencies
        • Gradle Source Code
        • Permissions
        • SDK Lifecycle
        • Building the SDK
        • Start Smart Authentication
        • Source Code
        • References
        • FAQ
      • Face Liveness (deprecated)
        • Release Notes
  • iOS
    • Getting Started with the SDK
    • Standalone Modules
      • Document Detector
        • Release Notes
        • Current Version
        • Requirements
        • Installing the SDK
        • Setting up the SDK
          • Setting properties
          • Messages Settings
          • Customization
          • Detection Steps
          • Upload Mode
          • Advanced Settings
            • Proxy configuration
            • Preview configuration
        • Start Document Detection
        • References
        • FAQ
      • Face Liveness
        • Release Notes
        • Installation
        • Current Version
        • Requirements
        • SDK Lifecycle
        • Building the SDK
        • Start Liveness Verification
        • Source Code
        • References
        • FAQ
      • Face Authenticator
        • Release Notes
        • Installation
        • Current Version
        • Requirements
        • Building the SDK
        • Start the SDK
        • References
        • FAQ
      • Smart Auth
        • Release Notes
        • Installation
        • Current Version
        • Requirements
        • SDK Lifecycle
        • Building the SDK
        • Start Smart Authentication
        • Source Code
        • References
        • FAQ
      • Face Liveness (deprecated)
        • Release Notes
  • REACT NATIVE
    • Standalone Modules
      • Document Detector
        • Release Notes
        • Current Version
        • Requirements
        • Installation
        • Hooks
        • Start Document Verification
        • Source Code
        • TypeScript References
        • Customizing Style
        • FAQ
      • Face Liveness
        • Release Notes
        • Current Version
        • Requirements
        • Installation
        • Hooks
        • Start Liveness Verification
        • Source Code
        • TypeScript References
        • FAQ
      • Face Authenticator
        • Release Notes
        • Current Version
        • Requirements
        • Installation
        • Hooks
        • Start Authentication Verification
        • Source Code
        • TypeScript References
        • FAQ
      • Smart Auth
        • Getting started
        • Release notes
        • Using Native Modules
          • Requirements
          • Gradle Source Code
          • Podfile Source Code
          • Native Module Android
          • Native Module iOS
          • Import Native Modules
          • Source Code
          • TypeScript References
          • FAQ
        • Using Expo Modules
          • Requirements
          • Create Local Expo Module
          • Gradle Source Code
          • Podspec Source Code
          • Native Module Android
          • Native Module iOS
          • Import Expo Modules
          • Source Code
          • TypeScript References
          • FAQ
  • WEB (JAVASCRIPT)
    • Standalone Modules
      • Document Detector
        • Getting started
        • SDK builder options
          • Analytics
          • Appearance
          • Messages
        • SDK methods
        • Event listeners
        • Customization
        • Release notes
      • Face Liveness
        • Customization
        • Release notes
      • Face Authenticator
        • Customization
        • Release notes
      • Smart Auth
        • SDK errors
        • Customization
        • Release notes
LogoLogo

2025 © Caf. - All rights reserved

On this page
  • How to Create a Native Module in Android
  • 1. Open the Android Project in Android Studio
  • 2. Create the Native Module
  • 3. Create the CafSmartAuthBridgeSettings.kt File
  • 4. Register the Native Module
  1. REACT NATIVE
  2. Standalone Modules
  3. Smart Auth
  4. Using Native Modules

Native Module Android

Last updated 10 days ago

How to Create a Native Module in Android

This detailed guide explains how to create a native module for the Android platform within a React Native application. The process follows the step-by-step instructions from the .

1. Open the Android Project in Android Studio

First, open the Android project of your React Native application in Android Studio. You can find the Android project in the folder: YourApp/android.

Using Android Studio helps in quickly identifying issues, such as syntax errors, and provides powerful tools for Android development.

2. Create the Native Module

Create a file named CafSmartAuthBridgeModule.kt in the directory 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

  • getName: This function returns the name of the native module, which will be used to access it in JavaScript within React Native. The name must be unique to avoid conflicts with other modules.

  • 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.

  • emitEvent: Allows sending events from the native side to JavaScript. Events can include success, errors, or other relevant statuses.

  • 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

class CafSmartAuthBridgeModule(private val reactContext: ReactApplicationContext) :
    ReactContextBaseJavaModule(reactContext) {

    @Nonnull
    override fun getName(): String {
        return CAF_SMART_AUTH_BRIDGE_MODULE
    }

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

        return CafSmartAuth.CafBuilder(mfaToken, reactContext).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 emitEvent(eventName: String, params: Any) {
        reactApplicationContext
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
            .emit(eventName, params)
    }

    private fun setupListener() = object : CafVerifyPolicyListener {
        override fun onSuccess(
            isAuthorized: Boolean,
            attemptId: String?,
            attestation: String?
        ) {
            emitEvent(eventName = CAF_SMART_AUTH_SUCCESS_EVENT, params = WritableNativeMap().apply {
                putBoolean(CAF_WRITABLE_MAP_IS_AUTHORIZED, isAuthorized)
                putString(CAF_WRITABLE_MAP_ATTEMPT_ID, attemptId)
                putString(CAF_WRITABLE_MAP_ATTESTATION, attestation)
            })
        }

        override fun onPending(isAuthorized: Boolean, attestation: String) {
            emitEvent(eventName = CAF_SMART_AUTH_PENDING_EVENT, params = WritableNativeMap().apply {
                putBoolean(CAF_WRITABLE_MAP_IS_AUTHORIZED, isAuthorized)
                putString(CAF_WRITABLE_MAP_ATTESTATION, attestation)
            })
        }

        override fun onError(failure: CafFailure, message: String?) {
            emitEvent(eventName = CAF_SMART_AUTH_ERROR_EVENT, params = WritableNativeMap().apply {
                putString(CAF_WRITABLE_MAP_ERROR_MESSAGE, failure.message)
            })
        }

        override fun onCancel() {
            emitEvent(eventName = CAF_SMART_AUTH_CANCEL_EVENT, params = true)
        }

        override fun onLoading() {
            emitEvent(eventName = CAF_SMART_AUTH_LOADING_EVENT, params = true)
        }

        override fun onLoaded() {
            emitEvent(eventName = CAF_SMART_AUTH_LOADED_EVENT, params = true)
        }
    }

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

    @ReactMethod
    fun startSmartAuth(
        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())
        }
    }

    @ReactMethod
    fun requestLocationPermissions(promise: Promise) {
        val activity = currentActivity ?: return

        Handler(Looper.getMainLooper()).post {
            try {
                requestPermission(
                    permission = Manifest.permission.ACCESS_FINE_LOCATION,
                    activity = activity
                )
                requestPermission(
                    permission = Manifest.permission.ACCESS_COARSE_LOCATION,
                    activity = activity
                )
                promise.resolve(true)
            } catch (e: Exception) {
                promise.reject(
                    PERMISSION_ERROR,
                    "$PERMISSION_ERROR_DESCRIPTION ${e.message}"
                )
            }
        }
    }

    companion object {
        private const val REQUEST_CODE = 1234

        private const val CAF_SMART_AUTH_BRIDGE_MODULE = "CafSmartAuthBridgeModule"

        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_WRITABLE_MAP_IS_AUTHORIZED = "isAuthorized"
        private const val CAF_WRITABLE_MAP_ATTEMPT_ID = "attemptId"
        private const val CAF_WRITABLE_MAP_ATTESTATION = "attestation"

        private const val CAF_WRITABLE_MAP_ERROR_MESSAGE = "message"

        private const val PERMISSION_ERROR = "PERMISSION_ERROR"
        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:

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.entries[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.entries[faceAuthenticatorSettings.getInt(FILTER)]
            }

            CafFaceAuthenticationSettingsModel(
                faceAuthenticatorSettings.optBoolean(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 fun parseTheme(json: JSONObject?): CafTheme {
        return json?.let {
            CafTheme(
                backgroundColor = json.optString(BACKGROUND_COLOR).takeIf { it.isNotEmpty() }
                    ?: BACKGROUND_COLOR_HEX,
                textColor = json.optString(TEXT_COLOR).takeIf { it.isNotEmpty() }
                    ?: TEXT_COLOR_HEX,
                progressColor = json.optString(PROGRESS_COLOR).takeIf { it.isNotEmpty() }
                    ?: PRIMARY_COLOR_HEX,
                linkColor = json.optString(LINK_COLOR).takeIf { it.isNotEmpty() }
                    ?: PRIMARY_COLOR_HEX,
                boxBackgroundColor = json.optString(BOX_BACKGROUND_COLOR).takeIf { it.isNotEmpty() }
                    ?: BOX_BACKGROUND_COLOR_HEX,
                boxBorderColor = json.optString(BOX_BORDER_COLOR).takeIf { it.isNotEmpty() }
                    ?: PRIMARY_COLOR_HEX,
                boxTextColor = json.optString(BOX_TEXT_COLOR).takeIf { it.isNotEmpty() }
                    ?: PRIMARY_COLOR_HEX
            )
        } ?: CafTheme(
            backgroundColor = BACKGROUND_COLOR_HEX,
            textColor = TEXT_COLOR_HEX,
            progressColor = PRIMARY_COLOR_HEX,
            linkColor = PRIMARY_COLOR_HEX,
            boxBackgroundColor = BOX_BACKGROUND_COLOR_HEX,
            boxBorderColor = PRIMARY_COLOR_HEX,
            boxTextColor = PRIMARY_COLOR_HEX
        )
    }

    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"
    }
}

4. Register the Native Module

To make the native module recognizable by React Native, create a file named CafPackage.kt in the same directory:

Example Implementation:

class CafPackage : ReactPackage {
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        return listOf(CafSmartAuthBridgeModule(reactContext))
    }

    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return emptyList()
    }
}

In the MainApplication.kt file, add the CafPackage module inside the getPackages method:

Example Implementation:

override fun getPackages(): List<ReactPackage> =
    PackageList(this).packages.apply {
        add(CafPackage())
    }

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

official documentation