wear: M1 - compile shared bitchat stack (protocol/noise/mesh) into :wear via synced sourcesets, 172 shared tests green

This commit is contained in:
callebtc 2026-07-28 16:19:56 +02:00
parent a355ed79d0
commit 711c73bbe7
4 changed files with 136 additions and 5 deletions

View File

@ -7,7 +7,7 @@
| Milestone | Title | Status |
|-----------|-------|--------|
| M0 | Scaffolding & plan document | done |
| M1 | Shared core compiles on Wear | pending |
| M1 | Shared core compiles on Wear | done |
| M2 | BLE transport & background service on watch | pending |
| M3 | Global chat | pending |
| M4 | Noise DMs & people screen | pending |
@ -139,14 +139,25 @@ round display. No `app/src/` changes.
### M1 — Shared core compiles on Wear
- [ ] Configure srcDir include list in `wear/build.gradle.kts`; resolve transitive dependencies by
- [x] Configure shared-source wiring in `wear/build.gradle.kts`; resolve transitive dependencies by
extending includes (never by copying Kotlin sources)
- [ ] Copy Geist Mono fonts; create wear theme/palette/peer-color files mirroring `ui/theme/`
- [ ] Wire shared unit tests (`protocol`, `noise`, `crypto`, `mesh`) into `:wear` test source set
- [ ] `./gradlew :app:test :wear:test` green
- [x] Copy Geist Mono fonts; create wear theme/palette/peer-color files mirroring `ui/theme/`
- [x] Wire shared unit tests (`protocol`, `noise`, `crypto`, `mesh`) into `:wear` test source set
- [x] `./gradlew :app:test :wear:test` green
**Implementation notes** (deviation from original plan): AGP 9 source directory sets no longer
support include/exclude filters, so a Gradle `Sync` task (`syncSharedAppSources`) materializes a
filtered mirror of `app/src/main/java` into `wear/build/sharedSrc` which is added as a source
root. App sources remain the single source of truth; nothing is hand-copied. Excluded:
`BluetoothMeshService`/`UnifiedMeshService` (phone monolith / Wi-Fi Aware multiplexer — the watch
composes its own service in M2). Two tiny wear-owned shims satisfy the only unresolvable
references from shared code: `com.bitchat.android.service.MeshServiceHolder` (BLE-toggle
interface, null) and `com.bitchat.android.wifiaware.WifiAwareController` (no-op).
**Success criteria**: the entire shared stack (protocol, noise, crypto, identity, mesh, model,
AppStateStore) compiles into `:wear`; both modules' unit tests pass; `app/src/` untouched.
**Result**: PASSED — `:wear` compiles the full shared stack; 172 shared unit tests pass on
`:wear` (0 failures), `:app` suite green; `app/src/` unchanged.
---

View File

