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

# Connections on Android

> Present the hosted Connections page in a Chrome Custom Tab, or render your own native wallet list with the headless engine.

<Note>
  This is an enterprise-only feature. Please [contact us](https://www.dynamic.xyz/book-a-call) to enable.
</Note>

On Android the [Connections](/docs/connections/overview) contract is the same as every other platform. The result comes back on a custom URL scheme, caught by a small return activity.

You have two options:

* **Basic** — present the hosted page in a Chrome Custom Tab. One Kotlin file to copy.
* **Headless** — render your own native wallet list, driven by a hidden `WebView`. Your app links no wallet SDK.

The basic flow is the recommended default. Use headless only when you need a fully native front end.

## Basic

The integration is one Kotlin file, [`FireblocksConnect.kt`](https://github.com/dynamic-labs-oss/iframe-fb/blob/main/android/app/src/main/java/com/fireblocks/connect/FireblocksConnect.kt) — the Android analog of iOS's `FireblocksConnectFlow`. It includes `FireblocksRedirectActivity`, the activity that catches the return.

<Tip>
  Present with a Chrome Custom Tab — Android's secure, sandboxed in-app browser, the analog of iOS's `ASWebAuthenticationSession`. The page returns to `<scheme>://wallet-callback`, caught by `FireblocksRedirectActivity`.
</Tip>

### 1. Add the dependency and return activity

Add Chrome Custom Tabs to your Gradle build.

```kotlin build.gradle.kts theme={"system"}
implementation("androidx.browser:browser:1.8.0")
```

Register `FireblocksRedirectActivity` in `AndroidManifest.xml` (replace `myapp` with your scheme) so the OS routes the return to your app. `singleTask` lets the return dismiss the Custom Tab.

```xml AndroidManifest.xml theme={"system"}
<activity
  android:name="com.fireblocks.connect.FireblocksRedirectActivity"
  android:exported="true"
  android:launchMode="singleTask">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="myapp" android:host="wallet-callback" />
  </intent-filter>
</activity>
```

### 2. Present the flow

Call `FireblocksConnect.present`. It appends `redirect_uri`, a random `nonce`, and `embedded=1`, verifies the returned nonce, and parses the result into `WalletConnection`.

```kotlin MainActivity.kt theme={"system"}
FireblocksConnect.present(
    context = this,
    flowURL = "https://your-connect-page.example/",
    scheme = "myapp",
) { result ->
    when (result) {
        is FireblocksConnectResult.Success -> { /* result.wallet.address, .chain … */ }
        is FireblocksConnectResult.Cancelled -> {}
        is FireblocksConnectResult.Error -> { /* result.reason */ }
    }
}
```

`WalletConnection` carries `address`, `chain` (`evm` or `solana`), `walletName`, and `walletImage`. Render `walletImage` in a `WebView` `<img>` — it's an SVG sprite that `ImageView` can't draw.

## Headless

Render your **own native list** with no visible web. `FireblocksHeadlessConnect` runs the connect logic in a hidden `WebView` and talks to it over a bridge — the same architecture as iOS.

<Note>
  **No SDK in your app.** Your app links no wallet SDK. It needs a hidden `WebView` pointed at the hosted engine page (`https://your-connect-page.example/headless.html`, set as `ENGINE_URL` in `FireblocksHeadlessConnect`) and your URL scheme. All the WalletConnect / MetaMask / Phantom logic — and the wallet list — comes from that hosted view.
</Note>

Copy [`FireblocksHeadlessConnect.kt`](https://github.com/dynamic-labs-oss/iframe-fb/blob/main/android/app/src/main/java/com/fireblocks/connect/FireblocksHeadlessConnect.kt) in addition to `FireblocksConnect.kt` (the visible fallback).

### 1. Get the wallet menu

The engine derives the list live from the Dynamic catalog and pushes it over the bridge (a `wallets` message) — set `FireblocksHeadlessConnect.onWallets`. Each entry carries `key`, `name`, `icon`, `chains` (`evm` / `solana`), `mode` (`headless` | `fallback`), and `featured`.

### 2. Drop in FireblocksHeadlessConnect

It owns a hidden `WebView`, bridges to it (`addJavascriptInterface` + `evaluateJavascript`), opens the wallet deeplink it returns, and calls you back. Pre-warm it at launch.

```kotlin MainActivity.kt theme={"system"}
FireblocksHeadlessConnect.prewarm(this)               // at launch
FireblocksHeadlessConnect.onWallets = { render(it) }  // the live list

FireblocksHeadlessConnect.connect(this, "metamask", "evm") { result ->
    when (result) {
        is FireblocksHeadlessConnect.Result.Success -> { /* result.wallet */ }
        is FireblocksHeadlessConnect.Result.FallbackRequired -> { /* visible flow */ }
        is FireblocksHeadlessConnect.Result.Failure -> { /* result.code */ }
    }
}
```

Auto-fallback is built in: wallets that can't go headless (Base Account passkey, and similar) return `FallbackRequired`, and you open the visible `FireblocksConnect` for that same wallet.

### 3. Sign a message and transaction

After a successful connect, call `sign()` with any string, or `signTransaction()` with a serialized transaction (signing only — no broadcast).

```kotlin MainActivity.kt theme={"system"}
FireblocksHeadlessConnect.sign(
    context,
    message = "Sign in to MyApp · ${Instant.now()}"
) { result ->
    when (result) {
        is SignResult.Success -> { /* result.signature — hex string */ }
        is SignResult.Failure -> { /* result.code, result.message */ }
    }
}
```

For EVM, pass a JSON transaction and receive an RLP-encoded hex string. For Solana, pass a base64-encoded serialized `VersionedTransaction` and receive base64-encoded signed bytes. Signing is only available for wallets connected through the headless engine.

### 4. Wire the manifest and redirect

Headless needs two manifest additions beyond the basic flow: a `phantom-headless` host on the same `FireblocksRedirectActivity` intent-filter, and a `<queries>` block so the app can open wallet deeplinks on Android 11+.

```xml AndroidManifest.xml theme={"system"}
<!-- inside the FireblocksRedirectActivity intent-filter -->
<data android:scheme="myapp" android:host="wallet-callback" />
<data android:scheme="myapp" android:host="phantom-headless" />

<!-- Android 11+ package visibility, at <manifest> level -->
<queries>
  <intent><action android:name="android.intent.action.VIEW" />
    <data android:scheme="metamask" /></intent>
  <intent><action android:name="android.intent.action.VIEW" />
    <data android:scheme="phantom" /></intent>
</queries>
```

Route the return activity to the engine first (for Phantom's redirect), then fall through to the visible flow.

```kotlin FireblocksRedirectActivity theme={"system"}
intent?.data?.let { uri ->
    if (!FireblocksHeadlessConnect.handleReturnURL(uri)) {
        FireblocksConnect.handleRedirect(uri)
    }
}
```

## Common pitfalls

* **Don't call `webView.onPause()` on the hidden `WebView`.** That suspends its relay socket. It stays alive for a normal app-switch; for long approvals a foreground service is the robust option.
* **Cancellation isn't auto-detected** by Custom Tabs. Treat "resumed with no result" as cancelled, or hold the pending flow in a `ViewModel`.
* **Test on a device with a wallet installed** — not a bare emulator.
* **Serve over HTTPS.** The flow mints WalletConnect URIs via WebCrypto, which needs a secure context.
