Server-controlled app updates, optional or forced
The gateway decides whether a TV may keep running its current build. Clients send X-Memby-Version on every request and ask GET /v1/update on each launch; the verdict is none, optional or mandatory. - internal/appupdate holds the decision as pure, tested logic: below minimumVersion is mandatory, below latestVersion is optional. - Admin page gains an App updates section — latest version, APK URL, notes, and a "Require this update" toggle that sets the forced floor. - Client shows a dismissable prompt for optional, and a full-screen panel that swallows Back for mandatory. Instructions say what the system installer will ask before it asks. Two safeguards: a client that cannot report its version is never forced, and the client ignores a verdict with no download URL, so a half-configured policy cannot produce an unblockable screen with a dead button. An unreachable gateway shows nothing. The verdict is deliberately not part of /v1/home: that payload is cached per user, while this answer depends on the requesting client's version. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
08360b75e4
commit
62f6345a40
@@ -86,6 +86,17 @@ key, so a changed key forces every user to uninstall and reinstall.
|
||||
URL is a static manifest, anything else is a Gitea host. `resolveApkUrl` lets a manifest
|
||||
use a relative `apkUrl`. Both are unit-tested in `UpdateSourceTest`.
|
||||
|
||||
**Forced updates are server-controlled.** `server/internal/appupdate` decides `none` /
|
||||
`optional` / `mandatory` from the client's `X-Memby-Version` header against an
|
||||
operator-set policy (admin page → App updates). `HomeViewModel.checkForAppUpdate` runs on
|
||||
every launch and `ui/UpdateScreen.kt` renders the verdict — mandatory covers the whole
|
||||
home screen with `zIndex(10f)`, swallows Back and offers no dismiss. Two safeguards worth
|
||||
preserving: a client with an unreadable version is never forced (it could not escape the
|
||||
prompt), and the client ignores any verdict without a `downloadUrl` (`isActionable`), so a
|
||||
half-configured policy cannot produce a blocking screen with a dead button. The verdict
|
||||
must stay out of `/v1/home`, which is cached per user while this answer varies per client
|
||||
build.
|
||||
|
||||
## Architecture
|
||||
|
||||
**Manual DI.** `ServiceLocator` (initialised in `MembyApp`) holds the single `SettingsStore`
|
||||
|
||||
Reference in New Issue
Block a user