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:¶
[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:¶
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:
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:
<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:
Overriding the callbacks:
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.
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:
// 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.
When the comparison starts, faceCompareStarted is triggered. It is recommended to present a progress indicator to the user at this stage:
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¶
val integrationModel = IntegrationModel(
idRegistration = IDRegistration(),
addressRegistration = AddressRegistration(),
data = ""
)
enQualifyFace.addIntegration(integrationModel)
override fun integrationAddCompleted() { Log.i(TAG, "integrationAddCompleted") }
override fun integrationAddFailed(errorMessage: String) { Log.i(TAG, "integrationAddFailed: $errorMessage") }
Closing Session and Clearing¶
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¶
Error Codes¶
FaceFailureCode¶
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:
override fun faceFailed(faceFailureCode: FaceFailureCode, additionalMessage: String?) {
Log.i(tag, "${object {}.javaClass.enclosingMethod?.name}")
}
⚠️ Important:
LuminosityFailedis 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:
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 |