Skip to content

Android - Face Module

The Face module analyzes whether a person is a real individual based on images obtained 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 particularly used in authentication processes that require liveness checks.

⚠️ Precondition: 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 the build.gradle.kts file:

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

3. Gradle Sync

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


Implementation

1. Permissions

Camera permission is automatically added to the manifest by the SDK and requested by the SDK at necessary points. 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 must be added to the layout of the Activity where the face process will take place. The SDK manages camera displays 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 the FaceCallbacks Interface

The FaceCallbacks interface contains the necessary callback methods to manage the face processes 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 processes can be started
            }
        }
    }

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

    // Liveness 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 stopped.
    }

    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 processes, EnQualifyFace.initialize() must be called. If another operation is attempted without initialization, 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 Application runtime context
stepCount Int Number of steps to be performed in the liveness flow
sessionModel SessionModel Session information to be used during the face process
baseModel BaseModel Model carrying basic data in the validation process
fragmentManager FragmentManager Manages the face fragments
containerID Int ID of the container where the face fragment will be added

Automatic Flow After Initialization

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


Face Processes

Functions

Function Default Success Condition Description
faceDetect() -15° ≤ angle ≤ 15° on X and Y axes Face detection in the camera — mandatory start for other steps
smileDetect() Smile value > 0.9 Smile detection
eyeCloseDetect() Eye closure value < 0.3 Eye close detection
faceRightDetect() Right turn angle ≥ 15° Detects head turning to the right
faceLeftDetect() Left turn angle ≥ 15° Detects head turning to the left
faceUpDetect() Upward lift angle ≥ 10° Detects head lifting upward
faceComplete() — Informs the SDK that all liveness steps are finished; camera analysis stops, data is sent to the back office
faceCompare() — Compares the liveness visuals with the biometric photo obtained from OCR/NFC
addFragment(fragment) — Adds a new fragment on top of the current 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 Liveness Flow

Below is a suggested example of a typical liveness flow. 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 close
    enQualifyFace.eyeCloseDetect()
}

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

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

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

Face Comparison (faceCompare)

faceCompare() performs face recognition by comparing the visual obtained during the liveness step with the biometric photo obtained from OCR and NFC processes.

Text Only
enQualifyFace.faceCompare()

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

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

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

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

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 and Clearing Session

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.


Getting Session Information

Text Only
// Function that returns the 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 conveyed 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 stopped when this error occurs.


Accessing the Result Data

Once the face process is completed, the data obtained can be accessed through the CustomerFace object:

Text Only
CustomerFace.getInstance()

For the CustomerFace model and descriptions of all fields, refer to the Core Module — CustomerFace page.


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 cannot be retrieved
sessionCloseCompleted(status: CallSessionTypeStatus) Triggered when the session is closed
sessionCloseFailed(errorMessage: String) Session closing error
integrationAddCompleted() Integration data added successfully
integrationAddFailed(errorMessage: String) Integration data could not be added
initializeFailed(failureCode: FailureCode, additionalMessage: String) Core initialization error

Face — Liveness Steps

Callback Description
initializeCompleted(module: EnModules) Initialization completed, Face ready
faceDetected() Face detected
smileDetected() Smile detected
eyeCloseDetected() Eye close detected
eyeCloseIntervalDetected() Eyes kept closed for a certain period detected
faceRightDetected() Head turning to the right detected
faceLeftDetected() Head turning to the left detected
faceUpDetected() Head lifting upward detected
faceSaveStarted() Data transfer to back office has begun
faceCompleted() Data successfully sent to back office
faceDetectFailed(failureCode: FaceFailureCode) Face detection failed
faceFailed(faceFailureCode, additionalMessage) Face process error

Face — Comparison

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