Skip to content

Android - Face Module

The Face module analyzes whether a person is a real individual based on images captured from the front camera. It can detect facial expressions and movements such as smiling, blinking, turning the head left/right, and looking up. It is primarily used in authentication processes that require liveliness checks.

⚠️ Prerequisite: Maven access must be provided and the Core Module must be added to the project before using the Face Module.


Adding to the Project

1. Add to libs.versions.toml file:

Text Only
[versions]
...
enqualify-plus = "x.x.x.x"

[libraries]
...
enqualify-plus-face = {
    group = "com.enqualify.plus",
    name = "face",
    version.ref = "enqualify-plus"
}

2. Add the dependency to build.gradle.kts file:

Text Only
implementation(libs.enqualify.plus.face)

3. Gradle Sync

Sync the Gradle files by clicking the "Sync Now" option.


Implementation

1. Permissions

Camera permission is automatically added to the manifest by the SDK and is requested at necessary points again by the SDK. The following line is for informational purposes only:

Text Only
<uses-permission android:name="android.permission.CAMERA" />

The Face module will not work if camera permission is not granted.

2. Activity Layout — FrameLayout

A FrameLayout that covers the entire screen should be added to the layout of the Activity where the face operation will be performed. The SDK manages camera screens and page transitions through this component:

Text Only
<FrameLayout
    android:id="@+id/fragmentContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

3. Adding FaceCallbacks Interface

The FaceCallbacks interface contains the necessary callback methods to manage face operations and must work with an Activity.

Adding to the class:

Text Only
class EnQualifyPlusActivity : AppCompatActivity(), FaceCallbacks

Overriding the callbacks:

Text Only
class EnQualifyPlusActivity : AppCompatActivity(), FaceCallbacks {

    override fun initializeCompleted(modules: EnModules) {
        Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
        when (modules) {
            EnModules.FACE -> {
                // Face operations can be started
            }
        }
    }

    override fun initializeFailed(failureCode: FailureCode, additionalMessage: String) {
        Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
    }

