From 73427f0decbd90878c275d86d69e77f1a5eeb2c5 Mon Sep 17 00:00:00 2001 From: Gae24 <96017547+Gae24@users.noreply.github.com> Date: Fri, 12 Sep 2025 19:12:22 +0200 Subject: [PATCH] android: rework implementation, bump MSRV don't store context inside clipboard, adapt code to the latest jni changes --- .github/workflows/test.yml | 2 +- Cargo.lock | 163 ++++++++++++++++++------------------- Cargo.toml | 4 +- src/common.rs | 1 + src/platform/android.rs | 153 +++++++++++++++++----------------- 5 files changed, 158 insertions(+), 165 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ebe92b1..8863f19 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: os: [macos-latest, windows-latest, ubuntu-latest] # Latest stable and MSRV. We only run checks with all features enabled # for the MSRV build to keep CI fast, since other configurations should also work. - rust_version: [stable, "1.71.0"] + rust_version: [stable, "1.77.0"] steps: - uses: actions-rust-lang/setup-rust-toolchain@v1 with: diff --git a/Cargo.lock b/Cargo.lock index b36347f..d032295 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,7 +34,7 @@ dependencies = [ "objc2-foundation", "parking_lot", "percent-encoding", - "windows-sys 0.52.0", + "windows-sys", "wl-clipboard-rs", "x11rb", ] @@ -147,7 +147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -243,30 +243,44 @@ checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] name = "jni" version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +source = "git+https://github.com/jni-rs/jni-rs.git?branch=master#71c7f17ed2131cdfd6639b5cf7fdaa92b8443753" dependencies = [ "cesu8", "cfg-if", "combine", "jni-sys", "log", - "thiserror", + "once_cell", + "paste", + "static_assertions", + "thiserror 2.0.16", "walkdir", - "windows-sys 0.45.0", ] [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "c30a312d782b8d56a1e0897d45c1af33f31f9b4a4d13d31207a8675e0223b818" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c199962dfd5610ced8eca382606e349f7940a4ac7d867b58a046123411cbb4" +dependencies = [ + "quote", + "syn 1.0.100", +] [[package]] name = "jpeg-decoder" @@ -421,9 +435,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "os_pipe" @@ -432,7 +446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29d73ba8daf8fac13b0501d1abeddcfe21ba7401ada61a819144b6c2a4f32209" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -458,6 +472,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -494,9 +514,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -555,7 +575,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -579,6 +599,12 @@ version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "syn" version = "1.0.100" @@ -590,6 +616,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tempfile" version = "3.10.1" @@ -599,7 +636,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -617,7 +654,16 @@ version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.35", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", ] [[package]] @@ -628,7 +674,18 @@ checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.100", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", ] [[package]] @@ -753,16 +810,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", + "windows-sys", ] [[package]] @@ -774,21 +822,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.0" @@ -820,12 +853,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" @@ -838,12 +865,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" @@ -856,12 +877,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" @@ -880,12 +895,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" @@ -898,12 +907,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" @@ -916,12 +919,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" @@ -934,12 +931,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" @@ -963,7 +954,7 @@ dependencies = [ "os_pipe", "rustix", "tempfile", - "thiserror", + "thiserror 1.0.35", "tree_magic_mini", "wayland-backend", "wayland-client", diff --git a/Cargo.toml b/Cargo.toml index 0f7db1b..ea7939c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" readme = "README.md" keywords = ["clipboard", "image"] edition = "2021" -rust-version = "1.71.0" +rust-version = "1.77.0" [features] default = ["image-data"] @@ -89,7 +89,7 @@ percent-encoding = "2.3.1" [target.'cfg(target_os = "android")'.dependencies] ndk-context = "0.1" -jni = "0.21" +jni = { git = "https://github.com/jni-rs/jni-rs.git", branch = "master" } [[example]] name = "get_image" diff --git a/src/common.rs b/src/common.rs index 3539568..2c41c60 100644 --- a/src/common.rs +++ b/src/common.rs @@ -90,6 +90,7 @@ impl std::fmt::Debug for Error { } impl Error { + #[cfg(not(target_os = "android"))] pub(crate) fn unknown>(message: M) -> Self { Error::Unknown { description: message.into() } } diff --git a/src/platform/android.rs b/src/platform/android.rs index 2f4da53..e82ddac 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -5,7 +5,8 @@ use std::{ use jni::{ objects::{JObject, JString}, - AttachGuard, JavaVM, + strings::JNIString, + Env, JavaVM, }; #[cfg(feature = "image-data")] @@ -18,40 +19,36 @@ impl From for Error { } } -pub(crate) struct Clipboard { - ctx: ndk_context::AndroidContext, +fn with_clipboard_access(callback: F) -> Result +where + F: FnOnce(&mut Env, JObject) -> Result, +{ + let ctx = ndk_context::android_context(); + + let jvm = unsafe { JavaVM::from_raw(ctx.vm().cast()) }; + + jvm.attach_current_thread(|env| { + let context = unsafe { JObject::from_raw(ctx.context().cast()) }; + let clipboard = env.new_string(c"clipboard")?; + + let clipboard_manager = env + .call_method( + context, + c"getSystemService", + c"(Ljava/lang/String;)Ljava/lang/Object;", + &[(&clipboard).into()], + )? + .l()?; + + callback(env, clipboard_manager) + }) } +pub(crate) struct Clipboard(()); + impl Clipboard { pub(crate) fn new() -> Result { - Ok(Self { ctx: ndk_context::android_context() }) - } - - fn vm(&self) -> Result { - // SAFETY: Valid pointer guaranteed by the `ndk_context` crate. - unsafe { jni::JavaVM::from_raw(self.ctx.vm().cast()) } - } - - fn context(&self) -> JObject { - // SAFETY: Valid pointer guaranteed by the `ndk_context` crate. - unsafe { JObject::from_raw(self.ctx.context().cast()) } - } - - fn clipboard_manager<'attachment>( - &self, - env: &mut AttachGuard<'attachment>, - ) -> Result, Error> { - let context = self.context(); - let clipboard = env.new_string("clipboard")?; - - Ok(env - .call_method( - context, - "getSystemService", - "(Ljava/lang/String;)Ljava/lang/Object;", - &[(&clipboard).into()], - )? - .l()?) + Ok(Self(())) } } @@ -65,30 +62,39 @@ impl<'clipboard> Get<'clipboard> { } pub(crate) fn text(self) -> Result { - let vm = self.clipboard.vm()?; - let mut env = vm.attach_current_thread()?; - let clipboard_manager = self.clipboard.clipboard_manager(&mut env)?; + with_clipboard_access(|env, clipboard_manager| { + if !env.call_method(&clipboard_manager, c"hasPrimaryClip", c"()Z", &[])?.z()? { + return Err(Error::ContentNotAvailable); + } - if !env.call_method(&clipboard_manager, "hasPrimaryClip", "()Z", &[])?.z()? { - return Err(Error::ContentNotAvailable); - } + let clip = env + .call_method( + clipboard_manager, + c"getPrimaryClip", + c"()Landroid/content/ClipData;", + &[], + )? + .l()?; - let clip = env - .call_method(clipboard_manager, "getPrimaryClip", "()Landroid/content/ClipData;", &[])? - .l()?; + if env.call_method(&clip, c"getItemCount", c"()I", &[])?.i()? == 0 { + return Err(Error::ContentNotAvailable); + } - if env.call_method(&clip, "getItemCount", "()I", &[])?.i()? == 0 { - return Err(Error::ContentNotAvailable); - } + let item = env + .call_method( + &clip, + c"getItemAt", + c"(I)Landroid/content/ClipData$Item;", + &[0.into()], + )? + .l()?; - let item = env - .call_method(&clip, "getItemAt", "(I)Landroid/content/ClipData$Item;", &[0.into()])? - .l()?; + let char_sequence = + env.call_method(item, c"getText", c"()Ljava/lang/CharSequence;", &[])?.l()?; + let text = env.cast_local::(char_sequence)?.to_string(); - let text = env.call_method(item, "getText", "()Ljava/lang/CharSequence;", &[])?.l()?; - let text = JString::from(text); - let text = env.get_string(&text)?; - Ok(text.into()) + Ok(text) + }) } pub(crate) fn html(self) -> Result { @@ -115,28 +121,26 @@ impl<'clipboard> Set<'clipboard> { } pub(crate) fn text(self, text: Cow<'_, str>) -> Result<(), Error> { - let vm = self.clipboard.vm()?; - let mut env = vm.attach_current_thread()?; - let clipboard_manager = self.clipboard.clipboard_manager(&mut env)?; + with_clipboard_access(|env, clipboard_manager| { + let label = env.new_string(c"label")?; + let text = env.new_string(JNIString::from(text))?; - let label = env.new_string("label")?; - let text = env.new_string(text)?; + let clip_data = env.call_static_method( + c"android/content/ClipData", + c"newPlainText", + c"(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;", + &[(&label).into(), (&text).into()], + )?; - let clip_data = env.call_static_method( - "android/content/ClipData", - "newPlainText", - "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;", - &[(&label).into(), (&text).into()], - )?; + env.call_method( + clipboard_manager, + c"setPrimaryClip", + c"(Landroid/content/ClipData;)V", + &[(&clip_data).into()], + )?; - env.call_method( - clipboard_manager, - "setPrimaryClip", - "(Landroid/content/ClipData;)V", - &[(&clip_data).into()], - )?; - - Ok(()) + Ok(()) + }) } pub(crate) fn html(self, _: Cow<'_, str>, _: Option>) -> Result<(), Error> { @@ -163,12 +167,9 @@ impl<'clipboard> Clear<'clipboard> { } pub(crate) fn clear(self) -> Result<(), Error> { - let vm = self.clipboard.vm()?; - let mut env = vm.attach_current_thread()?; - let clipboard_manager = self.clipboard.clipboard_manager(&mut env)?; - - env.call_method(clipboard_manager, "clearPrimaryClip", "()V", &[])?; - - Ok(()) + with_clipboard_access(|env, clipboard_manager| { + env.call_method(clipboard_manager, c"clearPrimaryClip", c"()V", &[])?; + Ok(()) + }) } }