依据 docs/项目工程改进方案.md 实施(第五梯队 CI/协作除外)。
安全性
- Release 剥离 Log.v/d/i/w(-assumenosideeffects),保留 Log.e
- 全量频道/成员 dump、消息正文、发送者 UID 改为 BuildConfig.DEBUG 保护
(实测 release dex 中 "Channel: id=" / "Client: id=" / selfId= 等均为 0 命中)
构建产物体积
- .gitignore 补 *.a(保留 .opus/lib 四个 ABI 预编译库)、.opus/install、
app/libs/*.aar、dnn/torch
- git rm --cached 除名 AAR、.gradle、.opus/{build,install}、dnn/torch
跟踪体积 50MB → 17.4MB,工作区文件不受影响
跨语言契约(本方案核心)
- 新增 go/teamspeak/contract.go:字段名常量 + BridgeContractVersion 单一事实来源
- 新增契约测试:反射断言常量与 struct tag 一致;Go/Kotlin 共用 golden 样本
- 新增 BridgeContract.kt:启动校验 AAR 契约版本,不匹配则阻止连接
- 修正文档:channel_order 是前驱频道 ID(链表指针)而非排序权重,
按它数值排序会打乱频道树;ChannelInfo.order 在 Go 侧不存在
状态模型
- ConnectionState 增 Idle 取代 null 编码,connectionState 不再可空
- applyClients 改为按频道差分更新,避免全量刷新导致频道树整体重组
- 频道数据 freshness 统一由 Repository 维护,修复 ViewModel 与 Repository
两份时间戳不同步导致的重复刷新
UI
- 修复 collectAsState() 在参数位置调用导致的 isSelf 快照失效
- 消除 9 处 !! 断言(ChannelListScreen)
- 硬编码 24.5/14.5dp 与 depth*24 收敛到 UiTokens.Spacing,缩进加 4 层上限
测试
- Kotlin 测试 7 → 106;Go 契约测试新增 24 个用例
- 各覆盖 InputValidator、消息送达确认、僵尸会话过滤、频道差分、
频道顺序语义、错误分类
其他修复
- classifyError 提取为纯函数并补测试;修复 too many clones (id=521)
未识别导致英文原文直接暴露给用户
版本号提升至 1.0.13(versionCode 13)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
115 lines
3.6 KiB
Kotlin
115 lines
3.6 KiB
Kotlin
plugins {
|
||
id("com.android.application")
|
||
id("org.jetbrains.kotlin.android")
|
||
id("org.jetbrains.kotlin.plugin.compose")
|
||
id("org.jetbrains.kotlin.plugin.serialization")
|
||
}
|
||
|
||
android {
|
||
namespace = "com.tsmobile.app"
|
||
compileSdk = 35
|
||
|
||
defaultConfig {
|
||
applicationId = "com.tsmobile.app"
|
||
minSdk = 26
|
||
targetSdk = 35
|
||
versionCode = 13
|
||
versionName = "1.0.13"
|
||
}
|
||
|
||
signingConfigs {
|
||
create("release") {
|
||
// The release keystore is kept at the repository root by default.
|
||
// Gradle properties can still override this path for CI or local setups.
|
||
val defaultStoreFile = rootProject.projectDir.parentFile
|
||
.resolve("keystore/tsmobile-release.jks")
|
||
|
||
storeFile = defaultStoreFile
|
||
storePassword = providers.gradleProperty("TSMOBILE_STORE_PASSWORD").orNull
|
||
keyAlias = providers.gradleProperty("TSMOBILE_KEY_ALIAS")
|
||
.orElse("tsmobile-release")
|
||
.get()
|
||
keyPassword = providers.gradleProperty("TSMOBILE_KEY_PASSWORD").orNull
|
||
}
|
||
}
|
||
|
||
buildTypes {
|
||
release {
|
||
isMinifyEnabled = true
|
||
isShrinkResources = true
|
||
signingConfig = signingConfigs.getByName("release")
|
||
proguardFiles(
|
||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||
"proguard-rules.pro"
|
||
)
|
||
}
|
||
}
|
||
|
||
compileOptions {
|
||
sourceCompatibility = JavaVersion.VERSION_17
|
||
targetCompatibility = JavaVersion.VERSION_17
|
||
}
|
||
|
||
kotlinOptions {
|
||
jvmTarget = "17"
|
||
}
|
||
|
||
buildFeatures {
|
||
compose = true
|
||
// 供 BuildConfig.DEBUG 使用:debug-only 的诊断日志靠它做常量折叠,
|
||
// R8 会把 `if (BuildConfig.DEBUG)` 块整体消除,release 包不留全量转储。
|
||
buildConfig = true
|
||
}
|
||
|
||
// 引入 gomobile 生成的 AAR
|
||
// 将 Go 编译产物放到 app/libs/ 目录下
|
||
// 文件名: teamspeak.aar
|
||
}
|
||
|
||
dependencies {
|
||
// Compose BOM
|
||
val composeBom = platform("androidx.compose:compose-bom:2024.12.01")
|
||
implementation(composeBom)
|
||
|
||
// Compose UI
|
||
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")
|
||
|
||
// Activity & Navigation
|
||
implementation("androidx.activity:activity-compose:1.9.3")
|
||
implementation("androidx.navigation:navigation-compose:2.8.5")
|
||
|
||
// Lifecycle & ViewModel
|
||
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
|
||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
|
||
implementation("androidx.lifecycle:lifecycle-service:2.8.7")
|
||
implementation("androidx.lifecycle:lifecycle-process:2.8.7")
|
||
|
||
// DataStore (替代 localStorage)
|
||
implementation("androidx.datastore:datastore-preferences:1.1.1")
|
||
|
||
// Coroutines
|
||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")
|
||
|
||
// JSON 序列化
|
||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
|
||
|
||
// HTTP client (更新检测)
|
||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||
|
||
// Coil 图片加载(Compose)
|
||
implementation("io.coil-kt.coil3:coil-compose:3.0.4")
|
||
|
||
// gomobile AAR — 放到 app/libs/teamspeak.aar
|
||
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
|
||
|
||
// Tests
|
||
testImplementation("junit:junit:4.13.2")
|
||
|
||
// Debug
|
||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||
}
|