Skip to content

Latest commit

 

History

History
718 lines (595 loc) · 51.5 KB

README.zh-CN.md

File metadata and controls

718 lines (595 loc) · 51.5 KB

Twig

English · 简体中文

一个体积优先的双面板 Android 文件管理器:所有来源都在同一棵树里就地展开,而不是各自 一个要「进去」的界面。原生 Kotlin + XML View,不引入 Material 库,依赖压到最低。 能手写就手写——WebDAV、S3 签名、git、restic 全部从零实现。

版本 1.18.1(versionCode 322)· minSdk 24 / targetSdk 34 / compileSdk 36 · GPL-3.0

横屏双面板

一棵树,所有来源 restic 备份,设备上解密 Jellyfin 媒体库当文件系统 占用图
更多 —— 目录对比、WiFi 共享、内置终端
目录对比 WiFi 共享 内置终端
其余截图 —— 浏览、媒体服务器、查看器、共享、终端

浏览

收藏横跨所有来源 目录长按菜单 跨来源复制 打包到对侧面板

来源、外观与快捷方式

文档树按授权的那个应用命名 特权访问:root 或 Shizuku 浅色主题,横屏 桌面上的快捷方式

媒体服务器与音乐

Emby 媒体库的「最新」 音乐播放器与波形 播放列表

查看器与对比

文本查看器,手写着色 Hex 查看器 文本对比,逐块合并箭头 保存的对比,从那行直接同步

共享、终端与 Git

UDP 扫到另一台 Twig 共享反过来挂成 WebDAV Git 状态与终端并排 命令固定到桌面

下载

页首版本对应当前源码,下载请以已发布版本为准。 每个 GitHub Release 都附有签名 APK:

文件 适合
Twig-<版本>-full.apk 拿不准就选它。 arm64-v8a + x86_64,带只读 RAR
Twig-<版本>-libre.apk 想要彻底的自由软件——不含 RAR,因为 junrar 不是自由软件
Twig-<版本>-full-arm64-v8a.apk / -x86_64.apk 只要单一架构、下载更小
Twig-<版本>-full-armeabi-v7a.apk 32 位 ARM 设备——实验性,未在真机上测试
Twig-<版本>-full-nfs-arm64-v8a.apk 64 位 ARM 设备,需要 NFS 支持

全部用同一把密钥签名,可以互相覆盖安装。证书 SHA-256 为 196f633e942bf4f32f875b98c8ff181138256ba329a63364e3a39f154c1a5e62。

不经应用商店自动更新:把 https://github.com/dev2ex/twig 加进 Obtainium。一个 Release 里有多个 APK,需要在 Filter APKs by Regular Expression 里指定一个,否则 Obtainium 每次都会让你手选、也没法后台更新—— 默认版填 -full\.apk$,自由软件版填 -libre\.apk$。

从 F-Droid 安装 Twig:提供 libre 版, 包含除 RAR 外的全部功能,通过可复现构建验证,使用与 GitHub APK 相同的签名密钥。 F-Droid 自动跟踪正式版标签,完成构建与仓库发布流程后提供更新。


为什么是 Twig

一棵树,所有来源。 本地存储、压缩包、FTP、SFTP、SMB、WebDAV、S3、restic 仓库和 Jellyfin/Emby 媒体库都就地展开。两侧可以放不同来源:把 SMB 上的目录压成本地 zip, 或把远程压缩包里的文件直接复制到 FTP 服务器。

体积小,资源友好。 1.18.0 full 构建的 arm64-v8a 单设备下载实测为 8.81 MiB,包含终端、 网络存储后端和媒体播放器。新增依赖前先测量 APK 增量。

这些工具可以一起用。

  • 浏览加密备份。 打开 restic 仓库,按日期浏览快照,将需要的文件复制到其他来源, 解密在设备上完成。
  • 文件旁边就是 Git。 查看本地或 SFTP 仓库的状态、历史、worktree 与子模块, 并排对比变更,也可为正在操作的目录打开终端。
  • 终端与文件管理联动。 从目录打开本地、SSH 或特权 shell,也能将终端当前目录定位回 文件树。独立的 SHIFT 与 | 键方便在手机上操作交互式工具和命令管道,字体、配色与字号 都可自定义。
  • 媒体库也在同一棵树里。 Jellyfin 与 Emby 和其他存储一起浏览,海报、元数据与字幕 直接取自服务器;可继续上次播放,并把播放进度同步回媒体库。
  • 跨来源对比与同步。 SMB 目录对本地存储、文档树对压缩包,查看差异后再执行增量或 镜像同步,目标侧较新的文件单独确认。
  • 共享已连接的来源。 通过 Twig 的 HTTP/WebDAV 服务,电脑可在一个连接中浏览与下载 来自不同存储来源及压缩包内部的文件。
  • 特权访问沿用原来的目录树。 普通权限无法访问时,root 或 Shizuku 可扩展本地访问能力; 受保护路径同样能参与复制、搜索和缩略图等操作。
  • 内置查看器与独立音乐播放器。 在正在浏览的来源中查看文件:文本可保留原编码编辑, 二进制可用十六进制检查,文档与媒体也能直接预览。本地和网络音乐可放进同一播放列表, 配合波形定位、双语同步歌词与后台播放。
  • 网页也能按自己的习惯改。 把自建面板、网页播放器放在文件旁边,添加到桌面, 再用内置调试工具折腾脚本、样式与页面元素;下载也能直接选 Twig 里的存储位置。

体积

1.18.0 正式签名 release APK 实测(R8 + 资源裁剪;1 MiB = 1,048,576 字节):

大小
Full APK(arm64-v8a + x86_64) 10.95 MiB
Libre APK(arm64-v8a + x86_64) 10.88 MiB
Full APK,仅 arm64-v8a 8.81 MiB
Full APK,仅 x86_64 8.93 MiB
Full APK,仅 armeabi-v7a(实验性) 8.56 MiB
Full APK 含 NFS,仅 arm64-v8a 8.98 MiB

构成(full arm64-v8a APK 内条目的压缩后大小):

组件 大小
classes*.dex(自研代码 + 全部 JVM 依赖) 3.31 MiB
libffmpegJNI.so 1.39 MiB
Bouncy Castle 的数据文件 1.18 MiB
资源(resources.arsc + res) 2.04 MiB
libsamba_jni.so(libsmb2) 485 KiB
libtwigzstd / libtermux / libtwigpty 110 KiB

