Open picked files through the content resolver, not std::fs

Loading a GPX on Android failed for every file in the picker. The dialog
plugin fires ACTION_GET_CONTENT, which returns a `content://` URI, and
`load_profile_from_path` handed that straight to `std::fs::read_to_string`
— "no such file or directory" for a file the rider is looking at. Picking
from Nextcloud makes it plainer: a document provider backed by a server
may have no local file at all until the resolver opens the stream, so
there was never a path to find.

So the command now takes a `FilePath` and reads through tauri-plugin-fs,
which opens a path directly and a URI via the resolver. The plugin is
here for `FsExt` alone; nothing in ui/ calls its commands, so the
capabilities are unchanged.

Two things that were derived from the filename can no longer be:

  - GPX is detected by content. A document id need not contain a name,
    let alone an extension. No YAML profile begins with `<`.
  - The route name falls back to the GPX's own <name>. Providers over
    real storage encode the filename in the last segment, but an opaque
    row id would have made a wretched route name.

save_fit had the same bug on the export side — PathBuf::from on a
save-dialog URI — and now writes down a resolver descriptor when handed
one.

Note for anyone rebuilding locally: gen/android/tauri.settings.gradle is
autogenerated and lists each plugin's Android project, so the new
plugin's Kotlin only reaches the APK after `cargo tauri android init` and
scripts/sync-android-sources.sh. CI already runs both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 19:08:36 +02:00
co-authored by Claude Opus 5
parent 5f0fe7b403
commit 5dff2500e2
4 changed files with 130 additions and 15 deletions
+5
View File
@@ -22,6 +22,11 @@ bikecontrol-fit = { workspace = true }
tauri = { version = "2", features = [] }
tauri-plugin-dialog = "2"
# Not for the frontend — nothing in `ui/` calls the fs commands. This is here
# for `FsExt::read_to_string`, the one API that can read what the *picker*
# returns on Android: a `content://` URI rather than a path. See
# `read_picked_file` in commands.rs.
tauri-plugin-fs = "2"
uuid = { workspace = true }
chrono = { workspace = true }