#[cfg(target_os = "android")]
use jni::{
JNIEnv,
objects::{JObject, JString, JValue},
};
#[cfg(target_os = "android")]
use winit::platform::android::activity::AndroidApp;
pub struct Clipboard {
#[cfg(not(target_os = "android"))]
manager: arboard::Clipboard,
#[cfg(target_os = "android")]
android_app: AndroidApp,
}
#[cfg(not(target_os = "android"))]
impl Default for Clipboard {
fn default() -> Self {
Self::new()
}
}
impl Clipboard {
#[cfg(not(target_os = "android"))]
#[must_use]
pub fn new() -> Self {
Self {
manager: arboard::Clipboard::new().expect("Failed to create clipboard"),
}
}
#[cfg(target_os = "android")]
pub fn new(android_app: AndroidApp) -> Self {
Self { android_app }
}
pub fn set_text(&mut self, text: &str) {
#[cfg(not(target_os = "android"))]
{
let _ = self.manager.set_text(text.to_string());
}
#[cfg(target_os = "android")]
{
set_clipboard_text(&self.android_app, text);
}
}
pub fn get_text(&mut self) -> Option<String> {
#[cfg(not(target_os = "android"))]
{
self.manager.get_text().ok()
}
#[cfg(target_os = "android")]
{
get_clipboard_text(&self.android_app)
}
}
pub fn clear(&mut self) {
#[cfg(not(target_os = "android"))]
{
let _ = self.manager.clear();
}
#[cfg(target_os = "android")]
{
clear_clipboard(&self.android_app);
}
}
}
#[cfg(target_os = "android")]
fn get_clipboard_manager<'a>(env: &mut JNIEnv<'a>, activity: &JObject<'a>) -> Option<JObject<'a>> {
let service_name = env.new_string("clipboard").ok()?;
let clipboard_manager = env
.call_method(
activity,
"getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;",
&[JValue::from(&service_name)],
)
.ok()?
.l()
.ok()?;
Some(clipboard_manager)
}
#[cfg(target_os = "android")]
fn get_clipboard_text(android_app: &AndroidApp) -> Option<String> {
let jvm = unsafe { jni::JavaVM::from_raw(android_app.vm_as_ptr().cast()).ok()? };
let mut env = jvm.attach_current_thread().ok()?;
let activity = unsafe { JObject::from_raw(android_app.activity_as_ptr().cast()) };
let clipboard_manager = get_clipboard_manager(&mut env, &activity)?;
let clip_data = env
.call_method(
&clipboard_manager,
"getPrimaryClip",
"()Landroid/content/ClipData;",
&[],
)
.ok()?
.l()
.ok()?;
if clip_data.is_null() {
return None;
}
let item = env
.call_method(
&clip_data,
"getItemAt",
"(I)Landroid/content/ClipData$Item;",
&[JValue::from(0)],
)
.ok()?
.l()
.ok()?;
if item.is_null() {
return None;
}
let char_seq = env
.call_method(
&item,
"coerceToText",
"(Landroid/content/Context;)Ljava/lang/CharSequence;",
&[JValue::from(&activity)],
)
.ok()?
.l()
.ok()?;
if char_seq.is_null() {
return None;
}
let j_string = env
.call_method(&char_seq, "toString", "()Ljava/lang/String;", &[])
.ok()?
.l()
.ok()?;
let rust_string: String = env.get_string(&JString::from(j_string)).ok()?.into();
Some(rust_string)
}
#[cfg(target_os = "android")]
fn set_clipboard_text(android_app: &AndroidApp, text: &str) {
let jvm = match unsafe { jni::JavaVM::from_raw(android_app.vm_as_ptr().cast()) } {
Ok(jvm) => jvm,
Err(_) => return,
};
let mut env = match jvm.attach_current_thread() {
Ok(env) => env,
Err(_) => return,
};
let activity = unsafe { JObject::from_raw(android_app.activity_as_ptr().cast()) };
let clipboard_manager = match get_clipboard_manager(&mut env, &activity) {
Some(manager) => manager,
None => return,
};
let label = match env.new_string("label") {
Ok(s) => s,
Err(_) => return,
};
let text_to_set = match env.new_string(text) {
Ok(s) => s,
Err(_) => return,
};
let clip_data = match env.call_static_method(
"android/content/ClipData",
"newPlainText",
"(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;",
&[JValue::from(&label), JValue::from(&text_to_set)],
) {
Ok(c) => match c.l() {
Ok(clip) => clip,
Err(_) => return,
},
Err(_) => return,
};
let _ = env.call_method(
&clipboard_manager,
"setPrimaryClip",
"(Landroid/content/ClipData;)V",
&[JValue::from(&clip_data)],
);
}
#[cfg(target_os = "android")]
fn clear_clipboard(android_app: &AndroidApp) {
let jvm = match unsafe { jni::JavaVM::from_raw(android_app.vm_as_ptr().cast()) } {
Ok(jvm) => jvm,
Err(_) => return,
};
let mut env = match jvm.attach_current_thread() {
Ok(env) => env,
Err(_) => return,
};
let activity = unsafe { JObject::from_raw(android_app.activity_as_ptr().cast()) };
if let Some(clipboard_manager) = get_clipboard_manager(&mut env, &activity) {
let _ = env.call_method(&clipboard_manager, "clearPrimaryClip", "()V", &[]);
}
}