Bouncy Castle 数据中有 1.16 MiB 来自 picnic 后量子签名算法的三张查找表 (lowmcL{1,3,5}.bin.properties)。引入完整 Bouncy Castle 只是因为 Android 自带的 阉割版缺 X25519,所以这 1.16 MiB 是纯粹的死重——见路线图。

每种来源都是独立的 Gradle 模块,所以构建时可以裁掉用不着的部分——去掉媒体播放与 网络来源,上表中的绝大部分就没了。


一句话架构

所有来源——本地、压缩包、FTP、SFTP、SMB、WebDAV、S3、restic、Jellyfin——实现同一套 FileSystem + XFile 接口,CopyEngine 通过 openInput() / openOutput() 在任意两者之间搬字节。

加一种来源 = 新增一个 FileSystem 实现。UI 与拷贝引擎零改动。

由此白捡两件事:任意来源之间可以互拷;裁掉模块就能构建精简版本。


功能

存储来源

来源 能力 实现
本地 读写全功能 java.io.File
私有空间 读写全功能 应用专属存储,通过本地文件系统浏览;不纳入备份,卸载时删除
Android/data/ 授权后读写 系统允许时,通过 SAF 持久授权整个目录或单个应用目录;也支持 root/Shizuku 访问
系统目录(/system、/vendor 等) 浏览可读目录与文件 即使无法列出 /,仍可直接访问可读的系统路径;受保护内容需 root/Shizuku,只读挂载仍保持只读
ZIP 读 + 写,老包 GBK 名自动识别 当文件系统挂载,点开即进入
7z 读 + 打包写入(LZMA2) commons-compress + xz
tar 只读 条目在包里是连续存放的,直接切片读——里面套着的压缩包不必先解出来就能打开
gz / xz / bz2 / zst 只读 不是归档而是单条数据流:挂成只有一个条目的包,于是 foo.tar.gz 打开是 foo.tar,再展开就是 tar,.tgz/.txz/.tbz2/.tzst 同理
zstd 只读 流式解压,复用 restic 已经带进包里的那份 libzstd,不多打一份
RAR 只读(RAR4 + RAR5) junrar,单独一个模块——只有 full 版带,libre 版没有 RAR
加密压缩包 读 zip/7z/rar,创建 AES-256 zip/7z WinZip AES 与老式 ZipCrypto 全手写
FTP 读写全功能 Apache Commons Net
SFTP 读写全功能 SSHJ(带完整 Bouncy Castle 解决 X25519)
SMB / CIFS 读写全功能 SMB2/3 用 libsmb2,经 NDK/JNI;旧设备可单独开启基于 jcifs-ng 的 SMB1
WebDAV 读写全功能 手写 PROPFIND/MKCOL/MOVE,不引 SDK
S3 兼容对象存储 读写全功能 手写 SigV4 + REST——AWS S3、MinIO、R2、OSS、COS、B2
restic 只读,解密 从零实现;仓库格式 v1 与 v2
Jellyfin / Emby 只读虚拟树 手写 REST;一份实现通吃两家,零新增依赖
SAF 文档树 读写 没有 MANAGE_EXTERNAL_STORAGE 时的系统级回退
特权(root / Shizuku) 读写 不是独立来源,而是普通 API 够不着的本地路径的回落
已安装应用 只读虚拟树 PackageManager;分包应用现打成 XAPK,复制出来的包不会漏掉 split_config.*

网络来源展开即连接,支持多服务器(每台一个唯一 scheme),配置持久化。解压就是一次 跨来源复制,压缩也是——所以"把 SMB 上的目录压缩到本地"不需要任何特例代码。 压缩弹窗记住上次选择的 ZIP/7z 格式和快速/标准/仅存储级别,即使取消或重启应用也保留。可在对侧面板选择目标,或保存到源目录。设置密码的 7z 默认加密文件名,也可关闭;ZIP 文件名始终可见。

1.18 新增功能

  • **SQLite 浏览:**数据库就地展开为表与只读数据网格,可跨表搜索、按列排序、筛选值或比较条件、执行只读 SQL、以十六进制查看 BLOB,并将选定列导出为 CSV。来源及大小限制见 SQLite 浏览说明。
  • **路径按钮:**点击地址栏展开祖先路径按钮,点击父目录可显示其直接子项。祖先滚出屏幕后,自动树缩进回收横向空间。显示设置可隐藏工具栏或单面板地址栏。
  • **更多存储入口:**内部存储与可移动存储可隐藏及排序;可用时,额外的共享配置文件存储入口显示在主存储旁。
  • restic 最近位置可恢复快照内目录,必要时解锁仓库;最新快照别名始终指向最新快照。
  • **动态图片:**Android 9+ 的图片查看器支持播放 GIF 与动态 WebP。
  • **播放改进:**一集播放完毕后,「继续观看」保留下一集入口,关闭自动播放时也有效。长按音轨/字幕按钮可静音或切换字幕。网络中断后自动重试;不支持的杜比视界 Profile 7 回退到 HDR10。
  • **网页搜索与网站:**可配置搜索主页及查询模板,选择系统搜索应用,并按完整主机名共用指定书签的网页设置。网页链接可复制、分享或交给默认浏览器打开。
  • **远程 RAR:**full 版可在支持高效随机读取的来源上按需读取压缩包,无需先下载整个文件。
  • **可选 NFS:**专用 arm64 full 安装包支持 NFSv3/v4 浏览、写入与断点续传;常规 full/libre 安装包不包含 NFS。

