1use tauri::{plugin::TauriPlugin, Runtime};
2
3#[cfg(target_os = "android")]
4use std::sync::atomic::{AtomicBool, Ordering};
5#[cfg(target_os = "android")]
6use std::sync::OnceLock;
7
8#[cfg(target_os = "android")]
9use tokio::sync::broadcast;
10
11#[cfg(target_os = "android")]
12const PLUGIN_IDENTIFIER: &str = "org.jakebot.blew";
13
14#[cfg(target_os = "android")]
15static AUTO_REQUEST_PERMISSIONS: AtomicBool = AtomicBool::new(true);
16
17#[cfg(target_os = "android")]
18static PERMISSIONS_TX: OnceLock<broadcast::Sender<BlePermissionStatus>> = OnceLock::new();
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum BlePermissionStatus {
23 Granted,
26 Denied,
28}
29
30impl BlePermissionStatus {
31 #[must_use]
32 pub fn is_granted(self) -> bool {
33 matches!(self, BlePermissionStatus::Granted)
34 }
35}
36
37#[derive(Clone, Debug)]
39pub struct BlewPluginConfig {
40 pub auto_request_permissions: bool,
45}
46
47impl Default for BlewPluginConfig {
48 fn default() -> Self {
49 Self {
50 auto_request_permissions: true,
51 }
52 }
53}
54
55pub fn are_ble_permissions_granted() -> bool {
59 #[cfg(target_os = "android")]
60 {
61 blew::platform::android::are_ble_permissions_granted()
62 }
63 #[cfg(not(target_os = "android"))]
64 {
65 true
66 }
67}
68
69pub fn request_ble_permissions() {
79 #[cfg(target_os = "android")]
80 {
81 blew::platform::android::request_ble_permissions();
82 }
83}
84
85#[cfg(target_os = "android")]
97pub fn permission_events() -> blew::util::BroadcastEventStream<BlePermissionStatus> {
98 let tx = PERMISSIONS_TX.get_or_init(|| broadcast::channel(16).0);
99 blew::util::BroadcastEventStream::new(tx.subscribe())
100}
101
102pub fn is_emulator() -> bool {
107 #[cfg(target_os = "android")]
108 {
109 blew::platform::android::is_emulator()
110 }
111 #[cfg(not(target_os = "android"))]
112 {
113 std::env::var("SIMULATOR_DEVICE_NAME").is_ok()
114 }
115}
116
117pub fn init<R: Runtime>() -> TauriPlugin<R> {
120 init_with_config(BlewPluginConfig::default())
121}
122
123pub fn init_with_config<R: Runtime>(config: BlewPluginConfig) -> TauriPlugin<R> {
125 #[cfg(target_os = "android")]
126 {
127 AUTO_REQUEST_PERMISSIONS.store(config.auto_request_permissions, Ordering::Relaxed);
128 let _ = PERMISSIONS_TX.get_or_init(|| broadcast::channel(16).0);
129 }
130 #[cfg(not(target_os = "android"))]
131 {
132 let _ = config;
133 }
134
135 tauri::plugin::Builder::<R>::new("blew")
136 .setup(|_app, api| {
137 #[cfg(target_os = "android")]
138 {
139 let ctx = ndk_context::android_context();
140 let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) };
141 blew::platform::android::init_jvm(vm);
142 api.register_android_plugin(PLUGIN_IDENTIFIER, "BlewPlugin")?;
143 }
144 let _ = api;
145 Ok(())
146 })
147 .build()
148}
149
150#[cfg(target_os = "android")]
158#[unsafe(no_mangle)]
159pub unsafe extern "C" fn Java_org_jakebot_blew_BlewPluginNative_autoRequestPermissionsEnabled(
160 _env: jni::EnvUnowned,
161 _class: jni::objects::JClass,
162) -> jni::sys::jboolean {
163 AUTO_REQUEST_PERMISSIONS.load(Ordering::Relaxed)
164}
165
166#[cfg(target_os = "android")]
175#[unsafe(no_mangle)]
176pub unsafe extern "C" fn Java_org_jakebot_blew_BlewPluginNative_onPermissionsChanged(
177 _env: jni::EnvUnowned,
178 _class: jni::objects::JClass,
179 granted: jni::sys::jboolean,
180) {
181 let status = if granted {
182 BlePermissionStatus::Granted
183 } else {
184 BlePermissionStatus::Denied
185 };
186 if let Some(tx) = PERMISSIONS_TX.get() {
187 let _ = tx.send(status);
188 }
189}