use tauri::{
async_runtime,
plugin::{Builder, TauriPlugin},
Wry,
};
mod commands;
#[cfg(feature = "mock")]
pub use blec::mock;
pub use blec::models;
pub use blec::{
check_permissions, get_handler, Error, Handler, OnDisconnectHandler, Result,
SubscriptionHandler, Timeouts, TimeoutsMs, ALLOW_IBEACONS,
};
pub fn try_init() -> Result<TauriPlugin<Wry>> {
#[cfg(not(target_os = "android"))]
async_runtime::block_on(blec::init())?;
let plugin = Builder::new("blec")
.invoke_handler(commands::commands())
.on_event(|_app, event| {
#[cfg(target_os = "android")]
if matches!(event, tauri::RunEvent::Ready) {
init_android();
}
if matches!(
event,
tauri::RunEvent::Exit | tauri::RunEvent::ExitRequested { .. }
) {
if let Ok(handler) = get_handler() {
if handler.is_connected() {
let _ = async_runtime::block_on(handler.disconnect());
}
}
}
})
.build();
Ok(plugin)
}
#[must_use]
pub fn init() -> TauriPlugin<Wry> {
try_init().expect("failed to initialize plugin")
}
#[cfg(target_os = "android")]
fn init_android() {
tauri::wry::prelude::dispatch(|env, activity, _webview| {
let bridge =
unsafe { blec::android::init_with(env.get_raw().cast(), activity.as_raw().cast()) };
let result = bridge
.and_then(|()| async_runtime::block_on(blec::init()).map(|_| ()))
.map_err(|e| e.to_string());
if let Err(e) = &result {
tracing::error!("could not initialize blec on android: {e}");
}
android_init().send_replace(Some(result));
});
}
#[cfg(target_os = "android")]
fn android_init() -> &'static tokio::sync::watch::Sender<Option<std::result::Result<(), String>>> {
static INIT: std::sync::OnceLock<
tokio::sync::watch::Sender<Option<std::result::Result<(), String>>>,
> = std::sync::OnceLock::new();
INIT.get_or_init(|| tokio::sync::watch::channel(None).0)
}
#[cfg(target_os = "android")]
const ANDROID_INIT_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(10);
pub(crate) async fn ready() -> Result<()> {
#[cfg(target_os = "android")]
{
let mut init = android_init().subscribe();
let mut state = init.borrow_and_update().clone();
if state.is_none() {
if tokio::time::timeout(ANDROID_INIT_TIMEOUT, init.changed())
.await
.is_err()
{
tracing::error!(
"blec was not initialized within {ANDROID_INIT_TIMEOUT:?} of the app \
becoming ready"
);
return Err(Error::HandlerNotInitialized);
}
state = init.borrow_and_update().clone();
}
match state {
Some(Ok(())) => {}
Some(Err(e)) => return Err(Error::Android(e)),
None => return Err(Error::HandlerNotInitialized),
}
}
Ok(())
}
pub(crate) async fn handler() -> Result<&'static Handler> {
ready().await?;
get_handler()
}