浏览

  • 一棵完整的树。 顶级节点包括内部存储、根目录、私有空间、可移动存储、回收站、 局域网(SMB)、FTP、SSH、WebDAV、S3、媒体服务器、文档树、收藏、网站和已安装应用。 压缩包是可展开的文件节点。左右面板可分别选择根项目显隐,私有空间默认显示。
  • 永远就地展开,从不"进入"目录。 当前目录就是你最后点的那个,它同时是新建、 复制、移动的目标。
  • 设置 → 显示 → 面板布局可选择跟随屏幕方向(默认)、单面板或双面板。 双面板左右并排,竖屏平板也可使用;单面板在横竖屏都支持左右滑动切换。长按面板切换按钮, 可把当前目录固定到另一侧并切换过去。
  • 多选、跨面板复制/移动(带进度与取消)、新建目录、重命名、递归删除、剪贴板式粘贴, 以及通过系统分享面板分享多个选中文件;路径栏显示勾选数量。 勾选或取消勾选起点后,长按同一目录中另一项的勾选框,即可按显示顺序对整段范围重复该动作, 包含两端,范围外的选择保留;列表与网格均支持。
  • 复制/移动可直接在对侧面板选择目标。可选在已有勾选时点按行切换选中状态; 外部多文件选择器支持点按多选。启用默认隐藏的「副本」操作可编辑并确认副本名,长按直接创建编号副本; 长按「重命名」则按相同规则为原文件编号改名。
  • 传输失败重试可直接从失败弹窗重试未完成项目,或跳过失败项继续。已完成文件保留; 来源大小和修改时间已知时,本地、FTP、SFTP 与 SMB 目标支持断点续传。
  • 远程文件分享支持后台准备、进度与取消,已准备好的文件可复用缓存。可直接读取的 本地文件,以及大小已知且不超过 512 KiB 的远程文件,可直接分享。
  • 回收站用于普通可写的本地共享存储,保存在文件原来的卷上。可逐项或批量恢复、 永久清理,空回收站自动隐藏。网络、仅 SAF 可访问及特权访问的删除仍为永久删除。 删除和清理任务支持后台进度,取消会在当前后端调用结束后停止。 长按删除仅在全部选中项都可回收时跳过确认;永久删除仍须确认。
  • 分享到 Twig可复制传入的文件,也可将分享文本保存为 UTF-8 文件并编辑文件名。 目标选择器支持双面板、排序、搜索和历史记录,遵循浏览界面的全屏与工具栏位置设置。
  • 浏览操作设置支持 OLED 纯黑主题、根项目和操作按钮排序及显隐;收藏与网站可在树中拖动排序。 可将工具栏放到底部,返回键可关闭、确认退出,或先逐级向上再关闭/退出;返回键触发退出时始终确认。 完成振动和前台完成通知分别设置,默认关闭;后台完成及传输失败时保留结果通知。
  • 长按快捷操作可直接复制、移动或压缩,新建文本文件,为原文件编号改名, 打开本目录排序,或刷新选中项缩略图;终端和音乐控件也提供快捷操作。
  • 目录详情可显示直接子文件夹/文件数量,默认只统计本地;空本地目录隐藏展开指示。 符号链接显示角标和目标路径;支持的来源提供 Unix 属性与可选权限行,SFTP 也支持权限行。
  • 文件夹图标区分下载、图片、音乐等常用本地目录;收藏保留目录或已安装应用图标。
  • 批量重命名选中的文件与目录,实时预览原名和新名。支持普通/正则替换、区分大小写, 可选择仅文件名、仅扩展名或两者;编号可放在原名前后,可设置起始编号与补零位数。 也可从开头或结尾的零基字符偏移插入、覆盖文字,或转换大小写(含英文标题格式: 常见短介词、冠词和连词保留小写,标题及副标题首尾除外)。模板支持 {name}(不含扩展名)、 {ext}(含点)、{number}、{date}(修改日期,格式为 yyyyMMdd)。 插入偏移超过名称长度时停在另一端,偏移 0 表示添加前缀或后缀;覆盖从该位置向右替换, 必要时延长名称。执行前检查目标名称重复及已有文件冲突。
  • 收藏与最近位置存的是"如何到达"(连接标签 + 路径),不是会话内的动态 scheme,所以重启后依然有效。
  • 恢复上次位置,逐级展开;恢复期间一碰列表就交还控制权。关闭位置记忆时默认展开收藏。
  • 网格视图,与缩略图开关正交,列数按面板宽度自适应。
  • 目录排序与视图可分别使用全局默认或本目录设置,视图可选列表、媒体网格、全部文件网格。 每项设置都可单独应用到子目录;本目录设置优先,其次是最近的递归祖先设置,再回到全局默认。 目录名称支持正序和倒序。
  • 缩略图支持本地和网络来源的图片与视频,可选在视频缩略图内显示记忆的播放进度条。
  • 目录/文件计数可显示在本地与 SMB 目录下方,默认关闭;日期格式可跟随系统地区, 或选择固定格式。
  • 属性卡片内嵌在文件行下方:EXIF、媒体轨道、应用信息、哈希。点击行左侧区域可显示或 隐藏属性,左右滑动切换 tab;基本信息先显示,媒体详情在后台加载。多选时可查看汇总大小与数量。
  • 应用检查位于「属性 → App」:签名证书、Activities、声明的快捷方式、Services、 Receivers 与 Providers。可直接启动允许访问的 Activity 与快捷方式,长按可启动的 Activity 可添加桌面快捷方式。
  • Android/data 的 SAF 访问集成在本地目录树中,授权持久保存,应用目录显示应用图标。 系统允许时可授权整个目录;也可从用户安装的应用中按名称/包名过滤,选中后直接请求对应 应用目录的授权。目录探测可选,因为系统可能隐藏实际存在的路径。是否可授权取决于系统选择器, root 或 Shizuku 仍可作为其他访问方式。
  • 可读系统目录入口:Android 禁止列出 / 时,仍可访问 /system、/vendor 等可读目录, 其中文件与子目录的访问继续受系统权限限制。
  • 显示设置可仅显示当前用户的内部存储,跳过其他共享配置文件的探测。
  • 占用图(SpaceSniffer 式 treemap)内嵌在面板里,支持双指缩放和一键重置缩放,长按菜单与树共用。 返回文件树后保留递归大小、占用比例背景和大小降序,分析过的行临时使用列表视图。 折叠子目录保留分析结果;折叠分析目标或其祖先时清除。
  • SD 卡与 U 盘在树根上各占一行,名字与容量取系统给的。不新增任何权限——普通 API 读不动的卷,可以用 SAF 授权,或者走特权侧列。
  • 跳转到路径:任何根行(服务器、内部存储、可移动卷、文档树、收藏)都能输入一条路径, 树逐级展开到那个目录或那个文件所在的行;路上遇到没连的服务器会先连上。 长按或双击路径栏可编辑当前目录的路径。
  • 长按上级目录按钮可将屏幕外绿色高亮的当前目录滚回可见位置;如果该行已在屏幕内, 则折叠目录树。
  • 分包 APK 直接安装:.xapk、.apks、.apkm 从包里读出来写进 PackageInstaller 会话,不用再装一个辅助 App。
  • Twig 可以当文件选择器,对外接 GET_CONTENT,对内替掉 SAF——所以能选到 SMB 共享里、压缩包里的文件,而系统选择器看不见这些来源。
  • 添加到桌面:目录和单个文件都能钉一个快捷方式——目录版展开到树里那一行;文件版 还能选怎么打开(自动识别、文本查看、十六进制查看,或提前选定一个应用,因为快捷方式 没法每次点都弹一次应用选择器),图标在已有缓存缩略图时直接用缩略图。跑在独立任务栈里, 点开不会经过主界面。

