Nativo
Nuestro SDK Nativo de Workflows para Android e iOS permite una experiencia más eficiente y segura para el usuario final. El SDK de Workflows de Nativo permite a los desarrolladores de aplicaciones móviles integrar la funcionalidad de Workflows en sus aplicaciones nativas de Android e iOS.
iOS
Requisitos
- iOS 13.0 - 26.0
- Xcode 14.0+
La integración del SDK de Workflows en tu aplicación iOS es un proceso sencillo que consta de los siguientes pasos:
Configuración
Instalación de CocoaPods
Para integrar el SDK de Workflows, primero asegúrate de contar con un archivo Podfile en la raíz de tu proyecto iOS.
Si tu proyecto no cuenta con un Podfile, abre una terminal, navega a la carpeta donde se encuentra el archivo .xcodeproj y ejecuta:
pod init
Este comando creará el archivo Podfile en tu proyecto.
Después, configura el Podfile para soportar la versión mínima de iOS requerida y agrega las dependencias del SDK:
platform :ios, '13.0'
target 'TuApp' do
use_frameworks!
pod 'WorkflowControllerDebug',
:podspec => 'https://storage.googleapis.com/workflows-mobile-sdk-artifacts/WorkflowiOS/WorkflowController/WorkflowsControllerDebug.podspec',
:configurations => ['Debug']
pod 'WorkflowControllerRelease',
:podspec => 'https://storage.googleapis.com/workflows-mobile-sdk-artifacts/WorkflowiOS/WorkflowController/WorkflowsControllerRelease.podspec',
:configurations => ['Release']
end
Importante: Reemplaza TuApp con el nombre del target de tu aplicación.
Si no tienes acceso al repositorio del SDK, por favor contacta a tu administrador de cuenta o a soporte, ya que necesitarás acceso al repositorio privado del SDK.
Agrega el siguiente bloque post_install al final de tu Podfile, fuera del bloque target de la aplicación.
Asegúrate de reemplazar Pods-TuApp por el nombre del target de tu aplicación, conservando siempre el prefijo Pods-.
Por ejemplo, si tu target se llama WorkflowsiOS, debes utilizar:
'Pods-WorkflowsiOS'
El Podfile debe quedar con una estructura similar a esta:
platform :ios, '13.0'
target 'WorkflowsiOS' do
use_frameworks!
pod 'WorkflowControllerDebug',
:podspec => 'https://storage.googleapis.com/workflows-mobile-sdk-artifacts/WorkflowiOS/WorkflowController/WorkflowsControllerDebug.podspec',
:configurations => ['Debug']
pod 'WorkflowControllerRelease',
:podspec => 'https://storage.googleapis.com/workflows-mobile-sdk-artifacts/WorkflowiOS/WorkflowController/WorkflowsControllerRelease.podspec',
:configurations => ['Release']
end
# Add post_install after the application target
post_install do |installer|
['WorkflowControllerDebug', 'WorkflowControllerRelease'].each do |pod_name|
Dir.glob("Pods/#{pod_name}/MediaPipeTasks*.xcframework").each do |path|
system("mv '#{path}' Pods/")
end
end
installer.pods_project.targets.each do |target|
if target.name == 'Pods-WorkflowsiOS'
['WorkflowControllerDebug', 'WorkflowControllerRelease'].each do |pod_name|
group = target.project.main_group.find_subpath("Pods/#{pod_name}", false)
next unless group
%w(MediaPipeTasksVision.xcframework MediaPipeTasksCommon.xcframework).each do |fw|
ref = group.new_file(fw)
ref.source_tree = 'SOURCE_ROOT'
target.frameworks_build_phase.add_file_reference(ref)
end
end
end
target.build_configurations.each do |config|
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64'
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
installer.pods_project.save
end
Importante: El bloque post_install no debe colocarse dentro de target 'TuApp' do. Debe agregarse después del end que cierra el target de la aplicación.
Abre una terminal, navega al directorio de tu proyecto y ejecuta el siguiente comando para instalar el SDK:
pod install
Importante: Recomendamos limpiar las dependencias de CocoaPods antes de volver a instalar.
Desde la carpeta ios, ejecuta:
rm -rf Pods
rm Podfile.lock
pod install
Esto asegura que las dependencias nativas se reinstalen correctamente y evita conflictos con versiones previamente instaladas.
Después de ejecutar:
pod install
CocoaPods generará un archivo .xcworkspace.
A partir de este punto, abre el proyecto utilizando el archivo:
WorkflowsiOS.xcworkspace
Importante: Después de instalar las dependencias con CocoaPods, utiliza siempre el archivo .xcworkspace y no el .xcodeproj, ya que el workspace incluye tanto tu aplicación como las dependencias instaladas por CocoaPods.
Configuración opcional de Build Settings
Si durante la compilación aparece un error relacionado con Sandbox, deny u Operation not permitted, ve a Build Settings > Build Options y configura:
User Script Sandboxing = NO
Esto permite que los scripts de CocoaPods accedan a los archivos necesarios durante la compilación.
Nota: Si tu proyecto compila correctamente, no es necesario cambiar esta opción.
Configuración de Permisos en Info.plist
Es necesario declarar los permisos que utiliza la aplicación.
Si tu proyecto cuenta con un archivo Info.plist, agrega las siguientes claves:
<key>NSCameraUsageDescription</key>
<string>3D Liveness Detection by FaceTec.</string>
<key>NSMicrophoneUsageDescription</key>
<string>We need access to your microphone to record audio.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>The application requires access to the device location.</string>
En proyectos recientes de Xcode, es posible que el archivo Info.plist no aparezca directamente en el proyecto. En este caso, selecciona:
Signing & Capabilities > + Capability
Agrega las siguientes capabilities:
Camera
Microphone
Location (When In Use)
Configura las descripciones que se mostrarán al usuario cuando la aplicación solicite cada permiso:
Camera:
3D Liveness Detection by FaceTec.
Microphone:
We need access to your microphone to record audio.
Location:
The application requires access to the device location.
#### **Recursos Personalizados**
Para usar recursos personalizados en tu aplicación, agrégalos directamente a la carpeta del proyecto:
| Tipo de Recurso | Formato | Ubicación |
|-----------------|---------|-----------|
| Textos | `.strings` | Carpeta del proyecto |
| Imágenes SVG | `.svg` | Carpeta del proyecto |
| Tipografías | `.otf`, `.ttf` | Carpeta del proyecto |
| Imágenes PNG/JPG | `.png`, `.jpg` | Asset Catalog |
### **Uso**
#### **Importación del SDK**
Para usar el SDK en tu código, primero debes importarlo:
```swift
import WorkflowSDK
Verificación de Licencia
Antes de crear o iniciar WorkflowController, debes configurar primero la licencia del SDK mediante verifyWorkflowsLicense.
La función recibe el certificado y la frase de contraseña como objetos Data.
import WorkflowSDK
WorkflowController.verifyWorkflowsLicense(
cert: certificateData,
passphrase: passphraseData
)
Las credenciales de licencia son proporcionadas de forma privada por REM.
Importante: No almacenes el certificado ni la frase de contraseña directamente en el código fuente, recursos públicos o control de versiones. Para producción, obtén estas credenciales desde un servicio seguro o utiliza un mecanismoismo de almacenamiento seguro como Keychain.
Configuración para pruebas
Para realizar pruebas locales, puedes convertir temporalmente las credenciales proporcionadas por REM a Base64 y posteriormente obtener los objetos Data requeridos por el SDK.
- El archivo
.pfxdebe convertirse completo a Base64. - El archivo
.passcontiene la frase de contraseña. Su contenido debe convertirse a Base64. - Después, ambos valores se decodifican nuevamente a
Dataantes de llamar averifyWorkflowsLicense.
Ejemplo:
import Foundation
enum LicenseConfig {
static func getCertBytes() -> Data {
let base64 = "CERTIFICATE_BASE64"
guard let data = Data(base64Encoded: base64) else {
fatalError("Invalid Base64 string for certificate")
}
return data
}
static func getPassphraseBytes() -> Data {
let base64 = "PASSPHRASE_BASE64"
guard let data = Data(base64Encoded: base64) else {
fatalError("Invalid Base64 string for passphrase")
}
return data
}
}
Posteriormente, configura la licencia antes de inicializar WorkflowController:
let cert = LicenseConfig.getCertBytes()
let passphrase = LicenseConfig.getPassphraseBytes()
WorkflowController.verifyWorkflowsLicense(
cert: cert,
passphrase: passphrase
)
Después de verificar la licencia, puedes inicializar el SDK:
let workflowController = WorkflowController(
baseUrl: "https://api.rem.tools",
apiKey: "tu_rem_api_key",
workflowCustomization: workflowCustomization
)
Nota: Esta forma debe utilizarse únicamente para pruebas locales. Base64 no es un mecanismo de seguridad ni cifrado, por lo que estos valores no deben incluirse en el código fuente que se publique, distribuya o almacene en el repositorio.
Configuración de Personalización
Puedes personalizar la apariencia del SDK mediante WorkflowCustomization:
let workflowCustomization = WorkflowCustomization(
textLocalization: WorkflowCustomization.TextLocalization(
localization: "Localizable.strings"
),
textFont: WorkflowCustomization.TextFont(
headerFont: "Oswald-Bold.ttf",
bodyFont: "Oswald-Regular.ttf",
buttonFont: "Merriweather-Bold.ttf"
),
theme: WorkflowCustomization.Theme(
primaryColor: "#045CC7",
secondaryColor: "#20DAC6",
buttonPrimaryColor: "#045CC7",
buttonSecondaryColor: "#20DAC6",
buttonTextPrimaryColor: "#FFFFFF",
buttonTextSecondaryColor: "#045CC7",
backgroundColor: "#FFFFFF",
facetecOverlayColor: "#FFFFFF",
showHeaderLogo: true
),
images: WorkflowCustomization.Images(
//png or jpg images
logo: "logo.png",
cameraPermissions: "camera.png",
uploadFaceScan: "upload.png",
uploadFaceSuccess: "success.png",
uploadFaceError: "error.png",
photoMathId: "id.png",
photoMathIdBack: "id_back.png",
cameraActiveTorch: "flash_on.png",
cameraInactiveTorch: "flash_off.png",
fingerPrint: "fingerprint.png",
//svg images
successPage: "success.svg",
errorPage: "error.svg",
unavailablePage: "unavailable.svg",
locationPage: "location.svg",
biometricSignPage: "biometric_sign.svg",
enrollFingerprintPage: "enroll_fingerprint.svg",
enrollBasicPage: "enroll_basic.svg",
enrollFullPage: "enroll_full.svg",
authPage: "auth.svg",
livenessPage: "liveness.svg",
faceEnrollment3dPage: "face_enrollment_3d.svg"
)
)
Archivos de localización
Los textos del SDK pueden ser personalizados utilizando los archivos base de localización.
Descargas oficiales:
Inicialización del WorkflowController
Inicializa el WorkflowController con la URL base, las API keys y la personalización:
let workflowController = WorkflowController(
baseUrl: "https://api.rem.tools",
apiKey: "tu_rem_api_key",
workflowCustomization: workflowCustomization
)
Iniciar un Workflow
Para iniciar un workflow, usa el método start con el ID del workflow que deseas iniciar:
workflowController.start(workflowId: "workflow_id") { (success: Bool, error: WorkflowError?) in
if success {
print("Workflow iniciado")
}
else {
print("Error al iniciar el workflow: \(String(describing: error))")
}
}
Manejo de Eventos de Workflow
Puedes escuchar eventos de workflow mediante closures:
workflowController.onWorkflowEvent() { (workflow: Workflow) in
print("Workflow UUID: \(workflow.uuid)")
}
workflowController.onStepEvent() { (step: Step) in
print("Evento de paso: \(step.status)")
}
Ejemplo Completo
El siguiente ejemplo muestra cómo verificar la licencia, inicializar el SDK, iniciar un workflow y escuchar sus eventos:
import SwiftUI
import WorkflowSDK
struct ContentView: View {
var body: some View {
VStack {
Button("Iniciar Workflow") {
openWorkflow()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
private func openWorkflow() {
// Verify license before initializing the SDK
WorkflowController.verifyWorkflowsLicense(
cert: LicenseConfig.getCertBytes(),
passphrase: LicenseConfig.getPassphraseBytes()
)
// Configure customization
let workflowCustomization = WorkflowCustomization(
// Customization settings
)
// Initialize Workflows SDK
let workflowController = WorkflowController(
baseUrl: "https://api.rem.tools",
apiKey: "tu_rem_api_key",
workflowCustomization: workflowCustomization
)
// Start workflow
workflowController.start(
workflowId: "workflow_id"
) { (success: Bool, error: WorkflowError?) in
print("Workflows START - Workflow started success: \(success)")
if let error = error {
print("Workflows START - Workflow Id Error: \(error.code)")
}
}
// Listen for workflow events
workflowController.onWorkflowEvent() { (workflow: Workflow) in
print("Workflows EVENT - UUID: \(workflow.uuid ?? "nil"), status: \(workflow.status ?? "nil")")
if workflow.status == "success" {
print("Workflows EVENT - Workflow completed successfully")
// Wait briefly before closing the controller to provide visual feedback
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
workflowController.dismiss()
}
}
if let error = workflow.error {
print("Workflows EVENT - Error: \(error)")
}
}
// Listen for step events
workflowController.onStepEvent() { (step: Step) in
print("Workflows STEP - Step: \(step.step ?? "nil"), status: \(step.status ?? "nil")")
if let error = step.error,
step.status == "error" || step.status == "failed" {
print("Workflows STEP - Error, fatal: \(error.fatal), errorId: \(String(describing: error.details?.errorID)), reason: \(String(describing: error.details?.reason)), stack: \(String(describing: error.details?.stack))")
if let extra = error.extra {
// Biometric scan status
if extra.idScanStatus != nil || extra.faceScanStatus != nil {
print("Workflows STEP - Extra, idScanStatus: \(String(describing: extra.idScanStatus)), faceScanStatus: \(String(describing: extra.faceScanStatus))")
}
// Permission error
if let permission = extra.permission {
print("Workflows STEP - Extra, permission denied: \(permission)")
// Dismiss WorkflowController
workflowController.dismiss()
}
// Device security status
if let secure = extra.secure {
print("Workflows STEP - Extra, is device secure: \(secure)")
}
// License status
if let isLicenseValid = extra.isLicenseValid {
print("Workflows STEP - Extra, is license valid: \(isLicenseValid)")
}
}
}
}
}
}
#Preview {
ContentView()
}
Si tienes alguna duda o problema con la integración del SDK, por favor contacta a soporte.
Android
La integración del SDK de Workflows en tu aplicación Android es un proceso sencillo que consta de los siguientes pasos:
Requisitos
- Android API 29 - 36
- Android Studio Hedgehog+
Configuración
Configuración de settings.gradle / settings.gradle.kts
En el archivo settings.gradle o settings.gradle.kts (Project Settings), cambia el modo de repositorios a PREFER_PROJECT:
Groovy (settings.gradle):
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
}
Kotlin DSL (settings.gradle.kts):
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
}
¿Por qué es necesario?
Esto permite que Gradle use el repositorio privado donde se encuentra el SDK de Workflows. Sin esta configuración, Gradle podría no encontrar o descargar correctamente el SDK.
Configuración de Repositorios
En el archivo build.gradle o build.gradle.kts de la aplicación, declara de dónde se obtendrán las dependencias:
Groovy (app/build.gradle):
repositories {
google()
mavenCentral()
maven {
url "https://us-central1-maven.pkg.dev/gendra-services/workflows-mobile"
credentials {
username = "_json_key"
password = file("tu_archivo_credenciales.json").text
}
}
}
android {
// Demás configuración existente del proyecto
// ...
}
// Demás contenido del archivo build.gradle
// ...
Kotlin DSL (app/build.gradle.kts):
repositories {
google()
mavenCentral()
maven {
url = uri("https://us-central1-maven.pkg.dev/gendra-services/workflows-mobile")
credentials {
username = "_json_key"
password = file("tu_archivo_credenciales.json").readText()
}
}
}
android {
// Demás configuración existente del proyecto
// ...
}
// Demás contenido del archivo build.gradle.kts
// ...
Importante
El valor de username debe mantenerse como _json_key. No se debe modificar.
En password debes indicar la ruta donde se encuentra el archivo JSON de credenciales.
El archivo de credenciales se utiliza únicamente para que Gradle pueda descargar el SDK.
No agregues el archivo JSON dentro de assets, res o cualquier otra carpeta que pueda incluirlo en el APK/AAB de la aplicación.
También se recomienda agregar este archivo al .gitignore para evitar subir las credenciales al repositorio.
Si no tienes acceso al repositorio o al archivo de credenciales, por favor contacta a tu administrador de cuenta o a soporte, ya que necesitarás acceso al repositorio privado del SDK.
Implementación de Dependencias
Agrega las dependencias de Workflows en el archivo build.gradle o build.gradle.kts de la aplicación:
Groovy (app/build.gradle):
dependencies {
// Workflows SDK
implementation ('tools.rem.workflows:sdk:1.22.6@aar') { transitive = true }
// Core dependencies
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.13.0'
implementation 'com.getkeepsafe.relinker:relinker:1.4.5'
implementation "androidx.window:window:1.0.0"
implementation 'com.google.android.gms:play-services-location:21.3.0'
implementation 'com.squareup.okhttp3:okhttp:5.1.0'
implementation 'com.squareup.okio:okio:3.16.0'
implementation 'com.google.code.gson:gson:2.13.1'
implementation "com.bugsnag:bugsnag-android:6.17.0"
implementation 'com.google.mediapipe:tasks-vision:0.10.32'
// Media dependencies
implementation "androidx.media3:media3-exoplayer:1.4.1"
implementation "androidx.media3:media3-exoplayer-hls:1.4.1"
implementation "androidx.media3:media3-exoplayer-dash:1.4.1"
implementation "androidx.media3:media3-exoplayer-rtsp:1.4.1"
implementation "androidx.media3:media3-exoplayer-smoothstreaming:1.4.1"
// Camera dependencies
implementation "com.otaliastudios:cameraview:2.7.2"
implementation "androidx.camera:camera-core:1.5.3"
implementation "androidx.camera:camera-camera2:1.5.3"
implementation "androidx.camera:camera-lifecycle:1.5.3"
implementation "androidx.camera:camera-video:1.5.3"
implementation "androidx.camera:camera-view:1.5.3"
implementation "androidx.camera:camera-extensions:1.5.3"
// Utilities dependencies
implementation 'com.google.guava:guava:27.1-android'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2'
}
Kotlin DSL (app/build.gradle.kts):
dependencies {
// Workflows SDK
implementation("tools.rem.workflows:sdk:1.22.6@aar") {
isTransitive = true
}
// Core dependencies
implementation("androidx.appcompat:appcompat:1.7.1")
implementation("com.google.android.material:material:1.13.0")
implementation("com.getkeepsafe.relinker:relinker:1.4.5")
implementation("androidx.window:window:1.0.0")
implementation("com.google.android.gms:play-services-location:21.3.0")
implementation("com.squareup.okhttp3:okhttp:5.1.0")
implementation("com.squareup.okio:okio:3.16.0")
implementation("com.google.code.gson:gson:2.13.1")
implementation("com.bugsnag:bugsnag-android:6.17.0")
implementation("com.google.mediapipe:tasks-vision:0.10.32")
// Media dependencies
implementation("androidx.media3:media3-exoplayer:1.4.1")
implementation("androidx.media3:media3-exoplayer-hls:1.4.1")
implementation("androidx.media3:media3-exoplayer-dash:1.4.1")
implementation("androidx.media3:media3-exoplayer-rtsp:1.4.1")
implementation("androidx.media3:media3-exoplayer-smoothstreaming:1.4.1")
// Camera dependencies
implementation("com.otaliastudios:cameraview:2.7.2")
implementation("androidx.camera:camera-core:1.5.3")
implementation("androidx.camera:camera-camera2:1.5.3")
implementation("androidx.camera:camera-lifecycle:1.5.3")
implementation("androidx.camera:camera-video:1.5.3")
implementation("androidx.camera:camera-view:1.5.3")
implementation("androidx.camera:camera-extensions:1.5.3")
// Utilities dependencies
implementation("com.google.guava:guava:27.1-android")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
}
Compatibilidad con el tema de la aplicación
Para evitar conflictos entre el tema de la aplicación y el tema incluido por el SDK, agrega el atributo tools:replace dentro de la etiqueta <application>:
tools:replace="android:theme"
También es necesario declarar el namespace tools en la etiqueta <manifest>.
Ejemplo completo:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:theme="@style/Theme.YourApplication"
tools:replace="android:theme">
<!-- Demás configuración de la aplicación -->
</application>
</manifest>
Configuración de Release Build
Para asegurar el correcto funcionamiento del SDK en compilaciones release, agrega la siguiente configuración dentro del bloque android del archivo build.gradle o build.gradle.kts de la aplicación.
Groovy (app/build.gradle):
android {
// Demás configuración...
buildTypes {
release {
shrinkResources false
// Demás configuración de release...
}
}
aaptOptions {
noCompress "tflite", "task"
}
sourceSets {
main {
assets.srcDirs += 'src/main/assets'
}
}
}
Kotlin DSL (app/build.gradle.kts):
android {
// Demás configuración...
buildTypes {
release {
isShrinkResources = false
// Demás configuración de release...
}
}
androidResources {
noCompress += listOf("tflite", "task")
}
sourceSets {
getByName("main") {
assets.srcDir("src/main/assets")
}
}
}
Esta configuración evita que algunos recursos utilizados por el SDK sean eliminados o comprimidos durante la generación del APK o AAB.
No excluyas librerías .so requeridas por el SDK usando packagingOptions o packaging.jniLibs.
Por ejemplo, NO debes usar:
Groovy (app/build.gradle):
packagingOptions {
exclude 'lib/arm64-v8a/libPhoenixAndroid.so'
}
Kotlin DSL (app/build.gradle.kts):
packaging {
jniLibs {
excludes += "lib/arm64-v8a/libPhoenixAndroid.so"
}
}
El SDK depende de librerías nativas como:
lib/arm64-v8a/libPhoenixAndroid.so
Si se excluyen, la aplicación puede compilar pero fallará en ejecución, especialmente en la inicialización de biometría.
Configuración de R8 Keep Rules / ProGuard
Agrega las siguientes reglas en el archivo de configuración correspondiente a tu proyecto.
En proyectos recientes de Android:
app/src/main/keepRules/rules.keep
En proyectos con la configuración tradicional:
app/proguard-rules.pro
Agrega las siguientes reglas:
# Keep the io.flutter.plugin.** package provided by Flutter
-dontwarn io.flutter.plugin.**
# The android.** package is provided by the OS at runtime.
-dontwarn android.**
#Rules for the Workflows Flutter
-keep class tools.rem.workflows_flutter_plugin.** { *; }
-keep class tools.rem.workflows_flutter_plugin.Models.Step.Step { *; }
-keep class tools.rem.workflows_flutter_plugin.Models.Workflow.Workflow { *; }
-keep class tools.rem.workflows_flutter_plugin.Models.WorkflowError { *; }
-keep class tools.rem.workflows_flutter_plugin.WorkflowsFlutterPlugin { *; }
-keepclassmembers class tools.rem.workflows_flutter_plugin.Models.Workflow.Workflow {
public <init>(...);
}
-keep class tools.rem.workflows_flutter_plugin.Models.ConfigFacetec { *; }
-keepclassmembers class tools.rem.workflows_flutter_plugin.Models.ConfigFacetec {
public <init>(...);
}
#Keep the mediapipe classes
-keep public class com.google.mediapipe.framework.Graph.** { *; }
-keep interface com.google.common.** { *; }
-keep class * extends com.google.common.flogger.** {*;}
-keep class com.google.common.** { *; }
-keep class com.google.common.flogger.** { *; }
-keep class com.google.mediapipe.tasks.** { *; }
-keep class com.google.mediapipe.tasks.vision.** { *; }
-keep class com.google.mediapipe.tasks.core.** { *; }
-keep class com.google.mediapipe.framework.ProtoUtil$* { *; }
-keep class com.google.mediapipe.framework.** { *; }
-keep class com.google.mediapipe.framework.image.** { *; }
-keep class com.google.mediapipe.** { *; }
-keep class com.google.mediapipe.proto.** { *; }
-keep class javax.lang.model.** { *; }
-keep class com.google.mediapipe.solutioncore.** { *; }
-keep class com.google.protobuf.** { *; }
-keep class org.tensorflow.lite.** { *; }
-dontwarn com.google.mediapipe.proto.CalculatorProfileProto$CalculatorProfile
-dontwarn com.google.mediapipe.proto.GraphTemplateProto$CalculatorGraphTemplate
-dontwarn org.tensorflow.lite.**
-dontwarn javax.annotation.**
-dontwarn javax.lang.model.**
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite.** { *; }
-keepclassmembers class com.google.mediapipe.**$$ExternalSyntheticLambda* { *; }
-keepattributes InnerClasses,EnclosingMethod,Signature,*Annotation*
#Keep the okhttp3 classes
-keep class okhttp3.** { *; }
-keep class okio.** { *; }
#Keep the gson classes
-keep class com.google.gson.** { *; }
#Keep the facetec classes
-dontwarn javax.annotation.Nullable
-dontwarn com.facetec.sdk.**
-keep,includecode,includedescriptorclasses class com.facetec.sdk.** { *; }
#Keep the cameraview classes
-keep class com.otaliastudios.cameraview.** { *; }
-dontwarn com.otaliastudios.cameraview.**
Recursos Personalizados
Para usar recursos personalizados, agrega los assets en la carpeta res según su tipo:
| Carpeta | Tipo de Recurso | Formatos |
|---|---|---|
drawable | Imágenes | .png, .jpeg |
font | Tipografías | .ttf, .otf |
raw | Imágenes vectoriales | .svg |
xml | Textos localizados | .xml |
Uso
Importación del SDK
Para usar el SDK en tu código Kotlin, primero debes importarlo:
import tools.rem.workflows.WorkflowsActivity
import tools.rem.workflows.model.WorkflowCustomization
import tools.rem.workflows_flutter_plugin.Models.Step.Step
import tools.rem.workflows_flutter_plugin.Models.Workflow.Workflow
import tools.rem.workflows_flutter_plugin.Models.WorkflowError
Verificación de Licencia
Antes de crear o iniciar WorkflowsActivity, debes configurar primero la licencia del SDK mediante verifyWorkflowsLicense.
La función recibe el certificado y la frase de contraseña como arreglos de bytes (ByteArray en Kotlin / byte[] en Java).
import tools.rem.workflows.WorkflowsActivity
WorkflowsActivity.verifyWorkflowsLicense(
certificateBytes,
passphraseBytes
)
Las credenciales de licencia son proporcionadas de forma privada por REM.
Importante: No almacenes el certificado ni la frase de contraseña directamente en el código fuente, recursos públicos o control de versiones. Para producción, obtén estas credenciales desde un servicio seguro o utiliza un mecanismo de almacenamiento seguro respaldado por Android Keystore.
Configuración para pruebas
Para realizar pruebas locales, puedes convertir temporalmente las credenciales proporcionadas por REM a Base64 y posteriormente obtener los ByteArray requeridos por el SDK.
- El archivo
.pfxdebe convertirse completo a Base64. - El archivo
.passcontiene la frase de contraseña. Su contenido debe convertirse a Base64. - Después, ambos valores se decodifican nuevamente a
ByteArrayantes de llamar averifyWorkflowsLicense.
Ejemplo:
import android.util.Base64
object LicenseConfig {
fun getCertBytes(): ByteArray {
val base64 = "CERTIFICATE_BASE64"
return Base64.decode(base64, Base64.DEFAULT)
}
fun getPassphraseBytes(): ByteArray {
val base64 = "PASSPHRASE_BASE64"
return Base64.decode(base64, Base64.DEFAULT)
}
}
Posteriormente, configura la licencia:
WorkflowsActivity.verifyWorkflowsLicense(
LicenseConfig.getCertBytes(),
LicenseConfig.getPassphraseBytes()
)
Nota: Esta forma debe utilizarse únicamente para pruebas locales. Base64 no es un mecanismo de seguridad ni cifrado, por lo que estos valores no deben incluirse en el código fuente que se publique, distribuya o almacene en el repositorio.
Configuración de Personalización
Puedes personalizar la apariencia del SDK mediante WorkflowCustomization:
val workflowCustomization = WorkflowCustomization(
textLocalization = WorkflowCustomization.TextLocalization(
localization = R.xml.localizable
),
textFont = WorkflowCustomization.TextFont(
headerFont = R.font.oswald_bold,
bodyFont = R.font.oswald_regular,
buttonFont = R.font.merriweather_bold
),
theme = WorkflowCustomization.Theme(
primaryColor = "#045CC7",
secondaryColor = "#20DAC6",
buttonPrimaryColor = "#045CC7",
buttonSecondaryColor = "#20DAC6",
buttonTextPrimaryColor = "#FFFFFF",
buttonTextSecondaryColor = "#045CC7",
backgroundColor = "#FFFFFF",
facetecOverlayColor = "#FFFFFF",
showHeaderLogo = true
),
images = WorkflowCustomization.Images(
//png or jpg images
logo = R.drawable.logo,
cameraPermissions = R.drawable.camera,
uploadFaceScan = R.drawable.upload,
uploadFaceSuccess = R.drawable.success,
uploadFaceError = R.drawable.error,
photoMathId = R.drawable.photo_match_id_image,
photoMathIdBack = R.drawable.photo_match_id_back_image,
cameraActiveTorch = R.drawable.flash_active,
cameraInactiveTorch = R.drawable.flash_inactive,
fingerPrint = R.drawable.flash_inactive,
//svg images
successPage = R.raw.success_image,
errorPage = R.raw.error_image,
unavailablePage = R.raw.unavailable_image,
locationPage = R.raw.geolocation_image,
biometricSignPage = R.raw.biometric_sign_image,
enrollFingerprintPage = R.raw.biometric_sign_image,
enrollBasicPage = R.raw.id_scan_image,
enrollFullPage = R.raw.id_scan_image,
authPage = R.raw.biometric_sign_image,
livenessPage = R.raw.biometric_sign_image,
faceEnrollment3dPage = R.raw.biometric_sign_image
)
)
Archivos de localización
Los textos del SDK pueden ser personalizados utilizando los archivos base de localización.
Descarga el archivo localizable.xml ejecutando:
curl -L "https://storage.googleapis.com/workflows-mobile-sdk-artifacts/workflows/localization/localizable.xml" -o localizable.xml
Inicialización del WorkflowController
Inicializa el WorkflowsActivity con la URL base, las API keys y la personalización:
val workflowController = WorkflowsActivity(
"https://api.rem.tools", // Base URL
"tu_rem_api_key", // REM API Key
workflowCustomization // Personalización
)
Iniciar un Workflow
Para iniciar un workflow, usa el método start con el ID del workflow y el contexto de la actividad:
workflowController.start(
"workflow_id",
this
) { success: Boolean, error: WorkflowError? ->
if (success) {
Log.d("Workflow", "Workflow iniciado exitosamente")
}
else {
Log.e("Workflow", "Error al iniciar el workflow: $error")
}
}
Manejo de Eventos de Workflow
Puedes escuchar eventos de workflow mediante callbacks:
workflowController.onStepEvent { step: Step ->
Log.d("Workflow", "Evento de paso: ${step}")
}
workflowController.onWorkflowEvent { workflow: Workflow ->
Log.d("Workflow", "Evento de workflow: ${workflow}")
}
Ejemplo Completo
El siguiente ejemplo muestra cómo verificar la licencia, inicializar el SDK, iniciar un workflow y escuchar sus eventos:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Verify license before initializing the SDK
WorkflowsActivity.verifyWorkflowsLicense(LicenseConfig.getCertBytes(), LicenseConfig.getPassphraseBytes())
// Configure customization
val workflowCustomization = WorkflowCustomization(
// Customization settings
)
// Initialize Workflows SDK
val workflowController = WorkflowsActivity(
"https://api.rem.tools",
"tu_rem_api_key",
workflowCustomization
)
// Start workflow
workflowController.start(
"workflow_id",
this
) { success: Boolean, error: WorkflowError? ->
Log.d("Workflows START", "Workflow started success: $success")
if (error != null) {
Log.d("Workflows START", "Workflow Id Error: ${error.ordinal}")
}
}
// Listen for workflow events
workflowController.onWorkflowEvent { workflow: Workflow ->
Log.d("Workflows EVENT", "UUID: ${workflow.uuid}, status: ${workflow.status}")
if (workflow.status == "success") {
Log.d("Workflows EVENT", "Workflow completed successfully")
lifecycleScope.launch {
// It is recommended to wait briefly before closing the activity to provide visual feedback to the user
delay(2000.milliseconds)
// Dismiss WorkflowsActivity
WorkflowsActivity.dismiss()
}
}
}
// Listen for step events
workflowController.onStepEvent { step: Step ->
Log.d("Workflows STEP", "Step: ${step.step}, status: ${step.status}")
val error = step.error
if (error != null && (step.status == "error" || step.status == "failed")) {
Log.d("Workflows STEP", "Error, fatal: ${error.fatal}, errorId: ${error.details?.errorId}, reason: ${error.details?.reason}, stack: ${error.details?.stack}")
error.extra?.let { extra ->
// Biometric scan status
if (extra.idScanStatus != null || extra.faceScanStatus != null) {
Log.d("Workflows STEP", "Extra, idScanStatus: ${extra.idScanStatus}, faceScanStatus: ${extra.faceScanStatus}")
}
// Permission error
if (extra.permission != null) {
Log.d("Workflows STEP", "Extra, permission denied: ${extra.permission}")
// Dismiss WorkflowsActivity
WorkflowsActivity.dismiss()
}
// Device security status
if (extra.secure != null) {
Log.d("Workflows STEP", "Extra, is device secure: ${extra.secure}")
}
// License status
if (extra.isLicenseValid != null) {
Log.d("Workflows STEP", "Extra, is license valid: ${extra.isLicenseValid}")
}
}
}
}
}
}
Si tienes alguna duda o problema con la integración del SDK, por favor contacta a soporte.



