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:¶
[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:¶
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:
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:
<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:
Overriding the callbacks:
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.
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:
// 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.
When the comparison starts, faceCompareStarted is triggered. It is recommended to provide the user with a progress indicator at this stage:
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¶
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 and Clearing Session¶
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¶
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 conveyed 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 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:
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 |