Skip to content

Commit 376c9eb

Browse files
doublegateclaude
andcommitted
docs(plan): v1.8.0 monetization — freemium $2.99 Play unlock + 10-min demo
Maintainer-locked (2026-06-19): ship on Google Play as a free download with a one-time non-consumable "Full Unlock" IAP at $2.99 (Play Billing, single-listing freemium — not a paid app, not a second demo listing). The free tier is a time-limited demo: a 10-minute per-launch session with save-states, save-on-background/resume, and on-cart battery-backed SRAM persistence disabled; everything else (accuracy, video, audio, input, pause/FF, ROM library) is identical to paid. Added Workstream M (Entitlement/BillingManager, the three persistence locks + session timer, unlock/restore UI, debug override) and folded it into rc.1 of the release train. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent bb72f7b commit 376c9eb

1 file changed

Lines changed: 47 additions & 1 deletion

File tree

‎to-dos/plans/v1.8.0-android-plan.md‎

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@
77
> RetroAchievements, and Lua** to a follow-up point release. A shared **`rustynes-mobile`**
88
> bridge crate is factored here and reused by the v1.9.0 iOS plan.
99
10+
> **Monetization LOCKED by the maintainer (2026-06-19):** ship on the **Google Play Store**
11+
> as a **free download with a one-time in-app "Full Unlock" purchase priced at $2.99 USD**
12+
> (a managed/non-consumable IAP via Play Billing — the modern single-listing freemium model,
13+
> NOT two separate paid/free listings). The **free tier is a time-limited demo**: a **10-minute
14+
> play session per launch**, and with **save-states, save-on-background/resume, and on-cart
15+
> battery-backed SRAM persistence all disabled**. Everything else (full emulation accuracy,
16+
> video, audio, input, pause/fast-forward, the whole ROM library) is **identical to the paid
17+
> tier** — the demo is feature-complete except the three persistence gates + the session timer.
18+
> Purchasing the Full Unlock removes the timer and enables save-states / resume / SRAM. See the
19+
> "Monetization / licensing (Workstream M)" section below.
20+
1021
Research backing: [`research/v1.8.0-research-android.md`](research/v1.8.0-research-android.md).
1122

1223
## Context
@@ -161,6 +172,41 @@ language + the 2026–27 developer-verification rollout by (1) preparing dev ver
161172
depends solely on Play (also the home for the egui-debugger power-user build). **Effort: M;
162173
approval/calendar risk is outside our control.**
163174

175+
### M — Monetization / licensing (freemium $2.99 unlock) → rc.1
176+
A **single free Play listing** with a **one-time, non-consumable in-app purchase** ("Full
177+
Unlock", product id `full_unlock`, **$2.99 USD**) via the **Play Billing Library** (`com.android.
178+
billingclient:billing-ktx`, v7+). NOT a paid app and NOT a second "demo" listing — Play's modern
179+
guidance is one free app + an unlock IAP. Pieces:
180+
181+
- **`Entitlement` / `BillingManager`** (new Kotlin): on start, connect `BillingClient`, query the
182+
`full_unlock` product + the user's purchases, and expose a `@Volatile isUnlocked` the shell
183+
reads. A purchase must be **acknowledged** (`acknowledgePurchase`) or Play auto-refunds in 3
184+
days. Entitlement is cached locally (DataStore) for offline launches but is **re-verified
185+
against Play** on each connect (the cache is a convenience, not the source of truth). No
186+
server: a non-consumable IAP is owned forever and restored via `queryPurchasesAsync` (handles
187+
reinstall / new device / "restore purchases").
188+
- **Demo gating (the three locks + the timer), all enforced in the shell, core untouched:**
189+
1. **10-minute session timer** — a monotonic per-launch budget; on expiry, pause the emulator
190+
and show a non-dismissable "Demo time's up — Unlock full version for $2.99" sheet with the
191+
Buy + Restore buttons. Resets on next launch (per-session, not per-day).
192+
2. **Save-states disabled** — the Save/Load slot buttons are hidden/disabled in demo.
193+
3. **Save-on-background + auto-resume disabled** — `onPause` writes no `auto` state and a ROM
194+
never auto-resumes in demo.
195+
4. **Battery-backed SRAM not persisted** — the on-cart save RAM is never written to disk in
196+
demo (a battery save inside the snapshot is fine in-session, but nothing survives close).
197+
When `isUnlocked` flips true (purchase or restore), all four lift immediately, no restart.
198+
- **UI:** an always-visible "Unlock ($2.99)" affordance in demo; a "Restore purchase" entry; a
199+
subtle "Demo — N:MM left" indicator. After unlock, these disappear.
200+
- **Determinism-safe / accuracy-safe:** every gate is a *host* policy over persistence + pacing;
201+
the emulated framebuffer/audio for a given input is **byte-identical** in demo and paid (the
202+
demo is the same emulator, just without the persistence features + a session clock). The core,
203+
the `.rns` format, and AccuracyCoin are untouched.
204+
- **Maintainer-manual (can't be CI-self-certified):** create the `full_unlock` managed product
205+
in the Play Console, the pricing ($2.99) + tax, a license-tested account, and the closed-track
206+
upload to exercise the real purchase flow (Play Billing can't be fully tested on a sideloaded
207+
build). A **`BuildConfig`/debug override** (`debug.force_unlocked` / `debug.force_demo`) lets the
208+
gating + timer + UI be verified on-device without Play. **Effort: M.**
209+
164210
### Perf / thermal / battery *(cross-cutting, in B/C/F)*
165211
Perf is **not** the gate (the NES is light: ≤2 ms/frame headless, ample on modern arm64) —
166212
**thermals/battery** are. Default run-ahead **off or 1** on Android (keep the budget-throttle
@@ -177,7 +223,7 @@ run-ahead/fast-forward. **Determinism-safe** — all frontend pacing/scheduling
177223
| **beta.3 — Audio + input + touch** | C + D | (c) | audio lifecycle/latency; input determinism (touch + gamepad → one mask) |
178224
| **beta.4 — Storage + SAF + states** | E | (c) | **Risk #3** no-bundled-ROM/SAF + persistable grants; `.rns` cross-device |
179225
| **beta.5 — Compose shell + feature port** | F | (c) | native feel + parity (shaders/TAS/palettes); RA/netplay/Lua marked deferred |
180-
| **rc.1 — Store/legal/signing** | G | (c) | **Risk #2** Play policy (partial) + signing + 16 KB align + AAB in CI |
226+
| **rc.1 — Store/legal/signing + monetization** | G + M | (c) | **Risk #2** Play policy (partial) + signing + 16 KB align + AAB in CI; **freemium $2.99 unlock + 10-min demo gating** |
181227
| **v1.8.0 release** | docs (`docs/android.md`), CHANGELOG, `rustynes-mobile`+`rustynes-android`+Gradle committed, signed AAB on a closed track + sideload APK | (c) | full host-CI gate suite green; AccuracyCoin 100%/139 |
182228

183229
## Critical files / crates

0 commit comments

Comments
 (0)