Skip to main content

tauri_plugin_blew/
lib.rs

1use tauri::{plugin::TauriPlugin, Runtime};
2
3#[cfg(target_os = "android")]
4use std::sync::atomic::{AtomicBool, Ordering};
5
6#[cfg(target_os = "android")]
7const PLUGIN_IDENTIFIER: &str = "org.jakebot.blew";
8
9#[cfg(target_os = "android")]
10static AUTO_REQUEST_PERMISSIONS: AtomicBool = AtomicBool::new(true);
11
12/// Plugin configuration.
13#[derive(Clone, Debug)]
14pub struct BlewPluginConfig {
15    /// If `true` (default), Android BLE runtime permissions are requested
16    /// immediately when the plugin loads. Set to `false` to defer the request
17    /// so your app can show an explanation modal first, then call
18    /// [`request_ble_permissions`] when the user is ready.
19    pub auto_request_permissions: bool,
20}
21
22impl Default for BlewPluginConfig {
23    fn default() -> Self {
24        Self {
25            auto_request_permissions: true,
26        }
27    }
28}
29
30/// Check whether Android BLE runtime permissions have been granted.
31///
32/// Always returns `true` on non-Android platforms.
33pub fn are_ble_permissions_granted() -> bool {
34    #[cfg(target_os = "android")]
35    {
36        blew::platform::android::are_ble_permissions_granted()
37    }
38    #[cfg(not(target_os = "android"))]
39    {
40        true
41    }
42}
43
44/// Trigger the Android BLE runtime permissions dialog.
45///
46/// Fire-and-forget — the dialog is presented asynchronously on the host
47/// activity's UI thread. Poll [`are_ble_permissions_granted`] (or retry
48/// `blew::Central::new` / `blew::Peripheral::new`) to detect when the user
49/// responds.
50///
51/// Must be called after the Tauri app has finished initializing the plugin
52/// (the Android side needs the host `Activity`, which is captured during
53/// plugin load). No-op on non-Android platforms.
54pub fn request_ble_permissions() {
55    #[cfg(target_os = "android")]
56    {
57        blew::platform::android::request_ble_permissions();
58    }
59}
60
61/// Check whether the app is running on an emulator or simulator.
62///
63/// Returns `true` on Android emulators and iOS simulators, `false` on real devices
64/// and non-mobile platforms.
65pub fn is_emulator() -> bool {
66    #[cfg(target_os = "android")]
67    {
68        blew::platform::android::is_emulator()
69    }
70    #[cfg(not(target_os = "android"))]
71    {
72        std::env::var("SIMULATOR_DEVICE_NAME").is_ok()
73    }
74}
75
76/// Initialize the plugin with default configuration (auto-requests BLE
77/// permissions on load).
78pub fn init<R: Runtime>() -> TauriPlugin<R> {
79    init_with_config(BlewPluginConfig::default())
80}
81
82/// Initialize the plugin with a custom [`BlewPluginConfig`].
83pub fn init_with_config<R: Runtime>(config: BlewPluginConfig) -> TauriPlugin<R> {
84    #[cfg(target_os = "android")]
85    {
86        AUTO_REQUEST_PERMISSIONS.store(config.auto_request_permissions, Ordering::Relaxed);
87    }
88    #[cfg(not(target_os = "android"))]
89    {
90        let _ = config;
91    }
92
93    tauri::plugin::Builder::<R>::new("blew")
94        .setup(|_app, api| {
95            #[cfg(target_os = "android")]
96            {
97                let ctx = ndk_context::android_context();
98                let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) };
99                blew::platform::android::init_jvm(vm);
100                api.register_android_plugin(PLUGIN_IDENTIFIER, "BlewPlugin")?;
101            }
102            let _ = api;
103            Ok(())
104        })
105        .build()
106}
107
108/// JNI entry point invoked from `BlewPluginNative.autoRequestPermissionsEnabled()`
109/// on Android to read the flag set by [`init_with_config`].
110///
111/// # Safety
112///
113/// Invoked by the JVM through the normal JNI calling convention; safe provided
114/// the signature matches the Kotlin `external fun` declaration.
115#[cfg(target_os = "android")]
116#[unsafe(no_mangle)]
117pub unsafe extern "C" fn Java_org_jakebot_blew_BlewPluginNative_autoRequestPermissionsEnabled(
118    _env: jni::EnvUnowned,
119    _class: jni::objects::JClass,
120) -> jni::sys::jboolean {
121    AUTO_REQUEST_PERMISSIONS.load(Ordering::Relaxed)
122}