@ -52,6 +52,98 @@ android {
}
}
// Shared bitchat protocol stack: compiled from :app sources in place (never moved/copied by hand).
// AGP's source directory sets no longer support include/exclude filters, so a Sync task
// materializes a filtered mirror into build/sharedSrc and that directory is added as a source
// root. The app sources remain the single source of truth; extend the include list below (don't
// copy files into wear/src) when the compiler reveals a missing transitive dependency.
// Deliberately excluded: ui (except the DebugSettingsManager the mesh layer references),
// onboarding, nostr (except pure-Kotlin Bech32), net, geohash, wifi-aware, hotspot, voice
// features, and the phone's foreground service.
val sharedSourceIncludes = listOf(
"com/bitchat/android/protocol/**",
"com/bitchat/android/noise/**",
"com/bitchat/android/crypto/**",
"com/bitchat/android/identity/**",
"com/bitchat/android/mesh/**",
"com/bitchat/android/model/**",
"com/bitchat/android/sync/**",
"com/bitchat/android/favorites/**",
"com/bitchat/android/services/AppStateStore.kt",
"com/bitchat/android/services/ContactDirectory.kt",
"com/bitchat/android/services/ContactIdentityResolver.kt",
"com/bitchat/android/services/PrivateMessageArrivalOrder.kt",
"com/bitchat/android/services/SeenMessageStore.kt",
"com/bitchat/android/services/VerificationService.kt",
"com/bitchat/android/services/meshgraph/**",
"com/bitchat/android/service/TransportBridgeService.kt",
"com/bitchat/android/nostr/Bech32.kt",
"com/bitchat/android/nostr/GeohashAliasRegistry.kt",
"com/bitchat/android/features/file/FileUtils.kt",
"com/bitchat/android/ui/debug/DebugSettingsManager.kt",
"com/bitchat/android/ui/debug/DebugPreferenceManager.kt",
"com/bitchat/android/util/AppConstants.kt",
"com/bitchat/android/util/ByteArrayExtensions.kt",
"com/bitchat/android/util/ByteArrayWrapper.kt",
"com/bitchat/android/util/BinaryEncodingUtils.kt",
)
val sharedSourceExcludes = listOf(
"com/bitchat/android/model/FileSharingManager.kt",
// Legacy phone monolith and Wi-Fi Aware multiplexer; the watch composes its own service
// (MeshCore-style) in M2 instead of reusing these.
"com/bitchat/android/mesh/BluetoothMeshService.kt",
"com/bitchat/android/mesh/UnifiedMeshService.kt",
)
val syncSharedAppSources = tasks.register<Sync>("syncSharedAppSources") {
from("../app/src/main/java") {
include(sharedSourceIncludes)
exclude(sharedSourceExcludes)
}
into(layout.buildDirectory.dir("sharedSrc"))
}
// The app's own unit tests for the shared packages also run in the wear module, so shared
// behavior is continuously verified on both targets. Includes the app's JVM shims for
// android.util.Log/Base64 (app/src/test/kotlin/android) that the shared code needs on the JVM.
val syncSharedAppTests = tasks.register<Sync>("syncSharedAppTests") {
from("../app/src/test/java") {
include(
"com/bitchat/android/protocol/**",
"com/bitchat/android/crypto/**",
"com/bitchat/android/mesh/**",
)
}
from("../app/src/test/kotlin") {
include(
"android/**",
"com/bitchat/android/mesh/**",
"com/bitchat/FileTransferTest.kt",
)
}
into(layout.buildDirectory.dir("sharedTestSrc"))
}
android {
sourceSets {
getByName("main") {
java.srcDir("build/sharedSrc")
kotlin.srcDir("build/sharedSrc")
}
getByName("test") {
java.srcDir("build/sharedTestSrc")
kotlin.srcDir("build/sharedTestSrc")
}
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
dependsOn(syncSharedAppSources)
}
tasks.matching { it.name.contains("UnitTest", ignoreCase = true) }.configureEach {
dependsOn(syncSharedAppTests)
}
kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)

View File

@ -0,0 +1,17 @@
package com.bitchat.android.service
/**
* Wear shim for the phone's MeshServiceHolder.
*
* The shared `DebugSettingsManager` (compiled from app sources) references this holder only to
* toggle BLE transport from the phone's debug UI, which does not exist on the watch. The real
* holder is typed against `BluetoothMeshService`, which the watch deliberately does not include.
*/
object MeshServiceHolder {
interface BleToggle {
fun setBleTransportEnabled(enabled: Boolean)
}
val meshService: BleToggle? = null
}

View File

@ -0,0 +1,11 @@
package com.bitchat.android.wifiaware
/**
* Wear shim for the phone's WifiAwareController.
*
* Referenced only by the shared `DebugSettingsManager` debug-UI toggle. Wi-Fi Aware is out of
* scope for the watch (Bluetooth mesh only), so this is a no-op.
*/
object WifiAwareController {
fun setEnabled(value: Boolean) = Unit
}