Skip to content

Add in-app updates with Stable (F-Droid) and Alpha (GitHub) channels - #141

Merged
lostf1sh merged 3 commits into
mainfrom
feature/in-app-updater
Oct 4, 2026
Merged

lostf1sh merged 3 commits into
mainfrom
feature/in-app-updater

Conversation

@lostf1sh

@lostf1sh lostf1sh commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Adds an in-app update checker with two release channels.

What

  • Updates screen: a new row in the Settings root, also linked from Settings → About, the settings search, and the update notification (ACTION_OPEN_UPDATES).

  • Channels:

    • Stable reads https://f-droid.org/api/v1/packages/<id> (suggestedVersionCode) and downloads f-droid.org/repo/<id>_<code>.apk.
    • Alpha reads GitHub prereleases tagged vX.Y.Z-alpha.N and picks the PixelPlayerOSS-<version>-<abi>.apk asset matching Build.SUPPORTED_ABIS. Versions are compared as semver, then by alpha number.
  • Installed channel: taken from the running build's version name. Anything with a pre-release suffix (alpha and PR builds share the CI key) counts as Alpha; plain versions count as F-Droid.

  • Same-channel updates:

    • The APK downloads with progress.
    • Before installing, the app checks the archive's package name, that its version code is higher, and that its signer is in the installed signing history.
    • It asks for "install unknown apps" if needed, then installs through a PackageInstaller session. UpdateInstallReceiver handles the confirmation, cancel, and conflict results.
  • Switching channels: F-Droid and GitHub builds are signed with different keys, so nothing is installed across channels. A "Reinstall required" dialog walks through:

    1. Back up (opens Backup & Restore).
    2. Get the other build (F-Droid page, or the alpha APK download).
    3. Uninstall (ACTION_DELETE).

    The same dialog appears if a same-channel download turns out to have a different signer, e.g. a side-loaded build.

  • Daily check: an optional WorkManager check notifies once per new same-channel release. It defaults on for alpha builds and off for F-Droid builds, where the F-Droid client already notifies.

  • Backups: update preferences describe the install, not user taste, so they are excluded from backups.

Notes for review

  • New permissions: REQUEST_INSTALL_PACKAGES and REQUEST_DELETE_PACKAGES. F-Droid builds only download from f-droid.org/repo on the Stable channel and never install GitHub APKs. This is documented in docs/FDROID.md and PRIVACY.md, but the F-Droid reviewers may still ask.
  • alpha-release.yml's tag and asset naming is now a contract for alpha update discovery (noted in docs/RELEASE.md).
  • New strings are translated to Turkish only. The other locales add MissingTranslation warnings, like other recent strings.

Testing

  • ./gradlew :app:compileDebugKotlin :app:testDebugUnitTest passes.
  • New AppReleaseSourceTest covers:
    • F-Droid suggested version.
    • Newest alpha chosen by version rather than list order.
    • ABI preference order.
    • Drafts, stable tags, and releases without a matching APK skipped.
    • Alpha ordering across base versions, and PR builds.
    • Channel detection.
  • Device smoke test (API 36 emulator):
    • Full update: with the repo URL temporarily pointed at a local server (reverted before commit), 0.2.0 updated to 0.3.0. Download progress, the unknown-sources grant, the system confirmation, and the install all worked, and the app became installer of record. Cancelling the prompt returns to "available".
    • Real F-Droid APK: a debug-key-signed 0.2.0 release build downloaded the real F-Droid 0.3.0 APK, detected the signer mismatch, and showed the reinstall prompt.
    • Channel switch: switching to Alpha found 0.3.0-alpha.16. The switch dialog's Back up and Uninstall buttons open the right screens.
    • Leftover APKs in cache/updates are removed on the next start.
  • The background worker's notification was not exercised end to end; the notification intent's navigation was.

Review follow-up (b299a98)

  • Background downloads: they run under a dataSync foreground service (UpdateDownloadService) that mirrors progress. Without it, leaving the app mid-download got the socket aborted once the process was cached.
  • Cancel: it aborts the OkHttp call. A cancelled job no longer overwrites the status. Cancel is no longer offered after the session is committed.
  • Install prompt: it opens directly only in the foreground; otherwise a "Finish updating" notification carries the confirmation intent.
  • Debug builds: they get reinstall guidance instead of an in-place update.
  • Notifications: a release is marked notified only if the notification can actually be shown.
  • Verified on an API 36 emulator:
    • Background flow: release builds signed with the debug key, served from a throttled local repo (the URL change was reverted before commit). Started the download, went home: the foreground service kept it running, the notification appeared, tapping it opened the system prompt, and 0.2.0 updated to 0.3.0. Both notifications were cleared afterwards.
    • Cancel: cancelling at 32% stays on Idle.
    • Settings search: opens Updates; the highlightKey query matches the route, no crash.

Settings gains an Updates screen (also linked from Settings > About) that checks the chosen channel: Stable reads the F-Droid package API and downloads from f-droid.org/repo; Alpha reads GitHub prereleases tagged vX.Y.Z-alpha.N and picks the APK for the device ABI.

Same-channel updates download and install through PackageInstaller after checking the APK's package, version code, and signer. F-Droid and GitHub builds are signed with different keys, so a cross-channel release (or a signer mismatch) opens a guided switch instead: back up, get the other build, uninstall.

An optional daily WorkManager check notifies about new same-channel releases; it defaults on for alpha builds and off for F-Droid builds. Update preferences describe the install, so they are excluded from backups.
@greptile-apps

greptile-apps Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Adds in-app update checking and installation system.

The PR appears safe to merge; no outstanding previous finding or actionable new failure was identified.

Summary

The PR adds Stable and Alpha update discovery, same-channel installation, reinstall guidance for channel switches, and optional daily notifications. The follow-up serializes download retries with prior downloads and APK cleanup.

Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Check[Check selected channel] --> Available{New release?}
  Available -->|No| Current[Up to date]
  Available -->|Yes, reinstall required| Reinstall[Back up and reinstall]
  Available -->|Yes, same channel| Download[Download APK]
  Download --> Verify[Verify package, version and signer]
  Verify -->|Valid| Install[PackageInstaller confirmation]
  Verify -->|Different signer| Reinstall
Loading

Reviews (3) · Last reviewed commit: "Keep an earlier cancel's cleanup from de..."

Comment thread app/src/main/java/com/lostf1sh/pixelplayeross/data/update/AppUpdateManager.kt Outdated
Comment thread app/src/main/java/com/lostf1sh/pixelplayeross/data/update/UpdateCheckWorker.kt Outdated
- Keep downloads alive in the background with a dataSync foreground service; leaving the app mid-download let the system cache the process and abort its socket.
- Cancel aborts the OkHttp call directly and no longer lets the dying job overwrite the status; it is only offered before a session is committed, since the system prompt owns that decision.
- Open the install prompt directly only in the foreground; otherwise, including after process recreation, post a notification carrying the confirmation intent.
- Offer debug builds a reinstall instead of an in-place update: their applicationId suffix means the published APK installs beside them.
- Don't record a release as notified when the app's or the update channel's notifications are blocked.
Comment thread app/src/main/java/com/lostf1sh/pixelplayeross/data/update/AppUpdateManager.kt Outdated
cancel() launched the directory cleanup as the manager's job, so a check started right after it cancelled the job without stopping the blocking delete, and a retried download could lose its files to it. Cleanups now chain on a dedicated job that every download joins first, and a new download also waits for the previous download job to unwind.
@lostf1sh
lostf1sh merged commit 9b9ba98 into main Oct 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant