import java.util.Properties // OpenDance Studio —— Android 辅助 App(应用模块) // // 契约:contracts/websocket-keypoints.md v0.1.0(本端为**发送方**) // contracts/skeleton-topology.md v0.1.0(MediaPipe 33 点) plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "com.opendance.studio" compileSdk = 34 defaultConfig { applicationId = "com.opendance.studio" minSdk = 29 // Android 10(文档 T-AND-009 验收要求 Android 10+) targetSdk = 34 versionCode = 1 versionName = "0.1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" // 契约版本,供关于页与日志显示 buildConfigField("String", "CONTRACT_VERSION", "\"0.1.0\"") buildConfigField("String", "WS_SUBPROTOCOL", "\"opendance.pose.v1\"") } /** * 签名配置:优先读取 keystore.properties(已被 .gitignore 忽略), * 否则读取环境变量;两者都没有时回退到 debug 签名。 * * 这样 `./gradlew assembleRelease` 在没有任何私密文件的情况下也能产出**已签名**的 * APK(用 debug key),便于 CI 与验收;正式发布必须提供自己的 keystore * (见 build/gen-keystore.sh 与 README.txt)。 */ val keystorePropertiesFile = rootProject.file("keystore.properties") val keystoreProperties = Properties() if (keystorePropertiesFile.exists()) { keystorePropertiesFile.inputStream().use { keystoreProperties.load(it) } } fun signingValue(key: String, env: String): String? = keystoreProperties.getProperty(key) ?: System.getenv(env) val releaseStoreFile = signingValue("storeFile", "KEYSTORE_PATH") val hasReleaseKeystore = releaseStoreFile != null && file(releaseStoreFile).exists() signingConfigs { if (hasReleaseKeystore) { create("release") { storeFile = file(releaseStoreFile!!) storePassword = signingValue("storePassword", "KEYSTORE_PASSWORD") keyAlias = signingValue("keyAlias", "KEY_ALIAS") keyPassword = signingValue("keyPassword", "KEY_PASSWORD") } } } buildTypes { debug { applicationIdSuffix = ".debug" isMinifyEnabled = false // debug 保留全部 ABI,方便在模拟器(x86_64)上调试 ndk { abiFilters.clear() abiFilters.addAll(listOf("arm64-v8a", "armeabi-v7a", "x86", "x86_64")) } } release { // 发布包只打包真实手机用得到的 ABI。 // x86/x86_64 仅供模拟器使用,却占掉约 27 MB(几乎是发布包的一半), // 对真机用户纯属浪费。 ndk { abiFilters.clear() abiFilters.addAll(listOf("arm64-v8a", "armeabi-v7a")) } isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro", ) signingConfig = if (hasReleaseKeystore) { signingConfigs.getByName("release") } else { // 回退:用 debug 签名,保证 CI 上 assembleRelease 也能产出已签名 APK signingConfigs.getByName("debug") } } } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } buildFeatures { compose = true buildConfig = true } packaging { resources { excludes += setOf( "/META-INF/{AL2.0,LGPL2.1}", "/META-INF/DEPENDENCIES", "/META-INF/LICENSE*", "/META-INF/NOTICE*", ) } // MediaPipe 的 .task 模型必须保持不压缩,否则运行时按文件路径读取会失败 androidResources { noCompress += "task" } } testOptions { unitTests.isReturnDefaultValues = true } lint { abortOnError = false } } dependencies { // ---- Android 基础 ---- implementation("androidx.core:core-ktx:1.13.1") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.6") implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6") implementation("androidx.activity:activity-compose:1.9.2") // ---- Compose ---- val composeBom = platform("androidx.compose:compose-bom:2024.10.01") implementation(composeBom) androidTestImplementation(composeBom) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") implementation("androidx.compose.material:material-icons-extended") debugImplementation("androidx.compose.ui:ui-tooling") // ---- CameraX(T-AND-002)---- val cameraX = "1.4.0" implementation("androidx.camera:camera-core:$cameraX") implementation("androidx.camera:camera-camera2:$cameraX") implementation("androidx.camera:camera-lifecycle:$cameraX") implementation("androidx.camera:camera-view:$cameraX") // ---- MediaPipe Pose 端侧推理(T-AND-003)---- implementation("com.google.mediapipe:tasks-vision:0.10.35") // ---- WebSocket 推流(T-AND-005)---- implementation("com.squareup.okhttp3:okhttp:4.12.0") // ---- 权限 ---- implementation("com.google.accompanist:accompanist-permissions:0.34.0") // ---- 单元测试(T-AND-004 等纯逻辑,跑在 JVM 上,无需设备)---- testImplementation("junit:junit:4.13.2") testImplementation("org.jetbrains.kotlin:kotlin-test:2.0.21") androidTestImplementation("androidx.test.ext:junit:1.2.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") androidTestImplementation("androidx.compose.ui:ui-test-junit4") }