## How Android camera permissions work

Android camera access is gated by multiple layers, all of which must be satisfied before document capture can begin:

1. **Manifest permission** (`android.permission.CAMERA`)
    
   Required for any camera usage. If this permission is missing, the system will block access immediately.

2. **Runtime permission** (Android 6.0 / API 23+)
    
   The system permission prompt appears only when your app explicitly requests camera access.

3. **User decisions and device policy**
    
   Camera access may still be unavailable depending on user action or system restrictions:
   - If the user denies permission, your app may usually request it again.
   - If the user denies permission and selects **Don’t ask again** (or the OS implicitly applies it), the system will **not show the prompt again**. In this state, the user must manually re-enable access from **App Settings**.
   - On some devices, camera access may be **restricted by parental controls, MDM, or work profile policies**, making it unavailable regardless of user action.

If camera access is unavailable, the **host app** is responsible for:
- Detecting the denied or restricted state
- Guiding the user to **Settings** when permission can no longer be requested

Without camera access, the DocV document capture flow **cannot be completed**.

## Camera authorization states

Camera access can be in one of the following states:

| State                     | Meaning                                         | What you can do                           |
|---------------------------|-------------------------------------------------|-------------------------------------------|
| Granted                   | User approved camera access                    | Camera capture can proceed                |
| Denied                    | User denied access                             | You may re-request permission             |
| Denied with “Don’t ask again” | User permanently denied access                  | Direct user to App Settings               |
| Restricted                | Access blocked by system policy (MDM, work profile, parental controls) | Cannot be resolved programmatically       |

## Required configuration

Before using any DocV SDK feature that accesses the camera, your app must be correctly configured at the manifest level.

### 1) Declare the camera permission

Add the camera permission to your app’s `AndroidManifest.xml`:

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

If this permission is missing:
- The system will block all camera access.
- The DocV SDK will not be able to start a capture session.

### 2) Declare camera hardware requirement (recommended)

If your document capture flow **requires** a camera (typical for DocV), declare the camera as required:

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

This ensures your app is only installable on devices with camera hardware.

If you want your app to remain installable on devices **without** a camera (not typical for DocV capture), set:

```xml
android:required="false"
```

In this case, your app must detect the absence of camera hardware and prevent users from starting a document capture flow.

## Recommended integration patterns

### Option 1 — Handle denied permission after SDK launch

If the DocV SDK returns an error indicating camera permission was declined or unavailable, the host app should:
1. Detect the permission-related error from the SDK result.
2. Explain why camera access is required.
3. Provide a clear path to **App Settings**.

**Suggested UI copy**
- **Title:** Camera access needed
- **Body:** Camera access is required to complete document capture. Please enable it in Settings to continue.
- **Action:** Open Settings

**When to use this approach**
- When permission denial is expected to be rare.
- When you prefer a simpler integration with recovery handled after failure.

### Option 2 — Pre-check and request permission before launching the SDK (recommended)

This approach prevents starting a DocV session when camera access is unavailable.

**Host app flow**
1. Check whether camera permission is granted.
2. If not granted:
   - If rationale should be shown, present explanation UI, then request permission.
   - Otherwise, request permission directly.
3. If permission is **blocked** or **restricted**, show app-owned UI and provide **Open Settings**.
4. Launch the DocV SDK **only** after permission is granted.

**Benefits**
- Avoids launching the SDK into an unrecoverable state.
- Improves conversion by preventing dead-end sessions.
- Gives the host app full control over permission UX.

## Example implementation

The following example demonstrates a complete pre-check and request flow before launching a DocV session using the Activity Result API and the Predictive DocV SDK.

