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

# Hello Journey

> A minimal Jetpack Compose app that loads a GBG GO journey.

A minimal integration that loads a web-based identity journey inside a native Android app. This example compiles and runs as a standalone Jetpack Compose app.

## What This Example Demonstrates

* Initializing a `BridgeHost` with typed capability slots
* Declaring document capture support via handler assignment
* Displaying a journey by attaching a `WebView` in Compose
* Observing bridge errors through a `BridgeHostDelegate`

## Complete Source

```kotlin theme={null}
package com.example.hellojourney

import android.os.Bundle
import android.webkit.WebView
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.gbg.gbgbridge.capabilities.CameraDetector
import com.gbg.gbgbridge.core.BridgeHost
import com.gbg.gbgbridge.core.BridgeHostDelegate

// App Entry Point

class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      MaterialTheme {
        JourneyScreen()
      }
    }
  }
}

// Journey Screen

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun JourneyScreen() {
  val context = LocalContext.current

  // 1. Create the bridge host — held in remember so it survives recomposition
  val host = remember {
    BridgeHost(hostVersion = "1.0.0").apply {
      // 2. Declare document capture support by setting a handler
      documentCapture.handler = { _ ->
        documentCapture.awaitCompletion()
      }

      // 3. Detect and report camera permission state
      documentCapture.permissionState = CameraDetector.check(context).permissionState
    }
  }

  // 4. Set the journey URL
  val journeyUrl = "https://journey.example.com"

  // 5. Observe bridge errors via the delegate, mirrored into Compose state.
  //    BridgeHost holds its delegate weakly, so keep this strong reference.
  var lastError by remember { mutableStateOf<String?>(null) }
  val delegate = remember {
    object : BridgeHostDelegate {
      override fun onError(host: BridgeHost, error: Throwable) {
        lastError = error.message ?: error.toString()
      }
    }
  }

  DisposableEffect(Unit) {
    host.delegate = delegate
    onDispose { host.detach() }
  }

  Scaffold(
    topBar = { TopAppBar(title = { Text("Identity Verification") }) }
  ) { innerPadding ->
    Column(
      modifier = Modifier
        .fillMaxSize()
        .padding(innerPadding)
    ) {
      // 6. Embed the WebView, attach it to the host, and load the journey
      AndroidView(
        modifier = Modifier.weight(1f),
        factory = { ctx ->
          WebView(ctx).also { webView ->
            host.attach(webView)
            webView.loadUrl(journeyUrl)
          }
        }
      )

      // 7. Show errors if any
      lastError?.let { message ->
        ErrorBanner(message = message) {
          lastError = null
          host.clearError()
        }
      }
    }
  }
}

// Error Banner

@Composable
fun ErrorBanner(message: String, onDismiss: () -> Unit) {
  Row(
    modifier = Modifier
      .fillMaxWidth()
      .background(MaterialTheme.colorScheme.error)
      .padding(horizontal = 16.dp, vertical = 4.dp),
    verticalAlignment = Alignment.CenterVertically
  ) {
    Text(
      text = message,
      style = MaterialTheme.typography.bodySmall,
      color = MaterialTheme.colorScheme.onError,
      maxLines = 2,
      overflow = TextOverflow.Ellipsis,
      modifier = Modifier.weight(1f)
    )
    IconButton(onClick = onDismiss) {
      Icon(
        imageVector = Icons.Default.Close,
        contentDescription = "Dismiss",
        tint = MaterialTheme.colorScheme.onError
      )
    }
  }
}
```

## How It Works

1. **`BridgeHost(hostVersion = ...)`** creates a host with typed capability slots. No separate configuration object is needed. `BridgeHost` is main-thread-only, so construct it and call its methods on the main thread — composition already runs there.

2. **Setting a handler** on `documentCapture` declares the capability as supported. The handler uses `awaitCompletion()` to suspend until the UI layer calls `complete()`.

3. **`CameraDetector.check(context)`** detects camera hardware and permission state. The `permissionState` is included in capability query responses.

4. **`host.attach(webView)`** configures the WebView (enables JavaScript and DOM storage), installs a bootstrap-injecting `WebViewClient`, and registers the `window.GBGBridge` JavaScript interface. There is no `BridgeWebView` wrapper on Android — you create a plain `WebView` (here via `AndroidView`), attach it to the host, and call `loadUrl()` yourself.

5. **Error observation** — unlike iOS, where `host.lastError` is `@Published`, the Android `lastError` property is not observable. Instead, implement `BridgeHostDelegate.onError` and mirror errors into Compose state. The host holds its delegate in a weak reference, so keep a strong reference to it (here via `remember`) or it will be garbage-collected and silently stop firing.

6. **`DisposableEffect.onDispose`** calls `host.detach()` when the screen leaves composition. Detach cancels any in-flight capture, removes the JavaScript interface, and clears the message buffers, so the host is ready for a clean re-attach.

## What Happens at Runtime

```
1. App launches -> JourneyScreen composes
2. AndroidView creates the WebView; host.attach() configures it
3. Journey URL loads; bootstrap script injected at page start (window.GBGBridge namespace created)
4. Web journey sends: capability.query request via window.GBGBridge.postMessage(...)
5. Built-in handler responds: { environment: "android", capabilities: { camera.document: { supported: true, permissionState: "granted" } } }
6. Web journey adapts its flow based on available capabilities and permissions
```

## Required Project Configuration

To compile this example, add the SDK dependency to your module's `build.gradle.kts` (no repository setup is needed beyond `mavenCentral()`):

```kotlin theme={null}
dependencies {
  implementation("com.gbg:gbgbridge-sdk:0.1.0-alpha01")
}
```

The only manifest entry this minimal example needs is the Internet permission:

```xml theme={null}
<uses-permission android:name="android.permission.INTERNET" />
```

If your journey URL uses HTTP during development, note that Android blocks cleartext traffic by default. Add a network security configuration scoped to your local development hosts (from the emulator, your host machine is `10.0.2.2`, not `localhost`):

```xml theme={null}
<!-- res/xml/network_security_config.xml — debug builds only -->
<network-security-config>
  <domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="false">10.0.2.2</domain>
    <domain includeSubdomains="false">localhost</domain>
  </domain-config>
</network-security-config>
```

Reference it from the manifest with `android:networkSecurityConfig="@xml/network_security_config"` on the `<application>` element. Never ship an unscoped `android:usesCleartextTraffic="true"` in production.

## Next Steps

* [Two-Way Communication](/docs/go-v2/developer-integration/sdks/android/examples/two-way-communication) — Send events and handle requests
* [Advanced Integration](/docs/go-v2/developer-integration/sdks/android/examples/advanced-integration) — Custom handlers, lifecycle, and error handling
