feat(android): native Android client — full app parity build (A1–A36 + S1)
Some checks failed
relay-tripwire / cross-tenant-tripwire (push) Has been cancelled
ios / package-tests (APIClient) (push) Has been cancelled
ios / package-tests (HostRegistry) (push) Has been cancelled
ios / package-tests (SessionCore) (push) Has been cancelled
ios / package-tests (WireProtocol) (push) Has been cancelled
ios / testsupport-tests (push) Has been cancelled
ios / app-tests (push) Has been cancelled
ios / ipad-tests (push) Has been cancelled
ios / integration-tests (push) Has been cancelled
ios / ui-test (push) Has been cancelled
ios / ios17-floor-tests (push) Has been cancelled
Some checks failed
relay-tripwire / cross-tenant-tripwire (push) Has been cancelled
ios / package-tests (APIClient) (push) Has been cancelled
ios / package-tests (HostRegistry) (push) Has been cancelled
ios / package-tests (SessionCore) (push) Has been cancelled
ios / package-tests (WireProtocol) (push) Has been cancelled
ios / testsupport-tests (push) Has been cancelled
ios / app-tests (push) Has been cancelled
ios / ipad-tests (push) Has been cancelled
ios / integration-tests (push) Has been cancelled
ios / ui-test (push) Has been cancelled
ios / ios17-floor-tests (push) Has been cancelled
Mirror the iOS P0+P1 client as a Gradle multi-module app. Pure-JVM (Kover ≥80% gated): :wire-protocol (frozen contract + HostEndpoint CSWSH origin + byte-exact codec), :session-core (SessionEngine + reconnect/ping/gate/digest reducers), :api-client, :client-tls (pure PKCS12/keymanager), :transport-okhttp (OkHttp WS+REST). Framework: :app (Compose M3 Adaptive, Hilt, FCM, 11 screens + NavGraph), :terminal-view (Termux terminal-emulator/-view via JitPack, Apache-2.0 — the renderer seam proven headless), :host-registry (DataStore), :client-tls-android (AndroidKeyStore + Tink). Highlights: single-key-home mTLS (non-exportable AndroidKeyStore key + re-reading X509KeyManager + connectionPool.evictAll on rotation, ping-pong single-commit so a failed rotation never clobbers the prior identity); FCM Allow/Deny trust split (Deny=BroadcastReceiver, Allow=trampoline Activity hosting BiometricPrompt); per-consumer-Channel EventBus (R10); config-surviving RetainedSessionHolder; byte-exact KeyByteMap; §5.4 pairing warning tiers. Verified: ~484 JVM tests + Kover ≥80% on the pure modules + :app assembles to an APK; all framework modules assemble. Device behaviors (rendering/IME/FCM/biometric/camera, E2E A34/A35, S2 real-handset FCM spike) deferred to android/DEVICE_QA_CHECKLIST.md per plan §7 (no emulator/Firebase here). Built via multi-agent orchestration (TDD builders → adversarial cross-review → fix → re-verify → independent gate); progress in android/PROGRESS_ANDROID.md.
This commit is contained in:
@@ -1,27 +1,28 @@
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// :app — the Android application (Compose UI, ViewModels, Hilt DI, FCM push,
|
||||
// DeepLinkRouter, DesignSystem, EventBus). Mirrors iOS App/WebTerm.
|
||||
// :app — the Android application (Compose Material 3 + Adaptive design system,
|
||||
// Hilt DI skeleton, launcher MainActivity). Mirrors iOS App/WebTerm.
|
||||
//
|
||||
// SCAFFOLD STUB ONLY. This module is COMMENTED OUT in settings.gradle.kts because
|
||||
// this build environment has NO Android SDK. The block below is the intended shape;
|
||||
// it applies the Android Gradle Plugin, which cannot resolve without an SDK.
|
||||
// A13 establishes the Android UI-stack version matrix for every later Android task
|
||||
// (the app-stack baseline, analogous to how A1 established the JVM catalog). All
|
||||
// UI versions are resolved in gradle/libs.versions.toml.
|
||||
//
|
||||
// TODO(android-sdk): uncomment the include in settings.gradle.kts AND this block,
|
||||
// add AGP + google() to pluginManagement, and provide local.properties -> sdk.dir.
|
||||
// Plugins (AGP 9 has BUILT-IN Kotlin → never apply org.jetbrains.kotlin.android):
|
||||
// com.android.application · kotlin.plugin.compose · ksp · dagger.hilt.android
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/*
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("com.google.gms.google-services")
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.compose.compiler)
|
||||
alias(libs.plugins.ksp)
|
||||
alias(libs.plugins.hilt)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "wang.yaojia.webterm"
|
||||
compileSdk = 35
|
||||
// compileSdk 36 (Android 16): the contemporaneous androidx/Compose line for
|
||||
// Kotlin 2.3.21 requires compiling against SDK 36+ (BOM 2025.11 → ui 1.9.5).
|
||||
// targetSdk stays 35 per plan §2 (compileSdk ≥ targetSdk is the normal rule).
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "wang.yaojia.webterm"
|
||||
@@ -31,18 +32,91 @@ android {
|
||||
versionName = "0.1.0"
|
||||
}
|
||||
|
||||
buildFeatures { compose = true }
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
// No applicationId suffix — keep it stable for deep-link testing (A32).
|
||||
}
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
}
|
||||
|
||||
kotlin { jvmToolchain(17) }
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Modules A15 wires into the DI graph.
|
||||
implementation(project(":wire-protocol"))
|
||||
implementation(project(":session-core"))
|
||||
implementation(project(":api-client"))
|
||||
implementation(project(":transport-okhttp"))
|
||||
implementation(project(":client-tls"))
|
||||
// A15: bridge the mTLS device identity (:client-tls-android) onto the shared client, and
|
||||
// provide the DataStore-backed stores (:host-registry). :client-tls-android exposes
|
||||
// :client-tls via `api`, but the explicit dep above is kept for clarity.
|
||||
implementation(project(":client-tls-android"))
|
||||
implementation(project(":host-registry"))
|
||||
// Terminal render (A16): RemoteTerminalSession/RemoteTerminalView for the live terminal (A21) and
|
||||
// the raw Termux TerminalEmulator for A18's off-screen thumbnail rasterisation (§6.7). :terminal-view
|
||||
// scopes Termux as `implementation`, so :app names the emulator directly for the off-screen path.
|
||||
implementation(project(":terminal-view"))
|
||||
implementation(libs.termux.terminal.emulator)
|
||||
// QR pairing (A19): CameraX + on-device ML Kit barcode scanning.
|
||||
implementation(libs.bundles.camerax)
|
||||
implementation(libs.mlkit.barcode.scanning)
|
||||
// Push (A30) + nav/deep-links (A32).
|
||||
implementation(platform(libs.firebase.bom))
|
||||
implementation(libs.firebase.messaging)
|
||||
implementation(libs.androidx.biometric)
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
// OkHttp is `implementation` (not `api`) in :transport-okhttp, so :app names OkHttpClient /
|
||||
// ConnectionPool directly in NetworkModule/TlsModule.
|
||||
implementation(libs.okhttp)
|
||||
// Coroutines are used directly by the wiring (EventBus fan-out, engine confinement scope).
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
// Preferences DataStore is named in StorageModule's @Provides return types.
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
|
||||
// AndroidX foundation
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
|
||||
// Compose (the BOM governs every version below)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.bundles.compose)
|
||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||
|
||||
// Hilt (Dagger) — KSP annotation processing
|
||||
implementation(libs.hilt.android)
|
||||
ksp(libs.hilt.compiler)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
|
||||
// JVM unit tests (no device) — the frozen design-token spec, EventBus fan-out, and the
|
||||
// RetainedSessionHolder lifecycle invariant driven by a fake transport under virtual time.
|
||||
testImplementation(libs.bundles.unit.test)
|
||||
testImplementation(project(":test-support")) // FakeTermTransport for the holder lifecycle test (FIX 6)
|
||||
testRuntimeOnly(libs.junit.platform.launcher)
|
||||
}
|
||||
|
||||
// Local (JVM) unit tests run on the JUnit 5 platform, matching the pure modules.
|
||||
tasks.withType<Test>().configureEach {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user