Prerequisites
- An Android project with minSdk 24 (Android 7.0) or higher and compileSdk 34
- JDK 17 and Kotlin 2.x
- Access to Maven Central (no credentials or extra repositories required)
- A journey URL from the GBG Go Core SDK (see Journey URL)
Step 1: Add the SDK
Add GBGBridge from Maven Central in your moduleβsbuild.gradle.kts:
mavenCentral() is in your repository list β no other repository setup is needed.
See Getting Started β Installation for full details.
Step 2: Obtain a journey URL
Your app needs a journey URL to load inside the bridge WebView. This URL is generated server-side using the GBG Go Core SDK β your backend calls the Core SDK, receives a session URL, and passes it to the Android app.See Journey URL for the full pattern, including URL shape, authentication, and configuration options.
Step 3: Add manifest entries
Declare the permissions your integration uses inAndroidManifest.xml:
CAMERA permission must also be requested at runtime β handle that inside your capture surface (see Capture Screens).
Android blocks cleartext HTTP by default (API 28+) β the equivalent of App Transport Security on iOS. If you load a journey from a local development server, add a network_security_config.xml that permits cleartext only for 10.0.2.2, localhost, and 127.0.0.1, ideally in a debug source set.
Step 4: Initialize BridgeHost
Create aBridgeHost β this is the coordinator that routes messages between the WebView and your native code. In Compose, hold it in remember { } (or a ViewModel) so it survives recomposition; in the View system, keep it as a property on your Activity or Fragment.
BridgeHost is main-thread-only: state-mutating methods throw IllegalStateException when called off the main thread. From a coroutine, hop back with withContext(Dispatchers.Main) before touching the host.
Step 5: Set up capture handlers
Attach handlers to the typed capability slots. Setting a handler declares that your app supports that capability β no separate configuration step.Option A: Your Own Capture Surface (Development / Early Integration)
Android ships no built-in stub views β your app supplies the capture UI. The handler pattern is to suspend until your UI completes the capture:activeRequest state flow, and resolve the request from the UI:
See Capture Screens for capture-UI patterns: a CameraX live preview, the system photo picker, and a placeholder bitmap for emulator development.
Option B: Smart Capture SDKs (Production)
Smart Capture integration for Android is not yet available. The capture surface is designed to be swappable β the handler setup above stays identical, and only the UI presented when a request arrives changes.Step 6: Set permission state
Report camera permission state so the web journey can check permissions before attempting capture:CameraDetector only distinguishes GRANTED from NOT_DETERMINED β Android cannot tell never-asked apart from permanently denied without app-side state. After running your own runtime-permission flow, set the richer DENIED state on the slots yourself.Step 7: Display the journey
There is noBridgeWebView wrapper on Android. Create a plain WebView, attach the host, and load the journey URL β attach() configures the WebView for you (enables JavaScript and DOM storage, installs the bootstrap-injecting WebViewClient and a default WebChromeClient, and registers the JavaScript interface).
- Custom WebViewClient β if you need your own navigation policy or error logging, subclass
BootstrapInjectingWebViewClientand pass it viahost.attach(webView, client = ...). Setting a plainWebViewClientyourself would remove bootstrap injection; callsuper.onPageStarted(...)in your subclass to keep it. - Custom WebChromeClient β set it after
attach(). Attaching installs a default chrome client and overwrites whatever was there before. - SSL errors β never call
handler.proceed()inonReceivedSslError. Cancel the load and surface the failure instead. - Lifecycle β call
detach()when the screen goes away but the host may be reused, anddispose()for terminal teardown (ComposeDisposableEffect.onDispose, ActivityonDestroy, or ViewModelonCleared). Afterdispose()the host cannot be reused.detach()cancels in-flight typed-slot captures automatically, but cancel them explicitly first when you want to control the cancellation reason β and you must cancel manually if you registered raw handlers instead of the typed slots.
See Embedding Guide for Compose and View-system integration patterns.
Step 8: Build and Run
- Build and run on a physical device for real camera capture; on the emulator, use the photo-picker or placeholder-bitmap paths from Capture Screens.
- The web journey loads in the WebView.
- When the journey reaches a document or selfie step, the bridge sends a capture request.
- Your handler runs, your capture surface presents, the user captures, and the result flows back to the web journey.
localhost on the emulator is the emulator itself β use 10.0.2.2 to reach your host machine, or run adb reverse tcp:3000 tcp:3000.
Verify It Works
Check for these signs of a successful integration:- The web journey loads and renders correctly in the WebView.
capability.queryis handled automatically β the journey knows which capabilities your app supports.- Document capture requests trigger your capture surface, and the captured image is returned to the journey.
- Selfie capture requests trigger your selfie surface, and the result is returned.
- Cancellation flows work β dismissing the capture surface sends a
cancelledresponse.
Common Issues
| Symptom | Likely Cause | Fix |
|---|---|---|
| Blank WebView | Cleartext HTTP blocked (API 28+) | Use HTTPS, or add a network_security_config.xml scoped to 10.0.2.2/localhost for local dev |
Blank WebView on emulator with a localhost URL | localhost is the emulator itself | Use 10.0.2.2, or adb reverse tcp:3000 tcp:3000 |
capability.query returns empty | No handlers set | Set handler on at least one typed slot before loading the URL |
| Capture surface never appears | activeRequest not collected | Collect host.documentCapture.activeRequest (collectAsState()) and show your surface when it is non-null |
| Capture result not received by journey | complete() not called | Ensure every exit path calls complete(...) or cancelIfBusy(...) |
| Delegate callbacks stop firing | Delegate was garbage-collected (WeakReference) | Hold a strong reference to your delegate |
IllegalStateException from host methods | Called off the main thread, or after dispose() | Call from the main thread (withContext(Dispatchers.Main)); never reuse a disposed host |
See Troubleshooting for a comprehensive diagnostic guide.
Complete Minimal Example
Putting it all together β a minimal Compose screen that loads a journey and handles document capture with your own capture surface:DocumentCaptureScreen is your own composable β see Capture Screens for ready-made patterns.
See Hello Journey for the full annotated example.
Whatβs Next
Once the basic integration is working:- Add selfie capture β same pattern as document capture, using
host.selfieCapture. - Add custom capabilities β see Capability Handling.
- Build a production-quality capture surface with CameraX β see Capture Screens.
- Review the Security Guide before shipping to production.