Skip to content

Releases: chjy090/DSH-Mobile

DSH Mobile v0.4.5

Choose a tag to compare

@chjy090 chjy090 released this 27 Oct 15:32

DSH Mobile v0.4.5 发布说明

发布对象:Android APK(DeepSeek Harness dsh Web GUI 的远程 WebView 客户端)
源码仓库:DSH-Mobile
源码版本:0.4.5 / versionCode 12
发布包准备日期:2026-10-27

状态说明:按本轮任务约束,源码改动尚未 git commit/git tag,也未创建 GitHub Release;
本文件与 APK 为待发布产物,需由维护者复核后提交/发布。

1. 版本与校验

项目 内容
版本名 / versionCode 0.4.5 / 12
applicationId ai.deepseek.dsh.mobile
最低系统 Android 8.0(API 26);targetSdk 35
发布包 DSH-Mobile-v0.4.5-release.apk
发布包大小 25,541,227 字节(24.36 MiB / 25.54 MB)
发布包 SHA-256 3eb4394d9ceb50c7d8ec3e8cc4e0a6a88e8428001d3379e8e825100b52cbb4e3
签名证书 CN=DSH Mobile, OU=Personal, O=Personal, L=NA, ST=NA, C=CN;证书 SHA-256 45dee26ac9aa785dda281970bc07072665624ef381616d310331f849216f62c8(与 v0.4.0–v0.4.4 相同,可覆盖升级)
Debug 包 本轮未构建、未随发布包分发(仅提供 release)
构建命令 ./gradlew assembleRelease lintRelease --offline
Lint lintRelease:0 errors, 16 warnings;报告中的 warning 路径均不涉及本轮新增/修改的 DownloadHistory.kt、MainActivity.kt 下载历史逻辑与 file_paths.xml(见 §4)

校验方法(在本目录执行):

sha256sum -c SHA256SUMS-v0.4.5.txt

2. v0.4.5 变更内容

2.1 App 内「最近下载」(核心兜底)

用户实测反馈:v0.4.4 下载文件名、重名、打开入口均正常,但 Realme 文件管理器的「最近」
仍不显示下载的文档(「下载」分类可见)。这类 OEM「最近」通常只聚合媒体库条目或系统下载记录,
应用无法强制把任意文档塞进厂商「最近」。v0.4.5 因此增加了不依赖 ROM 规则的 App 内历史:

  • 数据模型:DownloadHistory.Entry,字段为
    displayName / contentUri(String?) / legacyPath(String?) / mime / size / savedAt。
    • Android 10+ 保存 MediaStore 实际集合返回的 content Uri;
    • Android 8/9 保存公共 Download/ 下文件的绝对路径;
    • 不记录 URL、token、cookie 等任何网络凭据。
  • 存储:独立 SharedPreferences(dsh_download_history)+ JSON,最多 50 条,最新在前;
    写入/读取/解析失败均静默降级(跳过写入或返回空列表),不影响下载本身。
  • 写入时机:Downloader 成功回调携带最终文件名、content Uri/绝对路径、MIME 与字节数;
    WebHost 在 onDone 先写历史,再复用原有完成提示/「打开」逻辑。
  • 菜单入口:应用菜单新增「最近下载」,认证网关模式与直连模式都显示;
    位置在「刷新」之后、模式相关菜单项之前。
  • 列表与打开:每项显示「文件名 + 保存时间(yyyy-MM-dd HH:mm)」;点击用
    ACTION_VIEW + FLAG_GRANT_READ_URI_PERMISSION 打开。打开前先验证 Uri 可读:
    • Android 10+ 直接使用 MediaStore content Uri;
    • Android 8/9 使用新增的 FileProvider(authority ${applicationId}.fileprovider)
      把 Download/ 绝对路径转成临时可读 Uri。
  • 失败提示:文件被移动/删除或系统没有可处理应用时,Toast
    「无法打开「文件名」:文件可能已被移动或删除,或没有可处理的应用。」,不崩溃。
  • 清除记录:对话框底部「清除记录」→ 二次确认;只清 App 内历史,不删除任何文件。
  • 重启保留:历史在 App 进程重启后仍存在;清除数据/卸载会清空。

2.2 媒体类型分流(Android 10+,提升「最近」可见性)

Downloader 在 MIME 确定后选择 MediaStore 集合与相对目录:

