Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Gradle / Android 构建产物
.gradle/
.kotlin/
.gradle-user-home/
.pnpm-store/
build/
Expand Down
11 changes: 7 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# AGENTS.md

> Miuix 独立体验版:`standardPersonal` 使用包名 `com.dsh.client.miuix`、名称 DSHA Miuix、本机签名和 `debuggable=false`,可与原应用并存且数据独立。该构建类型不替换正式版 E7E3 签名规则,也不进入正式 release 目录。构建方式与验证边界见 `docs/ui-miuix.md`。

> 2026-10-17 UI 更新:按用户指定接入 `compose-miuix-ui/miuix`,覆盖历史“无 Kotlin / 仅 Material3”界面限制。Kotlin 限于 `ui/miuix` 的 Compose 展示层,业务保持 Java 17、单模块 `:app`。固定 Miuix 0.8.8 / Compose Android 1.10.5 / Kotlin Compose 编译器 2.3.20;保留 Standard API 30、Low API 23。当前迁移范围、定向检查与未验边界见 `docs/ui-miuix.md`;不将源码验证写成正式 APK 或真机交付。

> 2026-10-15 本轮新增授权:仅同步当前应用源码、构建脚本、必要文档与 Star History 到 GitHub;不上传 APK、离线归档、设备取证、密钥或本机交付资料,不发布 Release 或网站。下方历史“未上传”描述对应各自当时的本地交付状态,本轮源码同步范围见 docs/source-updates/build173.md。

> build173 PiP 补充交付(2026-10-15):C17 标准包修复“小窗显示时停止 → 重启 → 再进入”残留 Task 0.275 缩放;失效空宿主退休,新代次使用新 Task,同代网页实例继续保留。62 项定向 Activity 宿主回归、76 项开机边界恢复关联测试及最终 37 项软件门禁通过;两版 JUnit 各1700项、40 skip,Release Lint/完整资产/历史签名通过。正式同签名 Android13 原应用覆盖后,真实重启恢复、受管更新、PiP 停止及导出停止后的新窗口5/30秒、同代恢复/返回/旋转均有实际记录。1269条数据无增删,个人配置、会话、附件字节保留;历史4条未知记录与1条追加消息缺口继续保留。有限日志有2次 renderer 报告和2次 SAF 系统捕获 NPE,实际导出成功,不作零崩溃结论。标准 APK SHA c11abc4197f747b690897ea2198c990514eedd5d6ca10b9111ce7da90ed02f57 / 288099627 bytes,已替换 release 同名 rc2.1 标准包;C14 原件及封存材料保留,Low172 原字节不动。回执 artifacts/deliveries/build173-standard-pip-fix.json,源码 artifacts/source/build173-pip-fix/,补充报告 docs/releases/v0.2.24-build173-pip-20261015.md;不发布 GitHub、网站或 Release。
Expand All @@ -13,7 +17,7 @@
- 本轮新增修复只看仍开放的 issue / PR,已关闭条目不追做,插件收录跳过。QuickChat 已按用户要求撤回,不再恢复其生产入口。标准版优先,Low 不安装、不做设备验收,既有 Low172 交付文件和校验保持原字节;不能把软件双版通过写成设备双版通过。
- 引擎只保留 bxroot / proot。首次升级优先 bxroot;升级后的显式 proot 选择保留。启动阶段明确引擎失败、无插件故障和用户业务提交,且本轮受管后代树与 launcher 都已确认退出,才允许一次自动 proot 重试。慢启动、超时、未知执行结果和已开始的写任务不能借此重放。proroot 只保留历史记录读取,不保留生产执行。
- 彻底重装/完整格式化功能移除,包括生产入口、Intent 触发和擦除后端。外置工作区维护只能断开私有引用,不遍历清理、覆盖或改名其内容;旧恢复日志不能自动重放外置写删。冷测速等合法私有临时清理必须使用受控目标及 NOFOLLOW,不能继承完整格式化能力。
- 原生外壳继续 Java17 / Material3 和现有导航,只迁移透明状态栏、背景跟色、图标明暗与 Insets。状态栏保持可见,键盘、挖孔和 PiP 的安全区规则保留;双网页内核使用同一个网页实例切换全屏 / PiP / 隐藏。恢复全屏时按系统当前 PiP 状态同步并幂等清除自有缩放与位移,不能依赖旧几何缓存宣称变换已清除。
- 原生外壳的业务继续 Java 17,导航流程保持;界面允许按本轮 Miuix 迁移说明使用 Kotlin / Compose。透明状态栏、背景跟色、图标明暗与 Insets 继续共用原生处理。状态栏保持可见,键盘、挖孔和 PiP 的安全区规则保留;双网页内核使用同一个网页实例切换全屏 / PiP / 隐藏。恢复全屏时按系统当前 PiP 状态同步并幂等清除自有缩放与位移,不能依赖旧几何缓存宣称变换已清除。
- 内置移动插件固定 3.1.0 / ab3a85bead1c53b723e8627f5b44e3c306d85604;DSH 仍 0.2.0-rc.2,Ubuntu base10 不变。上游已吸收的本地差异退休,安全服务端替换、快捷键搜索、可见区域 CSS 和视口合并仍须实际回归。
- 屏幕确认策略为全部手动 / 按应用自定义 / 全部允许。全部允许覆盖敏感页面,仅免 DSHA 确认;系统能力、实际目标、运行代次、最新帧与节点身份仍校验。持久规则只在本机,停止使本轮许可失效,规则修改或撤销使排队请求失效,不从备份导入授权。
- 不实现整合包/市场及插件收录。任务通知采用标准进度通知,不请求系统 promoted 胶囊;原生终端、插件安装及生命周期脚本继续开放,工具审批不能宣称强沙箱。
Expand All @@ -28,8 +32,7 @@ DSHA 重构骨架。本文让你不扫全库就能上手 —— 读它之前先
## 一句话

