From 727e700b098b6603c73b57a970eed556163bb81a Mon Sep 17 00:00:00 2001 From: FancyFlame Date: Mon, 2 May 2022 10:21:01 +0800 Subject: [PATCH 1/6] Added android support --- Cargo.toml | 4 ++ src/android_clipboard.rs | 104 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +- 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 src/android_clipboard.rs diff --git a/Cargo.toml b/Cargo.toml index 205ea97..ff5373c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,10 @@ objc = "0.2" objc_id = "0.1" objc-foundation = "0.1" +[target.'cfg(target_os = "android")'.dependencies] +jni = "0.19" +ndk-context = "0" + [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="ios", target_os="emscripten"))))'.dependencies] x11-clipboard = { version = "0.5.1", optional = true } smithay-clipboard = { version = "0.6.0", optional = true } diff --git a/src/android_clipboard.rs b/src/android_clipboard.rs new file mode 100644 index 0000000..73c7709 --- /dev/null +++ b/src/android_clipboard.rs @@ -0,0 +1,104 @@ +use crate::common::{ClipboardProvider, Result}; +use jni::objects::JString; +use std::ffi::CStr; + +pub struct AndroidClipboardContext; + +impl ClipboardProvider for AndroidClipboardContext { + fn get_contents(&mut self) -> Result { + let ctx = ndk_context::android_context(); + let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?; + let env = vm.attach_current_thread()?; + let class_ctx = env.find_class("android/content/Context")?; + let cb = env.get_static_field(class_ctx, "CLIPBOARD_SERVICE", "Ljava/lang/String;")?; + let cb_manager = env + .call_method( + ctx.context() as jni::sys::jobject, + "getSystemService", + "(Ljava/lang/String;)Ljava/lang/Object;", + &[cb], + )? + .l() + .unwrap(); + + let clip_data = env + .call_method( + cb_manager, + "getPrimaryClip", + "()Landroid/content/ClipData;", + &[], + )? + .l() + .unwrap(); + + //return Ok(format!("{:?}", clip_data)); + + let item = env + .call_method( + clip_data, + "getItemAt", + "(I)Landroid/content/ClipData$Item;", + &[0i32.into()], + )? + .l() + .unwrap(); + + let char_seq = env + .call_method(item, "getText", "()Ljava/lang/CharSequence;", &[])? + .l()?; + + let string = env + .call_method(char_seq, "toString", "()Ljava/lang/String;", &[])? + .l() + .unwrap(); + + let jstring = JString::from(string.into_inner()); + + let ptr = env.get_string_utf_chars(jstring)?; + let s; + unsafe { + s = CStr::from_ptr(ptr).to_owned().into_string()?; + } + env.release_string_utf_chars(jstring, ptr)?; + Ok(s) + } + + fn set_contents(&mut self, text: String) -> Result<()> { + let ctx = ndk_context::android_context(); + let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?; + let env = vm.attach_current_thread()?; + let class_ctx = env.find_class("android/content/Context")?; + let cb = env.get_static_field(class_ctx, "CLIPBOARD_SERVICE", "Ljava/lang/String;")?; + let cb_manager = env + .call_method( + ctx.context() as jni::sys::jobject, + "getSystemService", + "(Ljava/lang/String;)Ljava/lang/Object;", + &[cb], + )? + .l() + .unwrap(); + + let class_clip_data = env.find_class("android/content/ClipData")?; + + let clip_data = env.call_static_method( + class_clip_data, + "newPlainText", + "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;", + &[ + env.new_string("text").unwrap().into(), + env.new_string(text).unwrap().into(), + ], + )?; + + env.call_method( + cb_manager, + "setPrimaryClip", + "(Landroid/content/ClipData;)V", + &[clip_data], + )? + .v()?; + + Ok(()) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 092a23c..ebcb8d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,9 @@ pub mod x11_clipboard; #[cfg(windows)] pub mod windows_clipboard; +#[cfg(target_os = "android")] +pub mod android_clipboard; + #[cfg(target_os = "macos")] pub mod osx_clipboard; @@ -63,7 +66,7 @@ pub type ClipboardContext = windows_clipboard::WindowsClipboardContext; #[cfg(target_os = "macos")] pub type ClipboardContext = osx_clipboard::OSXClipboardContext; #[cfg(target_os = "android")] -pub type ClipboardContext = nop_clipboard::NopClipboardContext; // TODO: implement AndroidClipboardContext +pub type ClipboardContext = android_clipboard::AndroidClipboardContext; #[cfg(target_os = "ios")] pub type ClipboardContext = nop_clipboard::NopClipboardContext; // TODO: implement IOSClipboardContext #[cfg(not(any( From ee1b03fe3be59227d2ebadd3122d2c90c74e1728 Mon Sep 17 00:00:00 2001 From: FancyFlame Date: Mon, 2 May 2022 10:42:44 +0800 Subject: [PATCH 2/6] Added `new` function to android support --- src/android_clipboard.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/android_clipboard.rs b/src/android_clipboard.rs index 73c7709..348c874 100644 --- a/src/android_clipboard.rs +++ b/src/android_clipboard.rs @@ -101,4 +101,11 @@ impl ClipboardProvider for AndroidClipboardContext { Ok(()) } +} + +impl AndroidClipboardContext{ + #[inline] + pub fn new() -> Result{ + Ok(AndroidClipboardContext) + } } \ No newline at end of file From 01671c8ea41181c125e3a554144a2f452780add3 Mon Sep 17 00:00:00 2001 From: FancyFlame <31250625+Fancyflame@users.noreply.github.com> Date: Mon, 2 May 2022 11:12:53 +0800 Subject: [PATCH 3/6] Update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 676860c..01b4b6e 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,17 @@ fn get_contents(&mut self) -> Result>; fn set_contents(&mut self, String) -> Result<(), Box>; ``` -`ClipboardContext` is a type alias for one of {`WindowsClipboardContext`, `OSXClipboardContext`, `X11ClipboardContext`, `NopClipboardContext`}, all of which implement `ClipboardProvider`. Which concrete type is chosen for `ClipboardContext` depends on the OS (via conditional compilation). +`ClipboardContext` is a type alias for one of +{ + +- `WindowsClipboardContext` +- `AndroidClipboardContext` +- `OSXClipboardContext` +- `X11ClipboardContext` +- `NopClipboardContext` + +} +,all of which implement `ClipboardProvider`. Which concrete type is chosen for `ClipboardContext` depends on the OS (via conditional compilation). ## License From 0ff4907889447cd6ac75c88aa3db110649165de7 Mon Sep 17 00:00:00 2001 From: FancyFlame <31250625+Fancyflame@users.noreply.github.com> Date: Mon, 23 May 2022 09:16:22 +0800 Subject: [PATCH 4/6] rustfmt --- src/android_clipboard.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/android_clipboard.rs b/src/android_clipboard.rs index 348c874..e8ca4f3 100644 --- a/src/android_clipboard.rs +++ b/src/android_clipboard.rs @@ -103,9 +103,9 @@ impl ClipboardProvider for AndroidClipboardContext { } } -impl AndroidClipboardContext{ +impl AndroidClipboardContext { #[inline] - pub fn new() -> Result{ + pub fn new() -> Result { Ok(AndroidClipboardContext) } -} \ No newline at end of file +} From 256ec20953dd2373c69247cf263a46b77de80fbd Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Mon, 23 May 2022 03:32:05 +0200 Subject: [PATCH 5/6] Fix code formatting --- src/android_clipboard.rs | 42 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/src/android_clipboard.rs b/src/android_clipboard.rs index e8ca4f3..a0b7067 100644 --- a/src/android_clipboard.rs +++ b/src/android_clipboard.rs @@ -22,35 +22,23 @@ impl ClipboardProvider for AndroidClipboardContext { .unwrap(); let clip_data = env - .call_method( - cb_manager, - "getPrimaryClip", - "()Landroid/content/ClipData;", - &[], - )? + .call_method(cb_manager, "getPrimaryClip", "()Landroid/content/ClipData;", &[])? .l() .unwrap(); - //return Ok(format!("{:?}", clip_data)); + // return Ok(format!("{:?}", clip_data)); let item = env - .call_method( - clip_data, - "getItemAt", - "(I)Landroid/content/ClipData$Item;", - &[0i32.into()], - )? + .call_method(clip_data, "getItemAt", "(I)Landroid/content/ClipData$Item;", &[ + 0i32.into() + ])? .l() .unwrap(); - let char_seq = env - .call_method(item, "getText", "()Ljava/lang/CharSequence;", &[])? - .l()?; + let char_seq = env.call_method(item, "getText", "()Ljava/lang/CharSequence;", &[])?.l()?; - let string = env - .call_method(char_seq, "toString", "()Ljava/lang/String;", &[])? - .l() - .unwrap(); + let string = + env.call_method(char_seq, "toString", "()Ljava/lang/String;", &[])?.l().unwrap(); let jstring = JString::from(string.into_inner()); @@ -85,18 +73,12 @@ impl ClipboardProvider for AndroidClipboardContext { class_clip_data, "newPlainText", "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;", - &[ - env.new_string("text").unwrap().into(), - env.new_string(text).unwrap().into(), - ], + &[env.new_string("text").unwrap().into(), env.new_string(text).unwrap().into()], )?; - env.call_method( - cb_manager, - "setPrimaryClip", - "(Landroid/content/ClipData;)V", - &[clip_data], - )? + env.call_method(cb_manager, "setPrimaryClip", "(Landroid/content/ClipData;)V", &[ + clip_data, + ])? .v()?; Ok(()) From 26fc7f2b2e3be5f493d7919eb761dd8e90d3fa73 Mon Sep 17 00:00:00 2001 From: FancyFlame Date: Mon, 23 May 2022 22:26:36 +0800 Subject: [PATCH 6/6] changed Android implementation --- Cargo.toml | 4 +-- README.md | 5 +--- src/android_clipboard.rs | 62 ++++++++++++++++++---------------------- 3 files changed, 31 insertions(+), 40 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ff5373c..7e04896 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,8 @@ objc_id = "0.1" objc-foundation = "0.1" [target.'cfg(target_os = "android")'.dependencies] -jni = "0.19" -ndk-context = "0" +jni = ">=0.19" +ndk-context = ">=0.1" [target.'cfg(all(unix, not(any(target_os="macos", target_os="android", target_os="ios", target_os="emscripten"))))'.dependencies] x11-clipboard = { version = "0.5.1", optional = true } diff --git a/README.md b/README.md index 01b4b6e..44ec52b 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,7 @@ fn get_contents(&mut self) -> Result>; fn set_contents(&mut self, String) -> Result<(), Box>; ``` -`ClipboardContext` is a type alias for one of -{ +`ClipboardContext` is a type alias for one of the following types implementing `ClipboardProvider`. Which concrete type is chosen for ClipboardContext depends on the OS(via conditional compilation). - `WindowsClipboardContext` - `AndroidClipboardContext` @@ -36,8 +35,6 @@ fn set_contents(&mut self, String) -> Result<(), Box>; - `X11ClipboardContext` - `NopClipboardContext` -} -,all of which implement `ClipboardProvider`. Which concrete type is chosen for `ClipboardContext` depends on the OS (via conditional compilation). ## License diff --git a/src/android_clipboard.rs b/src/android_clipboard.rs index a0b7067..1c096f3 100644 --- a/src/android_clipboard.rs +++ b/src/android_clipboard.rs @@ -4,51 +4,52 @@ use std::ffi::CStr; pub struct AndroidClipboardContext; +impl AndroidClipboardContext { + #[inline] + pub fn new() -> Result { + Ok(AndroidClipboardContext) + } +} + impl ClipboardProvider for AndroidClipboardContext { fn get_contents(&mut self) -> Result { let ctx = ndk_context::android_context(); let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?; let env = vm.attach_current_thread()?; let class_ctx = env.find_class("android/content/Context")?; - let cb = env.get_static_field(class_ctx, "CLIPBOARD_SERVICE", "Ljava/lang/String;")?; - let cb_manager = env + let service_field = + env.get_static_field(class_ctx, "CLIPBOARD_SERVICE", "Ljava/lang/String;")?; + let clipboard_manager = env .call_method( ctx.context() as jni::sys::jobject, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", - &[cb], + &[service_field], )? - .l() - .unwrap(); + .l()?; let clip_data = env - .call_method(cb_manager, "getPrimaryClip", "()Landroid/content/ClipData;", &[])? - .l() - .unwrap(); - - // return Ok(format!("{:?}", clip_data)); + .call_method(clipboard_manager, "getPrimaryClip", "()Landroid/content/ClipData;", &[])? + .l()?; let item = env .call_method(clip_data, "getItemAt", "(I)Landroid/content/ClipData$Item;", &[ 0i32.into() ])? - .l() - .unwrap(); + .l()?; let char_seq = env.call_method(item, "getText", "()Ljava/lang/CharSequence;", &[])?.l()?; - let string = - env.call_method(char_seq, "toString", "()Ljava/lang/String;", &[])?.l().unwrap(); - - let jstring = JString::from(string.into_inner()); + let jstring = JString::from( + env.call_method(char_seq, "toString", "()Ljava/lang/String;", &[])?.l()?.into_inner(), + ); let ptr = env.get_string_utf_chars(jstring)?; - let s; - unsafe { - s = CStr::from_ptr(ptr).to_owned().into_string()?; - } + + let output_string = unsafe { CStr::from_ptr(ptr).to_owned().into_string()? }; + env.release_string_utf_chars(jstring, ptr)?; - Ok(s) + Ok(output_string) } fn set_contents(&mut self, text: String) -> Result<()> { @@ -56,16 +57,16 @@ impl ClipboardProvider for AndroidClipboardContext { let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?; let env = vm.attach_current_thread()?; let class_ctx = env.find_class("android/content/Context")?; - let cb = env.get_static_field(class_ctx, "CLIPBOARD_SERVICE", "Ljava/lang/String;")?; - let cb_manager = env + let service_field = + env.get_static_field(class_ctx, "CLIPBOARD_SERVICE", "Ljava/lang/String;")?; + let clipboard_manager = env .call_method( ctx.context() as jni::sys::jobject, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", - &[cb], + &[service_field], )? - .l() - .unwrap(); + .l()?; let class_clip_data = env.find_class("android/content/ClipData")?; @@ -76,7 +77,7 @@ impl ClipboardProvider for AndroidClipboardContext { &[env.new_string("text").unwrap().into(), env.new_string(text).unwrap().into()], )?; - env.call_method(cb_manager, "setPrimaryClip", "(Landroid/content/ClipData;)V", &[ + env.call_method(clipboard_manager, "setPrimaryClip", "(Landroid/content/ClipData;)V", &[ clip_data, ])? .v()?; @@ -84,10 +85,3 @@ impl ClipboardProvider for AndroidClipboardContext { Ok(()) } } - -impl AndroidClipboardContext { - #[inline] - pub fn new() -> Result { - Ok(AndroidClipboardContext) - } -}