Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
goofyz opened this issue Jan 18, 2024 · 23 comments · May be fixed by #1394
Open

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

goofyz opened this issue Jan 18, 2024 · 23 comments · May be fixed by #1394

Comments

@goofyz
Copy link
Collaborator

goofyz commented Jan 18, 2024

背景

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

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

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

  • 讀寫不需額外權限。
  • 會在移除程式後自動刪除。
  • 分為 external 和internal storage
  • 其他程式不能讀取

建議

我建議將 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。

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

其他考慮

  • 考慮到現今使用者需要使用 /build 來除蟲,用 external storage 來做 app-specific directory 會好一點。
  • 如果 schema directory 的檔案太多太大,複製到app-specific directory 需要的時間會增多,變相部署的時間會延長。
  • 由於是複製,會浪費了儲存空間
  • 「同步」的話,可以用同一方法拿取目錄權限,將檔案複製出去。
  • 將檔案由 app-specific directory 導出到其他目錄的話,有機會不能覆寫現有的檔案。變相「同步」每次都需建立新資料來處理。這部份暫未研究清楚。

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

  • 「同步」的作用是什麼?現在還有人在用嗎?
  • 「共享資料來」的作用是什麼?還有需要嗎?

參考:

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

@WhiredPlanck
Copy link
Collaborator

WhiredPlanck commented Jan 18, 2024

@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 私有目录
    • 优点:保留传统方式,几乎不需要修改和增加代码,甚至还能减少代码;性能依旧
    • 缺点:Android 11+ 不能直接访问,需要 DocumentProvider(已于 d598b85 添加,所以问题不大);用户如有通过同步/备份软件直接同步数据的需求,目前解决方式尚不明确;数据会随应用卸载而自动移除。

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

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

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

@goofyz
Copy link
Collaborator Author

goofyz commented Jan 18, 2024

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

@WhiredPlanck
Copy link
Collaborator

WhiredPlanck commented Jan 18, 2024

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

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

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

@wxyzh
Copy link

wxyzh commented Jan 19, 2024

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

@goofyz
Copy link
Collaborator Author

goofyz commented Jan 23, 2024

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

  • 私有目錄太過點深入,不方便操作: 因為同文大部份的改動都需用戶自行將檔案放入 rime 目錄。私有目錄需要去到 /sdcard/Android/data/com.osfans.trime/files/,有點麻煩。
  • 私有目錄不是任何檔案軟件也能顯示:如你所說,要支援讀取 DocumentProvider 的才可顯示。有些廠商內置的檔案軟件已經不能顯示 /Android/data,這增加了使用者的障礙。
  • 私有目錄會在 trime 移除後自動刪除,大大增加用戶掉失資料的風險:對現在尚不算穩定的 trime 來說,很多使用者都會嘗新後又用回舊的 (有 bugs 😥),若用戶忘記自行備份或匯出的話,又是一個麻煩。

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

@WhiredPlanck
Copy link
Collaborator

WhiredPlanck commented Jan 23, 2024

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

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

@WhiredPlanck
Copy link
Collaborator

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

@goofyz
Copy link
Collaborator Author

goofyz commented Jan 23, 2024

  • 我們不應假設使用者已安裝某些特定的程式。對新使用者來說,使用 trime 前要先安裝某些特定的 app 是一個大障礙。使用文檔或螢幕指示引導用戶可減少使用問題,但從減少安裝/使用步驟才是最好的。
  • Google 的建議應該是指程式自動生成的檔案,才需自動刪除。自動刪除build 資料符合規範。但 rime 資料來是使用者自主設立,需要使用者自主修改當中的檔案,我不認為可以自動替他們刪除。Trime 只是 rime 的其中一個前端,該資料夾可能會給其他前端使用,也可能有其他用途。舉個極端的例子:刪除 MS word 時也不會連 "my document" 資料一起刪除。
  • 根據文檔,使用我提議的方法 可最低兼容 4.4 ,也不需外置存储权限 (我測試過 Android 7,13,14)。Media content (photo, music, document, 特定資料來, etc) 才需要用 media store API。

@goofyz
Copy link
Collaborator Author

goofyz commented Jan 25, 2024

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

@mokapsing
Copy link

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

这样就需要联网权限了

@WhiredPlanck
Copy link
Collaborator

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

@WhiredPlanck WhiredPlanck pinned this issue Jan 25, 2024
@demon-Dark
Copy link

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

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

@hzq21
Copy link

hzq21 commented Feb 4, 2024

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
Copy link
Collaborator

WhiredPlanck commented Feb 15, 2024

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

@Freed-Wu
Copy link
Contributor

在 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
Copy link
Collaborator Author

goofyz commented Apr 10, 2024

在 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
Copy link
Collaborator Author

goofyz commented Apr 10, 2024

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

@Freed-Wu
Copy link
Contributor

若有需要的話,可以加個 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 吗?

@goofyz
Copy link
Collaborator Author

goofyz commented Apr 11, 2024

若有需要的話,可以加個 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
Copy link
Collaborator

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

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

@WhiredPlanck
Copy link
Collaborator

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

@goofyz
Copy link
Collaborator Author

goofyz commented May 3, 2024

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

不會更易有 bug 嗎? 😁

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

@WhiredPlanck
Copy link
Collaborator

不會更易有 bug 嗎? 😁

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

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants