Requirements
| Requirement | Minimum | Notes |
|---|---|---|
Android (minSdk) | API 24 (Android 7.0) | Covers the vast majority of active devices |
compileSdk / targetSdk | 34 | Your app should compile against API 34 or later |
| JDK | 17 | Required by the Android Gradle Plugin toolchain |
| Kotlin | 2.x | The SDK is built with Kotlin 2.2.10 |
| Artifact | AAR | Published to Maven Central |
androidx.annotation and kotlinx-serialization-json, and exposes kotlinx-coroutines-android as an api dependency — coroutine types appear on the public surface of CaptureCapability, so your app needs coroutines available (which any modern Android project already has).
Installation
GBGBridge is published to Maven Central ascom.gbg:gbgbridge-sdk. Add the dependency to your module’s build.gradle.kts:
mavenCentral(), which most projects already declare in settings.gradle.kts:
Minimal Integration
The steps below take you from an empty screen to a running journey: create aBridgeHost, declare a capability, attach a WebView, and load the journey URL.
1. Add the Imports
2. Initialize the Bridge Host
TheBridgeHost manages message routing between the WebView and your native code. The simplest way to create one is with a host version string. In Compose, hold it in remember so it survives recomposition:
BridgeHost is a main-thread-only class — construct it and call its methods on the main thread.
3. Declare Capabilities via Typed Slots
BridgeHost exposes typed capability slots for well-known capture operations. Setting a handler on a slot declares support — no separate configuration step needed:
CameraDetector.check(context) needs a Context — in Compose, use LocalContext.current. It reports GRANTED or NOT_DETERMINED; if your app runs its own permission flow, set the richer DENIED/RESTRICTED states on the slot yourself.
4. Display the Journey
There is no prebuilt WebView component on Android — you create a standardWebView and attach the host to it. attach() configures the WebView internally (JavaScript, DOM storage, bootstrap injection, message interface), so there is no separate configure step. In Compose, wrap the WebView in AndroidView:
host.attach(webView) before loadUrl() so the bootstrap script is in place when the page loads, and call host.detach() when the screen leaves composition. If you use the View system rather than Compose, the wiring is the same: create a WebView in your Activity or Fragment, attach, load, and call detach() (or dispose() for terminal teardown) in onDestroy().
5. Run Your App
Build and run. The web journey loads inside the native WebView. When the journey sends acapability.query request, GBGBridge automatically responds with the capabilities derived from your typed slots — including which are supported and their permission state.
Alternative: Configuration-Based Initialization
If you need explicit control over the capability map (e.g., for custom capabilities or static declarations), use the configuration-based constructor:capabilitiesProvider lambda as a second constructor argument. It is re-evaluated on every capability read, so you can reflect dynamic state (such as permission changes) without polling — this replaces the mutable capability map the iOS SDK exposes.
Both initialization paths support register(handler) for custom BridgeCapabilityHandler implementations. See the Capability Handling Guide for details.
What Happens Under the Hood
When you callhost.attach(webView), several things happen automatically:
- WebView configuration: JavaScript and DOM storage are enabled, and a bootstrap-injecting
WebViewClientplus a plainWebChromeClientare installed. This overwrites any clients you set earlier — if you need a customWebChromeClient, set it afterattach(); for a customWebViewClient, subclassBootstrapInjectingWebViewClientand pass it viaattach(webView, client = ...). - Bootstrap script injection: A JavaScript snippet is evaluated in
onPageStarted(main frame only) that initializeswindow.GBGBridgewith areceive()function. - JavaScript interface registration: The host is exposed to the page via
addJavascriptInterfaceunder the nameGBGBridge. Web content sends messages to native viawindow.GBGBridge.postMessage(jsonString)— note this differs from iOS, where the channel iswindow.webkit.messageHandlers.gbgBridge. - Capability query handler: A built-in handler for the
capability.queryaction is registered. The query response is built dynamically from typed slots and custom capabilities, including permission state metadata.
Bootstrap timing diverges from iOS. On iOS the bootstrap is injected via
WKUserScript at document start, guaranteed to run before any page JavaScript. On Android, injection happens in onPageStarted on a best-effort basis — a head script that synchronously calls window.GBGBridge.receive could race the injection. Keep this in mind if your web content interacts with the bridge during initial page load.Required App Configuration
GBGBridge itself does not force any entries into your app — the library manifest declares nothing. However, depending on the capabilities you expose, your host app will need appropriate manifest entries:android:required="false" keeps your app installable on devices without a camera; check availability at runtime with CameraDetector.check(context).
Local development over HTTP
Android blocks cleartext HTTP traffic by default (API 28+). If your journey URL points at a local development server (e.g.,http://10.0.2.2:3000), add a network security config that permits cleartext for development hosts only. Create res/xml/network_security_config.xml:
<application> element in your manifest:
android:usesCleartextTraffic="true" unscoped — keep cleartext exceptions limited to local development hosts (or to a debug source set override, so release builds stay locked down).
Common Integration Pitfalls
A handful of issues catch most teams when first wiring up the bridge — wrong WebView setup, missing capability declarations, cleartext policy rejecting non-HTTPS dev URLs, and malformed messages on the web side. The notes below explain what to check.WebView not receiving messages
Ensure you callhost.attach(webView) before loadUrl() — a plain WebView without attach() has no bootstrap script and no GBGBridge JavaScript interface. Also avoid replacing the WebViewClient after attaching: that removes the bootstrap injection. If you need custom navigation handling, subclass BootstrapInjectingWebViewClient and pass it to attach(webView, client = ...), calling super.onPageStarted to keep the bootstrap.
Capability query returns empty
If usingBridgeHost(hostVersion = ...): check that you set a handler on at least one typed slot, or registered a custom capability via registerCustomCapability(). If using BridgeHost(configuration = ...): check that you passed capabilities in your BridgeConfiguration (or supplied a capabilitiesProvider).
Cleartext HTTP blocked
If your journey URL useshttp:// and the page fails to load (typically net::ERR_CLEARTEXT_NOT_PERMITTED), add the network security config shown above. On the emulator, remember that your host machine is 10.0.2.2 — localhost refers to the emulator itself. Alternatively, forward the port with adb reverse tcp:3000 tcp:3000 and use http://localhost:3000.
Messages not decoding
GBGBridge expects messages in the Bridge Message Protocol format. If you see decode errors inhost.lastError (or via delegate.onError), verify that the web content is sending correctly structured JSON.
Next Steps
- Concepts — Understand the architecture and mental model
- Messaging Guide — Learn to send events and handle requests
- Capability Handling Guide — Register handlers for camera and custom capabilities
- API Reference — Detailed reference for every public symbol