Docs · Mobile Apps

Android Setup

Building the Android shell in Android Studio.

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):

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:

<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.
  • 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.

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. Or move on to Submitting to the Stores for release signing.