    // Liveliness step callbacks
    override fun faceDetected() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun smileDetected() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun eyeCloseDetected() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun eyeCloseIntervalDetected() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun faceRightDetected() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun faceLeftDetected() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun faceUpDetected() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }

    // Comparison callbacks
    override fun faceCompareStarted() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun faceCompareCompleted() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun faceCompareFailed(errorMessage: String) { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }

    // Completion / error callbacks
    override fun faceSaveStarted() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun faceCompleted() {
        Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
        enQualifyFace.faceCompare()     // faceCompare is started after faceCompleted
    }
    override fun faceDetectFailed(failureCode: FaceFailureCode) { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun faceFailed(faceFailureCode: FaceFailureCode, additionalMessage: String?) {
        Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
        // LuminosityFailed is not a critical error; the process should not be halted.
    }

    override fun integrationAddCompleted() { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
    override fun integrationAddFailed(errorMessage: String) { Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}") }
}

4. Initializing EnQualifyFace

Before starting face operations, EnQualifyFace.initialize() must be called. If another operation is attempted before initializing, the SDK will not work.

Text Only
val sessionModel = SessionModel(
    callType = "NewCustomer",
    reference = UUID.randomUUID().toString()
)

val baseModel = BaseModel(
    baseURL = "https://deveqmapi.enqura.com",
    signallingCertificateList = listOf("enqura"),
    mapiCertificateList = listOf("enqura"),
    mobileUser = "mobile",
    countryCode = "TUR",
    locale = "TR",
    useEmbeddedLocalSound = false
)

EnQualifyFace.initialize(
    context = this,
    fragmentManager = supportFragmentManager,
    containerID = R.id.fragmentContainer,
    sessionModel = sessionModel,
    baseModel = baseModel,
    stepCount = 3
)

Initialize Parameters

Parameter Type Description
context Context Runtime context of the application
stepCount Int Number of steps to be taken in the liveliness flow
sessionModel SessionModel Session information to be used during the face operation
baseModel BaseModel Model carrying basic data during the verification process
fragmentManager FragmentManager Manages the Face fragments
containerID Int The ID of the container where the Face fragment will be added

Automatic Flow After Initialization

After the initialize() is called, the SDK automatically executes the steps of Token → Session → Settings in order. In case of errors, the relevant callbacks are triggered. When all steps are successfully completed, initializeCompleted is triggered, and face operations can be started.


Face Operations

Functions

Function Default Success Condition Description
faceDetect() -15° ≤ angle ≤ 15° on X and Y axis Face detection on the camera — mandatory starting point for other steps
smileDetect() Smile value > 0.9 Smile detection
eyeCloseDetect() Eye closure value < 0.3 Eye closure detection
faceRightDetect() Right turn angle ≥ 15° Head turning to the right detection
faceLeftDetect() Left turn angle ≥ 15° Head turning to the left detection
faceUpDetect() Upward angle ≥ 10° Head lifting up detection
faceComplete() Informs the SDK that all liveliness steps are completed; camera analysis stops, data is sent to the backoffice
faceCompare() Compares the liveliness image with the biometric photo obtained from OCR/NFC
addFragment(fragment) Adds a new fragment on top of the existing fragment
replaceFragment(fragment) Replaces the current fragment with a new one
addIntegration(integrationModel) Adds integration data
closeSession(isFinished: Boolean) Closes the session
clear() Clears the SDK
getSessionUId() Returns the UId of the current session.

Typical Liveliness Flow

Below is a suggested example of a typical liveliness flow. The steps can be customized according to the stepCount value and your workflow:

Text Only
// 1. Face detection
enQualifyFace.faceDetect()

override fun faceDetected() {
    // 2. Smile
    enQualifyFace.smileDetect()
}

override fun smileDetected() {
    // 3. Eye closure
    enQualifyFace.eyeCloseDetect()
}

override fun eyeCloseDetected() {
    // All steps completed — finish the flow
    enQualifyFace.faceComplete()
}

override fun faceCompleted() {
    // Data sent to the backoffice — start comparison
    enQualifyFace.faceCompare()
}

override fun faceCompareCompleted() {
    // Liveliness and comparison successful — proceed to the result page
    enQualifyFace.replaceFragment(FaceResultFragment())
}

Face Comparison (faceCompare)

faceCompare() performs the face recognition process by comparing the image obtained during the liveliness step with the biometric photo obtained from OCR and NFC.

Text Only
enQualifyFace.faceCompare()

When the comparison starts, faceCompareStarted is triggered. It is recommended to present a progress indicator to the user at this stage:

Text Only
override fun faceCompareStarted() {
    // Show progress
}

override fun faceCompareCompleted() {
    // Comparison successful; redirect to the result page
}

override fun faceCompareFailed(errorMessage: String) {
    // Comparison failed; error handling
}

Adding Integration Data

Text Only
val integrationModel = IntegrationModel(
    idRegistration = IDRegistration(),
    addressRegistration = AddressRegistration(),
    data = ""
)
enQualifyFace.addIntegration(integrationModel)
Text Only
override fun integrationAddCompleted() { Log.i(TAG, "integrationAddCompleted") }
override fun integrationAddFailed(errorMessage: String) { Log.i(TAG, "integrationAddFailed: $errorMessage") }

Closing Session and Clearing

Text Only
enQualifyFace.closeSession(isFinished = true)

override fun sessionCloseCompleted() {
    Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}

override fun sessionCloseFailed(errorMessage: String) {
    Log.i(tag, "${object {}.javaClass.enclosingMethod?.name} $errorMessage")
}

// Clear the SDK
enQualifyFace.clear()

ℹ️ After the session is closed, initialize() must be called again to use the SDK again.


Retrieving Session Information

Text Only
// Function returning SessionUId
enQualifyFace.getSessionUId()

Error Codes

FaceFailureCode

Text Only
enum class FaceFailureCode(val errorMessage: String) {
    SmilingError("No smile detected during the capture session."),
    FaceDetectionError("No face detected during the capture session."),
    FaceUpError("No Face Up during the capture session."),
    FaceLeftError("No Face Left detected during the capture session."),
    FaceRightError("No Face Right during the capture session."),
    EyeCloseError("No Eye close during the capture session."),
    EyeOpenError("No Eye Open detected during the capture session."),
    FaceTimeout("FaceTimeout"),
    EyeCloseIntervalError("No Eye close during the capture session."),
    NoFaceFrameCountError("NoFaceFrameCountError"),
    LuminosityFailed("LuminosityFailed"),   // Not critical
    Failed(""),
    FaceSaveFailed("")
}

It is communicated through the faceFailed callback:

Text Only
override fun faceFailed(faceFailureCode: FaceFailureCode, additionalMessage: String?) {
    Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}

⚠️ Important: LuminosityFailed is a warning and is not critical. The process should not be halted when this error occurs.


Accessing Result Data

After the face operation is completed, the data obtained can be accessed through the CustomerFace object:

Text Only
CustomerFace.getInstance()

Refer to the Core Module — CustomerFace page for the explanation of the CustomerFace model and all its fields.


Callback Reference

Token / Session / Settings

Callback Description
tokenCreateCompleted(isNewCreatedToken: Boolean) Triggered when the token is created
tokenCreateFailed(errorMessage: String) Token creation error
sessionAddFailed(errorMessage: String) Session creation error
settingsGetFailed(errorMessage: String) Triggered when settings could not be retrieved
sessionCloseCompleted(status: CallSessionTypeStatus) Triggered when the session is closed
sessionCloseFailed(errorMessage: String) Session closing error
integrationAddCompleted() Integration data successfully added
integrationAddFailed(errorMessage: String) Integration data could not be added
initializeFailed(failureCode: FailureCode, additionalMessage: String) Core initialize error

Face — Liveliness Steps

Callback Description
initializeCompleted(module: EnModules) Initialization completed, Face is ready
faceDetected() Face detected
smileDetected() Smile detected
eyeCloseDetected() Eye closure detected
eyeCloseIntervalDetected() Eyes were detected to be closed for a certain duration
faceRightDetected() Head turning to the right detected
faceLeftDetected() Head turning to the left detected
faceUpDetected() Head lifting up detected
faceSaveStarted() Data is being sent to the backoffice
faceCompleted() Data has been successfully sent to the backoffice
faceDetectFailed(failureCode: FaceFailureCode) Face detection failed
faceFailed(faceFailureCode, additionalMessage) Face operation error

Face — Comparison

Callback Description
faceCompareStarted() Comparison started
faceCompareCompleted() Comparison successfully completed
faceCompareFailed(errorMessage: String) Comparison error