--- import Base from '../layouts/Base.astro'; import PageHeader from '../components/PageHeader.astro'; import NotBuilt from '../components/NotBuilt.astro'; import Screenshots from '../components/app/Screenshots.astro'; import platform from '../data/platform.json'; import { appFeatures, requiresDeployment } from '../data/app.mjs'; import { playPolicy } from '../data/beta.mjs'; /** * `/app/` — PLAN.md §10, phase 5. * * --------------------------------------------------------------------------------------- * THE PAGE LEADS WITH A LIMITATION, ON PURPOSE * --------------------------------------------------------------------------------------- * Directly under the lede, before a single feature, this page says the app ships pointed at * nothing and will not open until it is given the address of a deployment. That is an * unusual thing to put above the fold and it is the right thing here, because the * alternative is somebody installing a 13 MB client and discovering it on the first screen. * §1's "understated honesty" is cheapest to keep exactly at the moment it costs a download. * * --------------------------------------------------------------------------------------- * TWO WAYS TO GET IT, EQUALLY WEIGHTED — AND ONE OF THEM IS CURRENTLY OFF * --------------------------------------------------------------------------------------- * The org lead's call: the sideload and the beta get the same visual weight, with the beta * arguing for itself on delivery and updates rather than on being the only door. Both * panels are the same component, side by side, neither styled as the primary. * * The APK panel then has a second state, because the published build does not work. It * renders `platform.androidApk.serviceable ? : `. That flag is a person's judgement rather than a * fetched fact — `checkFacts.mjs` asserts the assets EXIST but cannot assert they run — so * turning the link back on is one boolean in `platform.json`, in the same commit as * whatever release fixed it. * * Note what this deliberately does not do: it does not remove the panel. A page that simply * omitted sideloading while the build is broken would read, to somebody who was told the * APK exists, as a page hiding it. * * --------------------------------------------------------------------------------------- * THE SCREENSHOT SLOT IS EMPTY AND THAT IS THE DECISION (D26) * --------------------------------------------------------------------------------------- * §10 promised "the 14 existing screenshots". They exist, and they are the wrong fourteen: * a July trusted-device smoke test against an unseeded development instance, captured * before the theming work landed, showing mostly login and two-factor screens over an * almost empty home page. Shipping them would break D4 (real screenshots, from the review * stack, not placeholders) and would show an app that no longer looks like that. * * So `Screenshots.astro` renders nothing until phase 9 fills it — the phase that already * stands up the review stack and seeds presentable content, and now also captures the app * against it, so the phone shots and the web shots show the same deployment. The component * exists now so the slot has a defined shape and phase 9 is a data change. */ const title = 'The Android app'; const description = 'A native Android client for a Runic Gateway deployment: the shard, the site and your ' + 'account, themed by whichever community you point it at.'; const apk = platform.androidApk; const release = platform.releases['Android-app']; const releasePage = `${platform.gitea.base}/${platform.gitea.org}/Android-app/releases`; const downloadBase = `${releasePage}/download/${release}`; ---

A native Android client — Kotlin and Compose, not a website in a frame. It shows the live game data a deployment publishes, the news and wiki it hosts, and the parts of your account that make sense on a phone.

It is open source like everything else here, and it carries no Google messaging dependency: notifications arrive over a server the operator runs.

{requiresDeployment.title}

{requiresDeployment.body}

What it does

Grouped by what you would open it for. Most of this is conditional on the deployment you connect to — a community that runs no game module has a news and account app, and that is a legitimate way to run this.

{ appFeatures.map((group) => (

{group.heading}

{group.blurb &&

{group.blurb}

}
    {group.items.map((item) => (
  • {item.title}

    {item.body}

    {item.gate && (

    Needs {item.gate}

    )}
  • ))}
)) }

Two ways to get it

Neither is the “real” one. Sideloading works today and always will; the closed test is how it reaches a phone through Play, with updates that install themselves.

Direct download

The signed APK

{ apk.serviceable ? ( <>

Built and signed by the same CI that cuts every release. Android asks you to allow installing from your browser or file manager the first time; the checksum file is there so you can verify what you downloaded before you do.

Download {release} Checksums

) : ( <>

The published build is being replaced. {release} is on the releases page but does not install and run correctly, so this page does not link it — a download that wastes your time is worse than no download.

The next release restores this. Nothing about the app has been withdrawn and the source has not moved; it is one build that went out wrong.

The releases page

) }

Android {apk.minAndroid} or newer · installs as {platform.androidApplicationId}

Google Play

The closed beta

Delivery through Play, and updates that arrive on their own instead of being downloaded again. It is a closed test, so a place on it has to be granted — the list is being collected now.

It has not opened yet, and the page says why in full rather than promising a date: Play needs {playPolicy.testersRequired} people opted in for {playPolicy.testerDays} {' '}days before the app can go any further, and a tester needs somewhere to point it.

Join the list

No email is ever sent · the address is used for the tester list and nothing else