APK 用 bxroot/proot 把完整 Ubuntu rootfs 搬进 app 私有目录,在里面跑 Node 24 +
pnpm + `@deepseek-ai/dsh`(**0.2.0-rc.2**)的 Web UI(默认动态端口,手动模式可指定 `3080`)。原生层是纯 Java 17、
Material3、单 Gradle 模块 `:app`。
pnpm + `@deepseek-ai/dsh`(**0.2.0-rc.2**)的 Web UI(默认动态端口,手动模式可指定 `3080`)。原生业务层是 Java 17,界面使用 Miuix / Compose 与既有 Material3 控件,单 Gradle 模块 `:app`。

## 技术约束(围绕这些设计)

Expand All @@ -55,7 +58,7 @@ Material3、单 Gradle 模块 `:app`。
- **签名规则(最新验收要求)**:`release` 和手机验收统一沿用 E7E3 历史发布证书(完整指纹见 build 131 记录);A3F4 仅保留为历史手机验收规则,不得用于本轮正式验收或放入 `release`。覆盖安装前核对包名、版本码、证书和 `debuggable=false`,不得卸载后用错误证书替代。

- **发布交付目录为 `F:\DSHA_RESTART\release\<APK文件名版本>\`**:只放两版 APK 和对应 `.apk.sha256`,同组历史 APK 放该版本的 `history/`。源码放 `artifacts/source/`,回执放 `artifacts/deliveries/`,其他历史原件移至 `artifacts/release-history/`;迁移记录保留原始 SHA,旧封档正文不改。
- **Java 17,无 Kotlin**,单模块 `:app`。
- **业务 Java 17,Kotlin 仅用于 Miuix 界面**,单模块 `:app`。
- `applicationId com.dsh.client`;Java 包 `com.deepseekharness.app`;标准版 `minSdk 30`、兼容版 `minSdk 23`,
`compileSdk/targetSdk 37`(SDK 平台包 `android-37.0`)、AGP 9.1.1、Gradle 9.3.1、NDK 26、**arm64-v8a only**。
- 离线 rootfs(`assets/offline-rootfs.bin`)等大型生成归档**不提交**;正式本地/CI 包必须含经过核对的完整 Ubuntu 与 DSH 分包,不能把源码检出缺少归档当作正式精简交付。
Expand Down
8 changes: 6 additions & 2 deletions BUILD.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# DSHA 现行构建说明

> Miuix 独立体验版:使用 `:app:assembleStandardPersonal` 构建 `com.dsh.client.miuix`(DSHA Miuix),本机签名、非调试、数据独立,可保留原应用。此包不进入正式 release;正式版继续使用 E7E3 签名。完整离线资产仍是打包前提,见 [UI 迁移说明](docs/ui-miuix.md)。

先读 [README](README.md)、[AGENTS](AGENTS.md)、[贡献流程](CONTRIBUTING.md) 与[接手指南](docs/接手指南.md)。应用版本以 `app/build.gradle` 为准,DSH 依赖以 `tools/dsh-runtime/package.json`/锁文件为准,完整交付和未验证范围以本轮报告为准。

本轮只做本地正式产物与软件验证,不操作手机、不生成额外调试/审计 APK、不上传 GitHub 或部署网站。JVM 测试名称中的 Debug 表示测试变体,不授权 assembleDebug 或安装额外 APK。
本次 UI PR 提交源码、构建配置与必要文档,不上传 APK、离线归档、设备取证或密钥,不发布 Release 或网站。JVM 测试名称中的 Debug 表示测试变体;独立体验版通过 Personal 构建类型生成。

## 环境与 flavor

| 输入 | 现行要求 |
|---|---|
| JDK / 原生语言 | Java 17,无 Kotlin |
| JDK / 原生语言 | Java 17;Miuix 展示层使用 Kotlin 2.3.20 / Compose 编译器,JVM 目标仍为 17 |
| Gradle / AGP | 仓库 Wrapper 9.3.1 / AGP 9.1.1 |
| Android SDK | `platforms;android-37.0`、build-tools 36.0.0;compile/target API 37 |
| NDK | 26.3.11579264;终端与 Low proot 按各自受管入口使用 API 23 与真实 ELF 对齐检查 |
Expand All @@ -20,6 +22,8 @@

配置本机 `local.properties` 的 `sdk.dir`,并将 JAVA_HOME、ANDROID_SDK_ROOT/ANDROID_HOME、DSHA_PYTHON 指向已有工具。不要复制开发者的绝对工具链目录。版本与固定 APK 文件名由 `app/build.gradle` 的 `versionCode`、`versionName` 和 `dshaApkNameVersion` 定义,不在本页再维护另一组当前数字。

Miuix UI 的依赖、混合界面边界与不依赖离线归档的定向检查见 [UI 迁移说明](docs/ui-miuix.md)。AGP 9.1.1 使用内置 Kotlin 支持,不重复应用旧 `org.jetbrains.kotlin.android` 插件。

## 输入准备与证明

离线 rootfs 和大型生成物不提交;固定输入、生成順序与 CI 传递方式见 CONTRIBUTING。Ubuntu 基础环境身份独立于 DSH 与 UI 版本。`offline-rootfs.bin` / `dsh-runtime.bin` 为 split-runtime-v1,独立应急 pinned 输入由 `tools/recovery-runtime/lock.json` 约束,不能用正式环境作为救援依赖。
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,11 @@ agent 通过本机 `127.0.0.1:3090` 桥调用以下能力(token 门控,`/app

