Claude/fix android file permissions r pq9z#31
Merged
Conversation
The readUri/writeUri plugin commands exist only in the Android APK. Calling them on desktop (or before the APK is rebuilt) caused "Plugin not found" errors every time a document was opened, because the uriPermission plugin is not registered on non-Android platforms. Introduce src/lib/utils/platform.ts with isAndroid() which checks navigator.userAgent — reliable in the Tauri WebView, zero extra deps. All three call sites (loadDocument, handleSave non-session path, handleSaveAs, and SessionManager.saveToOriginal) now guard the readContentUri / writeContentUri calls with isAndroid() so the plugin-fs fallback path is taken on desktop. https://claude.ai/code/session_01J39pMh3ZymXAf9W5K3Ziyi
…lugin-fs I/O The plugin:uriPermission|* IPC calls were silently rejected on Android because UriPermissionPlugin is only registered in the Kotlin pluginManager — it is invisible to the Rust IPC router which enforces capabilities. This means takePersistablePermission was a no-op, so content:// URI permissions expired after every app restart, breaking Recents and cross-session saves. The readUri/writeUri commands added in the previous commits had the same problem and are removed here. Changes: - MainActivity.kt: override onActivityResult to automatically call ContentResolver.takePersistableUriPermission for any content:// URI returned by a file-picker activity. This runs entirely in native Kotlin, requires no IPC, and fires before the JavaScript layer is involved. - UriPermissionPlugin.kt: remove the non-functional readUri/writeUri commands; add a note explaining why plugin:uriPermission|* cannot be invoked via JS. - useFileOperations.ts: restore readFile/writeFile from @tauri-apps/plugin-fs for all file I/O (plugin-fs uses ContentResolver on Android for content:// URIs); fix handleSaveAs state update bug where setPath/startSession/ addDocument/markClean were gated behind `if (bytes)` and so never ran for non-content:// (desktop) saves. - SessionManager.saveToOriginal: restore direct writeFile call; add comment explaining plugin-fs handles content:// via ContentResolver. - commands.ts: remove readContentUri/writeContentUri (were never functional). - platform.ts: deleted (no longer needed). https://claude.ai/code/session_01J39pMh3ZymXAf9W5K3Ziyi
…on persistence The plugin was only registered in Kotlin's pluginManager.load() but not on the Rust IPC router, causing all plugin:uriPermission|* calls to fail with "Plugin not found". Add a tauri::plugin::Builder registration in lib.rs so that takePersistablePermission is properly routed to the Kotlin implementation, persisting content:// URI permissions across app restarts and fixing the "Permission Denial" error when reopening documents from the Recents list. https://claude.ai/code/session_01J39pMh3ZymXAf9W5K3Ziyi
Add explicit ::<_, ()> turbofish so rustc can resolve the DeserializeOwned bound for the PluginApi config type parameter. https://claude.ai/code/session_01J39pMh3ZymXAf9W5K3Ziyi
…ndroid Tauri's dialog plugin uses ACTION_GET_CONTENT which returns a temporary, non-persistable content:// URI — takePersistableUriPermission always throws a SecurityException on such URIs, so Recents always fails after restart. Add FilePickerPlugin.kt which launches ACTION_OPEN_DOCUMENT (grants FLAG_GRANT_PERSISTABLE_URI_PERMISSION) and calls ContentResolver.takePersistableUriPermission() inside the activity result callback — the only window where the grant is valid — before returning the URI. Register the plugin in MainActivity, lib.rs, and commands.ts. On Android, handleOpen now calls plugin:filePicker|openFile instead of the Tauri dialog open(). Desktop handleOpen is unchanged. https://claude.ai/code/session_01J39pMh3ZymXAf9W5K3Ziyi
plugin:name|command IPC from JavaScript is checked against Tauri's ACL
(deny-by-default). Custom inline plugins have no defined permissions, so
all commands are blocked — causing "Plugin not found" errors even after
register_android_plugin succeeds.
Add commands/android.rs with pick_file_to_open and
take_persistable_uri_permission as regular Tauri commands registered via
invoke_handler!. These use PluginHandle::run_mobile_plugin_async which
goes through JNI directly, bypassing the ACL entirely. Store the plugin
handles in app state from the plugin setup callbacks.
Update commands.ts to call invoke('pick_file_to_open') and
invoke('take_persistable_uri_permission') instead of the blocked
plugin:filePicker|openFile and plugin:uriPermission|takePersistablePermission
IPC paths.
https://claude.ai/code/session_01J39pMh3ZymXAf9W5K3Ziyi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.