From ffa7ed26619101778b892c072669f8ce96e3d85a Mon Sep 17 00:00:00 2001 From: AskaEth Date: Sun, 4 Oct 2026 19:27:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(chat):=20=E4=BF=9D=E5=AD=98=E9=99=84?= =?UTF-8?q?=E4=BB=B6=E6=97=B6=E4=B8=8D=E8=A6=81=E8=A2=AB=20SAF=20=E8=A1=A5?= =?UTF-8?q?=E6=88=90=E5=8F=8C=E6=89=A9=E5=B1=95=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实机复现(2026-10-05):把服务端附件 `809320…e.md` 保存到下载目录,落盘成了 `809320…e.md.txt` —— 因为 `CreateDocument` 传的是服务端**字节嗅探所得的基类型** (`.md` 也会报 `text/plain`),而 SAF 的 Downloads provider 会按该 mime 给文件名**补扩展名**。 改为传 `*/*`:没有可补的扩展名,原扩展名因此保真。 **「用其它应用打开」那条路刻意不改** —— 那里要的正是「能读懂这段内容的类型」,照旧用嗅探所得的 mime。 实机复核:同一枚附件保存后落盘为 `809320…e.md`(95 字节,与源一致),不再出现 `.md.txt`。 --- app/src/main/kotlin/top/yeij/cyrene/ui/chat/ChatScreen.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/top/yeij/cyrene/ui/chat/ChatScreen.kt b/app/src/main/kotlin/top/yeij/cyrene/ui/chat/ChatScreen.kt index 7acc3d2..6c06e69 100644 --- a/app/src/main/kotlin/top/yeij/cyrene/ui/chat/ChatScreen.kt +++ b/app/src/main/kotlin/top/yeij/cyrene/ui/chat/ChatScreen.kt @@ -178,9 +178,13 @@ fun ChatScreen( val previewFileName = previewTarget?.suggestedFileName() ?: FALLBACK_PREVIEW_FILE_NAME // 「保存到下载目录」走系统 SAF:用户自己挑落点,本端不申请任何存储权限。 - // 契约带上的 MIME 用文档自己的类型(服务端嗅探所得,不按扩展名猜)。 + // ⚠️ 这里的 MIME **不能用 `previewType`**:那是**字节嗅探所得的基类型**(`.md` 也报 + // `text/plain`),而 SAF 的 Downloads provider 会按该 mime 给文件名**补扩展名** ⇒ + // 2026-10-05 实机复现:存的 `…e.md` 变成了 `…e.md.txt`。传 `*/*` 就没有可补的扩展名, + // 原扩展名因此保真。(「用其它应用打开」那条路刻意相反:那里要的正是「能读懂这段内容的 + // 类型」,所以照旧用嗅探所得 mime。) val createDocument = rememberLauncherForActivityResult( - remember(previewType) { ActivityResultContracts.CreateDocument(previewType) }, + ActivityResultContracts.CreateDocument("*/*"), ) { destination -> val ready = previewState as? DocumentPreviewState.Ready if (destination == null || ready == null) return@rememberLauncherForActivityResult