crossbow_android/plugin/
jni_singleton.rs

1use super::JniRustType;
2use async_channel::Receiver;
3use jni::{
4    errors::*,
5    objects::{GlobalRef, JClass, JObject, JValue},
6    signature::{JavaType, TypeSignature},
7    JNIEnv,
8};
9use std::collections::HashMap;
10
11#[derive(Clone)]
12pub struct JniSingleton {
13    name: String,
14    instance: GlobalRef,
15    methods: HashMap<String, JniSingletonMethod>,
16    signals: HashMap<String, Vec<JavaType>>,
17    receiver: Receiver<Signal>,
18}
19
20#[derive(Clone, Debug)]
21pub struct Signal {
22    pub name: String,
23    pub args: Vec<JniRustType>,
24}
25
26#[derive(Clone)]
27pub struct JniSingletonMethod {
28    class: GlobalRef,
29    signature: TypeSignature,
30}
31
32impl JniSingleton {
33    pub fn new(name: &str, instance: GlobalRef, receiver: Receiver<Signal>) -> Self {
34        Self {
35            name: name.to_string(),
36            instance,
37            methods: HashMap::new(),
38            signals: HashMap::new(),
39            receiver,
40        }
41    }
42
43    pub fn get_instance(&self) -> JObject {
44        self.instance.as_obj()
45    }
46
47    pub fn get_name(&self) -> &str {
48        &self.name
49    }
50
51    pub fn get_receiver(&self) -> &Receiver<Signal> {
52        &self.receiver
53    }
54
55    pub fn get_method(&self, name: &str) -> Option<&JniSingletonMethod> {
56        self.methods.get(name)
57    }
58
59    pub fn get_methods(&self) -> &HashMap<String, JniSingletonMethod> {
60        &self.methods
61    }
62
63    pub(crate) fn add_method(&mut self, name: &str, class: GlobalRef, signature: TypeSignature) {
64        self.methods
65            .insert(name.to_owned(), JniSingletonMethod { class, signature });
66    }
67
68    pub(crate) fn add_signal_info(&mut self, name: &str, args: Vec<JavaType>) {
69        self.signals.insert(name.to_owned(), args);
70    }
71
72    pub fn call_method<'a>(
73        &'a self,
74        env: &'a JNIEnv,
75        name: &str,
76        args: &[JValue],
77    ) -> Result<JValue<'a>> {
78        let method = match self.get_method(name) {
79            Some(method) => method,
80            None => Err(Error::MethodNotFound {
81                name: name.to_owned(),
82                sig: "".to_owned(),
83            })?,
84        };
85        let class: JClass = method.class.as_obj().into();
86        let method_id = env.get_method_id(class, name, method.signature.to_string())?;
87
88        let result = env.call_method_unchecked(
89            self.get_instance(),
90            method_id,
91            method.signature.ret.clone(),
92            args,
93        )?;
94        env.exception_check()?;
95        Ok(result)
96    }
97}