---
title: Android 캡처
description: Human Plugin 생성 응답으로 받은 링크를 사용해, Custom Tabs로 Android 앱에서 Journey 플로우의 캡처를 실행합니다.
canonical: https://developer.unico.io/ko/human-plugin/integrations/android
locale: ko
generated_by: markdown-export
---

# Android 캡처

Custom Tab에서 캡처를 열고 사용자를 앱으로 다시 돌아오게 합니다.

백엔드에서 Journey 플로우와 앱의 URL 스킴을 사용하는 `callbackUri`로 Human Plugin에 프로세스를 생성합니다. 앱은 응답의 `link`를 [Custom Tab](https://developer.chrome.com/docs/android/custom-tabs)으로 엽니다. 캡처가 끝나면 브라우저가 앱의 스킴을 열고 앱이 제어권을 넘겨받습니다.

생성 응답의 Human Plugin `id`를 보관하세요. 백엔드는 이 값으로 결과를 읽습니다.

## 사전 요구 사항

- Android 8(API 26) 이상
- `androidx.browser:browser:1.5.0`
- `myapp`처럼 앱마다 고유한 URL 스킴

## 1. 의존성 추가

`build.gradle`에서:

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

## 2. 앱의 스킴으로 프로세스 생성

백엔드에서 스킴을 `callbackUri`로 전송합니다.

```json
{
  "flow": "humantoken",
  "clientId": "client-a",
  "correlationId": "order-1001",
  "credential": { "type": "DUI_TYPE_NG_BVN", "value": "<document number>" },
  "callbackUri": "myapp://callback"
}
```

그리고 `id`와 `link`를 앱에 반환합니다.

## 3. 액티비티 생성

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

class CaptureActivity : AppCompatActivity() {

    private var processId: String? = null

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

        processId = intent.getStringExtra(EXTRA_PROCESS_ID)
        val link = intent.getStringExtra(EXTRA_LINK) ?: return finish()

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

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        if (intent.data?.scheme == "myapp") {
            // 캡처가 끝났습니다. 백엔드에 결과를 읽도록 요청하세요.
            // GET /v1/processes/{id}를 사용하고, Human Plugin id를 전달합니다.
            onCaptureEnded(processId)
        }
    }

    private fun onCaptureEnded(processId: String?) {
        finish()
    }

    companion object {
        const val EXTRA_PROCESS_ID = "process_id"
        const val EXTRA_LINK = "link"
    }
}
```

## 4. `AndroidManifest.xml`에 스킴 등록

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

<activity
    android:name=".CaptureActivity"
    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`을 사용하면 Android가 복귀 인텐트를 같은 액티비티의 `onNewIntent()`로 전달합니다.

## 5. 캡처 시작

```kotlin
val intent = Intent(this, CaptureActivity::class.java)
    .putExtra(CaptureActivity.EXTRA_PROCESS_ID, id)
    .putExtra(CaptureActivity.EXTRA_LINK, link)
startActivity(intent)
```

## 요약

| 파일 | 용도 |
|---|---|
| `build.gradle` | `androidx.browser` 의존성 |
| `CaptureActivity.kt` | Custom Tab을 열고 복귀를 처리합니다 |
| `AndroidManifest.xml` | 앱 스킴용 인텐트 필터 |

```mermaid
sequenceDiagram
  participant A as Your app
  participant B as Your back-end
  participant H as Human Plugin
  participant T as Custom Tab
  A->>B: start the capture
  B->>H: POST /v1/processes (Journey flow, callbackUri myapp://callback)
  H-->>B: 201, id + link
  B-->>A: id + link
  A->>T: open link
  Note over T: capture
  T-->>A: myapp://callback
  A->>B: capture ended (id)
  B->>H: GET /v1/processes/{id}
  H-->>B: 200, final state
```

## WebView 사용하기

> **경고:** Custom Tabs 사용을 권장합니다. Custom Tabs를 사용할 수 없는 경우에만 WebView를 사용하세요.

캡처에는 JavaScript, DOM 스토리지, 미디어 자동 재생, 그리고 카메라 및 위치 권한이 필요합니다.

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

`AndroidManifest.xml`에 권한을 선언하고, `link`를 로드하기 전에 런타임에 카메라와 위치 권한을 요청하세요.

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

WebView에서는 `link`를 직접 여세요. 그 안에 Journeys SDK를 로드하지 마세요.