diff --git a/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java b/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java index a449bcf8..0ff81506 100644 --- a/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java +++ b/app/src/main/java/eu/faircode/netguard/ServiceSinkhole.java @@ -803,22 +803,24 @@ private void checkUpdate() { JSONObject jroot = jarray.getJSONObject(0); // JSONObject jroot = new JSONObject(json.toString()); if (jroot.has("tag_name") && jroot.has("html_url") && jroot.has("assets")) { + // Always link to the release page rather than a specific + // asset: the user picks the right download there, and + // the notification doesn't depend on asset ordering. String url = jroot.getString("html_url"); JSONArray jassets = jroot.getJSONArray("assets"); if (jassets.length() > 0) { - JSONObject jasset = jassets.getJSONObject(0); - if (jasset.has("name")) { - long available = jroot.getLong("tag_name"); - String name = jasset.getString("name"); - Log.i(TAG, "Tag " + available + " name " + name + " url " + url); - - long current = Util.getSelfVersionCode(ServiceSinkhole.this); - if (current < available) { - Log.i(TAG, "Update available from " + current + " to " + available); - showUpdateNotification(name, url); - } else - Log.i(TAG, "Up-to-date current version " + current); - } + long available = jroot.getLong("tag_name"); + String name = (jroot.has("name") && !jroot.isNull("name") && jroot.getString("name").length() > 0) + ? jroot.getString("name") + : getString(R.string.title_version, available); + Log.i(TAG, "Tag " + available + " name " + name + " url " + url); + + long current = Util.getSelfVersionCode(ServiceSinkhole.this); + if (current < available) { + Log.i(TAG, "Update available from " + current + " to " + available); + showUpdateNotification(name, url); + } else + Log.i(TAG, "Up-to-date current version " + current); } } } catch (JSONException ex) { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b3825919..4f8f7323 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -346,6 +346,7 @@ Your internet traffic is not being sent to a remote VPN server. No active internet connection TrackerControl is busy Update available, tap to download + Version %1$d You can allow (greenish) or deny (reddish) Wi-Fi or mobile internet access by tapping on the icons next to an app Incoming (push) messages are mostly handled by the system component Play services, which is allowed internet access by default Managing all (system) apps can be enabled in the settings diff --git a/docs/RELEASING.md b/docs/RELEASING.md index ab5870f2..a5f889b7 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -65,8 +65,14 @@ The script writes the signed APK and verification metadata to `output/release-2026071301/` and creates a draft GitHub release containing: - `TrackerControl-githubRelease-latest.apk` -- `SHA256SUMS.txt` -- `BUILD-INFO.txt` +- `TrackerControl-githubRelease-latest.apk.sha256sums` +- `TrackerControl-githubRelease-latest.apk.build-info.txt` + +The two metadata files are named with the APK filename as a prefix so they +always sort alphabetically after it. The GitHub API returns release assets +ordered by name, and the app's in-app update checker always downloads +`assets[0]`; without this ordering it can fetch `BUILD-INFO.txt` instead of +the APK (#681). Install and test the APK from the draft, then publish the release in GitHub. diff --git a/scripts/build_and_sign.sh b/scripts/build_and_sign.sh index eee5fb0f..38e12f7a 100755 --- a/scripts/build_and_sign.sh +++ b/scripts/build_and_sign.sh @@ -129,9 +129,14 @@ readonly OUTPUT_DIR=${RELEASE_OUTPUT_DIR:-"$ROOT/output/release-$TAG"} mkdir -p "$OUTPUT_DIR" readonly ALIGNED_APK="$TEMP_DIR/TrackerControl-githubRelease-aligned.apk" readonly TEMP_SIGNED_APK="$TEMP_DIR/TrackerControl-githubRelease-signed.apk" +# Asset names must sort alphabetically after the APK: the GitHub API +# returns release assets ordered by name, and the app's update checker +# (ServiceSinkhole#checkUpdate) always takes assets[0] as the download. +# Using the APK filename as a literal prefix guarantees it sorts first +# under any lexicographic comparison, regardless of case-folding rules. readonly SIGNED_APK="$OUTPUT_DIR/TrackerControl-githubRelease-latest.apk" -readonly SIGNED_SUMS="$OUTPUT_DIR/SHA256SUMS.txt" -readonly OUTPUT_BUILD_INFO="$OUTPUT_DIR/BUILD-INFO.txt" +readonly SIGNED_SUMS="$OUTPUT_DIR/TrackerControl-githubRelease-latest.apk.sha256sums" +readonly OUTPUT_BUILD_INFO="$OUTPUT_DIR/TrackerControl-githubRelease-latest.apk.build-info.txt" [[ ! -e $SIGNED_APK ]] || fail "signed output already exists: $SIGNED_APK" printf 'Aligning and signing the APK...\n'