Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 54 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ jobs:
run: |
set -euo pipefail
raw="$(sed -nE 's/^version:[[:space:]]*//p' pubspec.yaml | head -n1 | tr -d '\r')"
if [[ ! "$raw" =~ ^([0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?)\+([0-9]+)$ ]]; then
if [[ ! "$raw" =~ ^([0-9]+\.[0-9]+\.[0-9]+-xarleyn\.([1-9][0-9]*))\+([1-9][0-9]*)$ ]]; then
echo "Unexpected pubspec version format: $raw" >&2
exit 1
fi
version_name="${BASH_REMATCH[1]}"
version_code="${BASH_REMATCH[3]}"
tag="v${version_name}"
asset_name="meshcore-wardrive-${version_name}-${version_code}.apk"
asset_name="meshcore-wardrive-xarleyn-${version_name}-${version_code}.apk"
{
echo "raw=${raw}"
echo "version_name=${version_name}"
Expand All @@ -61,6 +61,27 @@ jobs:
} >> "$GITHUB_OUTPUT"
echo "Publishing ${raw} as ${tag} (${asset_name})"

- name: Ensure versionCode increases
env:
GH_TOKEN: ${{ github.token }}
VERSION_CODE: ${{ steps.version.outputs.version_code }}
run: |
set -euo pipefail
max_published_code=0
while IFS= read -r asset_name; do
if [[ "$asset_name" =~ -([0-9]+)\.apk$ ]] &&
(( BASH_REMATCH[1] > max_published_code )); then
max_published_code="${BASH_REMATCH[1]}"
fi
done < <(
gh api --paginate "repos/${GITHUB_REPOSITORY}/releases?per_page=100" \
--jq '.[].assets[].name'
)
if (( VERSION_CODE <= max_published_code )); then
echo "versionCode ${VERSION_CODE} must exceed published ${max_published_code}." >&2
exit 1
fi

- name: Ensure release tag is free
env:
GH_TOKEN: ${{ github.token }}
Expand Down Expand Up @@ -119,6 +140,37 @@ jobs:
- name: Build release APK
run: flutter build apk --release

- name: Verify release APK identity and signature
env:
EXPECTED_CERT_SHA256: ${{ secrets.ANDROID_CERT_SHA256 }}
VERSION_NAME: ${{ steps.version.outputs.version_name }}
VERSION_CODE: ${{ steps.version.outputs.version_code }}
run: |
set -euo pipefail
if [[ -z "$EXPECTED_CERT_SHA256" ]]; then
echo "Missing secret ANDROID_CERT_SHA256" >&2
exit 1
fi
apk="build/app/outputs/flutter-apk/app-release.apk"
build_tools="$(find "$ANDROID_HOME/build-tools" -mindepth 1 -maxdepth 1 -type d | sort -V | tail -n1)"
aapt="$build_tools/aapt"
apksigner="$build_tools/apksigner"
test -x "$aapt"
test -x "$apksigner"

badging="$($aapt dump badging "$apk" | head -n1)"
[[ "$badging" == *"name='io.github.xarleyn.meshcore.wardrive'"* ]]
[[ "$badging" == *"versionCode='${VERSION_CODE}'"* ]]
[[ "$badging" == *"versionName='${VERSION_NAME}'"* ]]

"$apksigner" verify --verbose "$apk"
actual_cert="$($apksigner verify --print-certs "$apk" | sed -nE 's/^Signer #1 certificate SHA-256 digest: (.*)$/\1/p' | head -n1 | tr -d ':[:space:]' | tr '[:upper:]' '[:lower:]')"
expected_cert="$(printf '%s' "$EXPECTED_CERT_SHA256" | tr -d ':[:space:]' | tr '[:upper:]' '[:lower:]')"
if [[ -z "$actual_cert" || "$actual_cert" != "$expected_cert" ]]; then
echo "APK signer SHA-256 does not match ANDROID_CERT_SHA256." >&2
exit 1
fi

- name: Prepare release asset
env:
ASSET_NAME: ${{ steps.version.outputs.asset_name }}
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## Unreleased

### Breaking change

- Changed the Android package ID from `mintylinux.meshcore.wardrive` to
`io.github.xarleyn.meshcore.wardrive`. Android installs this fork separately
from the original application and from older fork builds. No data migration
is provided: the new package starts with empty local data and default
settings, and requires permissions, background/MIUI settings, device access,
widgets, offline maps, and credentials to be configured again.

### Added

- Independent Map Display toggles for privacy zones and GPS exclusion zones.
Expand Down Expand Up @@ -36,6 +45,13 @@
both their descriptions and their unlock logic, without converting between
units: the thresholds stay 10/100/500, so 100 miles and 100 km unlock the
same badge depending on the setting.
- Debug builds now use `io.github.xarleyn.meshcore.wardrive.debug`, allowing a
debug and signed release build of the fork to coexist.
- Fork releases use `base-xarleyn.N` version names. The update checker compares
both the base version and fork revision, ignores downgrades, and selects the
newest compatible tag from multiple GitHub releases.
- The release workflow verifies package ID, version name, increasing version
code, APK signature, release tag, and asset name before publication.

### Fixed

Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ companion radio over USB or Bluetooth, and displays coverage on a Flutter map.

## 📥 Download Pre-built APK

**Latest Release:** [Download from Releases Repository](https://github.com/mintylinux/Meshcore-Wardrive-Android)
**Latest Release:** [Download the xarleyn fork](https://github.com/xarleyn/Meshcore-Wardrive-Android-Source/releases)

The fork uses Android package ID `io.github.xarleyn.meshcore.wardrive`, so it
can coexist with the original app. The first fork-identity release is a clean
installation and does not migrate data from `mintylinux.meshcore.wardrive`.

## 🚀 Features

Expand Down Expand Up @@ -47,8 +51,8 @@ companion radio over USB or Bluetooth, and displays coverage on a Flutter map.

1. Clone this repository:
```bash
git clone https://github.com/mintylinux/Meshcore-Wardrive-Android-Source.git
cd meshcore_wardrive
git clone https://github.com/xarleyn/Meshcore-Wardrive-Android-Source.git
cd Meshcore-Wardrive-Android-Source
```

2. Install dependencies:
Expand All @@ -69,7 +73,8 @@ flutter run
### Building Release APK

Configure the stable signing key once, then use the release script. It
increments Android's build number and keeps the displayed version synchronized:
increments Android's build number and the `-xarleyn.N` fork revision while
keeping the displayed version synchronized:

```powershell
.\tool\build_release.ps1
Expand Down
7 changes: 5 additions & 2 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ if (releaseTaskRequested) {
}

android {
namespace = "mintylinux.meshcore.wardrive"
namespace = "io.github.xarleyn.meshcore.wardrive"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion

Expand All @@ -57,7 +57,7 @@ android {
}

defaultConfig {
applicationId = "mintylinux.meshcore.wardrive"
applicationId = "io.github.xarleyn.meshcore.wardrive"
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
Expand All @@ -74,6 +74,9 @@ android {
}

buildTypes {
debug {
applicationIdSuffix = ".debug"
}
release {
signingConfig = signingConfigs.getByName("release")
}
Expand Down
4 changes: 4 additions & 0 deletions android/app/src/debug/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MeshCore Wardrive (xarleyn Debug)</string>
</resources>
2 changes: 1 addition & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<uses-feature android:name="android.hardware.usb.host" />

<application
android:label="meshcore_wardrive"
android:label="@string/app_name"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package mintylinux.meshcore.wardrive
package io.github.xarleyn.meshcore.wardrive

import android.Manifest
import android.content.Context
Expand All @@ -24,10 +24,10 @@ import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
private val TAG = "MeshcoreFeedback"
private val CHANNEL = "mintylinux.meshcore.wardrive/feedback"
private val WIFI_CHANNEL = "mintylinux.meshcore.wardrive/wifi_location"
private val CHANNEL = "io.github.xarleyn.meshcore.wardrive/feedback"
private val WIFI_CHANNEL = "io.github.xarleyn.meshcore.wardrive/wifi_location"
private val TRACKING_SETTINGS_CHANNEL =
"mintylinux.meshcore.wardrive/tracking_settings"
"io.github.xarleyn.meshcore.wardrive/tracking_settings"
private var toneGenerator: ToneGenerator? = null

private fun getVibrator(): Vibrator {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package mintylinux.meshcore.wardrive
package io.github.xarleyn.meshcore.wardrive

import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
Expand Down
2 changes: 1 addition & 1 deletion android/app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MeshCore Wardrive</string>
<string name="app_name">MeshCore Wardrive (xarleyn)</string>
<string name="widget_status_idle">Ожидание</string>
<string name="widget_label_samples">Замеры</string>
<string name="widget_label_lora">LoRa</string>
Expand Down
2 changes: 1 addition & 1 deletion android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MeshCore Wardrive</string>
<string name="app_name">MeshCore Wardrive (xarleyn)</string>
<string name="widget_status_idle">Idle</string>
<string name="widget_label_samples">Samples</string>
<string name="widget_label_lora">LoRa</string>
Expand Down
24 changes: 23 additions & 1 deletion docs/INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@
3. Wait for installation to complete
4. Tap **Open** or find the app in your app drawer

### Important: fresh fork identity

Starting with `v1.0.44-xarleyn.1`, this fork uses the Android package ID
`io.github.xarleyn.meshcore.wardrive`. The original application and older fork
builds used `mintylinux.meshcore.wardrive`, so Android treats this release as a
different application. Both applications can be installed at the same time.

There is no automatic data migration. The new fork starts with an empty
database and default settings; data in the old package is not available to it.
Treat old measurements and settings as lost for this transition. Uninstalling
the old package permanently deletes its private data.

### Step 4: Grant Permissions
On first launch, grant these permissions:
- **Location** (Always) - Required for GPS tracking
Expand Down Expand Up @@ -61,6 +73,8 @@ On first launch, grant these permissions:
- Make sure you have enough storage space (need ~100MB free)
- For an update, make sure the APK has the same application ID and signing
certificate as the installed app, and a higher build number
- `v1.0.44-xarleyn.1` is a fresh install and does not replace a build using
`mintylinux.meshcore.wardrive`
- Restart your device and try again

### Permissions denied
Expand All @@ -74,7 +88,9 @@ On first launch, grant these permissions:

## Updating

To update to a new version:
For updates between releases that already use
`io.github.xarleyn.meshcore.wardrive`:

1. Download the new APK
2. Install over the existing app (data will be preserved)

Expand All @@ -83,6 +99,12 @@ Uninstalling deletes the app's settings and collected wardrive data. If Android
rejects the update because its signing certificate differs, export or back up
important data before uninstalling the old app.

The one-time change from `mintylinux.meshcore.wardrive` to
`io.github.xarleyn.meshcore.wardrive` is not an update. After installing the
new fork, grant permissions again and reconfigure background location, battery
optimization/MIUI autostart, Bluetooth or USB access, widgets, offline maps,
and any stored credentials. No migration from the old package is provided.

## Uninstalling

1. Go to Settings → Apps → MeshCore Wardrive
Expand Down
Loading