osfans / trime

同文安卓輸入法平臺3.x/Android-rime/Rime Input Method Engine for Android
http://osfans.github.io/trime/
GNU General Public License v3.0
3.13k stars 375 forks source link

移除「外部儲存空間的權限和存取權」 #1186

Open goofyz opened 9 months ago

goofyz commented 9 months ago

背景

為解決「權限 」的問題 (#997 ),研究了一會 Android 的scoped storage framework 。

我們可以使用 ACTION_OPEN_DOCUMENT_TREE 來拿取某目錄 (/rime) 的權限。不過就算拿到權限,也不能直接使用 File() 來讀取,而必須便用 Uri。由於 rime 不能用 Uri 來部署,所以我們需要另一處可以直接讀寫的目錄。

而 Android 中的程式都有各自的 app-specific directory。此目錄:

建議

我建議將 rime 需要用到的檔案搬到 "app-specific directory" (/sdcard/Android/data/com.osfans.trime) 來使用。具體步驟為:

  1. 用戶將相關 schema 檔放在某一目錄 (e.g. /sdcard/rime)(可以選取其他目錄)
  2. Trime 第一次啟動時,會問用戶選取 schema 的目錄。用戶揀選後,Trime 可以拿到讀寫權限。該權限會儲存起來,以後使用。
  3. Trime 會先將第一步的目錄複製到 “app-specific directory ”。
  4. Trime 用app-specific directory 為 rime 的用戶目錄作部署。
  5. 成功部署後,build 目錄為 /sdcard/Android/data/com.osfans.trime/build
  6. 往後每次手動部署時,會重覆 3 - 5 。程式開啟時的話則只需做 4 - 5。

這樣對用戶和開發者而言,改動最少而又能做到現在一樣的功能。大家覺得如何?

其他考慮

題外話又有少許相關的問題

參考:

https://developer.android.com/training/data-storage/shared/documents-files#grant-access-directory,

WhiredPlanck commented 9 months ago

@goofyz 使用 App 私有目录我曾经做过,理论上还是很可行的。不过有如下一些顾虑:

摒弃传统存储访问方式,也就是通过 URI 读写(实际原理是通过 Android 的存储访问框架(Storage Access Framework,SAF)进行)。由于同文的后端库 librime 只支持传统的文件系统方式,曾经想过三种方式:

  1. 同文内置钩子(hook),实现 SAF 与传统方式之间的转换。
    • 优点:可以保留现有的工作模式,用户可以方便直接同步、读取和修改数据等。
    • 缺点:钩子代码需要额外工作和维护,并且有潜在的违规风险;SAF 读写文件有性能损耗,可能会进一步降低用户体验。
  2. 修改 librime 库使其支持使用文件描述符(file descriptor,fd;URI 有相应 API 转换为 fd)
    • 优点:同样可以保留现有工作模式
    • 缺点:工作量太大,librime 未必同意修改;加大引入 bug 的可能性;同样有 SAF 性能问题。
  3. 直接使用 app 私有目录

综合来看,第三种方式,也就是我们目前讨论的方式是最合适的。另外,从外部复制同步我也想过,但是显然不太优雅,而且会增加潜在工作量。所以我觉得最佳实践是参照 fcitx5-android 的做法,实现数据导入导出的功能;同时我们也能增加清空 build 的功能等。这样也符合主流做法。

最后回答一下提出的两个问题:

  1. “同步”是同步用户资料夹和同步资料夹之间的数据,同步资料夹指定为用户资料夹下的 sync 目录。区别在于同步文件夹下使用 UUID 作为子目录进行区分(多用户情况下),用户资料夹中的二进制数据在此会转换为纯文本数据。
  2. 关于 Rime 的数据分布和作用,官方有更详尽的解释,有长期使用 Linux 经验者可能会觉得很熟悉。
goofyz commented 9 months ago

方案 3 如何實現檔案導入?DocumentProvider 是用於導出的嗎? 看來要研究一下 fcitx5 了。

WhiredPlanck commented 9 months ago

方案 3 如何實現檔案導入?DocumentProvider 是用於導出的嗎? 看來要研究一下 fcitx5 了。

DocumentProvider 是一个定义对应用私有存储目录访问方式的工具。Android 11+ 无法直接访问 Android/data 来访问应用私有目录,DocumentProvider 就是来解决应用数据对外交互的需求的。目前质感文件和 MT 管理器都可以利用它来访问应用私有目录(如果应用有 DocumentProvider 的话)。

数据导入/导出,导入可以从外部存储选择压缩包,让应用解压到用户资料夹去;导出则反向操作。

wxyzh commented 9 months ago

用f5a的做法好像没啥问题。

goofyz commented 9 months ago

看了 f5a 的作法,是直接使用私有目錄,似乎跟我的建議分別只是一開始如何置入 rime 文件。 若只使用私有目錄,似乎對同文使用者來說不太方便:

基於以上原因,我還是認為我的建議比只使用私有目錄好。

WhiredPlanck commented 9 months ago

@goofyz MT 管理器和质感文件是我所知的同文用户中(QQ 社群)最常用的文件管理器,还有 NMM 等,这些文件管理器都支持添加存储,特别是可以添加 DocumentProvider。MT 管理器也有黑魔法可以在用户授权后访问 Android/data。通过适当的引导和社区解答,前两个问题其实都不算难事。

引导开发者尽量使用应用内置存储、内置存储数据随应用卸载删除都是 Google 指定的规范,这也是为了应用在被卸载后不会在外置存储内留下“屎山”,这些和 iOS 上的情况其实是异曲同工的。我倾向于在遵守这些规范的前提下尽可能增强围绕前增强应用能力。个人建议是有更清晰的文档来引导用户如何管理这些配置,并逐渐增强同文本身对数据的管理能力(比如细分出更多种类的数据导入,开放管理方案、词典的用户界面等)。

WhiredPlanck commented 9 months ago

@goofyz 另外我们的应用是最低兼容 Android 5.0 的,Android 小于等于 9.0 的版本仍然需要外置存储权限,Android 10 是过渡版本。可以要也可以不要(不要的话则通过 MediaStore API 访问),Android 11 起强制使用分区存储,无权限只可使用 MediaStore API 访问外置存储。

goofyz commented 9 months ago
goofyz commented 9 months ago

想深一層,要「使用者自行操作資料夾」還是落了下乘。如果 Trime 有 UI 去下載輸入法、修改設定和鍵盤的話, 新手可以輕鬆使用,老手可以在私有目錄細緻修改,就不會有這串討論了。

mokapsing commented 9 months ago

想深一層,要「使用者自行操作資料夾」還是落了下乘。如果 Trime 有 UI 去下載輸入法、修改設定和鍵盤的話, 新手可以輕鬆使用,老手可以在私有目錄細緻修改,就不會有這串討論了。

这样就需要联网权限了

WhiredPlanck commented 9 months ago

@goofyz 我还是觉得从外面一遍遍复制很奇怪 …… 需要更多讨论

demon-Dark commented 9 months ago

想深一層,要「使用者自行操作資料夾」還是落了下乘。如果 Trime 有 UI 去下載輸入法、修改設定和鍵盤的話, 新手可以輕鬆使用,老手可以在私有目錄細緻修改,就不會有這串討論了。

可以以第三方软件的形式来提供给新手可视化选择下载部署方案。只需要trime暴露私有目录允许其他软件读写就可以了。包括词库云端同步也可以靠第三方软件去读写同步

hzq21 commented 9 months ago

Android 10还能把数据放 外置内存卡(插在手机上的TF储存卡)上,13就不能存在外置的TF卡上了,如果选了非data的目录部署会报错闪退,data里又看不到所以不能选。对应的机器分别是xperia xz2和1 iii,其他输入法如Microsoft SwiftKey、Google Gboard输入中文又很难受。

更新:直接把TF内存卡格式化成内置的 Emulated storage 就完事了,ADB命令执行完还要在系统设置里迁移一下才可以,以前竟然不知道唉(内存卡要足够快,我的是samsung Pro plus 2023,这张卡4k小文件读和写都在10MB/s以上)

WhiredPlanck commented 8 months ago

@goofyz 我认真想了一段时间,觉得你的建议还是很合理的。我还是赞成你的方案 ~

Freed-Wu commented 8 months ago

在 PC 上,我 rime 的配置在 ~/.config/ibus/rime 下面,所以我可以在 termux 中把 dotfiles git clone 下来,再直接 cp .config/ibus/rime /sdcard/rime 使用相同的配置。如果切换到 /sdcard/Android/data/com.osfans.trime/files, 在没有 root 权限的情况下似乎无法 cp 。 Just my 2c.

goofyz commented 7 months ago

在 PC 上,我 rime 的配置在 ~/.config/ibus/rime 下面,所以我可以在 termux 中把 dotfiles git clone 下来,再直接 cp .config/ibus/rime /sdcard/rime 使用相同的配置。如果切换到 /sdcard/Android/data/com.osfans.trime/files, 在没有 root 权限的情况下似乎无法 cp 。 Just my 2c.

若有需要的話,可以加個 broadcast 指令去將檔案覆製到 /sdcard/Android/data 入面。

goofyz commented 7 months ago

最近正在實作此功能,發現原來 rime/trime 會修改 default.custom.yaml ,所以有需要將此檔案導出回 /rime。 不知還有沒有其他非 build 檔案會被 rime/trime 修改呢?

Freed-Wu commented 7 months ago

若有需要的話,可以加個 broadcast 指令

:+1:

这些文件管理器都支持添加存储,特别是可以添加 DocumentProvider

termux 也提供给了文件管理器一个 content://com.termux.documents/tree/%2data%2data%2com.termux%2files%2home 。 trime 是也打算提供一个 content://com.osfans.trime/tree/%2storage%2emulated%20%2Android%2data%2com.osfans.trime%2files 用于在文件管理器中浏览 shared_data_dir 和 user_data_dir ?如果是这样,我可以直接指明 user_data_dir 为 content://com.termux.documents/tree/%2data%2data%2com.termux%2files%2home%2.config%2ibus%2rime 和 shared_data_dir 为 content://com.termux.documents/tree/%2data%2data%2com.termux%2files%2usr%2share%2rime-data 吗?

goofyz commented 7 months ago

若有需要的話,可以加個 broadcast 指令

👍

这些文件管理器都支持添加存储,特别是可以添加 DocumentProvider

termux 也提供给了文件管理器一个 content://com.termux.documents/tree/%2data%2data%2com.termux%2files%2home 。 trime 是也打算提供一个 content://com.osfans.trime/tree/%2storage%2emulated%20%2Android%2data%2com.osfans.trime%2files 用于在文件管理器中浏览 shared_data_dir 和 user_data_dir ?如果是这样,我可以直接指明 user_data_dir 为 content://com.termux.documents/tree/%2data%2data%2com.termux%2files%2home%2.config%2ibus%2rime 和 shared_data_dir 为 content://com.termux.documents/tree/%2data%2data%2com.termux%2files%2usr%2share%2rime-data 吗?

現在已經有呀 content://com.osfans.trime.provider/tree (不肯定是否正確)

WhiredPlanck commented 6 months ago

最近正在實作此功能,發現原來 rime/trime 會修改 default.custom.yaml ,所以有需要將此檔案導出回 /rime。 不知還有沒有其他非 build 檔案會被 rime/trime 修改呢?

trime 目前会在 default.custom.yaml 不存在时创建一个空的出来以暂时解决某些情况下方案列表为空的问题

WhiredPlanck commented 6 months ago

@goofyz 我有点想仿照 https://github.com/YuriSizuku/android-SafFile 写一个钩子来让 librime 可以通过 SAF 读写文件

goofyz commented 6 months ago

@goofyz 我有点想仿照 https://github.com/YuriSizuku/android-SafFile 写一个钩子来让 librime 可以通过 SAF 读写文件

不會更易有 bug 嗎? 😁

就是擔心讀寫速度會受到大影響。

WhiredPlanck commented 6 months ago

不會更易有 bug 嗎? 😁

就是擔心讀寫速度會受到大影響。

是的。这只是从技术上看着很酷 ……