Nova 等启动器的快捷方式/动作选择列表中,还提供 Twig shortcut 和 Twig command shortcut。前者选择目录、文件或网站后确认;后者选择命令运行的位置, 复用 Twig 内的命令配置对话框。SFTP 位置使用 SSH;本地目录(包括 /)默认使用应用 UID, 只有勾选默认关闭的**「使用特权会话」**,才按设置中的特权方式使用 Root(su)或 Shizuku。 创建后的快捷方式记住所选方式及 Shizuku 的 UID;身份不可用或发生变化时会报错,不会回退到 其他 shell。启动器可将结果绑定到图标的上划/下划手势,无需再放一个桌面图标。 压缩包内部、restic、Git 等临时挂载位置无法在进程重启后直接恢复,因此不可选择; 不支持的位置会禁用并淡化「选择」按钮。

网站与页面定制

把自建服务的面板、网页音乐播放器、常用网站放进 Twig 的**「网站」**根节点,也可以 顺手折腾一下网页,让它更合自己的习惯。每个网站用系统 WebView 在独立任务中打开, 也能添加桌面快捷方式直接启动。 如果页面还开着,再次点击会回到原来的页面,不会重新通过网址加载。

也可用 Twig 打开其他应用传入的 HTTP(S) 链接,以网站默认设置打开临时页面,只有点击 **「添加网站」才加入书签。长按「网站」**根节点可修改默认设置:显示选项用于初始化新书签; 共用脚本、样式、隐藏元素和拦截规则也作用于已有页面。网站自己的 User-Agent 覆盖共用值, 脚本、样式和规则则与共用设置组合生效。

展开**「网站 → 添加网站…」**,填写名称和网址,再选择打开方式。长按树中的网站条目, 可以编辑或添加到桌面。每个网站都能单独设置:

  • 显示与手势:全屏打开,分别隐藏工具栏、状态栏、导航栏,屏幕方向、桌面模式, 页面顶部下拉刷新、滚动方向锁定和禁用文本选择。默认双击隐藏/显示工具栏;网页自身 需要双击事件时,可以关闭这个选项。
  • 定制页面:展开**「高级」**,添加 JavaScript、CSS,以及要隐藏元素的 CSS 选择器。 给面板换个样式、去掉横幅,或者加上自己的小工具。脚本在页面加载完成后执行;脚本、 样式和隐藏元素只作用于与保存网址同源的主页面。
  • 请求与登录:自定义 User-Agent、手动添加请求 Header,以及 HTTP Basic 用户名和 密码。保存的 Basic 凭据和 Header 值使用 Twig 的密钥存储加密。Header 仅用于同源 GET 页面导航,不会统一附加到所有接口和资源请求。
  • 请求拦截:每行一个域名或网址规则,支持 * 通配符,拦截 WebView 能提供的图片、 脚本、接口等资源请求。普通域名也匹配其子域名。这是手动资源规则,不是完整的广告 过滤订阅引擎,也不拦截主页面导航。
  • 边看边改:工具栏的**「网页调试」提供控制台、执行脚本、预览 CSS 和元素选择器。 可以复制选择器或 HTML、点选隐藏元素,再把修改保存到网站配置。需要电脑 DevTools 时, 开启「电脑调试(本次会话)」**,配合 USB 调试和电脑 Chrome 的 chrome://inspect/#devices 使用;部分 Android 开发版系统会强制开启 WebView 调试。
  • 文件下载:HTTP(S) 下载默认保存到 Downloads/twig。确认文件名,也可以通过 Twig 自己的选择器另选可写位置,包括已连接的存储,支持传输进度与取消。
  • 文件上传:网页请求上传时,使用 Twig 自己的选择器选择一个或多个文件,包括已连接 存储中的文件;单选或多选遵循网页的请求。
  • 摄像头、麦克风与位置:每个书签可为保存的 HTTPS 来源分别设置询问、允许或拒绝, 同时仍需 Android 权限。临时页面始终询问,新书签不继承授权;书签来源变化后重置授权。
  • 禁止截图:每个网站独立设置,默认关闭,解锁后的最近任务预览也遵循这个选项。
  • 后台运行:按网站开启,默认关闭。用带通知的前台服务维持已打开的网站会话,适用于 媒体播放或其他页面任务;关闭页面或通过通知结束。它能改善后台存活,但不能保证绕过 Android 省电限制或网站自身的暂停逻辑,让页面始终不间断运行。

Cookie 和登录状态保存在 Twig 共用的 WebView 数据中,与其他浏览器分开;不同网站书签 不是彼此隔离的账号容器。网站兼容性取决于设备的系统 WebView。

设置了 Twig 主密码后,Twig 处于锁定状态时,打开网站或返回已有网站任务都需要先解锁。 Cookie 和原页面保留,已开启的后台运行继续。锁定期间网站窗口禁止截图与最近任务预览; 解锁后遵循该网站的**「禁止截图」**选项。

对比

  • 目录对比(Beyond Compare 式):两侧逐行对齐,中间一列状态符号;横屏并排,竖屏一次 显示一侧但状态列始终在。两侧可以是任意来源,小文件按内容比,而不是只看大小与时间。 大文件也可主动发起完整内容比较;读取失败单独显示为错误,并从同步中排除。
  • 单向同步方向写死「同步到左侧 / 同步到右侧」,不跟着活动侧走(同步是不可逆的)。 默认增量:只推源侧独有与两侧不同的,目标侧多余的保留;关掉即镜像,多余的一并 删除。目标侧更新的那些单独列出,不勾就不覆盖。
  • 保存的对比与收藏并排放在树根上,常看的一对点一下就打开,也能直接从那行发起同步。
  • 文本对比双栏、长行横向同步滚动、逐差异块合并;图片对比并排,缩放平移联动。
  • 二进制走 hex 对比。 一个文件是不是二进制,只有读了才知道,所以判定为二进制、或者 大到文本视图扛不住的那一对,会转交给 hex 对比而不是停在"无法比较"。两侧按相同偏移 对齐、不做重新同步——对固件、改过的可执行文件这类结构固定的东西,这才是诚实的答案; 短的那侧用空行补齐,否则它先到底,末尾的差异永远翻不到。

