maverick_os 0.2.5

Maverick OS
Documentation
use jni::objects::{GlobalRef, JObject};
use jni::{JNIEnv, JavaVM};
use std::sync::Arc;

#[derive(Clone)]
pub struct OsClipboard {
    vm: Arc<JavaVM>,
    context: GlobalRef,
}

impl OsClipboard {
    pub fn new(vm: &JavaVM) -> Self {
        let vm = Arc::new(unsafe { JavaVM::from_raw(vm.get_java_vm_pointer()).unwrap() });
        
        let context = {
            let mut env = vm.attach_current_thread().expect("Failed to attach thread");
            let context_obj = ndk_context::android_context().context().cast();
            let context_obj = unsafe { JObject::from_raw(context_obj) };
            env.new_global_ref(context_obj).expect("Failed to create global ref")
        };
        
        Self { vm, context }
    }

    pub fn get_content(&self) -> String {
        self.get_content_impl().unwrap_or_default()
    }

    fn get_content_impl(&self) -> Result<String, Box<dyn std::error::Error>> {
        let mut env = self.vm.attach_current_thread()?;
        let context = self.context.as_obj();

        let clipboard_string = env.new_string("clipboard")?;
        let clipboard_service = env
            .call_method(
                context,
                "getSystemService",
                "(Ljava/lang/String;)Ljava/lang/Object;",
                &[(&clipboard_string).into()],
            )?
            .l()?;

        let clipboard_manager = JObject::from(clipboard_service);

        let primary_clip = env
            .call_method(
                clipboard_manager,
                "getPrimaryClip",
                "()Landroid/content/ClipData;",
                &[],
            )?
            .l()?;

        if primary_clip.is_null() {
            return Ok(String::new());
        }

        let item_count = env
            .call_method(&primary_clip, "getItemCount", "()I", &[])?
            .i()?;
        if item_count == 0 {
            return Ok(String::new());
        }

        let clip_item = env
            .call_method(
                primary_clip,
                "getItemAt",
                "(I)Landroid/content/ClipData$Item;",
                &[0i32.into()],
            )?
            .l()?;

        let text = env
            .call_method(
                clip_item,
                "getText",
                "()Ljava/lang/CharSequence;",
                &[],
            )?
            .l()?;
        if text.is_null() {
            return Ok(String::new());
        }

        let java_string = env.call_method(text, "toString", "()Ljava/lang/String;", &[])?.l()?;
        let rust_string = env.get_string(&java_string.into())?.into();
        Ok(rust_string)
    }

    pub fn set_content(&self, text: String) {
        let _ = self.set_content_impl(text);
    }

    fn set_content_impl(&self, text: String) -> Result<(), Box<dyn std::error::Error>> {
        let mut env = self.vm.attach_current_thread()?;
        let context = self.context.as_obj();

        let clipboard_string = env.new_string("clipboard")?;
        let clipboard_service = env
            .call_method(
                context,
                "getSystemService",
                "(Ljava/lang/String;)Ljava/lang/Object;",
                &[(&clipboard_string).into()],
            )?
            .l()?;

        let clipboard_manager = JObject::from(clipboard_service);

        let clip_data_class = env.find_class("android/content/ClipData")?;
        let label = env.new_string("label")?;
        let text_string = env.new_string(&text)?;
        let clip_data = env
            .call_static_method(
                clip_data_class,
                "newPlainText",
                "(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Landroid/content/ClipData;",
                &[(&JObject::from(label)).into(), (&JObject::from(text_string)).into()],
            )?
            .l()?;

        env.call_method(
            clipboard_manager,
            "setPrimaryClip",
            "(Landroid/content/ClipData;)V",
            &[(&clip_data).into()],
        )?;

        Ok(())
    }
}