MIME MediaStore 集合 RELATIVE_PATH
image/* MediaStore.Images.Media.EXTERNAL_CONTENT_URI Pictures/
video/* MediaStore.Video.Media.EXTERNAL_CONTENT_URI Movies/
audio/* MediaStore.Audio.Media.EXTERNAL_CONTENT_URI Music/
其他(文档/归档/apk/未知) MediaStore.Downloads.EXTERNAL_CONTENT_URI Download/

复用与适配:

  • 文件名解析、清洗、扩展名补全、MIME 推断与文件头嗅探逻辑不变;
  • 重名查重改为目标集合:Android 10+ 查询本次 MIME 对应的集合与 RELATIVE_PATH +
    DISPLAY_NAME,不再只看 Downloads/Download/;同名递增仍为 (1)、(2);
  • DATE_ADDED/DATE_MODIFIED(秒)/SIZE/IS_PENDING 写入逻辑复用;
  • notifyChange 同时通知单条记录与实际写入的集合(Images/Video/Audio/Downloads);
  • onDone 返回的 content Uri 是 MediaStore insert 返回的实际集合条目 Uri,
    供历史记录与打开入口使用;
  • 完成提示按实际目录区分:文档仍是「已保存到系统下载目录:…」,媒体文件改为
    「已保存到系统媒体库(Pictures/Movies/Music):…」,避免把 Pictures/ 说成下载目录;
  • Android 8/9 不变:仍统一写 Download/(不强制分流),完成后调用
    MediaScannerConnection.scanFile 补索引,保证旧系统行为稳定。

2.3 额外索引尝试(可选、低成本)

Android 10+ 在 IS_PENDING 解除后,尝试通过 MediaStore.MediaColumns.DATA(已 deprecated)
查询文件绝对路径;只有查到以 / 开头的非空路径时才调用一次
MediaScannerConnection.scanFile。查不到、抛异常或无回调只写 DSHDownloader 日志:

  • supplemental media scan skipped: no absolute path
  • supplemental media scan returned no uri
  • supplemental media scan failed

不影响下载结果,也不阻塞完成提示(MediaStore 记录 + notifyChange 仍是主路径)。

2.4 FileProvider 决策

工程此前没有配置 FileProvider。本轮选择新增(而不是“API≤28 只显示路径 Toast”),原因:

  • Android 8/9 若直接把历史里的 file:// 路径交给 ACTION_VIEW,会触发
    FileUriExposedException;仅显示路径会让「最近下载」在旧系统上失去打开能力;
  • 工程已依赖 androidx.core,androidx.core.content.FileProvider 无需新增第三方依赖;
  • 暴露面可收紧到最小:res/xml/file_paths.xml 只声明
    <external-path name="downloads" path="Download/" />,authority 为
    ${applicationId}.fileprovider,exported=false,仅通过 FLAG_GRANT_READ_URI_PERMISSION
    临时授权;
  • Android 10+ 仍优先使用 MediaStore content Uri,不经过 FileProvider。

2.5 关于系统「最近」的限制(发布说明明确)

  • 部分 OEM(含 Realme/OPPO 等)的「文件管理 → 最近」只聚合媒体库条目或系统下载记录;
    普通 PDF/ZIP/APK 等文档即使已正确写入 MediaStore.Downloads,也可能不显示。
    这是厂商文件管理器策略,App 无法强制注入。
  • v0.4.5 的应对:
    1. App 内「最近下载」作为可靠兜底;
    2. Android 10+ 图片/视频/音频分流到媒体库(Pictures/、Movies/、Music/),
      系统图库/相册「最近」通常可见;
    3. Q+ 收尾尝试额外媒体扫描;
    4. 文档仍在 Download/,可在「文件 → 下载」分类查看。
  • 因此「系统最近看不到文档」不等于下载失败;请用 App 内「最近下载」或「文件 → 下载」。

2.6 其他

  • 版本号升至 0.4.5(versionCode 12);AndroidManifest.xml 顶部说明、README、
    docs/installation.md 与发布元数据同步。
  • 新增字符串:menu_download_history、download_history_item、download_history_empty、
    download_history_clear、download_history_clear_title、download_history_clear_message、
    download_history_cleared、download_history_open_failed、download_saved_media;
    下载打开失败的既有文案改为“文件已保存在系统相册或下载目录”,以适配媒体分流;无删除既有文案。
  • 未新增权限、未新增第三方依赖;TLS fail-open、Cookie 注入、Android 8/9
    WRITE_EXTERNAL_STORAGE 申请、扫码/相册配对、静默登录、返回手势菜单均未改动。

3. 兼容性与行为矩阵

场景 行为
v0.4.4 覆盖升级 保留服务器地址、pin、deviceToken、WebView cookie、配对 profile;无需重新配对
Android 10+ 图片/视频/音频 分别落 Pictures/、Movies/、Music/,写入对应媒体集合;图库/相册「最近」通常可见
Android 10+ 文档/归档/apk/未知 仍在 Download/,可在「文件 → 下载」查看
Android 10+ 打开历史文件 MediaStore content Uri + ACTION_VIEW + 临时读权限
Android 8/9 下载 仍需 WRITE_EXTERNAL_STORAGE;统一写 Download/,完成后媒体扫描
Android 8/9 打开历史文件 FileProvider 临时 content Uri(仅 Download/);文件被移动/删除时 Toast 说明
无处理应用 Toast「无法打开…」,不崩溃
历史条数 最多 50 条,最新在前;App 重启后保留,清除记录只清历史不删文件
媒体文件重名 在对应集合/目录内递增 (1)、(2),不覆盖
服务端 MIME 错误 按服务端/嗅探到的 MIME 分流;无法识别时按文档处理,记录 Content-Type 便于排查

4. 构建与验证结果

  • 构建环境:JDK 21(Temurin)、Gradle Wrapper 8.10.2、Android SDK Platform 35;
    source toolchain/env.sh 后执行 ./gradlew assembleRelease lintRelease --offline。
  • 结果:BUILD SUCCESSFUL;assembleRelease 产出已签名 APK。
  • lintRelease:0 errors, 16 warnings。warning 类型为既有
    RequiresFeature、UnusedAttribute、LockedOrientationActivity、DiscouragedApi、
    SetJavaScriptEnabled、WebViewClientOnReceivedSslError、TrustAllX509TrustManager、
    CustomX509TrustManager、InsecureBaseConfiguration、DataExtractionRules、
    MonochromeLauncherIcon、ObsoleteSdkInt、UnusedResources 等;无新增文件相关 warning。
  • APK 元数据(aapt2 dump badging):
    • package: name='ai.deepseek.dsh.mobile' versionCode='12' versionName='0.4.5'
    • minSdkVersion:'26',targetSdkVersion:'35'
    • native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'
    • Provider:androidx.core.content.FileProvider,authority
      ai.deepseek.dsh.mobile.fileprovider,exported=false,grantUriPermissions=true
  • 签名(apksigner verify --print-certs):签名有效,证书 SHA-256
    45dee26ac9aa785dda281970bc07072665624ef381616d310331f849216f62c8。
  • 真机:本轮构建机 adb devices 无设备连接,未执行安装/交互测试;
    Android 10+ 媒体分流、Realme「最近」、Android 8/9 FileProvider 打开均未在真机验证,
    待按 mobile-apk/docs/manual-validation.md §14 清单执行。

5. 未验证项与风险

  1. 真机全链路未验证:无设备可用;MediaStore 各集合在具体 OEM 上的插入/索引行为
    (尤其 Realme/OPPO)需按 §14 实测。
  2. 媒体分流对「最近」的改善不确定:媒体库路径通常提升图库可见性,但厂商「文件 → 最近」
    是否展示文档由 ROM 决定,App 不保证;App 内「最近下载」是确定可用的兜底。
  3. Q+ DATA 查询:MediaStore.MediaColumns.DATA 已 deprecated,部分 ROM 返回空或抛异常;
    代码已捕获并跳过,只影响“额外扫描”这一步,不影响下载/索引主路径。
  4. FileProvider 仅 Download/:Android 8/9 的历史文件必须仍在公共 Download/;
    若用户移动/改名,打开会提示失败(符合预期)。Android 10+ 媒体文件使用 MediaStore Uri,
    不受此路径限制。
  5. 历史只记录 v0.4.5 之后的下载:升级前已下载的文件不会出现在「最近下载」列表中,
    需要时重新下载或到系统目录查看。
  6. 50 条上限:仅保留最近 50 条,更早记录会被淘汰;不提供导出/搜索。
  7. 未提交/未发布:按任务约束未执行 git commit/git tag/GitHub Release;
    版本说明、APK 与校验文件是待发布产物,提交与发布前请复核。
  8. 依赖缓存:构建使用 --offline(本机 Gradle 缓存完整);干净环境首次构建需联网下载依赖。

6. 产物清单

文件 说明
DSH-Mobile-v0.4.5-release.apk 已签名 release APK(根 deliverables/ 与 deliverables/release/ 各一份)
release-notes-v0.4.5.md 本说明(根 deliverables/ 与 release/ 各一份)
SHA256SUMS-v0.4.5.txt APK SHA-256 与版本元数据(根 deliverables/ 与 release/ 各一份,均可就地 sha256sum -c)

deliverables/index.html 已更新为 v0.4.5(保留 v0.4.0–v0.4.4 历史下载链接),
deliverables/SHA256SUMS.txt 已追加 v0.4.5 校验行。

DSH Mobile v0.4.4

Choose a tag to compare

@chjy090 chjy090 released this 27 Oct 15:13

DSH Mobile v0.4.4 发布说明

发布对象:Android APK(DeepSeek Harness dsh Web GUI 的远程 WebView 客户端)
源码仓库:DSH-Mobile
源码版本:0.4.4 / versionCode 11(源码已提交并打 tag v0.4.4)
发布包准备日期:2026-10-27

1. 版本与校验

项目 内容
版本名 / versionCode 0.4.4 / 11
applicationId ai.deepseek.dsh.mobile
最低系统 Android 8.0(API 26);targetSdk 35
发布包 DSH-Mobile-v0.4.4-release.apk
发布包大小 25,520,407 字节(24.34 MiB / 25.52 MB)
发布包 SHA-256 860f90638d1d8e66094452c703eb4d74b17f28c38e1bc9c123a6ab7a383a8126
签名证书 CN=DSH Mobile, OU=Personal, O=Personal, L=NA, ST=NA, C=CN;证书 SHA-256 45dee26ac9aa785dda281970bc07072665624ef381616d310331f849216f62c8(与 v0.4.0–v0.4.3 相同,可覆盖升级)
Debug 包 本轮未构建、未随发布包分发(发布仅提供 release)

校验方法(在本目录执行):

sha256sum -c SHA256SUMS-v0.4.4.txt

2. v0.4.4 变更内容

2.1 下载文件保留原文件名(核心)

  • 文件名解析优先级(Downloader.kt):
    1. Content-Disposition 的 filename*=UTF-8''...(RFC 5987,percent-decode,支持 charset 声明);
    2. Content-Disposition 的 filename="..."(percent-decode、引号内 ;、反斜杠转义、常见乱码修复);
    3. URL 最后一段(去掉 query/fragment 后按 UTF-8 percent-decode);
    4. 兜底 dsh-download(能推断 MIME 扩展名时补上,如 dsh-download.pdf)。
  • 中文与空格原样保留:说明.pdf 保存为 说明.pdf;不再追加 -yyyyMMdd-HHmmss 时间戳,
    旧的时间戳函数与相关常量已删除。
  • 清洗规则:去掉路径分隔符(/、\)与 : * ? " < > |、控制字符、首尾空格与尾部点;
    防路径穿越(取最后一段)、拒绝空名/./..;以 . 开头的名字加 _ 前缀避免变成隐藏文件;
    文件名过长(UTF-8 > 180 字节)时截断基础名并保留扩展名。
  • 原名没有扩展名时按已推断 MIME 用 MimeTypeMap.getExtensionFromMimeType 补(含常见类型兜底表)。
  • 重名规则:说明.pdf → 说明 (1).pdf → 说明 (2).pdf …(按最后一个点拆分,扩展名保持):
    • Android 10+:插入前查询 MediaStore.Downloads(RELATIVE_PATH = Download/ + DISPLAY_NAME);
      RELATIVE_PATH 不带斜杠的旧数据也兼容查询;
    • Android 8/9:目标 File 已存在则按同规则递增;
    • 同进程并发下载用 synchronized + 占位集合串行分配,避免两个线程算中同一个名字。
  • onDone 回传最终保存名:Android 10+ 以 MediaStore 实际 DISPLAY_NAME 为准(系统并发改名也能对上),
    提示与磁盘/索引一致。

2.2 MIME 推断更准确

  • 顺序:响应 Content-Type → 下载监听给的 type → 扩展名映射 → 文件头嗅探 → application/octet-stream;
    前两步中 application/octet-stream 视为“未知”,继续往下推断。
  • 文件头嗅探只读前 512 字节(不缓冲整个文件,读到的字节随后原样写入目标文件):
    PDF(%PDF-)、ZIP 系(PK\x03\x04,含 docx/xlsx/pptx/apk)、PNG、JPEG、GIF、WebP、MP4、
    MP3(ID3/帧同步)、可严格解码的 UTF-8 文本。
  • 仍无法判断时允许 application/octet-stream;文件名扩展名优先保留。

2.3 MediaStore / 索引可见性(Android 10+)

目标是让下载完成后在系统「文件 → 最近 / 下载」中可见:

  • insertPendingDownload 补写 DATE_ADDED、DATE_MODIFIED、SIZE=0,保留 IS_PENDING=1、
    RELATIVE_PATH=Download/;若个别 OEM 拒绝这些列,自动退回最小字段重试,保证下载仍可进行。
  • 时间单位说明:MediaStore 的 DATE_ADDED / DATE_MODIFIED 官方定义是秒(epoch seconds),
    不是毫秒;写毫秒会把时间显示成数万年后的错误值,因此这里按秒写入(currentTimeMillis()/1000)。
  • finalizePendingDownload:IS_PENDING=0、DATE_MODIFIED=now、实际 SIZE(按已写入字节数),
    仅在 DATE_ADDED 缺失时补写;个别 OEM 拒绝整组更新时自动退回“只解除 pending”,保证文件可见。
  • 完成后同时 notifyChange(具体 uri) 与 notifyChange(MediaStore.Downloads.EXTERNAL_CONTENT_URI)。
  • API 29+ 不再重复调用 MediaScannerConnection.scanFile:scoped storage 下公共路径扫描既不可靠也冗余,
    MediaStore 记录 + notifyChange 才是「文件」应用的数据源。
  • API 26–28 落盘后仍调用 MediaScannerConnection.scanFile,并在回调里确认是否拿到 Uri;
    失败只记日志,不影响下载结果(文件已在 Download/)。

2.4 下载完成提示与「打开」入口(可选实现)

  • 文案保持不变:已保存到系统下载目录:<最终文件名>。
  • Android 10+ 且 Activity 在前台时,用轻量 AlertDialog 展示同一文案并带「打开」/「完成」:
    • 「打开」使用 ACTION_VIEW + MediaStore content Uri + FLAG_GRANT_READ_URI_PERMISSION,
      不申请额外权限;
    • 没有可处理的应用/打开失败时,仅 Toast「未找到可打开该文件的应用;文件已保存在系统下载目录:<文件名>」。
  • 后台、弹窗失败或 API ≤28(无 content Uri)时退回原 Toast 行为,不崩溃。

2.5 其他

  • 版本号升至 0.4.4(versionCode 11);AndroidManifest.xml 顶部说明与「关于」版本兜底字符串同步更新。
  • 新增字符串:download_open、download_done、download_open_failed;未删除既有下载文案。
  • 未新增权限、未新增第三方依赖;未改变 TLS fail-open、Cookie 注入、下载权限申请逻辑
    (API ≤28 仍需要 WRITE_EXTERNAL_STORAGE,Android 10+ 仍不需要存储权限)。

3. 兼容性说明

场景 行为
v0.4.3 覆盖升级 保留服务器地址、pin、deviceToken、WebView cookie、配对 profile;无需重新配对
Android 10+ 下载 文件保存到公共 Download/,保留原名;重名自动 (1)、(2)
Android 8/9 下载 仍需 WRITE_EXTERNAL_STORAGE;直接写公共 Download/,File.exists 查重,完成后媒体扫描
中文/空格文件名 原样保留;URL/Content-Disposition 中的 UTF-8 percent 编码会正确解码
无扩展名链接 按 Content-Type/MIME/文件头推断扩展名(如 .pdf)
服务端未给 Content-Disposition 用 URL 最后一段;仍失败则 dsh-download[.ext]
blob:/data: 链接 与旧版一致,提示「该链接暂不支持下载」
认证网关 / 证书 pin / 静默登录 / 前台续期 与 v0.4.3 一致,未改动
直连模式隐藏/解锁、相册二维码 与 v0.4.3 一致,未改动

4. 实现说明与取舍

  • 为什么不再用 URLUtil.guessFileName:它无法保证 RFC 5987 的 filename* 优先级,也容易被
    application/octet-stream 带偏;本版改为显式解析,并对老服务器常见的 Latin-1/CP1252 乱码做
    可逆修复(如 说明.pdf → 说明.pdf,正常中文/带重音的西文名不受影响)。
  • 为什么先读 512 字节:MediaStore 的 MIME_TYPE 在 insert 时就要确定;先读文件头再 insert 才能
    让索引里的类型正确,同时避免把整个文件缓存在内存。已读字节写回目标文件,不会丢内容。
  • 时间字段单位:见 §2.3;这是对“毫秒”需求的一处有意修正,避免 1970/数万年错误时间。
  • 索引可见性边界:不同厂商文件管理器「最近」的判定不同(有的读 MediaStore DATE_MODIFIED,
    有的读 DATE_ADDED,有的读文件系统 mtime)。本版三处都尽量补齐;仍无法保证所有 ROM 的
    「最近」列表一定展示,需真机验证。

5. 验证记录(构建机,2026-10-27)

项目 结果
source toolchain/env.sh && ./gradlew assembleRelease BUILD SUCCESSFUL
./gradlew lintRelease BUILD SUCCESSFUL;0 error / 16 warning(与 v0.4.3 相同集合,均为既有 TLS/WebView/Manifest 告警)
APK badging versionCode='11' versionName='0.4.4',minSdk 26 / targetSdk 35
签名 apksigner verify --print-certs 通过,证书 SHA-256 与 v0.4.3 一致(可覆盖升级)
zipalign zipalign -c -v 4 通过
文件名/MIME 离线用例 通过(RFC 5987、filename= percent、CP1252 乱码修复、URL 解码、路径穿越/隐藏文件/非法字符、扩展名拆分、超长截断、PDF/ZIP/PNG/JPEG/GIF/WebP/MP4/MP3/文本嗅探等 30+ 项,JVM 纯函数测试,未在真机跑)
真机安装/下载验证 未执行:adb devices 无设备(RMX5200 未连接);未启动模拟器

真机验证清单见仓库 mobile-apk/docs/manual-validation.md §13(v0.4.4 中文名 PDF / 普通文件 /
重名 (1) / 文件管理器「下载/最近」位置 / 打开入口 / API 26–28 回归),需在真机上逐项勾选后再对外发布。

6. 交付物

文件 说明
deliverables/DSH-Mobile-v0.4.4-release.apk 发布 APK
deliverables/release/DSH-Mobile-v0.4.4-release.apk 发布 APK(release 目录副本)
deliverables/release/SHA256SUMS-v0.4.4.txt 本版本校验文件(sha256sum -c 通过)
deliverables/release/release-notes-v0.4.4.md 本文件
deliverables/SHA256SUMS.txt 全量校验清单(已追加 v0.4.4 行)
deliverables/index.html 下载页(当前版本 v0.4.4,保留旧版本链接)

7. 未验证项与风险

  1. 真机未验证(无 adb 设备):MediaStore 索引、「最近」可见性、重名递增、打开 Intent、
    Android 8/9 媒体扫描均需真机确认。
  2. 部分 ROM 的「文件 → 最近」可能只展示最近修改的多媒体类型;下载类文件是否进入「最近」由厂商
    文件管理器决定,本版只能保证 MediaStore 记录与时间/大小字段正确。
  3. Android 8/9 若用户拒绝存储权限,仍按旧逻辑下载失败并提示,不会写入半成品。
  4. 极少数 OEM 若拒绝 SIZE/DATE_* 更新,会退回“只解除 pending”,文件可见但大小/时间可能不准。
  5. 从并发外部应用抢注同名文件时,最终以 MediaStore 自动改名为准,提示名与实际名仍一致。
  6. 离线文件名用例只覆盖纯解析函数,未覆盖 MediaStore/文件系统交互。

DSH Mobile v0.4.3

Choose a tag to compare

@chjy090 chjy090 released this 27 Oct 14:09

DSH Mobile v0.4.3 发布说明

发布对象:Android APK(DeepSeek Harness dsh Web GUI 的远程 WebView 客户端)
源码仓库:DSH-Mobile
源码版本:0.4.3 / versionCode 10(源码已提交并打 tag v0.4.3)
发布包准备日期:2026-10-27

1. 版本与校验

项目 内容
版本名 / versionCode 0.4.3 / 10
applicationId ai.deepseek.dsh.mobile
最低系统 Android 8.0(API 26);targetSdk 35
发布包 DSH-Mobile-v0.4.3-release.apk
发布包大小 25,519,147 字节(24.34 MiB / 25.52 MB)
发布包 SHA-256 35cfdfbae6c97b1bbb1821f36b55b459246c784fffafa23da84153091ddf0304
签名证书 CN=DSH Mobile, OU=Personal, O=Personal, L=NA, ST=NA, C=CN;证书 SHA-256 45dee26ac9aa785dda281970bc07072665624ef381616d310331f849216f62c8(与 v0.4.0/v0.4.1/v0.4.2 相同,可覆盖升级)
Debug 包 本轮未构建、未随发布包分发(发布仅提供 release)

校验方法(在本目录执行):

sha256sum -c SHA256SUMS-v0.4.3.txt

2. v0.4.3 变更内容

2.1 配对页新增「从相册选择二维码」

  • 配对页在「扫码配对」下方新增独立按钮「从相册选择二维码」;点击后打开系统照片选择器,
    选择相册中已有的配对二维码截图/照片即可完成识别,不需要相机、不需要存储权限。
  • 选择器使用 ActivityResultContracts.PickVisualMedia(androidx.activity:activity-ktx:1.9.3,
    未升级依赖):Android 13+ 走系统照片选择器;低版本/无照片选择器时由 androidx 自动回退为
    ACTION_OPEN_DOCUMENT + image 类型,同样不需要存储权限。
  • 解码复用现有 bundled ML Kit barcode-scanning(Barcode.FORMAT_QR_CODE,不依赖 Google Play 服务),
    未新增任何依赖或权限:
    • 读图在后台单线程(dsh-gallery-qr)完成,不阻塞主线程;
    • 先 inJustDecodeBounds 计算 inSampleSize,把最长边压到 3072 px 以内再解码,避免 48MP 原图
      OOM;按 EXIF 方向转正后交给 ML Kit;
    • 任何解码/识别异常只提示失败,不崩溃;20 秒兜底超时解除「进行中」状态,可再次选图;
    • Activity 销毁、超时或被新选图替代时,通过代际 token 丢弃迟到回调,不再弹 Toast/对话框。
  • 严格校验不放宽:图片来自任意相册,但识别出的原始文本仍必须通过 QrPairPayload.parse()
    (仅接受 https://…/__auth/pair#v=1&t=…&fp=…),与扫码完全同一条链路
    → 「确认配对」→ POST /__auth/pair/redeem 兑换;多码时取第一个可解析的。
  • 失败提示(新增文案):「未能从图片中识别配对二维码,请使用原图截图(勿用转发压缩图)重试」;
    用户取消选图时静默返回配对页。

2.2 直连模式默认隐藏(「关于」连点版本号 7 次解锁)

  • 配对页默认只显示:标题、说明、「扫码配对」、「从相册选择二维码」;说明文案改为
    「请让服务端管理页生成配对二维码后扫码」。原「服务器地址 / Token / 连接」不再直接暴露。
  • 隐藏解锁入口:应用菜单 →「关于」→ 版本号 TextView 现在可点击;连续点击 7 次
    (相邻两次间隔 ≤ 2 秒)切换解锁状态,Toast 分别为「直连模式已解锁」/「直连模式已锁定」。
  • 解锁后配对页出现可展开的「直连模式(高级,仅内网直连裸 dsh 或未部署认证网关时使用)」区域:
    点击标题展开/收起,展开后为原 URL / Token / 连接控件(另加一行直连模式说明),连接逻辑继续
    调用 attemptPair()(规范化地址、提取 token、保存直连配对),行为与 v0.4.2 一致。
  • 解锁状态存 SharedPreferences:独立文件 dsh_mobile_ui_prefs,键 direct_mode_unlocked;
    与配对数据分开存放,因此「忘记此设备」/重新配对清除 dsh_mobile_prefs 时不会重置解锁状态;
    只有系统「清除数据」/卸载才恢复默认隐藏。
  • 「关于」对话框改为自定义视图:版本号一行(可点,隐藏入口)+ 原包名 / Android 版本 /
    服务器地址 / 设备有效期信息(可选中复制),内容与 v0.4.2 等价。
  • 扫码配对、证书 pin、认证网关兑换/登录、启动静默登录、前台续期、返回手势菜单与 v0.4.2 一致,
    安全语义未改变;直连模式仍为 fail-open(仅隐藏入口,未删除)。

2.3 其他

  • 版本号升至 0.4.3(versionCode 10);「关于」版本兜底字符串同步更新。
  • 新增字符串:pair_gallery、pair_gallery_failed、pairing_message_scan、direct_mode_title、
    direct_mode_hint、direct_mode_unlocked、direct_mode_locked、about_version_line;
    删除旧 pairing_message(由 pairing_message_scan 取代)。
  • 未新增权限、未新增第三方依赖;相册解码与直连隐藏入口均在 MainActivity 内实现。
  • 诊断日志/Logcat 只记录失败异常类型、识别超时、解锁/锁定状态等,不打印二维码内容、
    ticket、token、deviceToken 或 cookie
    。

3. 兼容性说明

场景 行为
v0.4.2 覆盖升级 保留服务器地址、pin、deviceToken、WebView cookie、配对 profile;无需重新配对
v0.4.3 新配对页 默认只显示扫码/相册;直连模式需在「关于」连点版本号 7 次解锁
v0.4.2 已解锁? v0.4.2 没有该标记,升级后默认隐藏;需要直连的用户按 7 次点击解锁
直连解锁状态 存独立 dsh_mobile_ui_prefs,忘记设备/重新配对不清除;清除 App 数据/卸载后重置
Android 13+ 相册入口使用系统照片选择器
Android 8–12 androidx 回退 ACTION_OPEN_DOCUMENT(image 类型),不需要存储权限
无相机设备 可用相册入口;无照片选择器/文件选择器时退回原有扫码页行为,不崩溃
相册图片不是配对二维码 只 Toast 失败提示,不进入配对/兑换,不放宽 QrPairPayload 校验
认证网关 / 证书 pin / 静默登录 / 前台续期 与 v0.4.2 完全一致
直连模式(解锁后) fail-open TLS、地址 + token 行为与 v0.3.0–v0.4.2 一致

4. 验证记录(构建机,2026-10-27)

项目 结果
source toolchain/env.sh && ./gradlew assembleRelease BUILD SUCCESSFUL(含 lintVitalRelease)
./gradlew lintRelease BUILD SUCCESSFUL;0 error / 16 warning(与 v0.4.2 相同集合,均为既有项;新增代码无告警)
APK badging versionCode='10' versionName='0.4.3',minSdk 26 / targetSdk 35
签名 apksigner verify --print-certs 通过,证书 SHA-256 与 v0.4.2 一致(可覆盖升级)
zipalign zipalign -c -v 4 通过
真机安装/流程验证 未执行:adb devices 无设备(RMX5200 未连接);本轮未启动模拟器

真机验证清单见仓库 mobile-apk/docs/manual-validation.md §12(v0.4.3 相册配对 / 扫码回归 /
7 次点击解锁与锁定 / 直连模式连接内网 dsh),需在真机上逐项勾选后再对外发布。

5. 交付物

文件 说明
deliverables/DSH-Mobile-v0.4.3-release.apk 发布 APK
deliverables/release/DSH-Mobile-v0.4.3-release.apk 发布 APK(release 目录副本)
deliverables/release/SHA256SUMS-v0.4.3.txt 本版本校验文件(sha256sum -c 通过)
deliverables/release/release-notes-v0.4.3.md 本文件
deliverables/SHA256SUMS.txt 全量校验清单(已追加 v0.4.3 行)
deliverables/index.html 下载页(当前版本 v0.4.3,保留旧版本链接)

6. 敏感信息与安全

  • 相册识别到的二维码内容只在内存中解析,不落盘、不写日志;ticket 仍只存在于二维码 fragment。
  • 直连解锁标记不含任何凭据,仅一个布尔值。
  • 本次发布未改变任何认证网关/证书 pin 逻辑,也没有放宽 QrPairPayload 校验。

DSH Mobile v0.4.2

Choose a tag to compare

@chjy090 chjy090 released this 27 Oct 12:50

DSH Mobile v0.4.2 发布说明

发布对象:Android APK(DeepSeek Harness dsh Web GUI 的远程 WebView 客户端)
源码仓库:DSH-Mobile
源码版本:0.4.2 / versionCode 9(按任务要求未 git commit / 未打 tag,由 Lead 验证后处理)
发布包准备日期:2026-10-27

1. 版本与校验

项目 内容
版本名 / versionCode 0.4.2 / 9
applicationId ai.deepseek.dsh.mobile
最低系统 Android 8.0(API 26);targetSdk 35
发布包 DSH-Mobile-v0.4.2-release.apk
发布包大小 25,516,087 字节(24.33 MiB / 25.52 MB)
发布包 SHA-256 1ad91b691e183c03039c5f6e88a3639c9474a68a06b6b96f2620d3e9b61b3495
签名证书 CN=DSH Mobile, OU=Personal, O=Personal, L=NA, ST=NA, C=CN;证书 SHA-256 45dee26ac9aa785dda281970bc07072665624ef381616d310331f849216f62c8(与 v0.4.0/v0.4.1 相同,可覆盖升级)
Debug 包 本轮未构建、未随发布包分发(发布仅提供 release)

校验方法(在本目录执行):

sha256sum -c SHA256SUMS-v0.4.2.txt

2. v0.4.2 变更内容

2.1 启动即静默登录(不闪登录页)

  • App 冷启动进入 WebView 前,如果当前是认证网关模式、证书 pin 有效、已保存
    deviceToken,并且本机曾经成功登录过(authDeviceTrusted;v0.4.1 升级用户用
    authSessionEstablished 兼容),先在后台调用 POST /__auth/renew:
    • 成功:把服务端返回的 Set-Cookie 注入 WebView,随后直接加载 GUI 根地址;
      期间顶部显示「正在使用设备凭据登录…」与加载进度,不会先闪一下登录页。
    • 服务端返回 401(device_required / origin_changed / 设备 token 无效):
      加载登录页,并在顶部红色提示条明确显示「设备配对已失效,需要重新扫码配对」;
      点击提示条会弹出「忘记此设备」确认,确认后回配对页。
    • 网络/TLS 等暂时性失败:加载登录页,顶部提示「自动登录失败:网络或 TLS 连接异常,
      点击重试」;点击提示条或原生网络错误层的「重试」都会重新走静默登录,而不是只刷新
      登录页。
  • 首次扫码配对后仍必须完成网关登录(用户名/密码;TOTP 在服务端生成配对二维码时
    校验):静默登录只在设备凭据已经过一次真实登录验证后启用,避免绕过设备绑定这一第二因素。
  • 与 v0.4.0/v0.4.1 的“登录页出现后再续期”兜底共存:启动静默续期前会置位
    loginPageRenewTried,失败后登录页不会再自动触发一次同样的续期,避免
    “登录页 → renew → 登录页”死循环;成功进入 GUI 后复位。

2.2 前台恢复自动续期

  • onResume 时若 GUI 已登录且距上次成功 renew 超过 1 小时,后台静默 renew:
    • 更新 WebView 的 session Set-Cookie;
    • 记录 lastAuthRenewAt;
    • 若服务端返回可选的 deviceExpiresAt(毫秒时间戳),更新本机设备有效期;
    • 不刷新当前页面(只有续期期间页面恰好 401,才补一次 reload)。
  • 节流/去重:所有 renew(启动、401、前台)共享同一个 in-flight 标记;失败后 1 分钟内
    不会因前后台切换重复请求;Activity 销毁/退出登录/忘记设备时取消 Future 并作废回调代际。
  • 失败处理:401/device_required/origin_changed 不立刻打断当前页面,标记后下一次前台
    恢复或下一次导航
    时回登录页;网络/TLS 暂时失败只记日志,保留页面,下次前台再试。

2.3 菜单拆分:退出当前会话 / 忘记此设备

认证网关模式的应用菜单由原来的「登出」拆为两项(同时去掉与“忘记此设备”重复的旧
「重新配对」项):

  1. 退出当前会话
    • 仍执行 v0.4.0/v0.4.1 的登出流程:WebView POST /__auth/logout,清 WebView 的
      session cookie 后回登录页;
    • 保留 deviceToken 与 dsh_gate_device 设备绑定 cookie;
    • 下次冷启动 App 会静默 renew 自动登录(当前进程内不会立刻把会话建回来)。
  2. 忘记此设备(重新配对)
    • 二次确认后删除本机该 host 的 deviceToken(DeviceTokenStore.clear);
    • 清除该 host 的全部 WebView cookie(keepDeviceCookie = false,含设备绑定);
    • 清空 session/profile(服务器地址、pin、信任标记、设备有效期等);
    • 回到配对页,下次必须重新扫码。

直连模式(未部署认证网关)菜单保持 v0.4.1 的「刷新 / 重新配对 / 诊断日志 / 关于 /
退出应用」,返回手势状态机与 v0.4.1 完全一致。

2.4 设备有效期展示

  • AuthGateClient.renewDetailed 解析服务端可选字段 deviceExpiresAt(毫秒)。
  • 扫码配对时把 /__auth/pair/redeem 响应里的 expiresAt(网关侧设备有效期)作为初始
    设备有效期保存,因此新配对设备即使服务端暂未返回 deviceExpiresAt 也能展示。
  • 应用菜单「关于」新增一行「设备有效期至:yyyy-MM-dd HH:mm」;未知/旧数据/旧服务端
    显示「—」,不伪造数据。
  • 有效期只存 SharedPreferences 毫秒值,不敏感;不含 token/cookie。

2.5 其他

  • 版本号升至 0.4.2(versionCode 9);「关于」版本兜底字符串同步更新。
  • 新增字符串:退出当前会话、忘记此设备、启动静默登录、设备失效提示、网络重试提示、
    设备有效期等;删除旧的 menu_logout。
  • 诊断日志/Logcat 只记录 HTTP status、error code、host、hasDeviceExpiresAt 等,
    不打印 deviceToken、ticket、cookie 或 token 明文。

3. 兼容性说明

场景 行为
v0.4.1 覆盖升级 保留服务器地址、pin、deviceToken、WebView cookie;首次启动直接尝试静默登录
旧服务端不返回 deviceExpiresAt 静默登录/前台续期正常;「关于」显示“—”或扫码时的设备有效期,不报错
服务端未返回 redeem expiresAt 设备有效期显示“—”,其余功能不受影响
首次扫码配对 不静默登录,仍走网关登录页(用户名/密码)
网关设备被吊销 / origin 变更 启动/前台续期得到 401,提示需要重新扫码配对
直连模式(地址 + token) 行为与 v0.4.1 一致,无网关静默登录/菜单拆分
当前已部署服务端 /__auth/renew 响应 截至本轮构建只返回 expiresAt(session 到期)和 deviceId,本版不把它误当设备有效期;设备有效期来自配对响应。服务端将来返回 deviceExpiresAt 时会自动采用

4. 验证记录(构建机,2026-10-27)

项目 结果
source toolchain/env.sh && ./gradlew assembleRelease BUILD SUCCESSFUL(含 lintVitalRelease)
./gradlew lintRelease BUILD SUCCESSFUL;0 error / 16 warning(均为 v0.4.1 已有项,新增代码无告警)
APK badging versionCode='9' versionName='0.4.2',minSdk 26 / targetSdk 35
Manifest 检查 enableOnBackInvokedCallback=false 仍写入 APK(返回手势行为不变)
签名 apksigner verify --print-certs 通过,证书 SHA-256 与 v0.4.1 一致(可覆盖升级)
zipalign zipalign -c -v 4 通过
真机安装/流程验证 未执行:adb devices 无设备(RMX5200 未连接);emulator -accel-check 报 Could not open /dev/kvm : Permission denied,且无可用 AVD,未做软件模拟

真机验证清单见仓库 mobile-apk/docs/manual-validation.md §11(v0.4.2 静默登录/续期/菜单
拆分验证清单);请在真机上按该清单逐项勾选后再对外分发。

5. 升级与安装

  1. 可直接覆盖安装 v0.4.0/v0.4.1(同一 release 签名),配对信息、deviceToken、WebView
    cookie 均保留:
    adb install -r DSH-Mobile-v0.4.2-release.apk
  2. 若从 debug 包或 v0.3.0 及更早版本升级,需先卸载(签名不同):
    adb uninstall ai.deepseek.dsh.mobile
    adb install DSH-Mobile-v0.4.2-release.apk
  3. 升级后首次冷启动:已登录过的认证网关设备会先显示「正在使用设备凭据登录…」,
    成功后直接进入 GUI;首次配对设备仍进入登录页。
  4. 菜单入口仍是系统返回手势/返回键 → 应用菜单(无悬浮 ⋮ 按钮)。

6. 已知限制

  • 本轮未做真机回归:启动静默登录、前台 1 小时续期、菜单拆分、设备有效期展示
    仅通过编译、Android Lint 与代码审查验证,需按 manual-validation.md §11 真机确认。
  • 当前服务端未返回 deviceExpiresAt:前台续期无法延长/刷新设备有效期;「关于」中
    新配对设备显示配对响应里的有效期,v0.4.1 升级设备在重新配对前显示“—”。
  • 设备有效期不会自动续期:服务端设备 TTL 到期后仍会 401,需要重新扫码配对
    (v0.4.2 不会静默绕过设备有效期)。
  • Activity 销毁时的网络取消是尽力而为:Future.cancel(true) 不一定能中断
    HttpsURLConnection,但连接有 15 秒超时,且迟到回调会被代际标记丢弃。
  • 退出当前会话后立即从最近任务返回:当前进程可能仍停在登录页;只有冷启动/重新
    创建 Activity 才会静默自动登录,这是为了不让“退出”在用户眼前立刻失效。
  • 其它限制同 v0.4.1:入口依赖系统返回手势、认证网关换证书需重新扫码、直连模式仍是
    fail-open、无后台推送、仅适配 Android 8.0+。

7. 安全提示

  • 访问令牌与设备令牌等同于登录凭据:请勿外传、截图或提交到公开渠道;
    设备令牌经 Android Keystore 加密保存。
  • 静默登录只使用已保存的设备凭据;首次配对仍需在网关登录页输入用户名/密码(TOTP 在生成配对二维码时校验)。
  • 出现 TLS 警告条时,务必点开核对证书指纹与主机名;认证网关模式仍是严格证书 pin。
  • 只从可信来源下载 APK,并核对本文档给出的 SHA-256。
  • 服务器自签证书私钥(dsh.key.pem)应妥善保管,不要随 APK 或发布资产分发。

DSH Mobile v0.4.1

Choose a tag to compare

@chjy090 chjy090 released this 27 Oct 11:03

DSH Mobile v0.4.1 发布说明

发布对象:Android APK(DeepSeek Harness dsh Web GUI 的远程 WebView 客户端)
源码仓库:DSH-Mobile
源码版本:0.4.1 / versionCode 8(源码 commit d7d0277,tag v0.4.1)
发布包准备日期:2026-10-27

1. 版本与校验

项目 内容
版本名 / versionCode 0.4.1 / 8
applicationId ai.deepseek.dsh.mobile
最低系统 Android 8.0(API 26);targetSdk 35
发布包 DSH-Mobile-v0.4.1-release.apk
发布包大小 25,494,303 字节(24.31 MiB / 25.49 MB)
发布包 SHA-256 a9ad7979c1487e31b0c55ba374c39aed6e3f5b5990c3421ebf38748838848e80
签名证书 CN=DSH Mobile, OU=Personal, O=Personal, L=NA, ST=NA, C=CN;证书 SHA-256 45dee26ac9aa785dda281970bc07072665624ef381616d310331f849216f62c8
Debug 包 本轮未构建、未随发布包分发(发布仅提供 release)

校验方法(在本目录执行,SHA256SUMS-v0.4.1.txt 同时给出源码版本对照信息):

sha256sum -c SHA256SUMS-v0.4.1.txt

2. v0.4.1 变更内容

2.1 移除右上角悬浮 ⋮ 按钮

  • v0.4.0 及更早版本在 WebView 右上角固定一个 48dp 的 ⋮ 悬浮按钮,会压住 dsh Web GUI
    右上角的「打开右侧边栏」按钮
    ,导致该按钮无法点击。
  • v0.4.1 起彻底移除该悬浮按钮(不再 addView,相关构建代码/尺寸常量一并删除),
    dsh GUI 顶栏右上角恢复完整可点区域。
  • 顶部 TLS / 明文 HTTP 警告条原先为避让 ⋮ 按钮而下移 64dp,现改为贴在内容区顶部
    (topMargin 归零),不再与任何悬浮控件耦合。

2.2 返回手势打开「应用菜单」

  • 菜单入口改为系统返回手势(真我等手势导航机型从屏幕左/右边缘向内滑)或
    三键导航的返回键:在根页面按返回即弹出「应用菜单」。
  • 菜单由 AlertDialog.Builder().setItems(...) 列表实现,未引入任何新依赖,
    包含原有全部菜单项并新增「退出应用」:
    • 刷新
    • 重新配对
    • 退出登录(仅认证网关模式显示,行为与 v0.4.0 一致)
    • 诊断日志
    • 关于
    • 退出应用(finishAffinity(),仅结束任务栈,不卸载、不清数据)
  • 返回事件状态机(优先级从高到低):
    1. 错误覆盖层可见 → 关闭错误层(若渲染进程已死会重建 WebView,避免停在空白页);
    2. 输入法可见 → 收起输入法;
    3. WebView.canGoBack() → 网页后退;
    4. 应用菜单正在显示 → 关闭菜单;
    5. 否则 → 弹出「应用菜单」。
  • dsh 是单页应用(SPA),在根页面没有 WebView 历史,因此每次在根页面按返回都会弹菜单,
    这是 v0.4.1 的预期行为(不再有「再按一次返回键退出」的旧逻辑,退出请用菜单里的「退出应用」)。
  • 输入法可见性用 ViewCompat.setOnApplyWindowInsetsListener + WindowInsetsCompat
    维护:Android 11+ 直接读 ime() 的可见性,Android 10 及以下由 androidx 兼容层折算,
    不依赖 InputMethodManager 的猜测。
  • 首次进入主界面(已配对)时 Toast 提示一次
    「从屏幕边缘向内滑动(返回手势)可打开应用菜单」,用 SharedPreferences 键
    back_gesture_menu_hint_v1 记录,每个安装只提示一次。

2.3 预测性返回显式关闭

  • AndroidManifest.xml 设置 android:enableOnBackInvokedCallback="false":
    Android 13+ 不做预测性返回动画,返回手势/返回键统一交付
    Activity.onBackPressed → OnBackPressedDispatcher,避免不同 OEM/系统版本上的
    预测性返回差异,保证状态机行为一致。

2.4 其他

  • 版本号升至 0.4.1(versionCode 8);「关于」对话框的版本兜底字符串同步更新。
  • 移除不再使用的字符串资源:menu_open(⋮ 按钮无障碍描述)、exit_confirm
    (双击返回退出提示)。

3. 验证记录(构建机,2026-10-27)

项目 结果
./gradlew assembleRelease BUILD SUCCESSFUL(含 lintVitalRelease)
./gradlew lintRelease BUILD SUCCESSFUL;0 error / 16 warning(均为 v0.4.0 已有项,新增代码无告警)
APK badging versionCode='8' versionName='0.4.1',minSdk 26 / targetSdk 35
Manifest 检查 enableOnBackInvokedCallback=false 已写入 APK
签名 apksigner verify --print-certs 通过,证书 SHA-256 与 v0.4.0 一致(可覆盖升级)
真机安装/手势验证 未执行:adb devices 无设备(RMX5200 未连接);模拟器 /dev/kvm 被容器拒绝(Permission denied),-accel off 软件模拟未能在本轮完成开机/验证

真机验证清单见仓库 docs/manual-validation.md §10(v0.4.1 手势菜单验证清单);
请在真机上按该清单逐项勾选后再对外分发。

4. 升级与安装

  1. 可直接覆盖安装 v0.4.0(同一 release 签名),配对信息、token、WebView cookie 均保留:
    adb install -r DSH-Mobile-v0.4.1-release.apk
  2. 若从 debug 包或 v0.3.0 及更早版本升级,需先卸载(签名不同):
    adb uninstall ai.deepseek.dsh.mobile
    adb install DSH-Mobile-v0.4.1-release.apk
  3. 首次打开后的 1~2 秒会出现一次返回手势提示 Toast;之后不再出现。
  4. 菜单入口变化:不再有右上角 ⋮ 按钮,从屏幕边缘向内滑(或按三键返回键)打开。

5. 已知限制

  • 入口依赖系统返回手势/返回键:vivo/OPPO 等机型的「全屏手势」需保持系统默认的
    左右边缘内滑返回;若用户在系统设置里关闭了返回手势或改用手势 App,菜单可能难以发现
    (首次 Toast 与本文档用于引导)。
  • 认证网关模式换证书仍需重新扫码:证书 pin 是唯一信任锚(与 v0.4.0 相同)。
  • 直连模式仍是 fail-open:证书问题只提示不阻断,不可信网络建议改用认证网关模式。
  • 完全依赖服务端:App 不内置前端资源,服务端不可达时界面不可用。
  • 无后台推送/常驻:App 退到后台后连接可能被系统回收,需重新打开。
  • 本轮未做真机回归:手势导航/三键导航、键盘优先收起、错误层优先关闭等行为
    已在代码层实现并通过编译与静态检查,但需按 docs/manual-validation.md §10 在真机确认。
  • 仅适配 Android 8.0+:未针对平板/折叠屏做专门布局。

6. 安全提示

  • 访问令牌与设备令牌等同于登录凭据:请勿外传、截图或提交到公开渠道;
    配对信息保存在应用私有存储中(设备令牌经 Android Keystore 加密)。
  • 出现 TLS 警告条时,务必点开核对证书指纹与主机名,确认是预期服务器后再继续。
  • 只从可信来源下载 APK,并核对本文档给出的 SHA-256。
  • 服务器自签证书私钥(dsh.key.pem)应妥善保管,不要随 APK 或发布资产分发。

DSH Mobile v0.4.0

Choose a tag to compare

@chjy090 chjy090 released this 27 Oct 08:45

DSH Mobile v0.4.0 发布说明

发布对象:Android APK(DeepSeek Harness dsh Web GUI 的远程 WebView 客户端)
源码仓库:DSH-Mobile
源码 tag:v0.4.0(annotated tag)→ commit 12ae7f030e7c63fdfae41d3a1a9ce9aebb87b47e
发布包准备日期:2026-10-27

1. 版本与校验

项目 内容
版本名 / versionCode 0.4.0 / 7
applicationId ai.deepseek.dsh.mobile
最低系统 Android 8.0(API 26);targetSdk 35
发布包 DSH-Mobile-v0.4.0-release.apk
发布包大小 25,493,855 字节(24.31 MiB / 25.49 MB)
发布包 SHA-256 5d19e16e9445fc54e94f82ba1e89f356ceed03df71c7dbc726847b16c35552c5
Debug 包 DSH-Mobile-v0.4.0-debug.apk
Debug 包大小 26,521,977 字节(25.29 MiB / 26.52 MB)
Debug 包 SHA-256 849139bb2ee989fc2c3ae81237a1a286f6f593f82b0a060d75a40f8885abe207
签名证书 CN=DSH Mobile, OU=Personal, O=Personal, L=NA, ST=NA, C=CN;证书 SHA-256 45dee26ac9aa785dda281970bc07072665624ef381616d310331f849216f62c8

校验方法(在本目录执行,SHA256SUMS-v0.4.0.txt 同时给出源码 tag 对照信息):

sha256sum -c SHA256SUMS-v0.4.0.txt

2. 功能列表

  • 远程 WebView 客户端:用系统 WebView 加载远端 dsh Web GUI,不内置任何前端静态资源,界面与功能始终跟随所连接的 dsh 服务端版本,客户端无需频繁随前端重新发版。
  • TLS 处理
    • 直连模式:fail-open,证书不受信任时不阻断连接,仅在页面顶部显示黄色警告条;点开可查看证书主体、颁发者、有效期、SHA-256 与错误类型,并复制详情。
    • 认证网关模式:严格证书 pin,只信任配对二维码中携带的证书 SHA-256 指纹,指纹不匹配时阻断连接(fail-closed)。
  • 语音输入 / 录音:WebView 页面 getUserMedia / MediaRecorder 走原生麦克风授权;需要 https:// 安全上下文,首次使用弹出系统麦克风权限。
  • 下载与剪贴板:页面中的附件/导出文件交给 App 下载器保存到系统「下载」目录(API 29+ 经 MediaStore,API 28 及以下写公共 Downloads);WebView Clipboard API 不可用时回退到原生剪贴板桥。
  • 扫码配对 gateway 设备(v0.4.0 新增):CameraX 取景 + ML Kit 二维码识别,扫描认证网关生成的配对二维码,自动完成服务器地址、令牌与证书指纹配置;也可手动粘贴完整配对链接。
  • 设备令牌:设备令牌自动续期与登出,无需反复输入服务端凭据;支持重新配对。
  • fail-open 自签证书:v0.3.0 起 App 不内置任何证书或指纹,自签 HTTPS 服务器可直接连接(仅警告条);认证网关模式则由扫码获得 pin,仅对该 host 严格校验。
  • 诊断日志:App 内「⋮ → 诊断日志」收集 WebView / JS / 下载 / TLS 日志,写入前对 token= 等敏感查询参数脱敏,可一键复制。
  • Android 权限
    • INTERNET:访问远端服务。
    • ACCESS_NETWORK_STATE:网络状态判断。
    • CAMERA(运行时权限):扫码配对;无相机设备仍可安装,改用直连/粘贴链接。
    • VIBRATE:扫码成功震动反馈。
    • RECORD_AUDIO(运行时权限):语音输入。
    • MODIFY_AUDIO_SETTINGS:WebView 选择通信音频设备(缺失会报 NotReadableError)。
    • WRITE_EXTERNAL_STORAGE(maxSdkVersion=28):仅 Android 9 及以下保存下载文件。
  • 细节适配:沉浸式状态栏/导航栏、软键盘避让、深色模式跟随系统、返回键两级退出、401 鉴权失败覆盖层(重试 / 重新配对)、断网与渲染进程崩溃提示。

3. 前置要求

项目 要求
服务器 已部署并可访问 dsh(DeepSeek Harness),手机与服务器网络互通
认证网关 扫码配对与设备令牌需要服务端安装并启用 dsh-auth-gate v0.4+;未安装时仍可使用原有「服务器地址 + 访问令牌」直连模式
手机系统 Android 8.0(API 26)及以上
可选硬件 相机(扫码配对)、麦克风(语音输入)

4. 安装步骤

  1. Sideload 安装
    • 用 ADB:adb install -r DSH-Mobile-v0.4.0-release.apk
    • 或用文件管理器打开 APK,按系统提示允许「安装未知应用」后安装。
    • 安装前可先核对 SHA-256 与 §1 一致。
  2. 扫码配对 gateway 设备
    • 在服务端生成认证网关的配对二维码(由 dsh-auth-gate 的配对流程/管理页面生成)。
    • 打开 App →「扫码配对」→ 扫描该二维码;App 自动写入服务器地址、令牌与证书 SHA-256 指纹,并进入网关登录页。
    • 无法扫码时可粘贴完整配对链接;也可退回直连模式,手动填写服务器地址与访问令牌。
  3. 用户名 + 密码登录
    • 在网关登录页输入用户名与密码;若服务端启用了 TOTP,再输入 6 位动态验证码。
    • 登录成功后进入 dsh Web GUI;设备令牌会自动续期,后续无需重复输入凭据。

5. 已知限制

  • 完全依赖服务端:App 不含前端资源,服务端不可达或版本不兼容时界面无法使用。
  • 直连 fail-open 有 MITM 风险:证书校验失败仍会连接,仅显示警告条;不可信网络下建议改用认证网关模式、受信 CA 证书或 VPN。
  • 认证网关模式换证书需重新扫码:证书指纹是唯一信任锚,网关更换证书或地址后必须重新配对。
  • 语音输入仅 HTTPS:http:// 页面无法调用麦克风(WebView 安全上下文限制)。
  • 仅适配 Android 8.0+:未针对平板/折叠屏做专门布局,横竖屏以 WebView 自适应为主。
  • 无后台推送/常驻:App 退到后台后连接可能被系统回收,需重新打开;WebSocket 断线需重试或重进。
  • 下载能力有限:仅保存到系统下载目录,不提供下载队列管理/断点续传界面;超大文件受系统与网络影响。
  • Debug 包仅供调试:体积更大、可调试,不要分发给最终用户。

6. 安全提示

  • 访问令牌与设备令牌等同于登录凭据:请勿外传、截图或提交到公开渠道;配对信息保存在应用私有存储中。
  • 出现 TLS 警告条时,务必点开核对证书指纹与主机名,确认是预期服务器后再继续。
  • 优先使用认证网关模式(严格 pin);在咖啡厅、酒店等不可信网络中避免使用直连 fail-open,或先连接 VPN。
  • 只从可信来源下载 APK,并核对本文档给出的 SHA-256。
  • 服务器自签证书私钥(dsh.key.pem)应妥善保管,不要随 APK 或发布资产分发。
  • 共享设备或已 root 设备请自行评估风险;退出前可通过「重新配对」清空配对信息,或直接卸载。