> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gatlio.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Android

> Native billing enforcement for Android apps using Jetpack Compose — minimum API 26 (Android 8.0).

## Installation

Add the Gatlio Android SDK via a local composite build or published artifact:

```kotlin theme={null}
// settings.gradle.kts
includeBuild("path/to/gatlio-android") {
    dependencySubstitution {
        substitute(module("io.gatlio:compose")).using(project(":compose"))
        substitute(module("io.gatlio:core")).using(project(":core"))
    }
}

// app/build.gradle.kts
dependencies {
    implementation("io.gatlio:compose")
}
```

## Quick start

Wrap the authenticated portion of your app in `GatlioGate`:

```kotlin theme={null}
import io.gatlio.compose.GatlioGate

GatlioGate(
    tenantSlug = "acme",
    customerId = currentUser.stripeCustomerId,
    publishableKey = "pk_live_abc123",
    apiBase = "https://api.gatlio.io",
) {
    YourApp()
}
```

* **Active** — `YourApp` renders normally
* **Warning** — dismissable banner above content
* **Lockout** — full-screen overlay until card is updated

## Parameters

| Parameter        | Type                     | Required | Default   |
| ---------------- | ------------------------ | -------- | --------- |
| `tenantSlug`     | `String`                 | ✓        | —         |
| `customerId`     | `String`                 | ✓        | —         |
| `publishableKey` | `String`                 | ✓        | —         |
| `apiBase`        | `String`                 | ✓        | —         |
| `pollIntervalMs` | `Long`                   |          | `600_000` |
| `forcedStatus`   | `GatlioStatus?`          |          | `null`    |
| `callbacks`      | `GatlioCallbacks?`       |          | `null`    |
| `lockoutScreen`  | `@Composable (...)`      |          | built-in  |
| `warningBanner`  | `@Composable (...)`      |          | built-in  |
| `content`        | `@Composable () -> Unit` | ✓        | —         |

## Callbacks

```kotlin theme={null}
GatlioGate(
    tenantSlug = "acme",
    customerId = currentUser.stripeCustomerId,
    publishableKey = "pk_live_abc123",
    apiBase = "https://api.gatlio.io",
    callbacks = GatlioCallbacks(
        onLockout = { println("locked out") },
        onWarning = { println("warning") },
        onActive = { println("active") },
        onRecovered = { println("recovered") },
        onError = { err -> println("error: $err") },
    ),
) {
    YourApp()
}
```

Callbacks fire on status **transitions only**.

## Custom enforcement UI

```kotlin theme={null}
GatlioGate(
    // ...
    lockoutScreen = { triggerCardUpdate, _ ->
        MyBrandedLockout(onUpdate = triggerCardUpdate)
    },
    warningBanner = { triggerCardUpdate, dismissWarning ->
        MyBrandedBanner(onUpdate = triggerCardUpdate, onDismiss = dismissWarning)
    },
) {
    YourApp()
}
```

## Testing

### Force a state

```kotlin theme={null}
GatlioGate(
    tenantSlug = "acme",
    customerId = "cus_test",
    publishableKey = "pk_test_abc",
    apiBase = "https://api.gatlio.io",
    forcedStatus = GatlioStatus.Lockout,
) {
    YourApp()
}
```

No network calls are made when `forcedStatus` is set. Remove before shipping.

### Interactive sandbox

```kotlin theme={null}
import io.gatlio.compose.GatlioSandbox

GatlioSandbox(
    onLockout = { println("locked out") },
    onWarning = { println("warning") },
    onActive = { println("active") },
    onError = { err -> println("error: $err") },
) {
    YourApp()
}
```

A `DEV` badge appears in the bottom-right corner. Tap to open a bottom sheet with state pills and a callback log. Remove before shipping to production.

## Direct controller usage

For custom state management outside Compose:

```kotlin theme={null}
val controller = GatlioController(
    config = GatlioConfig(
        apiBase = "https://api.gatlio.io",
        tenantSlug = "acme",
        customerId = currentUser.stripeCustomerId,
        publishableKey = "pk_live_abc123",
    ),
)
controller.start()

// In a Compose context:
val state by controller.stateFlow.collectAsState()

// In a View-based context:
lifecycleScope.launch {
    controller.stateFlow.collect { state -> /* update UI */ }
}
```

Call `controller.dispose()` in `onDestroy` or when the controller is no longer needed.

## Minimum SDK

API 26 (Android 8.0).
