---
title: Android
description: Integra la jornada Human Token en tu aplicación Android usando Custom Tabs — ligero, seguro y sin necesidad de WebView.
canonical: https://developer.unico.io/es/human-token/integrations/app/android/link-integration
locale: es
generated_by: markdown-export
---

# Android

Integra la jornada Human Token en tu aplicación Android usando Custom Tabs.

Custom Tabs inicia la jornada Human Token en una pestaña del navegador dentro de tu aplicación. Cuando la jornada finaliza, el navegador redirige a tu esquema de URL personalizado y tu aplicación lo intercepta.

### Requisitos previos

- Android 8 (API 26) o posterior
- `androidx.browser:browser:1.5.0`
- Un esquema de URL único para tu aplicación (por ejemplo, `myapp`)

### Paso 1: Agregar la dependencia

En tu `build.gradle`:

```groovy
implementation("androidx.browser:browser:1.5.0")
```

### Paso 2: Definir tu esquema de URL

Elige un esquema único (por ejemplo, `myapp`). Se usa como `callbackUri` al crear el proceso:

```
callbackUri: myapp://callback
```

### Paso 3: Crear la actividad

```kotlin
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabsIntent

class HumanTokenActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val url = intent.getStringExtra(EXTRA_PROCESS_URL) ?: return finish()

        CustomTabsIntent.Builder()
            .build()
            .launchUrl(this, Uri.parse(url))
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        intent.data?.let { uri ->
            if (uri.scheme == "myapp") {
                // Journey completed — send processId to your backend
                val processId = uri.getQueryParameter("id")
                handleResult(processId)
            }
        }
    }

    private fun handleResult(processId: String?) {
        // Fetch the result from your backend via Get Process
        finish()
    }

    companion object {
        const val EXTRA_PROCESS_URL = "process_url"
    }
}
```

### Paso 4: Configurar AndroidManifest.xml

```xml
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.CAMERA" />

<activity
    android:name=".HumanTokenActivity"
    android:launchMode="singleTop"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="myapp" android:host="callback" />
    </intent-filter>
</activity>
```

`singleTop` asegura que Android llame a `onNewIntent()` en la actividad existente en lugar de crear una nueva.

### Paso 5: Iniciar la jornada

```kotlin
val intent = Intent(this, HumanTokenActivity::class.java)
    .putExtra(HumanTokenActivity.EXTRA_PROCESS_URL, processLink)
startActivity(intent)
```

### Resumen de archivos

| Archivo | Propósito |
|---|---|
| `build.gradle` | Dependencia de `androidx.browser` |
| `HumanTokenActivity.kt` | Inicio de Custom Tabs + manejo de redirección |
| `AndroidManifest.xml` | Filtro de intent para tu esquema de URL |

---

### Usar un WebView en lugar de Custom Tabs

> **Advertencia:** Custom Tabs es el enfoque recomendado. Es más seguro, maneja las redirecciones de forma nativa y requiere menos configuración. Use un WebView solo si Custom Tabs no es una opción en su proyecto.

Si debe usar un `WebView`, la journey requiere que JavaScript, almacenamiento DOM, reproducción automática de medios y permisos de cámara/geolocalización se otorguen explícitamente. Sin estas configuraciones, el marco de captura no se cargará o la cámara no se activará.

```kotlin
webView.settings.javaScriptEnabled = true
webView.settings.domStorageEnabled = true
webView.settings.mediaPlaybackRequiresUserGesture = false
webView.settings.setGeolocationEnabled(true)

webView.webChromeClient = object : WebChromeClient() {
    override fun onPermissionRequest(request: PermissionRequest) {
        if (PermissionRequest.RESOURCE_VIDEO_CAPTURE in request.resources) {
            request.grant(arrayOf(PermissionRequest.RESOURCE_VIDEO_CAPTURE))
        } else {
            request.deny()
        }
    }

    override fun onGeolocationPermissionsShowPrompt(
        origin: String,
        callback: GeolocationPermissions.Callback
    ) {
        callback.invoke(origin, true, false)
    }
}
```

También debe declarar los permisos `CAMERA`, `INTERNET` y `ACCESS_FINE_LOCATION` en su `AndroidManifest.xml` y solicitar los permisos de cámara y ubicación en tiempo de ejecución antes de cargar la URL de la jornada.

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```