查看器与播放器

所有查看器都经 FsRegistry 读取,本地、压缩包内、远程文件一视同仁。

  • 文本查看器,手写词法着色,多主题,捏合缩放字号,带 Markdown 预览模式
  • 文本编码是设置项,不是猜:统一走 TextCodec——BOM → 严格 UTF-8 → 你在设置里排好的 候选(默认 GB18030)。编辑不再只限 UTF-8:保存用读进来那个编码写回;原编码表示不了新输入的 字符时会问你要不要转成 UTF-8,而不是静默替换成 ?。查看器的编码菜单可按指定编码重新打开文件, 并记住每个文件的选择;选择自动可恢复正常检测。
  • 可写的本地及 SAF 文档、文本文件通过外部编辑器打开时可保存回原文件。应用不支持编辑时回退到查看, Twig 自带网络源及压缩包中的文件对外仍为只读。
  • Hex 查看器,虚拟滚动、可拖动滚动条、文本/HEX 搜索
  • PDF 阅读器,基于系统渲染器:连续或单页滚动、双击裁掉页边空白、文本选择, 系统支持时还有全文搜索(Android 15+);Android 15+ 可打开加密 PDF,并选择记住密码
  • 支持静态 SVG 查看与清晰缩放。图片查看器(降采样防 OOM),支持左右或上下滑动切换图片;幻灯片边扫边播,找到第一张就先显示。 勾选状态与文件面板同步,查看器显示已选数量,返回时定位当前图片
  • 视频/音频播放器(media3 + FFmpeg 软解),支持 AVI、M2TS、HDMV 音轨与 PGS 字幕;系统解码器崩溃时两级自动降级。M2TS 的跳转定位可能较慢
  • 剧集自动连播:Jellyfin/Emby 的队列由服务端给出,其余来源按文件名里的编号 (SxxExx、E01、纯数字)分组。上/下一集按钮只在真的算得出队列时才出现
  • 片头片尾跳过用于识别为同一系列且至少包含两集的视频,不要求集数编号连续。 在片头结束处点击片头按钮,或在片尾开始处点击片尾按钮,即可记忆同一系列共用的跳过边界; 再次点击已标记的按钮可清除。 若起播位置在片头内,自动跳到片头结束;播放到片尾标记时切换到下一集,最后一集则结束播放。
  • 继续观看在历史记录中有独立标签,列出本地及已保存文件服务器连接中的未看完视频。 可继续播放、从头播放或定位所在目录;移除条目只隐藏记录,保留续播位置。 打开或跳转后需连续播放十秒才更新进度。
  • 播放偏好记忆:手动选择的音轨、字幕与画面缩放方式,可在下一集及重新打开文件时恢复
  • 设置 → 播放器 → 打开视频时关闭音轨,可让每次打开的视频或剧集默认不播放音轨, 不影响纯音频播放。可通过音轨按钮恢复声音;关闭音轨期间不打断其他音乐。
  • 播放操作支持滑动定位、亮度/音量手势、长按临时加速,可切换音轨及内嵌/外挂字幕。 文字字幕可设置字号和颜色,视频播放时控件闲置十秒后自动隐藏。

独立音乐播放器

  • 跨来源听歌。 本地、网络与 SAF 音乐可加入播放列表;挂载中的压缩包和备份内的音乐 可使用临时队列播放。
  • 封面与歌词。 界面配色跟随封面,点击切换封面与歌词,横屏时两者并排显示; 在封面左右滑动切换歌曲,上下滑动调节媒体音量,播放器内显示音量百分比。
  • 双语同步歌词支持同目录 LRC、内嵌标签及媒体服务器提供的歌词。点击带时间的歌词行 可跳到对应位置,波形也可直接用于定位。
  • 播放列表管理支持收藏、名称/元数据搜索、排序、拖动调整顺序、随机与循环播放, 可从 M3U/M3U8 文件或目录(递归)追加歌曲并自动去重,保存的列表可导出为 .m3u8。
  • 后台播放支持通知栏、锁屏和耳机/蓝牙控制,通知提供收藏和退出,系统支持显示时还有 原生播放计时。重新打开播放器可恢复上次队列,可从菜单、通知或长按随机播放按钮退出。

媒体服务器(Jellyfin / Emby)

一份实现通吃两家——Emby 正是 Jellyfin 当年 fork 的上游,那批端点同源同名——而且 零新增依赖:OkHttp 本来就在,org.json 是 Android 运行期自带的。

  • 树按服务器上实际存在的媒体库组织,用你自己起的库名,而不是写死的几个类型。 电影库点进去直接是所有电影,剧集库是所有剧(只有多季时才分季),音乐库是 专辑 / 专辑艺术家 / 艺术家 / 文件夹四个分类。
  • 播放进度双向同步。「继续观看」就是服务器上那份,不是本机另存的一份;而且保持 服务端给的顺序,不套用你在文件浏览里选的排序。
  • 海报、标签、歌词、外挂字幕全部走 API。 这些信息若去读媒体文件本身,在网络上 每首歌要花几秒。「继续观看」用横版剧照,媒体库用竖版海报并按原始比例显示。
  • 搜索走服务端的索引,所以刮削成中文名的片子,用原名照样搜得到。
  • 设计上只读:服务端没有上传 API,而 DELETE /Items/{id} 删的是媒体库里的真实文件。

