280 lines
8.8 KiB
Rust
280 lines
8.8 KiB
Rust
#![allow(clippy::await_holding_lock)]
|
|
|
|
use std::sync::MutexGuard;
|
|
|
|
use anyhow::Result;
|
|
use jni::objects::{JClass, JObject, JString, JValue};
|
|
use jni::signature::{JavaType, Primitive};
|
|
use jni::JNIEnv;
|
|
|
|
pub use jni;
|
|
|
|
use crate as drpc;
|
|
|
|
mod jvm_types {
|
|
pub const BOOLEAN: &str = "java/lang/Boolean";
|
|
pub const INTEGER: &str = "java/lang/Integer";
|
|
pub const LONG: &str = "java/lang/Long";
|
|
pub const STRING: &str = "java/lang/String";
|
|
}
|
|
|
|
macro_rules! signature {
|
|
(bool) => {
|
|
"Z"
|
|
};
|
|
(i32) => {
|
|
"I"
|
|
};
|
|
(i64) => {
|
|
"J"
|
|
};
|
|
(class $path:expr) => {
|
|
const_format::formatcp!("L{};", $path)
|
|
};
|
|
}
|
|
|
|
const PATH_DISCORD_RPC: &str = "com/discord/rpc/DiscordRPC";
|
|
|
|
lazy_static::lazy_static! {
|
|
static ref RUNTIME: tokio::runtime::Runtime = tokio::runtime::Runtime::new().expect("unable to create tokio runtime");
|
|
}
|
|
|
|
fn is_null<'b, O>(env: JNIEnv, ref1: O) -> jni::errors::Result<bool>
|
|
where
|
|
O: Into<JObject<'b>>,
|
|
{
|
|
env.is_same_object(ref1, JObject::null())
|
|
}
|
|
|
|
// taking the [`JNIEnv`] as a reference is needed to satisfy the lifetime checker.
|
|
fn get_client<'a>(env: &'a JNIEnv<'a>, obj: JObject<'a>) -> Result<MutexGuard<'a, drpc::Client>> {
|
|
if !env.is_instance_of(obj, PATH_DISCORD_RPC)? {
|
|
bail!("not an instance of DiscordRPC");
|
|
}
|
|
|
|
let client = env.get_rust_field::<_, _, drpc::Client>(obj, "handle")?;
|
|
Ok(client)
|
|
}
|
|
|
|
// TODO: method to destory afterwards.
|
|
#[inline(always)]
|
|
pub fn jni_create<'a>(env: JNIEnv<'a>, _class: JClass) -> Result<JObject<'a>> {
|
|
let client = drpc::Client::default();
|
|
|
|
let jobj = env.alloc_object(PATH_DISCORD_RPC)?;
|
|
env.set_rust_field(jobj, "handle", client)?;
|
|
Ok(jobj)
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn jni_connect(env: JNIEnv, obj: JObject, client_id: JString) -> Result<()> {
|
|
let client = get_client(&env, obj)?;
|
|
|
|
let client_id = env.get_string(client_id)?.to_str()?.parse::<u64>()?;
|
|
|
|
if let Some(current_client_id) = client.client_id() {
|
|
if current_client_id != client_id {
|
|
RUNTIME.block_on(async { client.disconnect().await });
|
|
}
|
|
}
|
|
|
|
RUNTIME.block_on(async { client.connect(client_id).await })?;
|
|
Ok(())
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn jni_disconnect(env: JNIEnv, obj: JObject) -> Result<()> {
|
|
let client = get_client(&env, obj)?;
|
|
|
|
RUNTIME.block_on(async { client.disconnect().await });
|
|
Ok(())
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn jni_set_activity(env: JNIEnv, obj: JObject, j_activity: JObject) -> Result<()> {
|
|
let client = get_client(&env, obj)?;
|
|
|
|
let activity = jobject_to_activity(env, j_activity)?;
|
|
RUNTIME.block_on(async { client.set_activity(activity).await })?;
|
|
Ok(())
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn jni_clear_activity(env: JNIEnv, obj: JObject) -> Result<()> {
|
|
let client = get_client(&env, obj)?;
|
|
|
|
RUNTIME.block_on(async { client.clear_activity().await })?;
|
|
Ok(())
|
|
}
|
|
|
|
fn jobject_to_activity(env: JNIEnv, jobject: JObject) -> Result<drpc::models::Activity> {
|
|
let j_state = env.get_field(jobject, "state", signature!(class jvm_types::STRING))?;
|
|
let j_details = env.get_field(jobject, "details", signature!(class jvm_types::STRING))?;
|
|
let j_instance = env.get_field(jobject, "instance", signature!(bool))?;
|
|
let j_timestamps = env.get_field(
|
|
jobject,
|
|
"timestamps",
|
|
signature!(class "com/discord/rpc/ActivityTimestamps"),
|
|
)?;
|
|
let j_assets = env.get_field(jobject, "assets", signature!(class "com/discord/rpc/ActivityAssets"))?;
|
|
let j_party = env.get_field(jobject, "party", signature!(class "com/discord/rpc/ActivityParty"))?;
|
|
let j_secrets = env.get_field(jobject, "secrets", signature!(class "com/discord/rpc/ActivitySecrets"))?;
|
|
|
|
let mut activity = drpc::models::Activity::new();
|
|
if let JValue::Object(obj) = j_state {
|
|
if !is_null(env, obj)? {
|
|
activity = activity.state(env.get_string(obj.into())?);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_details {
|
|
if !is_null(env, obj)? {
|
|
activity = activity.details(env.get_string(obj.into())?);
|
|
}
|
|
}
|
|
if let JValue::Bool(b) = j_instance {
|
|
if b != 0 {
|
|
activity = activity.instance(true);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_timestamps {
|
|
if !is_null(env, obj)? {
|
|
let timestamps = jobject_to_activity_timestamps(env, obj)?;
|
|
activity = activity.timestamps(|_| timestamps);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_assets {
|
|
if !is_null(env, obj)? {
|
|
let assets = jobject_to_activity_assets(env, obj)?;
|
|
activity = activity.assets(|_| assets);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_party {
|
|
if !is_null(env, obj)? {
|
|
let party = jobject_to_activity_party(env, obj)?;
|
|
activity = activity.party(|_| party);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_secrets {
|
|
if !is_null(env, obj)? {
|
|
let secrets = jobject_to_activity_secrets(env, obj)?;
|
|
activity = activity.secrets(|_| secrets);
|
|
}
|
|
}
|
|
|
|
Ok(activity)
|
|
}
|
|
|
|
fn jobject_to_activity_timestamps(
|
|
env: JNIEnv,
|
|
jobject: JObject,
|
|
) -> Result<drpc::models::ActivityTimestamps> {
|
|
let j_start = env.get_field(jobject, "start", signature!(class jvm_types::LONG))?;
|
|
let j_end = env.get_field(jobject, "end", signature!(class jvm_types::LONG))?;
|
|
|
|
let mut timestamps = drpc::models::ActivityTimestamps::new();
|
|
if let JValue::Object(obj) = j_start {
|
|
if !is_null(env, obj)? {
|
|
if let JValue::Long(l) = env.call_method_unchecked(
|
|
obj,
|
|
(obj, "longValue", "()J"),
|
|
JavaType::Primitive(Primitive::Long),
|
|
&[],
|
|
)? {
|
|
timestamps = timestamps.start(l as u64);
|
|
}
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_end {
|
|
if !is_null(env, obj)? {
|
|
if let JValue::Long(l) = env.call_method_unchecked(
|
|
obj,
|
|
(obj, "longValue", "()J"),
|
|
JavaType::Primitive(Primitive::Long),
|
|
&[],
|
|
)? {
|
|
timestamps = timestamps.end(l as u64);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(timestamps)
|
|
}
|
|
|
|
fn jobject_to_activity_assets(
|
|
env: JNIEnv,
|
|
jobject: JObject,
|
|
) -> Result<drpc::models::ActivityAssets> {
|
|
let j_lrg_img = env.get_field(jobject, "largeImage", signature!(class jvm_types::STRING))?;
|
|
let j_lrg_txt = env.get_field(jobject, "largeText", signature!(class jvm_types::STRING))?;
|
|
let j_sml_img = env.get_field(jobject, "smallImage", signature!(class jvm_types::STRING))?;
|
|
let j_sml_txt = env.get_field(jobject, "smallText", signature!(class jvm_types::STRING))?;
|
|
|
|
let mut assets = drpc::models::ActivityAssets::new();
|
|
if let JValue::Object(obj) = j_lrg_img {
|
|
if !is_null(env, obj)? {
|
|
assets = assets.large_image(env.get_string(obj.into())?);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_lrg_txt {
|
|
if !is_null(env, obj)? {
|
|
assets = assets.large_text(env.get_string(obj.into())?);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_sml_img {
|
|
if !is_null(env, obj)? {
|
|
assets = assets.small_image(env.get_string(obj.into())?);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_sml_txt {
|
|
if !is_null(env, obj)? {
|
|
assets = assets.small_text(env.get_string(obj.into())?);
|
|
}
|
|
}
|
|
|
|
Ok(assets)
|
|
}
|
|
|
|
fn jobject_to_activity_party(env: JNIEnv, jobject: JObject) -> Result<drpc::models::ActivityParty> {
|
|
let j_id = env.get_field(jobject, "id", signature!(i32))?;
|
|
let j_min_size = env.get_field(jobject, "minSize", signature!(i32))?;
|
|
let j_max_size = env.get_field(jobject, "maxSize", signature!(i32))?;
|
|
|
|
let mut party = drpc::models::ActivityParty::new();
|
|
if let JValue::Int(l) = j_id {
|
|
party = party.id(l as u32);
|
|
}
|
|
if let (JValue::Int(l1), JValue::Int(l2)) = (j_min_size, j_max_size) {
|
|
party = party.size((l1 as u32, l2 as u32));
|
|
}
|
|
|
|
Ok(party)
|
|
}
|
|
|
|
fn jobject_to_activity_secrets(
|
|
env: JNIEnv,
|
|
jobject: JObject,
|
|
) -> Result<drpc::models::ActivitySecrets> {
|
|
let j_join = env.get_field(jobject, "join", signature!(class jvm_types::STRING))?;
|
|
let j_spectate = env.get_field(jobject, "spectate", signature!(class jvm_types::STRING))?;
|
|
let j_game = env.get_field(jobject, "game", signature!(class jvm_types::STRING))?;
|
|
|
|
let mut secrets = drpc::models::ActivitySecrets::new();
|
|
if let JValue::Object(obj) = j_join {
|
|
if !is_null(env, obj)? {
|
|
secrets = secrets.join(env.get_string(obj.into())?);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_spectate {
|
|
if !is_null(env, obj)? {
|
|
secrets = secrets.spectate(env.get_string(obj.into())?);
|
|
}
|
|
}
|
|
if let JValue::Object(obj) = j_game {
|
|
if !is_null(env, obj)? {
|
|
secrets = secrets.game(env.get_string(obj.into())?);
|
|
}
|
|
}
|
|
|
|
Ok(secrets)
|
|
}
|