- Check free space before downloading (only the not-yet-fetched bytes) and, on
Android O+, use StorageManager.getAllocatableBytes/allocateBytes so the system
can evict its cache; fail fast with a clear message instead of dying mid-write.
SessionInstaller sets a SessionParams size hint so staging can reserve space
too.
- Verify each APK at most once: files already verified during the download pass
are skipped in the final verification gate. Prefer SHA-256 and log SHA-1
fallback.
- An expired download URL (403/410) now clears the stored file lists and retries,
re-purchasing fresh URLs instead of repeatedly failing on the dead one.
- Cancel promptly mid-file rather than only between files, and cancel copyTo's
progress timer in a finally to avoid leaking the timer thread.
- Download.canInstall now requires a real .apk on disk; DownloadStatus
finished/running are Sets.
A download row previously stopped at COMPLETED and never reflected whether the
app actually installed. Add INSTALLING/INSTALLED states driven by a central
installer-event observer in DownloadHelper:
- COMPLETED -> INSTALLING on the installer's first progress event
- -> INSTALLED on success (row kept so the APK can still be exported)
- a failed install reverts INSTALLING -> COMPLETED so it can be re-installed
without re-downloading
Consumers that branched on COMPLETED are updated (App Details state, MicroG
status mapping, Downloads list icon). downloadStatus is stored as TEXT so no
schema migration is needed.
Cancelling a download cancelled the worker and marked the row CANCELLED but
never abandoned a PackageInstaller session that had already been staged for
install, leaking it. Add IInstaller.cancelInstall (no-op default, implemented by
SessionInstaller) and call it from DownloadHelper.cancelDownload.
Cross-process session persistence/reconciliation (committing a session staged
before a restart) is left as a follow-up; the startup session cleanup remains,
and is now cheap to recover from since CacheWorker keeps the downloaded files.
- CacheWorker no longer purges the files of a download that is still in-flight
or downloaded and awaiting install, so missing the system install prompt for
longer than the cache window no longer forces a re-download.
- observeDownloads now starts the next queued download only once nothing else
is purchasing/downloading/verifying, instead of merely checking for a
DOWNLOADING row. This serializes downloads so concurrent workers can't clobber
the shared foreground/progress notification.
- enqueue() no longer resets an active/verifying download back to QUEUED, so
the periodic update check and repeated taps can't trigger needless
re-downloads; when files are already downloaded & verified it installs
directly instead of re-running the pipeline.
- DownloadWorker only purges partial files on a genuine user cancellation;
system-initiated stops (lost connectivity, quota) keep partials and retry,
fixing downloads appearing to restart on a flaky network.
- Add NetworkType.CONNECTED constraint + exponential backoff and return
Result.retry() for transient/network failures (capped) instead of always
succeeding.
- Validate HTTP 206 before resuming a .tmp (overwrite on 200) and drop corrupt
files on verification failure so retries restart clean.
Fold Aurora Store's self-update back into the existing update pipeline
instead of a dedicated sheet/viewmodel/helper. The feed entry is mapped
to a regular App and added to the update list, reusing the standard
download + install path; it is never auto-installed silently.
- Add a dedicated "Self-update" section in the Updates tab with the
app-details navigation disabled (it's served from the Aurora OSS feed).
- Gate eligibility via PackageUtil.isSelfUpdateSupported(): vanilla/preload
flavors, not debug, not F-Droid (huawei excluded by flavor).
- Add a build-aware Settings toggle as the opt-out, removing the row
immediately when disabled.
- Exempt nightly self-updates from deleteInvalidUpdates (static version
code) and drop any stale self-update row when none is offered, so a
phantom update doesn't linger after a self-install.
Stream/ExpandedStream/Review/Search paging VMs were swallowing exceptions
inside manualPager and returning emptyList, so the pager never entered
LoadState.Error and the error-placeholder retry buttons were dead. Let
non-Auth exceptions propagate; keep the AuthException → SessionExpired
branch so 401 still hands off to Splash.
Also drop supervisorScope wrappers from CategoryStream/DevProfile/TopChart
VMs that launch no children — the surrounding try/catch already handles
everything.
Long-backgrounded sessions (especially on aggressive vendors like Huawei)
could leave the Play token invalid and OkHttp's connection pool full of
dead sockets, producing a perpetual loading spinner with no error.
- ComposeActivity#onStart probes the saved token via isSavedAuthDataValid;
on failure evicts the OkHttp pool and refreshes anonymous auth.
- AuthProvider gains authReady StateFlow, awaitReady(), and a mutex-guarded
refreshAnonymousAuth() so concurrent callers don't double-fetch.
- Play-backed ViewModels (AppDetails, DevProfile, MoreApps, Reviews, Search,
ExpandedStreamBrowse, Category) await refresh completion before issuing
helper calls, closing the race between resume-time refresh and screen
LaunchedEffect fetches.
- AppDetailsScreen rendered Error as Loading when app was null; reorder so
Error wins and wire a Retry button through Placeholder.
- Stream/Review/Search/DevProfile/CategoryBrowse error placeholders gain
Retry actions wired to PagingItems.retry() or the VM's fetch.
- CategoryViewModel was logging-only on failure; now emits ViewState.Error
with a matching Retry path in CategoriesPage.
Share a sort/filter sheet between InstalledScreen and BlacklistScreen via
new commons module (SortFilterState + SortFilterSheet + applyFilter/applySort
on InstalledAppMeta), persisted per-screen. InstalledScreen renders a new
InstalledAppListItem that surfaces size, last-update, and installer instead
of Play-listing extras. Blacklisted rows now desaturate their icons.
- AppUpdateItem now shows a disabled "Installing" button while the
download is COMPLETED and awaiting install, instead of briefly
reverting to "Update" between download-done and InstallerEvent.Installed.
- Section header replaces all-or-nothing "Update all" toggle with an
any-active check, so "Cancel all" appears as soon as a single update
is in flight. Same treatment for the ownership-approval section.
- Empty placeholder no longer hides the ignored-updates list when there
are no live updates.
- DownloadHelper.cancelAll also flips COMPLETED -> CANCELLED so the
Cancel-all button gives immediate feedback for pending-install rows.
- DownloadListItem: surface the failed icon for FAILED downloads too.