Complete Manual for Facial Detection in Android Applications
Estimated time: 45-60 minutes for complete configuration
What will you learn from this manual?
That manual will teach you how to implement and use SDK Face Detector JAAK for real-time facial recognition in native Android applications. No advanced technical skills required - Just follow the steps.
Before Start - Checks list
Be sure to have these elements ready:
- Android Studio Iguana installed
- Android device with API 26 + (Android 8.0 +)
- Access to Maren Repository JAAK
- Basic knowledge of Kotlin / Java
- Configure camera and storage permits
Contents Index
| Section | What will you do? | Time |
|---|---|---|
| Step 1 | Configure Project and Units | 10 min |
| Step 2 | Basic implementation of SDK | 20 min |
| Step 3 | Configure Permissions and FileProvider | 10 min |
| Step 4 | Management of responses and base64 | 15 min |
| Step 5 | Test functionalities | 10 min |
Step 1: Configure Project and Units
Objective
Configure the development environment and add the necessary SDK.
Technical requirements
| Requirement | Version | Mandatory? |
|---|---|---|
| Android Studio | Iguana | Yes |
| Fat | 8.4+ | Yes |
| minSdk | 26 | Yes |
| targetSdkVersion | 33 | Yes |
| Kotlin | 1.9.22+ | Yes |
1.1 Building (Project) configuration
buildscript {
ext.kotlin_version = "1.9.22"
ext.hilt_version = '2.46'
repositories {
google()
mavenCentral()
maven {
url 'https://us-maven.pkg.dev/jaak-platform/jaak-android'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:8.3.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
}
}
1.2 Settings.gradle configuration
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {
url 'https://us-maven.pkg.dev/jaak-platform/jaak-android'
}
}
}
1.3 Build.gradle configuration (Module app)
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk 35
defaultConfig {
minSdk 26
targetSdk 33
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_18
targetCompatibility JavaVersion.VERSION_18
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("androidx.constraintlayout:constraintlayout:2.2.0")
implementation("com.google.dagger:hilt-android:$hilt_version")
kapt("com.google.dagger:hilt-android-compiler:$hilt_version")
// Face Detector SDK
implementation("com.jaak.facedetector:jaakfacedetector-sdk:2.0.5")
}
Step 2: Basic implementation of SDK
Objective
To create the basic implementation of the Face Detector SDK Your application.
2.1 Create Application Class
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class TestApp : Application()
2.2 Main activity
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.jaak.facedetector.FaceDetectorSDK
import com.jaak.facedetector.FaceDetectorListener
import com.example.databinding.ActivityMainBinding
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity(), FaceDetectorListener {
private lateinit var binding: ActivityMainBinding
private lateinit var faceDetectorSDK: FaceDetectorSDK
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setupFaceDetector()
setupButtons()
}
private fun setupFaceDetector() {
faceDetectorSDK = FaceDetectorSDK(this, this)
// Basic configuration
faceDetectorSDK.setEnableCamera(true)
faceDetectorSDK.setEnableDisk(true)
faceDetectorSDK.setEnableCameraPhoto(true)
faceDetectorSDK.setEnableCameraVideo(true)
faceDetectorSDK.setEnableDiskPhoto(true)
faceDetectorSDK.setEnableDiskVideo(true)
faceDetectorSDK.setFacesOrDocuments(true)
faceDetectorSDK.setImageFormat("image/*")
faceDetectorSDK.setVideoFormat("video/*")
faceDetectorSDK.setImageSize(3)
faceDetectorSDK.setVideoSize(10)
}
private fun setupButtons() {
binding.btnFullMenu.setOnClickListener {
faceDetectorSDK.startFaceDetector(0) // Full Menu
}
binding.btnCameraOnly.setOnClickListener {
faceDetectorSDK.startFaceDetector(2) // Just camera
}
binding.btnStorageOnly.setOnClickListener {
faceDetectorSDK.startFaceDetector(4) // Storage only
}
}
override fun onSuccessFaceDetector(typeProcess: Int, uri: Uri?) {
// Manejar resultado exitoso
handleSuccess(typeProcess, uri)
}
override fun onErrorFaceDetector(text: String) {
// Handling mistakes
handleError(text)
}
private fun handleSuccess(typeProcess: Int, uri: Uri?) {
val message = when (typeProcess) {
1 -> "Photo captured from camera"
2 -> "Video captured from camera"
3 -> "Photo select from storage"
4 -> "Selected video from storage"
else -> "Completion"
}
binding.tvStatus.text = message
// Procure file if necessary
uri?.let { processFile(it, typeProcess) }
}
private fun handleError(text: String) {
binding.tvStatus.text = "Error: $text"
}
private fun processFile(uri: Uri, type: Int) {
// Here you can process the captured file
// For example, convert to base64 or send it to a server
}
}
2.3 Layout XML (activity _ main.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp">
<TextView
android:id="@+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="JAAK Face Detector"
android:textSize="24sp"
android:textStyle="bold"
android:textAlignment="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="20dp" />
<TextView
android:id="@+id/tvStatus"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Press a button to start"
android:textAlignment="center"
android:padding="15dp"
android:background="@drawable/rounded_background"
app:layout_constraintTop_toBottomOf="@id/tvTitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="30dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
app:layout_constraintTop_toBottomOf="@id/tvStatus"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="40dp">
<Button
android:id="@+id/btnFullMenu"
android:layout_width="280dp"
android:layout_height="60dp"
android:text="Complete Menu"
android:textSize="16sp"
android:layout_marginBottom="15dp" />
<Button
android:id="@+id/btnCameraOnly"
android:layout_width="280dp"
android:layout_height="60dp"
android:text="Camera only"
android:textSize="16sp"
android:layout_marginBottom="15dp" />
<Button
android:id="@+id/btnStorageOnly"
android:layout_width="280dp"
android:layout_height="60dp"
android:text="Storage only"
android:textSize="16sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Passage 3: Configure Permises and FileProvider
Objective
Configure camera and storage permits and FileProvider for file management.
3.1 AndroidManifes.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Permisos necesarios -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:replace="android:maxSdkVersion"
android:maxSdkVersion="28" />
<application
android:name=".TestApp"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
tools:replace="android:theme,android:name">
<activity
android:name=".MainActivity"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- FileProvider para manejo seguro de archivos -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
3.2 Create file _ paths.xml
Create File res/xml/file_paths.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="Pictures/" />
<external-path name="my_files" path="Android/data/${applicationId}/files/" />
<cache-path name="my_cache" path="." />
</paths>
3.3 Performance of Permises at Runtime
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import android.Manifest
import android.content.pm.PackageManager
class MainActivity : AppCompatActivity(), FaceDetectorListener {
companion object {
private const val CAMERA_PERMISSION_CODE = 100
private const val STORAGE_PERMISSION_CODE = 101
}
private fun checkAndRequestPermissions() {
val permissionsNeeded = mutableListOf<String>()
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.CAMERA)
}
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.P) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
permissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
}
if (permissionsNeeded.isNotEmpty()) {
ActivityCompat.requestPermissions(
this,
permissionsNeeded.toTypedArray(),
CAMERA_PERMISSION_CODE
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
CAMERA_PERMISSION_CODE -> {
if (grantResults.isNotEmpty() &&
grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
binding.tvStatus.text = "Permisos concedidos"
} else {
binding.tvStatus.text = "Permisos denegados"
}
}
}
}
}
PASO 4: Answers and Base64 Conversion Management
Objective
Implement complete management of responses from SDK and conversion to base64.
4.1 Utils class for Base64 conversion
import android.content.ContentResolver
import android.net.Uri
import android.util.Base64
import java.io.ByteArrayOutputStream
import java.io.FileInputStream
import java.io.InputStream
object Utils {
fun imageCameraUriToBase64(uri: Uri): String? {
return try {
val inputStream = FileInputStream(uri.path)
val bytes = inputStream.readBytes()
inputStream.close()
Base64.encodeToString(bytes, Base64.DEFAULT)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
fun imageFileUriToBase64(contentResolver: ContentResolver, uri: Uri): String? {
return try {
val inputStream = contentResolver.openInputStream(uri)
val bytes = inputStream?.readBytes()
inputStream?.close()
bytes?.let { Base64.encodeToString(it, Base64.DEFAULT) }
} catch (e: Exception) {
e.printStackTrace()
null
}
}
fun videoCameraUriToBase64(uri: Uri): String? {
return try {
val inputStream = FileInputStream(uri.path)
val bytes = inputStream.readBytes()
inputStream.close()
Base64.encodeToString(bytes, Base64.DEFAULT)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
fun videoFileUriToBase64(contentResolver: ContentResolver, uri: Uri): String? {
return try {
val inputStream = contentResolver.openInputStream(uri)
val bytes = inputStream?.readBytes()
inputStream?.close()
bytes?.let { Base64.encodeToString(it, Base64.DEFAULT) }
} catch (e: Exception) {
e.printStackTrace()
null
}
}
}
4.2 Complete Management of Responses
override fun onSuccessFaceDetector(typeProcess: Int, uri: Uri?) {
var base64Process = ""
var message = ""
uri?.let { fileUri ->
when (typeProcess) {
1 -> {
// Photo from camera
base64Process = Utils.imageCameraUriToBase64(fileUri) ?: ""
message = "Photo captured from camera"
}
2 -> {
// Video from camera
base64Process = Utils.videoCameraUriToBase64(fileUri) ?: ""
message = "Video captured from camera"
}
3 -> {
// Photo from storage
base64Process = Utils.imageFileUriToBase64(contentResolver, fileUri) ?: ""
message = "Photo select from storage"
}
4 -> {
// Video from storage
base64Process = Utils.videoFileUriToBase64(contentResolver, fileUri) ?: ""
message = "Selected video from storage"
}
}
binding.tvStatus.text = message
// Procure the base64 file
if (base64Process.isNotEmpty()) {
processBase64File(base64Process, typeProcess)
showFileInfo(fileUri, base64Process.length)
}
}
}
override fun onErrorFaceDetector(text: String) {
binding.tvStatus.text = "Error: $text"
// Log for debugging
android.util.Log.e("FaceDetector", "Error: $text")
}
private fun processBase64File(base64: String, type: Int) {
// Here you can process the base64 file
// Example: send them to a server, save them locally, etc.
android.util.Log.d("FaceDetector", "Reprocessing Base64 file type: $type")
android.util.Log.d("FaceDetector", "Basal size 64: ${base64.length} characters")
// Example of sending to server
// sendToServer(base64, type)
}
private fun showFileInfo(uri: Uri, base64Length: Int) {
val fileInfo = "Archivo procesado:\n" +
"URI: ${uri.path}\n" +
"Basal size 64: ${base64Length} characters\ n" +
"approx.: ${base64Length * 3 / 4 / 1024} KB"
// Show information on an additional TextView or log
android.util.Log.i("FaceDetector", fileInfo)
}
Step 5: Test Functionality
Objective
Check that all functionalities of SDK They work properly.
checklist
| Element | State | Description |
|---|---|---|
| Camera permits | ☐ | Application requests and gets permits |
| Photo capture | ☐ | It works from camera |
| Video capture | ☐ | It works from camera |
| File selection | ☐ | It works from storage |
| Basal conversion64 | ☐ | Files are turned right |
Test process
Step A: Test Permissions
- Install Application as a physical device
- Open Application first time
- Check request automatic permits
- Grant permits and storage
Step B: Test Capture from Camera
- Press "Just Camera"
- Take photo using the camera interface
- Check message success on screen
- Review login to confirm base64 conversion
Step C: Test Selection from Storage
- Press "Just Storage"
- Select File of images or videos
- Check processing file
- Confirm conversion a base64
Debugging and Troublehooting
Useful Loks for Debug
private fun enableDebugLogs() {
// Add to onCreate () for debugging
android.util.Log.d("FaceDetector", "=== INICIANDO DEBUG ===")
android.util.Log.d("FaceDetector", "SDK version: 2.0.5")
android.util.Log.d("FaceDetector", "Camera permits: ${checkCameraPermission ()}")
android.util.Log.d("FaceDetector", "Permisos almacenamiento: ${checkStoragePermission()}")
}
private fun checkCameraPermission(): Boolean {
return ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) ==
PackageManager.PERMISSION_GRANTED
}
private fun checkStoragePermission(): Boolean {
return if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.P) {
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ==
PackageManager.PERMISSION_GRANTED
} else {
true // No required on Android 10 +
}
}
Common problems
| Problem | Resolution |
|---|---|
| "Permission error" | Check AndroidManifes.xml and Runtime permissions |
| "FileProvider error" | Review file settings _ paths.xml |
| "Unfound file" | Check routes and FileProvider authorities |
| "SDK doesn't answer " | Check Hill dependencies and configuration |
Full reference
Methods SDK
| Method | Description | Example |
|---|---|---|
| SetEnableCamera (Boolean) | Capture from camera | faceDetectorSDK.setEnableCamera(true) |
| SetEnableDesk (Boolean) | Enabling selection from storage | faceDetectorSDK.setEnableDisk(true) |
| SetImageSize (Int) | Maximum image size in MB | faceDetectorSDK.setImageSize(3) |
| SetVideoSize (Int) | Maximum video size in MB | faceDetectorSDK.setVideoSize(10) |
| startFaceDetector (Int) | Start the process | faceDetectorSDK.startFaceDetector(0) |
Types of Process
| Type | Description | When used |
|---|---|---|
| 0 | Full Menu | Show all options |
| 2 | Just camera | Filling capture from camera |
| 4 | Storage only | Forging file selection |
Response Codes
| Code | Description |
|---|---|
| 1 | Photo captured from camera |
| 2 | Video captured from camera |
| 3 | Photo select from storage |
| 4 | Selected video from storage |
Problem solving
Problem: "compilation error"
// Incorrecto
implementation("com.jaak.facedetector:jaakfacedetector-sdk:2.0.5")
// Correct - Check Repository Access
maven {
url 'https://us-maven.pkg.dev/jaak-platform/jaak-android'
}
Problem: "Rejected permits"
// solution: Check Settings if permits are blocked
private fun openAppSettings() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
val uri = Uri.fromParts("package", packageName, null)
intent.data = uri
startActivity(intent)
}
Problem: "FileProvider authority conflicts"
<!-- Usar applicationId para evitar conflictos -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
Do you need some help?
Support information
- Problem description: What are you trying to do with what's going on?
- Android Studio Logs: Screenshots of errors and warnings
- Building Setup Unit versions
- Test device: Model, Android version, API level
- Replay steps: The exact sequence caused by the error
Support Contact
Email: support Always include: Logs, configuration, version of SDK
You have successfully implemented the SDK Face Detector JAAK your Android app. Your app can now capture and process face images / videos for KYC and biometric monitoring.