终端

  • 本地、SSH 与特权 shell。 可在选中目录打开本地系统 shell 或 SSH 会话,支持多个会话, 顶部切换。root 与 Shizuku shell 有独立入口并明确标示身份。本地 shell 使用真 PTY, 支持交互式终端程序。 普通本地 shell 始终使用应用 UID,不会因为 Twig 获得过 root 授权而自动提升权限。 「新建特权会话」使用设置中当前启用的 Root 或 Shizuku;Shizuku 的实际身份取决于其启动方式, root 启动为 UID 0,ADB 启动通常为 UID 2000。
  • 文件与命令双向跳转。「在此打开终端」从正在浏览的目录开始;「定位当前目录」将 shell 的工作目录定位回文件树。SSH shell 提供当前路径时,远程目录也能定位。
  • 适合触屏的附加键。 两排按键提供 CTRL、ALT、SHIFT、ESC、TAB、方向键、 Home/End、Page Up/Down 及常用 shell 符号。先点 SHIFT,再点方向键或 TAB 即可发送 组合键;方向键支持长按连发。
  • Shift 组合键操作交互式工具。 例如用 Shift+← 打开 Codex 的待回答问题,或用 Shift+Tab 切换 Claude Code 的权限模式。
  • 独立管道键 |。 将一条命令的输出交给下一条命令,例如 ls -l | grep .apk, 无需反复切换手机键盘的符号页。过滤日志、搜索输出、组合命令行工具时都经常用到。
  • 软键盘显隐。 工具栏按钮可随时显示或隐藏 Android 软键盘;点击命令提示符时弹出, 隐藏光标的程序不会自动唤起。键盘显隐或屏幕旋转时调整终端行列,SSH 远端 PTY 也同步调整。
  • 终端自定义。 可导入字体及兼容 Termux 的 colors.properties 配色,也可使用默认配置; 双指缩放字号。终端配色独立于应用主题,附加键条也跟随终端配色。
  • 保持亮屏可让会话持续显示,达到系统闲置超时后降低亮度,操作时恢复原来的亮度。
  • 命令快捷方式:命令留空可直接在指定位置打开终端;支持 SFTP 目录、服务器和本地目录,在终端里运行或后台执行, 可固定到桌面,也可通过启动器的 Twig command shortcut 入口绑定手势。 本地命令可勾选「使用特权会话」,默认不勾选。

Git

  • :git-lite 从零实现 GitRepo、ObjectStore、IndexFile 与 patience Diff
  • 状态与历史视图,双栏 diff 与文本查看器共用着色器
  • 本地仓库直读,SFTP 仓库经远程 exec git
  • worktree 与子模块处理到位,包括 gitdir: 里记的绝对路径在本机不存在的情况
  • 虚拟的**「工作区」节点**列出同一仓库的其他 worktree,点进去就是那条工作区完整的 状态/分支/历史视图

WiFi 共享

直接在 ServerSocket 上手写的极小 HTTP/1.1 服务,零新增依赖。电脑浏览器输 IP 就是 目录列表 + Range 下载 + 拖拽上传;同一个端口同时说 WebDAV,Finder、资源管理器 或另一台 Twig 都能直接挂载。默认只读,可选 Basic 认证,前台服务 + WiFi 锁, UDP 探测让另一台 Twig 扫一下就能把它存成连接。

安全

  • 密码、API key、token、私钥口令加密落盘:一把随机 256 位 DEK 加密这些字段,DEK 外面 套硬件 Keystore 密钥(默认,无感)或 scrypt(用户设的主密码)。开关主密码只换「谁包住 DEK」,字段密文一个字节都不重写。零新增依赖:scrypt 用的是包里早就有的 Bouncy Castle。
  • 主密码是程序锁,各入口都验证——主界面、「用 Twig 打开」、「复制到此」、文件选择器、 终端快捷方式、远程命令快捷方式、通过启动器配置的动作和网站任务。菜单里的「锁定」只丢掉 内存中的密钥,音乐照放、终端还在、共享继续。直接固定的文件桌面快捷方式是例外:本地和 SAF 文件打开不需要解密任何东西,直接跳过;只有指向服务器的文件才会先解锁,因为重连要 用到保存的(加密)凭据。
  • 指纹解锁走平台 BiometricPrompt(不引 androidx.biometric,APK 增量为 0),与主密码 并存;主密码始终是根钥匙。
  • 配置备份(.twigbak):连接、设置与网站书签(含脚本、样式和规则)导出成 JSON, 可选用单独的导出密码加密;保存位置走 Twig 自己的目录选择器——所以能直接存到 SMB / WebDAV / S3。导入是合并不是替换, token、host key 与 DEK 一律不导出。网站书签列表按整份恢复,Basic 凭据与 Header 在目标 设备重新加密;目录排序/视图设置和网站默认设置也包含在备份中。 不包含 WebView 的 Cookie、登录会话和私有空间文件。

模块

模块 内容
:core-fs 纯 JVM:XFile / FileSystem / FsRegistry / CopyEngine
:fs-local LocalFileSystem + priv/:root/Shizuku 共用的特权 shell 回落
:fs-archive ArchiveFileSystem + zip(读写、加密)/ 7z / tar / gz·xz·bz2·zst 单文件压缩,以及 ArchiveWriter
:fs-archive-rar RarFileSystem(RAR4 + RAR5)——单独成模块纯粹是为了让 libre 版能整块去掉
:fs-network FtpFileSystem / SftpFileSystem / WebDavFileSystem / S3FileSystem / JellyfinFileSystem
:fs-smb SmbFileSystem——libsmb2,经 NDK/JNI;Smb1FileSystem——按连接开启的 jcifs-ng 旧协议后端
:fs-restic restic 仓库读取器(纯 Kotlin)
:fs-zstd NativeZstd + ZstdInputStream——块解码与流式 zstd 解码,经 JNI
:git-lite 纯 Kotlin 迷你 git
:app 双面板 UI、查看器、终端、Git 视图、占用图、WiFi 共享、网站与页面定制

构建

使用 JDK 21,以及 app/build.gradle.kts 中指定版本的 Android SDK/NDK。

./gradlew :app:assembleFullDebug       # 可安装的 debug APK(带 RAR)
./gradlew :app:assembleFullRelease     # R8 优化的 release(带 RAR)
./gradlew :app:assembleLibreRelease    # F-Droid 版(无 RAR,100% 自由软件)
./gradlew :app:bundleLibreRelease     # libre 版上架用 AAB
./gradlew :app:assembleFullRelease -PabiSplit=true  # 三个单 ABI APK
./gradlew :app:assembleFullRelease -Planguages=zh   # 中文翻译 + 默认英文

full 和 libre 默认均不包含 NFS。在应用构建命令后加上 -PwithNfs=true, 即可包含 NFS 后端和连接编辑器:

./gradlew :app:assembleFullRelease -PwithNfs=true
# 只构建 arm64 NFS APK(release.sh 使用此方式):
./gradlew :app:assembleFullRelease -PwithNfs=true -PabiSplit=true -Pabis=arm64-v8a

