discord-rpc-client/src/java.rs

250 lines
9.4 KiB
Rust

use log::{log, log_enabled, trace, debug, info, warn, error};
use std::sync::{Arc, MutexGuard};
use jni::JNIEnv;
use jni::objects::{JClass, JString, JObject, JValue};
use jni::signature::{JavaType, Primitive};
use jni::sys::jstring;
pub use jni;
use crate as drpc;
const SIG_BOOL: &'static str = "Z";
const SIG_INT: &'static str = "I";
const SIG_LONG: &'static str = "J";
const SIG_NULLABLE_LONG: &'static str = "Ljava/lang/Long;";
const SIG_STRING: &'static str = "Ljava/lang/String;";
const NAME_DISCORD_RPC: &'static str = "com/discord/rpc/DiscordRPC";
lazy_static::lazy_static! {
static ref RUNTIME: Arc<tokio::runtime::Runtime> = Arc::new(tokio::runtime::Runtime::new().expect("unable to create tokio runtime"));
}
fn debug_and_discard_err<T, E: core::fmt::Debug>(result: Result<T, E>) -> Result<T, ()> {
result.map_err(|e| {
error!("{:?}", e);
()
})
}
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 !debug_and_discard_err(env.is_instance_of(obj, NAME_DISCORD_RPC))? {
error!("not an instance of DiscordRPC");
return Err(())
}
let client = env.get_rust_field::<_, _, drpc::Client>(obj, "handle");
debug_and_discard_err(client)
}
#[inline(always)]
pub fn Java_com_discord_rpc_DiscordRPC_create0<'a>(env: JNIEnv<'a>, _class: JClass) -> Result<JObject<'a>, ()> {
let client = drpc::Client::default();
let jobj = debug_and_discard_err(env.alloc_object(NAME_DISCORD_RPC))?;
debug_and_discard_err(env.set_rust_field(jobj, "handle", client))?;
Ok(jobj)
}
#[inline(always)]
pub fn Java_com_discord_rpc_DiscordRPC_connect0(env: JNIEnv, obj: JObject, client_id: JString) -> Result<(), ()> {
let client = get_client(&env, obj)?;
let client_id = debug_and_discard_err(
debug_and_discard_err(
debug_and_discard_err(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 });
}
}
debug_and_discard_err(RUNTIME.block_on(async { client.connect(client_id).await }))?;
Ok(())
}
#[inline(always)]
pub fn Java_com_discord_rpc_DiscordRPC_disconnect0(env: JNIEnv, obj: JObject) -> Result<(), ()> {
let client = get_client(&env, obj)?;
RUNTIME.block_on(async { client.disconnect().await });
Ok(())
}
#[inline(always)]
pub fn Java_com_discord_rpc_DiscordRPC_setActivity0(env: JNIEnv, obj: JObject, j_activity: JObject) -> Result<(), ()> {
let client = get_client(&env, obj)?;
let activity = jobject_to_activity(env, j_activity)?;
debug_and_discard_err(RUNTIME.block_on(async { client.set_activity(activity).await }))?;
Ok(())
}
#[inline(always)]
pub fn Java_com_discord_rpc_DiscordRPC_clearActivity0(env: JNIEnv, obj: JObject) -> Result<(), ()> {
let client = get_client(&env, obj)?;
debug_and_discard_err(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", SIG_STRING).map_err(|_| ())?;
let j_details = env.get_field(jobject, "details", SIG_STRING).map_err(|_| ())?;
let j_instance = env.get_field(jobject, "instance", SIG_BOOL).map_err(|_| ())?;
let j_timestamps = env.get_field(jobject, "timestamps", "Lcom/discord/rpc/ActivityTimestamps;").map_err(|_| ())?;
let j_assets = env.get_field(jobject, "assets", "Lcom/discord/rpc/ActivityAssets;").map_err(|_| ())?;
let j_party = env.get_field(jobject, "party", "Lcom/discord/rpc/ActivityParty;").map_err(|_| ())?;
let j_secrets = env.get_field(jobject, "secrets", "Lcom/discord/rpc/ActivitySecrets;").map_err(|_| ())?;
let mut activity = drpc::models::Activity::new();
if let JValue::Object(obj) = j_state {
if !is_null(env, obj).map_err(|_| ())? {
activity = activity.state(env.get_string(obj.into()).map_err(|_| ())?);
}
}
if let JValue::Object(obj) = j_details {
if !is_null(env, obj).map_err(|_| ())? {
activity = activity.details(env.get_string(obj.into()).map_err(|_| ())?);
}
}
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).map_err(|_| ())? {
let timestamps = jobject_to_activity_timestamps(env, obj)?;
activity = activity.timestamps(|_|timestamps);
}
}
if let JValue::Object(obj) = j_assets {
if !is_null(env, obj).map_err(|_| ())? {
let assets = jobject_to_activity_assets(env, obj)?;
activity = activity.assets(|_|assets);
}
}
if let JValue::Object(obj) = j_party {
if !is_null(env, obj).map_err(|_| ())? {
let party = jobject_to_activity_party(env, obj)?;
activity = activity.party(|_|party);
}
}
if let JValue::Object(obj) = j_secrets {
if !is_null(env, obj).map_err(|_| ())? {
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 = debug_and_discard_err(env.get_field(jobject, "start", SIG_NULLABLE_LONG))?;
let j_end = debug_and_discard_err(env.get_field(jobject, "end", SIG_NULLABLE_LONG))?;
let mut timestamps = drpc::models::ActivityTimestamps::new();
if let JValue::Object(obj) = j_start {
if !is_null(env, obj).map_err(|_| ())? {
if let JValue::Long(l) = debug_and_discard_err(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).map_err(|_| ())? {
if let JValue::Long(l) = debug_and_discard_err(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", SIG_STRING).map_err(|_| ())?;
let j_lrg_txt = env.get_field(jobject, "largeText", SIG_STRING).map_err(|_| ())?;
let j_sml_img = env.get_field(jobject, "smallImage", SIG_STRING).map_err(|_| ())?;
let j_sml_txt = env.get_field(jobject, "smallText", SIG_STRING).map_err(|_| ())?;
let mut assets = drpc::models::ActivityAssets::new();
if let JValue::Object(obj) = j_lrg_img {
if !is_null(env, obj).map_err(|_| ())? {
assets = assets.large_image(env.get_string(obj.into()).map_err(|_| ())?);
}
}
if let JValue::Object(obj) = j_lrg_txt {
if !is_null(env, obj).map_err(|_| ())? {
assets = assets.large_text(env.get_string(obj.into()).map_err(|_| ())?);
}
}
if let JValue::Object(obj) = j_sml_img {
if !is_null(env, obj).map_err(|_| ())? {
assets = assets.small_image(env.get_string(obj.into()).map_err(|_| ())?);
}
}
if let JValue::Object(obj) = j_sml_txt {
if !is_null(env, obj).map_err(|_| ())? {
assets = assets.small_text(env.get_string(obj.into()).map_err(|_| ())?);
}
}
Ok(assets)
}
fn jobject_to_activity_party(env: JNIEnv, jobject: JObject) -> Result<drpc::models::ActivityParty, ()> {
let j_id = env.get_field(jobject, "id", SIG_INT).map_err(|_| ())?;
let j_min_size = env.get_field(jobject, "minSize", SIG_INT).map_err(|_| ())?;
let j_max_size = env.get_field(jobject, "maxSize", SIG_INT).map_err(|_| ())?;
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", SIG_STRING).map_err(|_| ())?;
let j_spectate = env.get_field(jobject, "spectate", SIG_STRING).map_err(|_| ())?;
let j_game = env.get_field(jobject, "game", SIG_STRING).map_err(|_| ())?;
let mut secrets = drpc::models::ActivitySecrets::new();
if let JValue::Object(obj) = j_join {
if !is_null(env, obj).map_err(|_| ())? {
secrets = secrets.join(env.get_string(obj.into()).map_err(|_| ())?);
}
}
if let JValue::Object(obj) = j_spectate {
if !is_null(env, obj).map_err(|_| ())? {
secrets = secrets.spectate(env.get_string(obj.into()).map_err(|_| ())?);
}
}
if let JValue::Object(obj) = j_game {
if !is_null(env, obj).map_err(|_| ())? {
secrets = secrets.game(env.get_string(obj.into()).map_err(|_| ())?);
}
}
Ok(secrets)
}