Skip to content

v1.4.1: CDN trending + download/update fixes - #2

Open
Darkmintis wants to merge 16 commits into
mainfrom
fix/stability-and-release
Open

Darkmintis wants to merge 16 commits into
mainfrom
fix/stability-and-release

Conversation

@Darkmintis

@Darkmintis Darkmintis commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Description

Ships the v1.4.1 trending rework (CDN + offline cache) and four stability fixes found during review. No DB migration, no new permissions, no dependency changes.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Performance improvement

Related Issue

N/A

Changes Made

  • Trending – load the daily list from jsDelivr with a cacheDir fallback, and show a notice when browsing cached data (CachedTrendingDataSource, HomeRepositoryImpl, HomeState, HomeRoot, 7 locales)
  • Downloads – close the DownloadManager cursor on every path and null-guard COLUMN_LOCAL_URI (AndroidDownloader)
  • Update all – record per-app outcomes so successes are counted correctly (AppsViewModel)
  • Cleanup – delete partial APKs when leaving a screen instead of launching on the cancelled viewModelScope (DetailsViewModel, AppsViewModel)
  • Release – derive versionName/versionCode from the git tag in CI (app/build.gradle.kts, release.yml)
  • Docs – v1.4.1 release notes

Testing

  • Tested on Android device/emulator
  • All existing tests pass
  • Added new tests (if applicable)

Test Configuration:

  • Android device-tested

./gradlew testDebugUnitTest # BUILD SUCCESSFUL
./gradlew assembleDebug # BUILD SUCCESSFUL

Checklist

  • My code follows the code style of this project
  • I have performed a self-review of my code
  • I have commented my code where necessary
  • My changes generate no new warnings
  • I have updated the documentation accordingly
  • No lint errors (lint was not run locally)

Additional Notes

  • Deferred to a follow-up: WorkManager update notifications, translation hoisting, persistent release-check cache, encrypted token storage, crash reporting, Baseline Profile.

Adds PaginatedRepos.isFromCache so callers can tell network results apart from the stored trending snapshot without inferring it from other state.
Trending now reads the daily discovered list from jsDelivr (edge-cached, branch refs revalidated within minutes) instead of raw.githubusercontent.com, and keeps the last successful payload in cacheDir so the tab still renders offline.

CachedTrendingDataSource.loadTrending() returns a typed TrendingResult that distinguishes offline from missing data, so HomeRepositoryImpl can report the right reason and avoid falling through to the live API unnecessarily.
HomeState carries isShowingCachedData, populated from PaginatedRepos.isFromCache, and the Trending tab renders a compact notice so it is clear the visible list is stored data and installs still need a connection.
The status poll threw before cursor.close() for a missing download id, a failed download, and a completed download with a bad local URI, leaking a cursor each time. Wrap the query in try/finally and null-guard COLUMN_LOCAL_URI, which is nullable and would otherwise reach Uri.parse(null).
onCleared() launched cleanup on viewModelScope, which is already cancelled at that point, and cleanupUpdate() suspends on a cancelled job, so neither the details screen nor the apps screen actually removed an in-flight download when the user left.

Run that work on a small scope that outlives the ViewModel, and wrap the cancellation-path cleanup in NonCancellable so cancelUpdate() can finish its delete before rethrowing.
updateSingleApp() reset its state to Idle after a 2s success delay, and
updateAllApps() read that state after join(), so successes were never counted
and the summary reported "0 updated" even when every app succeeded.

Record the outcome before the delay, skip the delay inside a batch, and reset
success states once the batch finishes so a later update is not filtered out.
Also drops the unused previousStates allocation.
versionName and versionCode were hand-edited in app/build.gradle.kts while the
app compares BuildConfig.VERSION_NAME against the latest release tag. Cutting a
release without bumping the file made the in-app updater offer the version the
user was already running.

The release workflow now parses the pushed tag (v1.4.1 -> name 1.4.1, code
10401) and passes it to Gradle; local builds fall back to the values in
build.gradle.kts.
Covers CDN-served trending with offline fallback, the cached-results notice,
and the fixes landed on this branch.
Keeps scratch material (parked contributor tooling, session notes) out of the
repository without deleting it.
The disk copy was trusted indefinitely, so a week-old snapshot could be shown
as the live Trending list. Treat anything older than 48 hours, or with a
missing/malformed timestamp, as unusable, so Home falls back to its normal
offline state instead of presenting stale content as current.

Covered by TrendingSnapshotFreshnessTest.
PaginatedRepos now carries the snapshot timestamp through HomeState, and the
cached-results banner renders when the list was last updated plus a Retry
action. That makes the offline state actionable instead of leaving an undated
saved list looking like live data.
State the 48-hour window, mention the snapshot age and retry in the saved-results
notice, and replace the French-locale-specific line with the actual scope: all
seven UI locales carry the new string.
Reading configuration through LocalContext can miss configuration changes, and
lint flags it as an error. Use LocalConfiguration as the base for both the
localized context and the configuration provided to the tree.
DetailsAction.OnMessage was never emitted; details messages already flow
through DetailsEvent.OnMessage as resolved strings. Removing it also removes
the only LocalContext.getString call in DetailsRoot, which lint flagged.
Hoist the string out of the snackbar coroutine so it is resolved through
stringResource and follows configuration changes. Also removes the
LocalContext resource read lint flagged.
The permission is needed to detect whether an arbitrary tracked repo is
already installed, and the app is distributed outside Play where package
visibility cannot be declared in advance. Suppress the lint error with that
rationale instead of leaving lint failing.
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