此类 APK 的文件名以 -nfs.apk 结尾,例如签名后的 app/build/outputs/apk/full/release/app-full-release-nfs.apk。 包名、版本和签名配置保持一致。不支持 NFS 的构建仍保留已保存的 NFS 连接及其配置备份, 但隐藏 NFS 根节点和连接编辑器,换回支持 NFS 的构建即可使用。

-Planguages 限制打包的翻译资源,适用于 full/libre 和 debug/release。 多个语言用逗号分隔,例如 -Planguages=zh,en;使用 Android 资源限定符, 如 en、zh、ja、pt-rBR、b+zh+Hant。不传则保留全部语言。 默认英文资源始终保留,用于缺失翻译时回退;此参数不强制切换界面语言。

默认情况下,release 只有在仓库根存在 keystore.properties 时才签名(内含 storeFile / storePassword / keyAlias / keyPassword)。没有它时产出的是无签名包 app-<flavor>-release-unsigned.apk,不会回落到公开的 Android debug key。 自己签,或者本地测试直接用 debug 包。也可在 local.properties 设置 debugSign=true, 或传入 -PdebugSign=true,显式用 debug key 签名 release;命令行参数优先。 正式发布应使用项目自己的签名密钥。

测试:

./gradlew :core-fs:test :fs-archive:test :fs-network:test :fs-restic:test :git-lite:test
./gradlew :app:testFullReleaseUnitTest

说明:build-tools 固定 36.1.0;默认构建 arm64-v8a 与 x86_64。启用 ABI 拆分时还会 构建实验性的 armeabi-v7a APK。R8 开启,libsmb2 与 zstd 的 JNI 入口按名 keep。

准备发布时,在已提交且干净的工作区配置正式签名后,运行 tools/release.sh。脚本会 构建并校验六种 APK(五种常规包,加一个支持 NFS 的 arm64-v8a full 包),在 build/release/<版本>/ 生成安装包和双语发布说明,不打标签也不推送。 tools/release.sh --publish 还会打标签、推送并创建 GitHub Release。 重建尚无 NFS 编译选项的旧标签时,仍只生成原有的五种 APK。

加一种新来源

  1. 新建模块 :fs-xxx,实现 com.twig.core.FileSystem
  2. 启动时注册:FsRegistry.register(XxxFileSystem())
  3. 给用户一条导航到 XxxFileSystem.root() 的入口

浏览、复制、移动、删除、缩略图、搜索随即全部可用,无需改动。


许可

Twig 采用 GPL-3.0-only,见 LICENSE。

这是继承来的而不是选的:Termux 终端模拟器与 Jellyfin FFmpeg 解码器都是 GPLv3, 链接它们导致整个应用必须是 GPLv3。

  • LICENSE-EXCEPTIONS.md —— 与 UnRAR 许可下的 junrar 链接的附加许可、该许可要求的声明、LGPL 重新链接说明,以及商标保留
  • THIRD_PARTY.md —— 全部第三方组件及其版本与许可

商标。 "Twig" 名称与应用图标不在 GPL 授权范围内。代码可以自由 fork 和修改; 再分发修改版时请更换名称与图标,以免用户误认它来自本项目。

RAR 声明。 按 UnRAR 许可的要求:本程序中处理 RAR 的代码不得用于开发与 RAR (WinRAR)兼容的压缩器。Twig 只解压 RAR,不实现 RAR 压缩。


参与贡献

欢迎贡献。请先读 CLA.md——很短,不拿走你的版权,并解释了为什么这个项目 需要保留对自研代码另行授权的权利。

签署方式是在 PR 描述里加一行:

I have read the CLA (CLA.md) and I agree to its terms.

实现决策记在 AGENTS.md,那一长串代价高昂的排错记录按领域拆在 docs/lessons/ 下,一个领域一个文件。改终端、缩略图、TS 解复用这类 子系统之前,先读对应那份。


路线图

  • :fs-cloud —— Google Drive / Dropbox / OneDrive,走纯 REST,不用各家 SDK
  • 全局搜索
  • 体积:干掉 Bouncy Castle 那 1.2 MB 的 picnic 查找表,或者整个换成 Conscrypt ——两条路都需要真机验证 SSH 握手仍然找得到 X25519

