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
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,24 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Assemble + unit tests
if: github.event_name == 'pull_request'
run: cd android && bash ./gradlew :app:assembleDebug :app:testDebugUnitTest
- uses: actions/upload-artifact@v4
if: github.event_name == 'pull_request'
with:
name: app-debug.apk
path: android/app/build/outputs/apk/debug/app-debug.apk
if-no-files-found: error
- name: Assemble signed release + unit tests
# the release key lives on the runner host (AUDIOBRIDGE_KEYSTORE_PROPS in the runner's .env)
if: github.event_name != 'pull_request'
run: cd android && bash ./gradlew :app:assembleRelease :app:testDebugUnitTest && cp app/build/outputs/apk/release/app-release.apk app/build/outputs/apk/release/AudioBridge.apk
- uses: actions/upload-artifact@v4
if: github.event_name != 'pull_request'
with:
name: AudioBridge.apk
path: android/app/build/outputs/apk/release/AudioBridge.apk
if-no-files-found: error

desktop-tests:
# the desktop crate's tests need a real Windows host, so this one stays GitHub-hosted
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ cargo ndk -t arm64-v8a --platform 26 clippy -p audiobridge-android --all-targets
- Phone: `<filesDir>/audiobridge/client.key`, plus SharedPreferences `audiobridge` (`paired_pcs` JSON list of `{id, name, uri, muted}`, `mic_enabled` (also flipped remotely by a PC), `autostart_done`, `service_enabled` (the global switch, missing = on), headphone buttons: `headset_enabled`, `headset_single_earbud`, `headset_double_tap`, `headset_triple_tap`, `headset_target`).
- **Global switch (phone):** `Prefs.serviceEnabled` (written with `commit`). Off stops `BridgeService` (native `setPeers("[]")` → hub stopped, no notification, no media session); every start path (`MainActivity.startBridge`, `BootReceiver`, a sticky restart in `onStartCommand`) checks `Prefs.shouldRun()`. Pairings and settings stay. It never tells PCs to switch off: they just see the phone offline.
- **Default UDP port:** 47130, falling back to a random port.
- **Screenshots and releases:** the pairing QR contains the PC's secret. Never publish it; promo shots replace it with a QR of the repo URL. GitHub Releases ship `AudioBridge.exe` (the release build) and `AudioBridge.apk` (the debug-signed APK from this machine's `~/.android/debug.keystore`; a different key can't update an installed app).
- **Screenshots and releases:** the pairing QR contains the PC's secret. Never publish it; promo shots replace it with a QR of the repo URL. GitHub Releases ship `AudioBridge.exe` (the release build) and `AudioBridge.apk` (`:app:assembleRelease`, the `AudioBridge.apk` artifact of a push run on `main`). The APK is signed with the permanent release key on the netcup server: `~/.config/audiobridge/release.jks` with its passwords in `~/.config/audiobridge/keystore.properties` (both `chmod 600`, never commit them), passed to Gradle through `AUDIOBRIDGE_KEYSTORE_PROPS` (set in `~/.config/audiobridge-env.sh` and the runner's `~/runners/audiobridge/.env`); without it the release APK comes out unsigned. A different key can't update an installed app: moving from the old debug-signed APK (≤ 0.4.0) needs one uninstall + reinstall (re-pair the PCs), after that releases update in place. Losing the key means the same again for every user.

## Important Files
- `Cargo.toml`: workspace deps, and profiles you must keep:
Expand Down
19 changes: 19 additions & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import java.util.Properties
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
Expand All @@ -6,6 +7,12 @@ plugins {
id("org.jetbrains.kotlin.plugin.compose")
}

// Release signing key: AUDIOBRIDGE_KEYSTORE_PROPS names a properties file with storeFile, storePassword,
// keyAlias and keyPassword. Without it the release APK is built unsigned; debug builds are unaffected.
val releaseKeystoreProps: Properties? = providers.environmentVariable("AUDIOBRIDGE_KEYSTORE_PROPS").orNull
?.takeIf { it.isNotBlank() }
?.let { path -> Properties().apply { file(path).inputStream().use { load(it) } } }

android {
namespace = "app.audiobridge"
compileSdk = 35
Expand All @@ -21,11 +28,23 @@ android {
}
}

signingConfigs {
releaseKeystoreProps?.let { props ->
create("release") {
storeFile = file(props.getProperty("storeFile"))
storePassword = props.getProperty("storePassword")
keyAlias = props.getProperty("keyAlias")
keyPassword = props.getProperty("keyPassword")
}
}
}

buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
signingConfig = signingConfigs.findByName("release")
}
}

Expand Down
Loading