sdk

Manual Completo para Captura de Documentos en Aplicaciones 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ónQué harásTiempo
Paso 1Configurar proyecto y dependencias15 min
Paso 2Implementación básica del SDK25 min
Paso 3Configurar permisos y manifiestos10 min
Paso 4Manejo de respuestas e imágenes15 min
Paso 5Probar captura de documentos10 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

RequisitoVersión¿Obligatorio?
Android StudioIguana
minSdkVersion23 (Android 6.0+)
targetSdkVersion33
compileSdk35
Gradle8.4
Java/Kotlin17

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")
}

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

ElementoEstadoDescripción
Permisos de cámaraApp solicita y obtiene permisos automáticamente
Detección automáticaIA detecta documentos automáticamente
Captura de imágenesGenera original y crop de frente/reverso
Conversión base64Todas las imágenes se convierten correctamente
Manejo de erroresErrores se manejan correctamente

Proceso de Prueba

Paso A: Instalar y Configurar

  1. Compilar aplicación en Android Studio
  2. Instalar en dispositivo físico (no emulador)
  3. Verificar permisos se solicitan automáticamente

Paso B: Probar Captura Simple

  1. Abrir aplicación y presionar "Escanear Documento"
  2. Posicionar documento dentro del marco
  3. Esperar detección automática (marco verde)
  4. Verificar captura automática del frente
  5. Revisar logs para confirmar procesamiento

Paso C: Probar Documento Doble Cara

  1. Usar INE o documento similar con dos caras
  2. Capturar frente automáticamente
  3. Seguir instrucciones para voltear documento
  4. Capturar reverso automáticamente
  5. Verificar 4 imágenes en logs

Solución de Problemas

Problemas Comunes

ProblemaSolució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étodoDescripciónEjemplo
StampsSDK(context, listener)Constructor del SDKStampsSDK(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ónDescripciónCuándo usar
setAutoStampsClassification(true)Clasificación automática con IADetección inteligente de tipos
setCameraFacing(BACK)Selección de cámaraCámara trasera para mejor calidad
setShowDebugMode(true)Métricas en tiempo realDurante desarrollo

Estructura de Respuesta

ImagenDescripciónCuándo se genera
frontOriginalUriImagen completa del frenteSiempre
frontCropUriFrente recortado por IACuando detecta documento
backOriginalUriImagen completa del reversoSolo documentos doble cara
backCropUriReverso recortado por IACuando 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: soporte@jaak.ai Incluir siempre: Logs Android, configuración build.gradle, versión SDK


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.