xloop_android 0.1.0

android impl for xloop.
Documentation
use std::{
	cell::RefCell,
	ops::Deref,
	rc::Rc,
	sync::{atomic::AtomicBool, Arc},
};

use jnim::*;

use super::android;
use super::Platform;

mod proxy;
pub use proxy::*;

#[derive(Clone, Copy)]
pub struct AppRef<'a>(pub &'a ());
impl<'a> types::AppRef<Platform> for AppRef<'a> {
	fn terminate(&self) {
		// not surport
		// or send terminate message
	}
	fn proxy(&self) -> Option<AppProxy> {
		with_app(|vars| {
			if let Some(sender) = vars.sender.clone() {
				Some(AppProxy(vars.proxy.clone(), sender))
			} else {
				None
			}
		})
	}
}

pub struct AppHandle(&'static JEnv, JRc<android::app::Application>);
impl Deref for AppHandle {
	type Target = android::app::Application;
	fn deref(&self) -> &Self::Target {
		&self.1
	}
}
impl AppHandle {
	pub fn env(&self) -> &'static JEnv {
		self.0
	}
	pub fn assets() -> Option<ndkm::Assets> {
		with_app(|app| app.assets.clone())
	}
}
impl types::AppHandle<Platform> for AppHandle {
	fn singleton() -> Option<Self> {
		with_app(|app| {
			let env: &'static JEnv = JEnv::env(None)?;
			let app = app.application.as_ref()?;
			Some(AppHandle(env, app.global(env)?))
		})
	}
	fn with(&self, mut fun: impl FnMut(<Platform as types::Platform>::AppRef<'_>)) {
		fun(AppRef(&()))
	}
}

pub fn application_launched() {
	with_app_mut(|vars| {
		let (sender, recver) = ndkm::pipe();
		let fd = recver.fd_read;
		let cmd = Rc::new(AppCmd(recver));
		let recver = ndkm::Looper::current()?.add(fd, &cmd).ok()?;
		vars.recver = Some((cmd, recver));
		vars.sender = Some(sender);
		Some(())
	});
	with_app(|app| {
		app.delegate.as_ref()?.on_launch(AppRef(&()));
		Some(())
	});
}
pub fn application_terminate() {
	with_app(|app| {
		app.delegate.as_ref()?.on_terminate(AppRef(&()));
		Some(())
	});
	with_app_mut(|vars| {
		vars.sender = None;
		vars.recver = None;
		vars.delegate = None;
		vars.assets = None;
		vars._assets_java = None;
		Some(())
	});
}

pub(crate) fn bind_global_if_need(env: &JEnv, application: Option<&android::app::Application>) {
	let Some(application) = application else {
		return;
	};
	with_app_mut(|app| {
		if app.application.is_none() {
			app.application = application.global(env);
			let assets_java = application.get_assets(env)?;
			app._assets_java = assets_java.global(env);
			let assets = Some(ndkm::Assets::from_java(env.as_sys(), assets_java.as_sys()));
			app.assets = assets;
		}
		Some(())
	});
}

pub type RcDynAppDelegate = Rc<dyn types::AppDelegate<Platform>>;

pub struct App {
	delegate: Option<RcDynAppDelegate>,
	assets: Option<ndkm::Assets>,
	_assets_java: Option<JRc<android::content::res::AssetManager>>,
	application: Option<JRc<android::app::Application>>,
	proxy: Arc<AtomicBool>,
	sender: Option<ndkm::Sender<i64>>,
	recver: Option<(Rc<AppCmd>, ndkm::LooperGuard<AppCmd>)>,
}

thread_local! {
	static CACHE:RefCell<App> = RefCell::new(App {
		delegate: None,
		assets: None,
		_assets_java:None,
		application:None,
		proxy:Arc::new(AtomicBool::new(false)),
		sender:None,
		recver:None,
	});
}
pub(super) fn set_delegate(delegate: RcDynAppDelegate) {
	CACHE.with_borrow_mut(|cache| cache.delegate = Some(delegate))
}
fn with_app_mut<T>(mut fun: impl FnMut(&mut App) -> Option<T>) -> Option<T> {
	CACHE.with_borrow_mut(|app| fun(app))
}
fn with_app<T>(mut fun: impl FnMut(&App) -> Option<T>) -> Option<T> {
	CACHE.with_borrow(|app| fun(app))
}