feat(android): T-AND-001 工程骨架(AGP 8.7.3 + Gradle 8.9 + Compose)
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
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")
|
||||
}
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
# OpenDance Studio —— R8 / ProGuard 规则
|
||||
#
|
||||
# release 构建开启 minifyEnabled + shrinkResources。以下 keep 规则覆盖:
|
||||
# - MediaPipe(大量 JNI 与反射,且 tasks-vision 的 AutoValue 生成类需保留)
|
||||
# - OkHttp / Okio(平台相关实现按类名查找)
|
||||
# - Kotlin 协程与元数据
|
||||
# 删掉这些规则会导致 release 包在真机上运行时崩溃(debug 包不会暴露该问题)。
|
||||
|
||||
# ---------------------------------------------------------------- MediaPipe
|
||||
-keep class com.google.mediapipe.** { *; }
|
||||
-keep interface com.google.mediapipe.** { *; }
|
||||
-dontwarn com.google.mediapipe.**
|
||||
|
||||
# MediaPipe 使用 AutoValue 生成不可变类,依赖注解与泛型签名
|
||||
-keep class com.google.auto.value.** { *; }
|
||||
-dontwarn com.google.auto.value.**
|
||||
-keepclassmembers class * {
|
||||
@com.google.auto.value.AutoValue.Builder <methods>;
|
||||
}
|
||||
|
||||
# MediaPipe / TFLite 的 native 入口
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
# protobuf(MediaPipe 依赖)
|
||||
-keep class com.google.protobuf.** { *; }
|
||||
-dontwarn com.google.protobuf.**
|
||||
|
||||
# ---------------------------------------------------------------- OkHttp / Okio
|
||||
-dontwarn okhttp3.**
|
||||
-dontwarn okio.**
|
||||
-dontwarn org.conscrypt.**
|
||||
-dontwarn org.bouncycastle.**
|
||||
-dontwarn org.openjsse.**
|
||||
-keep class okhttp3.internal.platform.** { *; }
|
||||
-keep class org.conscrypt.** { *; }
|
||||
-keep class org.bouncycastle.** { *; }
|
||||
-keep class org.openjsse.** { *; }
|
||||
|
||||
# ---------------------------------------------------------------- Kotlin / 协程
|
||||
-keepattributes Signature, InnerClasses, EnclosingMethod, *Annotation*
|
||||
-keep class kotlin.Metadata { *; }
|
||||
-dontwarn kotlin.**
|
||||
-keepclassmembers class kotlinx.coroutines.** { volatile <fields>; }
|
||||
|
||||
# ---------------------------------------------------------------- AndroidX / Compose
|
||||
-dontwarn androidx.**
|
||||
-keep class androidx.camera.** { *; }
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- T-AND-002:相机采集 -->
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<!-- T-AND-006:主机发现(局域网扫描) -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera.any"
|
||||
android:required="true" />
|
||||
|
||||
<application
|
||||
android:name=".OpenDanceApp"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.OpenDance"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="34">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:screenOrientation="portrait"
|
||||
android:configChanges="orientation|screenSize|keyboardHidden"
|
||||
android:theme="@style/Theme.OpenDance">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 简笔骨架图标:与 OpenDance 的 33 点骨骼主题呼应 -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group android:translateX="18" android:translateY="18">
|
||||
<path
|
||||
android:strokeColor="#4EA1FF"
|
||||
android:strokeWidth="3"
|
||||
android:strokeLineCap="round"
|
||||
android:pathData="M36,8 L36,20 M36,20 L22,34 M36,20 L50,34 M36,20 L36,46 M36,46 L26,66 M36,46 L46,66 M22,34 L16,50 M50,34 L56,50" />
|
||||
<path
|
||||
android:fillColor="#FFD166"
|
||||
android:pathData="M36,4 m-4,0 a4,4 0,1 0,8 0 a4,4 0,1 0,-8 0" />
|
||||
<path
|
||||
android:fillColor="#FFD166"
|
||||
android:pathData="M22,32 m-3,0 a3,3 0,1 0,6 0 a3,3 0,1 0,-6 0" />
|
||||
<path
|
||||
android:fillColor="#FFD166"
|
||||
android:pathData="M50,32 m-3,0 a3,3 0,1 0,6 0 a3,3 0,1 0,-6 0" />
|
||||
<path
|
||||
android:fillColor="#FFD166"
|
||||
android:pathData="M16,50 m-3,0 a3,3 0,1 0,6 0 a3,3 0,1 0,-6 0" />
|
||||
<path
|
||||
android:fillColor="#FFD166"
|
||||
android:pathData="M56,50 m-3,0 a3,3 0,1 0,6 0 a3,3 0,1 0,-6 0" />
|
||||
<path
|
||||
android:fillColor="#FFD166"
|
||||
android:pathData="M36,46 m-3,0 a3,3 0,1 0,6 0 a3,3 0,1 0,-6 0" />
|
||||
<path
|
||||
android:fillColor="#FFD166"
|
||||
android:pathData="M26,66 m-3,0 a3,3 0,1 0,6 0 a3,3 0,1 0,-6 0" />
|
||||
<path
|
||||
android:fillColor="#FFD166"
|
||||
android:pathData="M46,66 m-3,0 a3,3 0,1 0,6 0 a3,3 0,1 0,-6 0" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#1B1F2A</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">OpenDance 辅助</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.OpenDance" parent="android:Theme.Material.NoActionBar">
|
||||
<item name="android:statusBarColor">#1B1F2A</item>
|
||||
<item name="android:navigationBarColor">#1B1F2A</item>
|
||||
<item name="android:windowBackground">#1B1F2A</item>
|
||||
</style>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user