JAAK Stamps SDK Android
⏱️ Tiempo estimado: 60-75 minutos para configuración completa
🎯 ¿Qué aprenderás en este manual?
Este manual te enseñará a implementar y usar el JAAKStamps SDK para captura automatizada de documentos de identidad en aplicaciones Android nativas con inteligencia artificial. No necesitas conocimientos técnicos avanzados - solo sigue los pasos.
📋 Antes de Empezar - Lista de Verificación
Asegúrate de tener estos elementos listos:
- Android Studio Iguana instalado
- Dispositivo Android 6.0+ (API 23+)
- Gradle 8.4 configurado
- Conocimientos básicos de Kotlin/Android
- Acceso a cámara funcional
- Documento de identidad(INE, IFE, Pasaporte...) para pruebas
🗂️ Índice de Contenidos
Sección | Qué harás | Tiempo |
---|---|---|
Paso 1 | Configurar proyecto y dependencias | 15 min |
Paso 2 | Implementación básica del SDK | 25 min |
Paso 3 | Configurar permisos y manifiestos | 10 min |
Paso 4 | Manejo de respuestas e imágenes | 15 min |
Paso 5 | Probar captura de documentos | 10 min |
PASO 1: Configurar Proyecto y Dependencias
🎯 Objetivo
Configurar el entorno de desarrollo Android y añadir las dependencias necesarias del SDK.
✅ Requisitos Técnicos
Requisito | Versión | ¿Obligatorio? |
---|---|---|
Android Studio | Iguana | Sí |
minSdkVersion | 23 (Android 6.0+) | Sí |
targetSdkVersion | 33 | Sí |
compileSdk | 35 | Sí |
Gradle | 8.4 | Sí |
Java/Kotlin | 17 | Sí |
1.1 Configuración build.gradle (Project)
buildscript {
ext.kotlin_version = "1.9.22"
ext.hilt_version = '2.48'
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 Configuración build.gradle (Module: app)
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdk 35
defaultConfig {
applicationId "tu.paquete.aqui"
minSdk 23
targetSdk 33
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
buildFeatures {
viewBinding = true
}
}
dependencies {
// Android Core
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.android.material:material:1.12.0'
// Dependency Injection
implementation("com.google.dagger:hilt-android:$hilt_version")
kapt("com.google.dagger:hilt-android-compiler:$hilt_version")
// CameraX
implementation 'androidx.camera:camera-core:1.3.4'
implementation 'androidx.camera:camera-camera2:1.3.4'
implementation 'androidx.camera:camera-lifecycle:1.3.4'
implementation 'androidx.camera:camera-view:1.3.4'
// TensorFlow Lite para IA
implementation 'org.tensorflow:tensorflow-lite:2.14.0'
implementation 'org.tensorflow:tensorflow-lite-support:0.4.4'
// ONNX Runtime para clasificación
implementation 'com.microsoft.onnxruntime:onnxruntime-android:1.16.3'
// JAAK Stamps SDK
implementation("com.jaak.stampssdk:jaakstamps-sdk:1.0.0-beta.2")
}
PASO 2: Implementación Básica del SDK
🎯 Objetivo
Crear la implementación base del JAAKStamps SDK con captura automatizada.
2.1 Application Class
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class JaakStampsSDKApp : Application()
2.2 MainActivity Básica
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.jaak.stampssdk.databinding.ActivityMainBinding
import com.jaak.stampssdk.sdk.StampsSDK
import com.jaak.stampssdk.ui.adapter.StampsListener
import com.jaak.stampssdk.utils.Utils
import com.jaak.stampssdk.utils.CameraFacing
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class MainActivity : AppCompatActivity(), StampsListener {
private lateinit var binding: ActivityMainBinding
private lateinit var stampsSDK: StampsSDK
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
configureStampsSDK()
binding.btnStartScanner.setOnClickListener {
startDocumentDetection()
}
}
private fun configureStampsSDK() {
stampsSDK = StampsSDK(this, this)
// Configuración recomendada
stampsSDK.setAlignmentTolerance(15) // Precisión de alineación (0-100)
stampsSDK.setMaskSize(80) // Área de detección (50-100)
stampsSDK.setCropMargin(100) // Margen de recorte (0-100)
stampsSDK.setCaptureDelay(1500) // Delay antes de captura (ms)
stampsSDK.setCameraFacing(CameraFacing.BACK) // Cámara trasera
stampsSDK.setAutoStampsClassification(true) // Clasificación automática con IA
// Herramientas de desarrollo (opcional)
stampsSDK.setShowConfigPanel(false)
stampsSDK.setShowDebugMode(false)
}
private fun startDocumentDetection() {
// El SDK maneja automáticamente permisos de cámara
stampsSDK.startStamps(1) // Modo 1: Detección automática con IA
}
override fun onSuccessStamps(
typeProcess: Int,
frontOriginalUri: Uri?,
frontCropUri: Uri?,
backOriginalUri: Uri?,
backCropUri: Uri?
) {
when (typeProcess) {
1 -> handleStampsResults(frontOriginalUri, frontCropUri, backOriginalUri, backCropUri)
}
}
private fun handleStampsResults(
frontOriginal: Uri?,
frontCrop: Uri?,
backOriginal: Uri?,
backCrop: Uri?
) {
// Convertir URIs a Base64 si es necesario
val frontOriginalBase64 = frontOriginal?.let {
Utils.uriToBase64(contentResolver, it) ?: ""
} ?: ""
val frontCropBase64 = frontCrop?.let {
Utils.uriToBase64(contentResolver, it) ?: ""
} ?: ""
Log.d("StampsResult", "Front Original: $frontOriginal")
Log.d("StampsResult", "Front Crop: $frontCrop")
Log.d("StampsResult", "Back Original: $backOriginal")
Log.d("StampsResult", "Back Crop: $backCrop")
Toast.makeText(this, "✅ Documentos capturados exitosamente", Toast.LENGTH_SHORT).show()
}
override fun onErrorStamps(text: String) {
Log.e("StampsError", text)
Toast.makeText(this, "❌ Error: $text", Toast.LENGTH_SHORT).show()
}
}
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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="📄 JAAK Stamps SDK Android"
android:textSize="20sp"
android:textStyle="bold"
android:layout_marginBottom="32dp"
app:layout_constraintBottom_toTopOf="@+id/btnStartScanner"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/btnStartScanner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="🚀 Escanear Documento"
android:textSize="16sp"
android:padding="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
PASO 3: Configurar Permisos y Manifiestos
🎯 Objetivo
Configurar correctamente los permisos y el manifiesto Android.
3.1 AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<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="com.jaak.stampssdk.JaakStampsSDKApp"
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="com.jaak.stampssdk.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>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.jaak.stampssdk"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>
3.2 file_paths.xml
Crear archivo 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/com.jaak.stampssdk/files/Jaak Stamps SDK/" />
</paths>
PASO 4: Manejo de Respuestas e Imágenes
🎯 Objetivo
Implementar el manejo completo de las imágenes capturadas y procesamiento.
4.1 Procesamiento Completo de Imágenes
private fun handleStampsResults(
frontOriginal: Uri?,
frontCrop: Uri?,
backOriginal: Uri?,
backCrop: Uri?
) {
val documentImages = DocumentImages()
// Procesar frente completo
frontOriginal?.let {
documentImages.frontOriginalBase64 = Utils.uriToBase64(contentResolver, it) ?: ""
documentImages.frontOriginalUri = it
}
// Procesar frente recortado
frontCrop?.let {
documentImages.frontCropBase64 = Utils.uriToBase64(contentResolver, it) ?: ""
documentImages.frontCropUri = it
}
// Procesar reverso completo (si existe)
backOriginal?.let {
documentImages.backOriginalBase64 = Utils.uriToBase64(contentResolver, it) ?: ""
documentImages.backOriginalUri = it
}
// Procesar reverso recortado (si existe)
backCrop?.let {
documentImages.backCropBase64 = Utils.uriToBase64(contentResolver, it) ?: ""
documentImages.backCropUri = it
}
// Determinar tipo de documento
val documentType = if (backOriginal != null || backCrop != null) "double_sided" else "single_sided"
// Procesar documento completo
processCompleteDocument(documentImages, documentType)
Log.d("StampsResult", "Documento procesado: $documentType")
Toast.makeText(this, "✅ Documento capturado exitosamente", Toast.LENGTH_SHORT).show()
}
data class DocumentImages(
var frontOriginalUri: Uri? = null,
var frontCropUri: Uri? = null,
var backOriginalUri: Uri? = null,
var backCropUri: Uri? = null,
var frontOriginalBase64: String? = null,
var frontCropBase64: String? = null,
var backOriginalBase64: String? = null,
var backCropBase64: String? = null
)
private fun processCompleteDocument(images: DocumentImages, type: String) {
// Crear payload para backend
val payload = mapOf(
"documentType" to type,
"timestamp" to System.currentTimeMillis(),
"images" to mapOf(
"frontOriginal" to (images.frontOriginalBase64 ?: ""),
"frontCrop" to (images.frontCropBase64 ?: ""),
"backOriginal" to (images.backOriginalBase64 ?: ""),
"backCrop" to (images.backCropBase64 ?: "")
),
"metadata" to mapOf(
"platform" to "android",
"sdkVersion" to "1.0.0-beta"
)
)
// Enviar a backend o procesar localmente
Log.d("DocumentPayload", "Documento listo para procesamiento: ${payload.keys}")
}
4.2 Manejo de Errores
override fun onErrorStamps(text: String) {
Log.e("StampsError", "Error en captura: $text")
val errorMessage = when {
text.contains("permission", ignoreCase = true) -> "⚠️ Permisos de cámara requeridos"
text.contains("camera", ignoreCase = true) -> "📱 Error de cámara - Verifica que no esté en uso"
text.contains("model", ignoreCase = true) -> "🤖 Error cargando modelos de IA - Reinicia la app"
text.contains("detection", ignoreCase = true) -> "🔍 Documento no detectado - Mejora la iluminación"
else -> "❌ Error: $text"
}
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show()
}
PASO 5: Probar Captura de Documentos
🎯 Objetivo
Verificar que todas las funcionalidades del SDK funcionan correctamente.
✅ Lista de Verificación
Elemento | ✅ | Descripción |
---|---|---|
Permisos de cámara | ☐ | App solicita y obtiene permisos automáticamente |
Detección automática | ☐ | IA detecta documentos automáticamente |
Captura de imágenes | ☐ | Genera original y crop de frente/reverso |
Conversión base64 | ☐ | Todas las imágenes se convierten correctamente |
Manejo de errores | ☐ | Errores se manejan correctamente |
🔍 Proceso de Prueba
Paso A: Instalar y Configurar
- Compilar aplicación en Android Studio
- Instalar en dispositivo físico (no emulador)
- Verificar permisos se solicitan automáticamente
Paso B: Probar Captura Simple
- Abrir aplicación y presionar "Escanear Documento"
- Posicionar documento dentro del marco
- Esperar detección automática (marco verde)
- Verificar captura automática del frente
- Revisar logs para confirmar procesamiento
Paso C: Probar Documento Doble Cara
- Usar INE o documento similar con dos caras
- Capturar frente automáticamente
- Seguir instrucciones para voltear documento
- Capturar reverso automáticamente
- Verificar 4 imágenes en logs
🛠️ Solución de Problemas
Problemas Comunes
Problema | Solución |
---|---|
"SDK no compatible" | ✅ Verificar minSdk 23+ y dependencias |
"Cámara no inicia" | ✅ Verificar permisos y dispositivo físico |
"Modelo no carga" | ✅ Reiniciar app y verificar memoria |
"Captura no funciona" | ✅ Usar dispositivo físico, no emulador |
Debug Avanzado
private fun enableDebugMode() {
stampsSDK.setShowDebugMode(true)
stampsSDK.setShowConfigPanel(true)
Log.d("Debug", "=== INFORMACIÓN DEL DISPOSITIVO ===")
Log.d("Debug", "Modelo: ${Build.MODEL}")
Log.d("Debug", "SDK: ${Build.VERSION.SDK_INT}")
Log.d("Debug", "RAM disponible: ${getAvailableMemory()}")
}
private fun getAvailableMemory(): String {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
return "${memoryInfo.availMem / (1024 * 1024)} MB"
}
📚 Referencia Completa
🔧 Métodos del SDK
Método | Descripción | Ejemplo |
---|---|---|
StampsSDK(context, listener) | Constructor del SDK | StampsSDK(this, this) |
startStamps(mode) | Inicia captura (modo 1: IA) | stampsSDK.startStamps(1) |
setAlignmentTolerance(int) | Tolerancia de alineación (0-100) | stampsSDK.setAlignmentTolerance(15) |
setMaskSize(int) | Área de detección (50-100) | stampsSDK.setMaskSize(80) |
setCropMargin(int) | Margen de recorte (0-100) | stampsSDK.setCropMargin(100) |
setCaptureDelay(int) | Delay antes de captura (ms) | stampsSDK.setCaptureDelay(1500) |
📡 Configuraciones Disponibles
Configuración | Descripción | Cuándo usar |
---|---|---|
setAutoStampsClassification(true) | Clasificación automática con IA | Detección inteligente de tipos |
setCameraFacing(BACK) | Selección de cámara | Cámara trasera para mejor calidad |
setShowDebugMode(true) | Métricas en tiempo real | Durante desarrollo |
📊 Estructura de Respuesta
Imagen | Descripción | Cuándo se genera |
---|---|---|
frontOriginalUri | Imagen completa del frente | Siempre |
frontCropUri | Frente recortado por IA | Cuando detecta documento |
backOriginalUri | Imagen completa del reverso | Solo documentos doble cara |
backCropUri | Reverso recortado por IA | Cuando detecta reverso |
🚨 Solución de Problemas Avanzados
Problema: "Error de modelos de IA"
// Verificar compatibilidad antes de usar
private fun checkAICompatibility() {
val hasRequiredMemory = Runtime.getRuntime().maxMemory() > 512 * 1024 * 1024 // 512MB
val hasRequiredAPI = Build.VERSION.SDK_INT >= 23
if (!hasRequiredMemory || !hasRequiredAPI) {
showError("Dispositivo no compatible con modelos de IA")
return
}
// Configurar para dispositivos de bajos recursos
if (Runtime.getRuntime().maxMemory() < 1024 * 1024 * 1024) { // < 1GB
stampsSDK.setMaskSize(70) // Reducir área de procesamiento
stampsSDK.setCaptureDelay(2000) // Más tiempo para procesar
}
}
Problema: "Documentos no se detectan"
// Configurar para mejorar detección
private fun optimizeForDetection() {
stampsSDK.setAlignmentTolerance(20) // Más tolerante
stampsSDK.setMaskSize(85) // Área más grande
stampsSDK.setCaptureDelay(2000) // Más tiempo para estabilizar
stampsSDK.setShowDebugMode(true) // Ver métricas en tiempo real
}
📞 ¿Necesitas Ayuda?
🆘 Información para soporte
- Descripción del problema: Qué documento intentas capturar vs qué sucede
- Logs de Android Studio: Screenshots de errores en Logcat
- Información del dispositivo: Modelo, versión Android, RAM
- Configuración Gradle: Versiones de dependencias
- Tipo de documento: INE, pasaporte, licencia, etc.
📋 Contacto de Soporte
📧 Email: [email protected] 📱 Incluir siempre: Logs Android, configuración build.gradle, versión SDK
¡Listo! 🎉
Has implementado exitosamente el JAAKStamps SDK en tu aplicación Android. Tu app ahora puede capturar documentos automáticamente usando inteligencia artificial, generando imágenes optimizadas para procesos KYC.
Updated about 8 hours ago