The Android shell is a thin native wrapper around your existing Hotwire web views, built on Hotwire
Native for Android. Setup means creating an Android Studio project, adding a few Gradle
dependencies, and dropping in the provided Kotlin sources.

## Creating the project

Open Android Studio and choose New Project, Empty Views Activity, named `OneShot` (package, for
example, `com.oneshot`). Set `minSdk = 28`, the floor Hotwire Native Android requires, and add the
dependencies in Kotlin DSL (`build.gradle.kts`):

```kotlin
dependencies {
    implementation("dev.hotwire:core:1.+")
    implementation("dev.hotwire:navigation-fragments:1.+")
    implementation("androidx.browser:browser:1.8.0")          // Custom Tabs for OAuth
    implementation("com.android.billingclient:billing-ktx:7.+") // Play Billing (for IAP)
}
```

## Adding the shell's source files

Add the Kotlin sources from `mobile/android/src/` to your package, replacing the generated
`MainActivity`, and copy `activity_main.xml` into `app/src/main/res/layout/`. Add these permissions
to the manifest:

```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.android.vending.BILLING" />
```

`MainActivity` already sets the content view, registers the route-decision handler, and calls
`BillingManager.syncEntitlements(this)` on launch; there's nothing else to wire up manually.

## Pointing it at your app

Set your app's URL in `Config.kt`. The Android emulator reaches a Rails server running on your host
machine at `http://10.0.2.2:3000`, not `localhost`.

## The app icon

Replace the default `ic_launcher` using Android Studio's Image Asset Studio (right-click `res`, New,
Image Asset), which generates both the adaptive icon layers and the density mipmaps from a source
image. Also export a 512x512 icon for the Play Store listing itself.

Verify the adaptive **foreground** layer isn't blank. Image Asset Studio can generate an empty
foreground image even when the legacy square icon looks correct, and on API 26+ launchers, which
use the adaptive icon, that renders as a featureless colored square with no visible logo at all.
Composite `mipmap-*/ic_launcher_foreground.webp` over the background color to sanity-check it
before you ship.

## What's in the shell

- `Config.kt`: the base URL and the path-configuration URL.
- `activity_main.xml`: the content layout, a full-screen `NavigatorHost` fragment.
- `MainActivity.kt`: the `HotwireActivity` host; registers the route-decision handler, and
  `handleExternalRoute` dispatches `/auth/` and `/iap/*` to native handling.
- `ExternalRouteDecisionHandler.kt`: intercepts `/auth/` (OAuth) and `/iap/*` (Play Billing) so
  neither loads inside the web view.
- `OAuthLauncher.kt`: opens Google and Apple sign-in in a Chrome Custom Tab. See
  [Auth in the App](/docs/mobile-apps/auth-in-the-app).
- `BillingManager.kt`: the full Play Billing flow, connecting, querying subscriptions, launching
  the purchase, acknowledging it, and posting the token to `/iap/purchase`, plus
  `syncEntitlements()`, which re-asserts active purchases on every launch. See
  [In-App Purchases](/docs/mobile-apps/in-app-purchases).

In-app purchase support is wired but optional: web Stripe billing works without any of the Play
Billing code running. Sign in with Apple is hidden on Android entirely, since Apple only requires
it on iOS.

## Local development gotchas

`http://10.0.2.2:3000` is cleartext HTTP, which Android blocks by default. For debug builds, allow
it with a network security configuration that permits cleartext traffic to `10.0.2.2`, or set
`android:usesCleartextTraffic="true"` on a debug manifest. This isn't needed once you're pointed at
an `https://` host.

## Next

See the equivalent setup for iOS: [iOS Setup](/docs/mobile-apps/ios-setup). Or move on to
[Submitting to the Stores](/docs/mobile-apps/submitting-to-the-stores) for release signing.
