151cea3ecb
Adds .gitea/workflows/build-android.yml — Linux runner installs JDK 17, Python 3.11, Android SDK/NDK, symlinks server/src/ledgrab into the Chaquopy python source dir, and runs assembleDebug on master pushes / assembleRelease on v* tags. APK is uploaded as an artifact and attached to the Gitea release on tag push. Conditional signing config in build.gradle.kts reads keystore from env vars (CI secrets) and falls back to debug signing locally. Gradle wrapper (gradlew/gradlew.bat/ gradle-wrapper.jar) committed so CI can drive the build. Rebuilds pydantic-core wheels for arm64-v8a and x86_64 — both were missing libpython3.11.so in NEEDED, which would have crashed at import on real devices. build-pydantic-core.sh rewritten as a multi-ABI builder: selects targets via args, sets RUSTFLAGS=-C link-arg=-Wl,--no-as-needed -C link-arg=-lpython3.11 to force the symbol-resolution dependency, uses the per-ABI sysconfigdata + libpython staged in android/.build-cache/, prefers `py -3.11` on Windows (Git Bash's python3.11 is an MSStore stub), uses the .cmd clang wrapper on Windows (fixes os error 193), and verifies NEEDED via llvm-readelf after each build. abiFilters restored to the full triple in build.gradle.kts; multi-ABI debug APK builds cleanly (~99 MB).
120 lines
4.2 KiB
Kotlin
120 lines
4.2 KiB
Kotlin
plugins {
|
|
id("com.android.application")
|
|
id("org.jetbrains.kotlin.android")
|
|
id("com.chaquo.python")
|
|
}
|
|
|
|
android {
|
|
namespace = "com.ledgrab.android"
|
|
compileSdk = 34
|
|
|
|
defaultConfig {
|
|
applicationId = "com.ledgrab.android"
|
|
minSdk = 24 // Android 7.0 — covers nearly all TV boxes
|
|
targetSdk = 34
|
|
versionCode = 1
|
|
versionName = "0.3.0"
|
|
|
|
ndk {
|
|
// All three ABIs: arm64-v8a (real TV hardware), x86_64 (modern
|
|
// emulators), x86 (legacy emulators). Wheels in android/wheels/
|
|
// must be kept in sync — see build-scripts/build-pydantic-core.sh.
|
|
abiFilters += listOf("arm64-v8a", "x86_64", "x86")
|
|
}
|
|
}
|
|
|
|
// Signing config from env vars (CI) — only registered when all four are set.
|
|
// Local release builds fall back to the debug signing config.
|
|
val ciKeystorePath = System.getenv("ANDROID_KEYSTORE_PATH")
|
|
val ciKeystorePassword = System.getenv("ANDROID_KEYSTORE_PASSWORD")
|
|
val ciKeyAlias = System.getenv("ANDROID_KEY_ALIAS")
|
|
val ciKeyPassword = System.getenv("ANDROID_KEY_PASSWORD")
|
|
val hasCiSigning = listOf(ciKeystorePath, ciKeystorePassword, ciKeyAlias, ciKeyPassword)
|
|
.all { !it.isNullOrBlank() } && file(ciKeystorePath!!).exists()
|
|
|
|
signingConfigs {
|
|
if (hasCiSigning) {
|
|
create("release") {
|
|
storeFile = file(ciKeystorePath!!)
|
|
storePassword = ciKeystorePassword
|
|
keyAlias = ciKeyAlias
|
|
keyPassword = ciKeyPassword
|
|
}
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
release {
|
|
isMinifyEnabled = false
|
|
signingConfig = if (hasCiSigning) {
|
|
signingConfigs.getByName("release")
|
|
} else {
|
|
signingConfigs.getByName("debug")
|
|
}
|
|
}
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
}
|
|
|
|
kotlinOptions {
|
|
jvmTarget = "17"
|
|
}
|
|
}
|
|
|
|
chaquopy {
|
|
defaultConfig {
|
|
version = "3.11"
|
|
|
|
pip {
|
|
// Pre-built wheels directory (for pydantic-core etc.)
|
|
// Must use absolute file:// URI with forward slashes
|
|
options("--find-links", "file:///${rootDir.absolutePath.replace("\\", "/")}/wheels/")
|
|
|
|
// ── Android-compatible dependencies ─────────────────
|
|
// Listed explicitly because pyproject.toml includes
|
|
// desktop-only packages with no Android wheels.
|
|
// See CLAUDE.md "Android Dependency Sync" for policy.
|
|
install("fastapi")
|
|
install("uvicorn") // without [standard] — no uvloop/httptools
|
|
install("httpx")
|
|
install("numpy")
|
|
install("pydantic") // needs pydantic-core wheel in wheels/
|
|
install("pydantic-settings")
|
|
install("PyYAML")
|
|
install("structlog")
|
|
install("python-json-logger")
|
|
install("python-dateutil")
|
|
install("python-multipart")
|
|
install("jinja2")
|
|
install("zeroconf")
|
|
install("aiomqtt")
|
|
install("openrgb-python")
|
|
// opencv-python-headless: no cp311 Android wheel on Chaquopy.
|
|
// LedGrab's cv2 usage is guarded with try/except ImportError
|
|
// and falls back to numpy/Pillow alternatives on Android.
|
|
install("Pillow")
|
|
install("websockets")
|
|
}
|
|
}
|
|
}
|
|
|
|
// LedGrab Python source is included via a directory junction:
|
|
// android/app/src/main/python/ledgrab -> server/src/ledgrab
|
|
// This is the standard Chaquopy way to include local Python packages.
|
|
// Create the junction (run from repo root, no admin needed):
|
|
// cmd /c "mklink /J android\app\src\main\python\ledgrab server\src\ledgrab"
|
|
|
|
|
|
dependencies {
|
|
implementation("androidx.core:core-ktx:1.13.1")
|
|
implementation("androidx.appcompat:appcompat:1.7.0")
|
|
implementation("androidx.leanback:leanback:1.0.0")
|
|
implementation("com.google.android.material:material:1.12.0")
|
|
implementation("androidx.lifecycle:lifecycle-service:2.8.7")
|
|
// QR code generation for displaying server URL on TV
|
|
implementation("com.google.zxing:core:3.5.3")
|
|
}
|