docs(android): plan M8 — Google Play
Records where the Play Console stands (7 of 11 setup tasks done), the org lead's three decisions of 2026-10-23 (reuse the release keystore as Play's app-signing key; CI publishes each v* tag to the internal track; demo.runicgateway.com on the Oxide rig is the review and beta site, website#222), what Play needs that the app lacks (target API 36, an AAB, the upload step), the order of work, and three open questions (content rating vs. no report/block, the data-safety premise, the dataSync push service). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -923,6 +923,71 @@ push, and Play (M6–M8) follow the designed app.
|
||||
its `NTFY_*` deploy config (§13).
|
||||
9. **M8 — Google Play**: Play Console listing, signing/upload key, and (optionally) an FCM build flavor
|
||||
— after the direct-APK release is stable.
|
||||
|
||||
**Planned 2026-10-23**, after v0.6.0. The direct APK stays the default channel (§10); Play is
|
||||
added beside it, not instead of it.
|
||||
|
||||
**Where the console stands (read 2026-10-23).** The app `com.runicgateway.app` exists as a
|
||||
draft on a **personal** developer account. 7 of 11 setup tasks are done: privacy policy
|
||||
(runicgateway.com `/privacy/`), ads (none), content rating, government, financial, health, and
|
||||
category + contact. Still open: sign-in details, target audience, data safety (started), the
|
||||
advertising ID declaration, and the store listing. No build has been uploaded yet.
|
||||
|
||||
**Decisions (org lead, 2026-10-23):**
|
||||
- **Signing: reuse the existing release keystore as Play's app-signing key.** It is uploaded at
|
||||
the first release with Play's "use my own key" option, through Google's encrypted export
|
||||
tool. So a Play install and a sideloaded APK from Gitea have the same signature, and a user
|
||||
can move between them without uninstalling. This cannot be changed after the first upload.
|
||||
- **CI publishes every `v*` tag to the internal track.** Promotion to closed testing, and later
|
||||
to production, is a click in the console, so a tag never reaches testers or review on its
|
||||
own.
|
||||
- **The review and beta site is `demo.runicgateway.com`**, a dedicated Runic Gateway deployment
|
||||
on the Oxide Rust rig. Google's reviewers and the closed-test testers sign in there. It is
|
||||
runicgateway.com's D27 demo, given a host. The website changes it needs (demo accounts, a
|
||||
lock on breaking settings, a periodic wipe) are their own feature: `RunicGateway/website#222`.
|
||||
|
||||
**What Play requires that the app does not do today:**
|
||||
- **Target API 36.** Since 2026-08-31, new apps and updates must target Android 16 (API 36).
|
||||
The app targets 35. That means `compileSdk`/`targetSdk` 36, an AGP bump (8.6.1 does not
|
||||
support 36), the SDK packages in both workflows, and a check of Android 16's behaviour
|
||||
changes on a device.
|
||||
- **An app bundle.** Play takes an AAB, not an APK. `release.yml` keeps building the signed
|
||||
APK for the Gitea release and adds `bundleRelease`, signed with the same keystore.
|
||||
- **The upload.** After the Gitea release, a step in `release.yml` uploads the AAB to the
|
||||
internal track through the Google Play Developer API (`edits` → bundle upload → track →
|
||||
commit). It authenticates with a service-account key in a new secret,
|
||||
`PLAY_SERVICE_ACCOUNT_JSON`, the account having release rights on this app only. The step
|
||||
uses `curl` + `jq` + `openssl` (for the JWT), in the style of the existing Gitea release step,
|
||||
and adds no Gradle plugin. Gradle Play Publisher is the alternative if the script proves
|
||||
brittle. **Google requires the first bundle to be uploaded by hand in the console**; CI owns
|
||||
every upload after it. A Play failure fails the run *after* the Gitea release exists, so
|
||||
the APK channel never waits on Google.
|
||||
|
||||
**Order of work:**
|
||||
1. **App + CI** (one PR into `edge`): API 36 + AGP bump, AAB, Play upload step. Walked on the
|
||||
emulator and the S22 before the PR.
|
||||
2. **First bundle**, uploaded by hand from the next tag, with the signing key enrolled.
|
||||
3. **Console declarations**: target audience 18+ (runicgateway.com D31), advertising ID none,
|
||||
data safety from runicgateway.com's generated `PLAY_DATA_SAFETY.md`, and sign-in details
|
||||
pointing at `demo.runicgateway.com` with a reviewer account. Then the store listing.
|
||||
4. **Closed test**, once the demo site is up: a personal account needs **12 testers opted in
|
||||
for 14 days in a row** before it can apply for production. runicgateway.com `/beta`
|
||||
collects them.
|
||||
|
||||
**Open, each waiting for a decision:**
|
||||
- **Content rating.** The questionnaire answered "Social or Communication: moderated chat;
|
||||
users or user-generated content can be reported". The app shows other players' chat (the
|
||||
Rust feed) but has no report or block action, and Play's user-generated-content policy
|
||||
expects both where that is declared. Either retake the questionnaire, or add report/block
|
||||
to the app.
|
||||
- **Data safety's premise.** The generated notes answer "not collected" almost everywhere,
|
||||
because the app talks only to a deployment the user chose. The notes call it the answer
|
||||
most likely to be questioned in review. Declaring user IDs and messages as collected,
|
||||
encrypted in transit and needed for the app to work, is the conservative alternative.
|
||||
- **The push foreground service.** Play asks for a declaration (and a video) for the
|
||||
`dataSync` service that holds the ntfy connection. Google reserves `dataSync` for transfers,
|
||||
and Android 15 limits it to six hours a day. This brings forward §13's "FCM flavor for
|
||||
Play?" question.
|
||||
10. **M9 — Native SSO login** (post-v1, additive; independent of M8): in-app "Sign in with Google /
|
||||
Discord" via the **Mobile SSO Authorization Bridge** (§4.2). **Backend-first**, mirroring M7's
|
||||
split:
|
||||
|
||||
Reference in New Issue
Block a user