android: a playlist's cover from a photo on the phone
The storage plugin gains pick_image — the document picker limited to images, the picked content copied into the app's cache so the Rust side gets a plain path. playlist_cover_set(id, path) is the desktop pick's install step split out and shared (squared, the master cover, the row's revision moved). A playlist's sheet offers Choose cover photo and Remove custom cover; the mock walk exercises both.
This commit is contained in:
1 parent
d95c3f48d1
commit
b50577fc81
15 files changed
+188
-12
No files matched your search
+33
@@ -91,4 +91,37 @@ class StoragePlugin(private val activity: Activity) : Plugin(activity) {
|
||||
}
|
||||
invoke.resolve()
|
||||
}
|
||||
/** A photo from the phone: the document picker limited to images, the
|
||||
* picked content copied into the app's cache so the Rust side gets a
|
||||
* plain path (a content URI is not one). */
|
||||
@Command
|
||||
fun pickImage(invoke: Invoke) {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.type = "image/*"
|
||||
startActivityForResult(invoke, intent, "imagePicked")
|
||||
}
|
||||
|
||||
@ActivityCallback
|
||||
fun imagePicked(invoke: Invoke, result: ActivityResult) {
|
||||
when (result.resultCode) {
|
||||
Activity.RESULT_OK -> {
|
||||
val uri = result.data?.data
|
||||
if (uri == null) { invoke.reject("no image came back"); return }
|
||||
try {
|
||||
val target = java.io.File(activity.cacheDir, "picked-cover-" + System.currentTimeMillis() + ".img")
|
||||
val input = activity.contentResolver.openInputStream(uri)
|
||||
if (input == null) { invoke.reject("the image could not be read"); return }
|
||||
input.use { i -> target.outputStream().use { o -> i.copyTo(o) } }
|
||||
val out = JSObject()
|
||||
out.put("path", target.absolutePath)
|
||||
invoke.resolve(out)
|
||||
} catch (e: Exception) {
|
||||
invoke.reject("the image could not be read: " + e.message)
|
||||
}
|
||||
}
|
||||
Activity.RESULT_CANCELED -> invoke.reject("cancelled")
|
||||
else -> invoke.reject("the picker failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// The three commands, as the Rust functions are named; the ACL slugs them
|
||||
// (`allow-pick-folder`, …) into permissions/autogenerated.
|
||||
const COMMANDS: &[&str] = &["pick_folder", "all_files_access", "request_all_files_access"];
|
||||
const COMMANDS: &[&str] = &["pick_folder", "all_files_access", "request_all_files_access", "pick_image"];
|
||||
|
||||
fn main() {
|
||||
// Registers the command permissions and, on Android, hands the Gradle
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Automatically generated - DO NOT EDIT!
|
||||
|
||||
"$schema" = "../../schemas/schema.json"
|
||||
|
||||
[[permission]]
|
||||
identifier = "allow-pick-image"
|
||||
description = "Enables the pick_image command without any pre-configured scope."
|
||||
commands.allow = ["pick_image"]
|
||||
|
||||
[[permission]]
|
||||
identifier = "deny-pick-image"
|
||||
description = "Denies the pick_image command without any pre-configured scope."
|
||||
commands.deny = ["pick_image"]
|
||||
@@ -9,6 +9,7 @@ whether it is granted, and the system screen that grants it.
|
||||
- `allow-pick-folder`
|
||||
- `allow-all-files-access`
|
||||
- `allow-request-all-files-access`
|
||||
- `allow-pick-image`
|
||||
|
||||
## Permission Table
|
||||
|
||||
@@ -74,6 +75,32 @@ Denies the pick_folder command without any pre-configured scope.
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`storage:allow-pick-image`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Enables the pick_image command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`storage:deny-pick-image`
|
||||
|
||||
</td>
|
||||
<td>
|
||||
|
||||
Denies the pick_image command without any pre-configured scope.
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
`storage:allow-request-all-files-access`
|
||||
|
||||
</td>
|
||||
|
||||
@@ -6,4 +6,4 @@ The phone's storage: pick a library folder (a Storage Access Framework tree,
|
||||
resolved to a path when the volume allows), and Android's all-files access —
|
||||
whether it is granted, and the system screen that grants it.
|
||||
"""
|
||||
permissions = ["allow-pick-folder", "allow-all-files-access", "allow-request-all-files-access"]
|
||||
permissions = ["allow-pick-folder", "allow-all-files-access", "allow-request-all-files-access", "allow-pick-image"]
|
||||
@@ -318,6 +318,18 @@
|
||||
"const": "deny-pick-folder",
|
||||
"markdownDescription": "Denies the pick_folder command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the pick_image command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "allow-pick-image",
|
||||
"markdownDescription": "Enables the pick_image command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Denies the pick_image command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
"const": "deny-pick-image",
|
||||
"markdownDescription": "Denies the pick_image command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "Enables the request_all_files_access command without any pre-configured scope.",
|
||||
"type": "string",
|
||||
@@ -331,10 +343,10 @@
|
||||
"markdownDescription": "Denies the request_all_files_access command without any pre-configured scope."
|
||||
},
|
||||
{
|
||||
"description": "The phone's storage: pick a library folder (a Storage Access Framework tree,\nresolved to a path when the volume allows), and Android's all-files access —\nwhether it is granted, and the system screen that grants it.\n\n#### This default permission set includes:\n\n- `allow-pick-folder`\n- `allow-all-files-access`\n- `allow-request-all-files-access`",
|
||||
"description": "The phone's storage: pick a library folder (a Storage Access Framework tree,\nresolved to a path when the volume allows), and Android's all-files access —\nwhether it is granted, and the system screen that grants it.\n\n#### This default permission set includes:\n\n- `allow-pick-folder`\n- `allow-all-files-access`\n- `allow-request-all-files-access`\n- `allow-pick-image`",
|
||||
"type": "string",
|
||||
"const": "default",
|
||||
"markdownDescription": "The phone's storage: pick a library folder (a Storage Access Framework tree,\nresolved to a path when the volume allows), and Android's all-files access —\nwhether it is granted, and the system screen that grants it.\n\n#### This default permission set includes:\n\n- `allow-pick-folder`\n- `allow-all-files-access`\n- `allow-request-all-files-access`"
|
||||
"markdownDescription": "The phone's storage: pick a library folder (a Storage Access Framework tree,\nresolved to a path when the volume allows), and Android's all-files access —\nwhether it is granted, and the system screen that grants it.\n\n#### This default permission set includes:\n\n- `allow-pick-folder`\n- `allow-all-files-access`\n- `allow-request-all-files-access`\n- `allow-pick-image`"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -33,6 +33,12 @@ pub struct AccessState {
|
||||
pub granted: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PickedImage {
|
||||
pub path: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub struct Storage<R: Runtime>(tauri::plugin::PluginHandle<R>);
|
||||
|
||||
@@ -91,9 +97,30 @@ fn request_all_files_access<R: Runtime>(app: tauri::AppHandle<R>) -> Result<(),
|
||||
}
|
||||
}
|
||||
|
||||
/// A photo from the phone: the document picker limited to images, the
|
||||
/// picked content copied into the app's cache — a plain path for the Rust
|
||||
/// side (a playlist's cover, for one).
|
||||
#[tauri::command]
|
||||
async fn pick_image<R: Runtime>(app: tauri::AppHandle<R>) -> Result<PickedImage, String> {
|
||||
#[cfg(mobile)]
|
||||
{
|
||||
use tauri::Manager;
|
||||
let storage = app.state::<Storage<R>>();
|
||||
storage
|
||||
.0
|
||||
.run_mobile_plugin::<PickedImage>("pickImage", ())
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
#[cfg(not(mobile))]
|
||||
{
|
||||
let _ = app;
|
||||
Err("the image picker here is the desktop's dialog".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("storage")
|
||||
.invoke_handler(tauri::generate_handler![pick_folder, all_files_access, request_all_files_access])
|
||||
.invoke_handler(tauri::generate_handler![pick_folder, all_files_access, request_all_files_access, pick_image])
|
||||
.setup(|app, api| {
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user