演进史
  • Phase 1 —— FileSystem/XFile 抽象与 CopyEngine;本地文件系统;双面板 UI; 多选/复制/移动/新建/改名/删除;R8 + 资源裁剪 + AAB 拆分。

  • Phase 1.5 / 1.6 —— 文本/Hex/图片查看器经 FsRegistry 通用;用其他应用打开; 复制进度与取消;SAF 回退。

  • Phase 2 / 2.5 —— 压缩包挂载:ZIP 从只读到读写(GBK 自识别);7z / RAR 只读; 解压复用 CopyEngine。

  • Phase 3 / 3.5 / 3.6 —— FTP,然后是 libsmb2 的 SMB,然后是 SSHJ 的 SFTP 与 手写 WebDAV;连接持久化与多服务器管理。

  • Phase 3.7 —— restic 仓库只读解密浏览,支持格式 v1 与 v2。

  • 0.8 / 0.9 —— 树式模型定型:就地展开、展开即连接、竖屏单面板 + 横屏双面板、深色主题。

  • 0.12 —— 收藏,覆盖全部来源,按需连接直达。

  • 0.45 —— 网格视图与缩略图开关解耦(正交)。

  • 0.62 —— 幻灯片:边扫边播、随机播放显示真实下标、悬浮栏自动隐藏。

  • 0.65 —— 位置记忆修复 + 最近位置历史;「跳转到所在目录」精确定位到文件行。

  • 0.70 —— 压缩打包:打包到对侧当前目录,可选 zip/7z 与移动模式,天然跨来源。

  • 0.73 —— 横屏布局重整:隐藏 Toolbar、操作列改双列、路径栏改 类型:/服务器/路径。

  • 0.74 —— 分组排序扩展到树式列表;修「展开位置没被记住」与「勾选时整列表闪一下」。

  • 0.77 —— 终端外观三件套:字体导入(判据是 advance 0.5em)、配色、双指缩放修复。 Twig 可作文件选择器。

  • 0.78 —— SFTP 命令快捷方式,终端或后台静默执行,可固定到桌面。

  • 0.79 —— 本地 shell 终端,走 termux 原生 PTY——不需要桥接线程、断线重连、 resize 探测那套远程逻辑。

  • 0.80 —— 本地 shell 好用起来:CmdShims 绕开「PATH 目录对应用 uid 不可读」, SshHome 给 OpenSSH 备好全绝对路径的 config,.mkshrc 把前缀搜索历史绑到上下键。

  • 0.96 —— WiFi 共享:手写 HTTP/1.1 服务,同一端口兼说 WebDAV,暴露的是整个 FsRegistry——浏览器能直接下载 SMB 上、甚至压缩包内的文件。

  • 0.97 / 0.98 / 0.99 —— 共享返工:「所有来源」按能否真的浏览来筛、按名字逐级解析 路径、修正非 ASCII 路径的链接编码;网页端重做(就地预览、排序、批量操作); 入口挪到操作列。

  • 1.00 —— 压缩包密码:读加密的 zip/7z/rar,创建 AES-256 加密包。zip 那套是 手写的——commons-compress 与 java.util.zip 对加密 zip 连读都不支持。

  • 1.01+ —— 往已有 zip 里加文件改成真追加(100MB 包实测 1875ms → 1ms);展开压缩包 等于选中包根;S3 兼容对象存储(手写 SigV4);root / Shizuku 特权访问,含基于 bindUserService、在特权侧分配 PTY 的特权终端。

  • 1.1 —— Jellyfin 与 Emby 成为一等来源:树按服务器自己的媒体库组织,播放进度 双向同步,海报/标签/歌词/字幕改从 API 拿而不是读媒体文件,搜索走服务端索引,剧集 自动连播。零新增依赖。连播对普通来源也可用(按文件名里的编号分组);另外只读来源上 的写操作入口现在直接不出现,而不是点下去才报错。

  • 1.2 —— 目录对比同步:增量或镜像,方向写死左右两项而不跟着活动侧,目标侧更新的 那些单独确认。RAR5 支持(junrar 升到 8.1.0,顺带修掉「密码错了却说对」);拆出 libre / full 两个 flavor,junrar 单独成模块移出 libre,F-Droid 版 100% 自由软件。 导航栏底色跟着当前页面走,底部不再是一道黑边。

  • 1.3 —— 安全:保存的密码经两层 Keystore 密钥加密落盘;主密码升级为程序锁,六个 入口全守;指纹解锁;.twigbak 配置备份走 Twig 自己的目录选择器(所以能直接存到 SMB / WebDAV)。SD 卡与 U 盘在树根上直接给入口。行高与文字大小拆成两档独立偏好, 一次调好的项目从菜单收进设置页;自适应图标 + Android 13 主题图标;应用图标长按菜单 新增「终端」。

  • 1.4 —— 别的应用授权的文档树按那个应用命名并用它的图标,不再显示一长串 document id;授权也能从侧栏交还。

  • 1.5 —— 任意根行都能跳转到路径;收藏与保存的对比可以指到文档树里面;终端附加键条 打磨(方向键长按连发、整条键条共用一个字号),息屏再亮不再丢掉模拟器尺寸。

  • 1.6 —— tar,以及 gz/xz/bz2/zst 单文件压缩。 tar 里的条目按偏移切片就地读取, 所以里面套着的压缩包不必先解出来就能打开、里面的视频还能 seek;单流格式挂成只有一个 条目的包,于是 foo.tar.gz 打开是 foo.tar,再展开就是 tar,.tgz/.txz/.tbz2/ .tzst 同理。容器里没记原始大小的,大小显示留空,不再写成 0 B。

  • 1.7 —— 内置 PDF 阅读器:连续或单页滚动、双击裁掉页边空白、文本选择,系统支持时 还有全文搜索(Android 15+)。判定为二进制、或大到文本视图扛不住的那一对,转交 hex 对比。 分包 APK(.xapk、.apks、.apkm)经 PackageInstaller 直接安装。SMB 不填共享名即 列出全部共享,FTP / SFTP / S3 连接可指定起始路径。文本编码候选里 GB18030 取代 GBK。 值得一提的修复:AVI 声音重组帧与 B 帧顺序、4K 网络播放撑爆堆、SSH 上的 git 工作区。

  • 1.8 —— 单个文件也能钉桌面快捷方式了,不再只有目录能:可以选怎么打开——自动识别、 文本查看、十六进制查看,或者提前选定某个应用(快捷方式没法每次点都弹一次应用选择器); 图标在已有缓存缩略图时直接用缩略图。主密码只在目标真的需要解密时才弹——服务器文件要靠 保存的加密凭据重连,本地和 SAF 文件从来不需要。快捷方式跑在独立任务栈里,打开不会经过 主界面,返回也不会绕回主界面。

  • 1.14 —— 网站书签支持按网站配置显示、脚本、样式、认证、下载、页面工具及可选后台运行, 网站任务遵循应用锁。新增系统允许的 Android/data SAF 授权、可读系统目录入口、可配置的 启动器快捷方式、支持替换与编号的批量重命名、多文件分享及选中项属性。长按或双击路径栏 可编辑路径;改进面板切换与全屏刘海适配。

  • 1.15 —— 本地回收站、更多批量重命名操作与模板、区分文件对比中的读取失败。 网站共用默认设置、临时页面、文件上传与独立截图设置;历史记录新增继续观看, 视频缩略图显示记忆进度,占用图增加重置缩放按钮。

  • 1.16 —— 私有空间、左右面板独立的根项目显隐、可递归继承的目录排序与视图设置、 分享文本保存与双面板目标选择器。后台删除、回收站批量恢复、可选底部工具栏和完成提醒。 剧集片头片尾跳过、音乐音量手势与通知退出/计时、网站摄像头/麦克风/位置权限,以及终端闲置调暗。

  • 1.16.1 —— 传输失败重试、断点续传与跳过失败项;远程分享支持后台准备和缓存复用。 新增勾选数量、文件夹图标和浏览、音乐、终端长按快捷操作;修复图片缩放误触菜单、 返回退出确认和缩略图刷新。

  • 1.17 —— OLED 纯黑主题、横竖屏单/双面板;根项目、收藏、网站和操作按钮排序。 复制、移动与压缩可在对侧面板选择目标,新增压缩级别、保存到源目录及可选的 7z 文件名加密。 Unix 属性、权限表示方式与符号链接目标;M3U/目录导入播放列表、静态 SVG 和 Android 15+ 加密 PDF。 可配置返回键行为和字幕外观;图片勾选同步与返回定位、编号副本/快速改名、空命令终端快捷方式。 改进目录计数、回收站、文件选择器、移动存储 SAF 访问与本地化;修复压缩临时输出可能覆盖源文件的问题。