## 架构

原生首页、设置、底部导航与共用管理卡片接入 Miuix;复杂表单与网页界面继续使用原有实现。迁移结构及验证范围见 [Miuix UI 说明](docs/ui-miuix.md)。

```
┌──────────────────────── APK ────────────────────────┐
│ 原生 Android(Java 17 · Material3 · 单模块 :app) │
│ 原生 Android(Java 17 · Miuix / Compose · 单模块 :app) │
│ ├ 启动 / 安装修复 / 配置 / 工作区 / 插件 / 终端 │
│ ├ 网页内核:系统 WebView(Standard)/ Gecko(Low) │
│ ├ 前台服务 + 看门狗 + 通知 + 画中画 │
Expand Down
14 changes: 13 additions & 1 deletion THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@

当前 Gradle 直接运行依赖坐标(不包含测试依赖;完整传递依赖许可审查仍需实际解析结果):

- `androidx.appcompat:appcompat:1.6.1`
- `androidx.appcompat:appcompat:1.7.1`
- `androidx.compose.foundation:foundation:1.10.5`
- `androidx.compose.ui:ui:1.10.5`
- `androidx.graphics:graphics-path:1.0.1`
- `androidx.webkit:webkit:1.15.0`
- `com.android.tools:desugar_jdk_libs:2.0.4`
- `com.google.android.material:material:1.11.0`
Expand All @@ -69,6 +72,15 @@
- `org.bouncycastle:bcprov-jdk15to18:1.85.2`
- `org.mozilla.geckoview:geckoview-arm64-v8a:143.0.20251003115653`
- `org.yaml:snakeyaml:2.4`
- `top.yukonga.miuix.kmp:miuix:0.8.8`

## Miuix 与 Compose 原生界面

- Miuix `top.yukonga.miuix.kmp:miuix:0.8.8`:Apache-2.0,源码与许可见 https://github.com/compose-miuix-ui/miuix/tree/v0.8.8 。
- AndroidX Compose UI / Foundation 1.10.5 与 AppCompat 1.7.1:Apache-2.0,源码见 https://android.googlesource.com/platform/frameworks/support/ 。
- Compose 的 Android 图形库 `androidx.graphics:graphics-path:1.0.1`:Apache-2.0;两版 APK 均包含 ARM64 `libandroidx.graphics.path.so`,AAR 及成员摘要由 `tools/native-dependencies.lock.json` 与实际 APK 门禁核对。
- Kotlin Compose 编译器 2.3.20:Apache-2.0,仅在构建时使用,源码见 https://github.com/JetBrains/kotlin 。
- Android 依赖由 Maven 获取固定发布版本,其 SHA-256 由 `gradle/verification-metadata.xml` 核验。

## 实际入库 JNI 成员与来源边界

Expand Down
45 changes: 43 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.plugin.compose'
}

// 两版共用功能和离线环境;只有 low 携带兼容旧 Android 的 Gecko 内核。
Expand Down Expand Up @@ -85,6 +86,15 @@ android {
if (!unsignedPackage) signingConfig signingConfigs.publish
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
// 用户授权的独立安装版;与正式应用分开保存数据,沿用本机密钥便于以后覆盖更新。
personal {
initWith release
applicationIdSuffix '.miuix'
versionNameSuffix '-miuix'
debuggable false
signingConfig signingConfigs.debug
matchingFallbacks = ['release']
}
}

packagingOptions {
Expand Down Expand Up @@ -112,7 +122,12 @@ android {
targetCompatibility JavaVersion.VERSION_17
}

testOptions {
unitTests.includeAndroidResources = true
}

buildFeatures {
compose = true
resValues = true
aidl = true
buildConfig = true
Expand All @@ -128,6 +143,18 @@ android {
}
}

kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
}
}

androidComponents {
beforeVariants(selector().withBuildType('personal')) { variant ->
variant.enableUnitTest = true
}
}

def verifyPublishCertificate = tasks.register("verifyPublishCertificate") {
doLast {
def signing = android.signingConfigs.publish
Expand Down Expand Up @@ -332,7 +359,8 @@ def verifyBackupDependencies = tasks.register("verifyBackupDependencies") {
}
hash.digest().encodeHex().toString()
}
["standardReleaseRuntimeClasspath", "lowReleaseRuntimeClasspath"].each { name ->
["standardReleaseRuntimeClasspath", "lowReleaseRuntimeClasspath",
"standardPersonalRuntimeClasspath", "lowPersonalRuntimeClasspath"].each { name ->
def resolved = configurations.getByName(name).resolvedConfiguration.resolvedArtifacts
lock.dependencies.each { expected ->
def selected = resolved.findAll { it.moduleVersion.id.group == expected.group && it.name == expected.name }
Expand Down Expand Up @@ -360,14 +388,26 @@ def prepareUiLanguages = tasks.register("prepareUiLanguages", Exec) {
"--output", layout.buildDirectory.dir("generated/uiLanguage").get().asFile.absolutePath)
}
tasks.named("preBuild").configure { dependsOn(prepareUiLanguages) }
// Kotlin 同样读取 Java 文案生成目录;显式声明生成顺序,支持独立 UI 编译。
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
dependsOn(prepareUiLanguages)
}
tasks.withType(JavaCompile).configureEach { dependsOn(prepareUiLanguages) }

dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
lowImplementation 'org.mozilla.geckoview:geckoview-arm64-v8a:143.0.20251003115653'
implementation 'androidx.appcompat:appcompat:1.6.1'
// 1.7 的 Dialog 提供 Compose 所需的视图生命周期。
implementation 'androidx.appcompat:appcompat:1.7.1'
// 1.15 保留 API 23;1.16 起要求 API 24,不能用于兼容版。
implementation 'androidx.webkit:webkit:1.15.0'
implementation 'com.google.android.material:material:1.11.0'
// Miuix 0.8.8 保留 API 23;界面使用 Kotlin,业务与运行时继续使用 Java 17。
implementation 'top.yukonga.miuix.kmp:miuix:0.8.8'
implementation 'androidx.compose.ui:ui:1.10.5'
implementation 'androidx.compose.foundation:foundation:1.10.5'
// Compose 的图形原生依赖也纳入实际 APK 成员校验。
implementation 'androidx.graphics:graphics-path:1.0.1'
standardImplementation 'dev.rikka.shizuku:api:13.1.5'
standardImplementation 'dev.rikka.shizuku:provider:13.1.5'
// 13.1.5 从 API 24 起可用;兼容版以 12.2.0 的相同 Binder 协议支持 Android 6。
Expand All @@ -382,6 +422,7 @@ dependencies {
implementation 'org.bouncycastle:bcprov-jdk15to18:1.85.2'
// 宿主凭据 YAML 仅使用 data-only SafeConstructor;来源、JAR 和许可由备份锁核验。
implementation 'org.yaml:snakeyaml:2.4'
testImplementation 'androidx.compose.ui:ui-test-junit4:1.10.5'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.robolectric:robolectric:4.15.1'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public void onStart() {
until(() -> page != null, "主界面未就绪");
runOnMainSync(
() ->
((com.google.android.material.bottomnavigation.BottomNavigationView)
((com.deepseekharness.app.ui.miuix.MiuixNavigation)
page.findViewById(R.id.bottom_nav))
.setSelectedItemId(R.id.nav_plugins));
until(() -> page.findViewById(R.id.btnPluginInstall) != null, "插件页没有加载");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,10 @@ public void onStart() {
MainActivity current = MainActivity.active();
runOnMainSync(
() ->
((com.google.android.material.bottomnavigation.BottomNavigationView)
((com.deepseekharness.app.ui.miuix.MiuixNavigation)
current.findViewById(R.id.bottom_nav))
.setSelectedItemId(R.id.nav_settings));
until(() -> MainActivity.active().findViewById(R.id.settings_tabs) != null, "设置页未加载");
until(() -> MainActivity.active().findViewById(R.id.settings_page) != null, "设置页未加载");
runOnMainSync(
() ->
audit(MainActivity.active().findViewById(android.R.id.content), "actual-settings"));
Expand All @@ -275,7 +275,7 @@ public void onStart() {
require(PtyTerminalFragment.preferred(getTargetContext()), "当前选择简易终端,需使用独立 PTY 验收");
runOnMainSync(
() ->
((com.google.android.material.bottomnavigation.BottomNavigationView)
((com.deepseekharness.app.ui.miuix.MiuixNavigation)
MainActivity.active().findViewById(R.id.bottom_nav))
.setSelectedItemId(R.id.nav_terminal));
until(
Expand Down Expand Up @@ -321,10 +321,10 @@ public void onStart() {
MainActivity current = MainActivity.active();
runOnMainSync(
() ->
((com.google.android.material.bottomnavigation.BottomNavigationView)
((com.deepseekharness.app.ui.miuix.MiuixNavigation)
current.findViewById(R.id.bottom_nav))
.setSelectedItemId(R.id.nav_settings));
until(() -> MainActivity.active().findViewById(R.id.settings_tabs) != null, "外观设置未加载");
until(() -> MainActivity.active().findViewById(R.id.settings_page) != null, "外观设置未加载");
runOnMainSync(
() -> ThemeController.select(MainActivity.active(), dark ? "dark" : "light"));
until(
Expand Down Expand Up @@ -367,7 +367,7 @@ public void onStart() {
until(
() -> MainActivity.active() != before && !ThemeController.isDark(MainActivity.active()),
"顶部切换无效");
require(MainActivity.active().findViewById(R.id.settings_tabs) != null, "切换丢失当前页面");
require(MainActivity.active().findViewById(R.id.settings_page) != null, "切换丢失当前页面");
runOnMainSync(() -> ThemeController.select(MainActivity.active(), "dark"));
until(() -> ThemeController.isDark(MainActivity.active()), "持久性检查准备失败");
output.putString("result", "PASS");
Expand Down
Loading