```kotlin
package com.socure.docv.sdk.sample

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.widget.Button
import android.widget.Toast
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.socure.docv.capturesdk.api.SocureDocVContext
import com.socure.docv.capturesdk.api.SocureSdk
import com.socure.docv.capturesdk.common.utils.SocureDocVFailure
import com.socure.docv.capturesdk.common.utils.SocureDocVSuccess

private const val TAG = "MainActivity"
private const val PUBLIC_KEY = "YOUR_PUBLIC_KEY_HERE"

class MainActivity : AppCompatActivity() {

private val requestCameraPermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
            if (granted) {
                startDocVFlow()
            } else {
                val canAskAgain = ActivityCompat.shouldShowRequestPermissionRationale(
                    this,
                    Manifest.permission.CAMERA
                )
                showCameraDeniedDialog(canAskAgain)
            }
        }

private val startForResult =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
            val data = result.data ?: return@registerForActivityResult

SocureSdk.getResult(data) { sdkResult ->
                Log.d(TAG, "SDK result: $sdkResult")

when (sdkResult) {
                    is SocureDocVSuccess -> {
                        Toast.makeText(
                            this,
                            "Success. deviceSessionToken=${sdkResult.deviceSessionToken}",
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                    is SocureDocVFailure -> {
                        Toast.makeText(
                            this,
                            "Error. deviceSessionToken=${sdkResult.deviceSessionToken}, error=${sdkResult.error}",
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                    else -> {
                        Toast.makeText(this, "Unknown result", Toast.LENGTH_SHORT).show()
                    }
                }
            }
        }

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

findViewById<Button>(R.id.actionStartSocureDocvSDK).setOnClickListener {
            checkAndRequestCameraPermission()
        }
    }

private fun checkAndRequestCameraPermission() {
        val granted = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.CAMERA
        ) == PackageManager.PERMISSION_GRANTED

if (granted) {
            startDocVFlow()
            return
        }

val shouldShowRationale = ActivityCompat.shouldShowRequestPermissionRationale(
            this,
            Manifest.permission.CAMERA
        )

if (shouldShowRationale) {
            showRationaleThenRequest()
        } else {
            requestCameraPermission.launch(Manifest.permission.CAMERA)
        }
    }

private fun showRationaleThenRequest() {
        AlertDialog.Builder(this)
            .setTitle("Camera access needed")
            .setMessage("Camera access is required to capture your document.")
            .setPositiveButton("Continue") { _, _ ->
                requestCameraPermission.launch(Manifest.permission.CAMERA)
            }
            .setNegativeButton("Cancel", null)
            .show()
    }

private fun showCameraDeniedDialog(canAskAgain: Boolean) {
        val message = if (canAskAgain) {
            "Camera permission is required to continue."
        } else {
            "Camera permission is disabled. Please enable it in Settings to continue."
        }

AlertDialog.Builder(this)
            .setTitle("Camera access needed")
            .setMessage(message)
            .setPositiveButton(if (canAskAgain) "Try again" else "Open Settings") { _, _ ->
                if (canAskAgain) {
                    requestCameraPermission.launch(Manifest.permission.CAMERA)
                } else {
                    openAppSettings()
                }
            }
            .setNegativeButton("Cancel", null)
            .show()
    }

private fun openAppSettings() {
        startActivity(
            Intent(
                Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                Uri.fromParts("package", packageName, null)
            ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
        )
    }

private fun startDocVFlow() {
        // Launch Socure DocV SDK (token is a placeholder for documentation)
        val intent = SocureSdk.getIntent(
            this,
            SocureDocVContext(
                // placeholder token (normally fetched from RiskOS™ backend)
                "DOCV_TRANSACTION_TOKEN_VALUE",
                PUBLIC_KEY,
                false,
                null,
                null
            )
        )
        startForResult.launch(intent)
    }
}
```

## Key points in the example

- **Checks permission first** using `ContextCompat.checkSelfPermission(...)` to avoid launching the SDK when camera access is unavailable.
- **Requests permission using the Activity Result API** via `ActivityResultContracts.RequestPermission()`, ensuring permission handling is lifecycle-aware and resilient to configuration changes.
- **Shows rationale when appropriate** using `shouldShowRequestPermissionRationale(...)` to explain why camera access is required before re-requesting permission.
- **Differentiates “ask again” vs “blocked” states**:
  - `canAskAgain == true`
    Show in-app UX and re-request permission.
  - `canAskAgain == false`
    Permission is likely blocked (“Don’t ask again”) or restricted by policy. Deep-link the user to **App Settings**.
- **Deep-links to App Settings** when permission can no longer be requested programmatically:
  - `Settings.ACTION_APPLICATION_DETAILS_SETTINGS`
  - `Uri.fromParts("package", packageName, null)`
- **Launches the DocV SDK only after permission is granted**, preventing unrecoverable or dead-end capture sessions.
- **Receives SDK results from `SocureSdk.getResult(data)`** and surfaces success or failure states to the user via Toasts or other app-owned UI.

## UX and implementation best practices
- Keep permission explanations short and contextual.
- Use consistent CTAs:
  - Re-requesting permission → **Continue** or **Try again**
  - Blocked permission → **Open Settings**
- Re-check permission after returning from Settings (commonly in `onResume`).
- Consider tracking analytics events such as:
  - `camera_permission_denied`
  - `camera_permission_blocked`
  - `opened_settings`
  - `docv_started`
  - `docv_success` and `docv_failure`.
