1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! The crate responsible for holding Makepad's Android-specific context states.
//!
//! These two states are:
//! 1. The JavaVM instance initialized by the JNI layer.
//!   * This cannot be set by foreign code outside this crate,
//!     as it is only ever set once during the lifetime of the app process.
//! 2. The current Makepad Activity instance.
//!   * This *can* be set by foreign code outside this crate,
//!     as the underlying Android platform may tear down and reconstruct
//!     the activity instance multiple times during the app's lifetime.
//!   * However, for safety reasons, we only permit a single caller
//!     to obtain the private "set_activity" function, which ensures that
//!     only the internal Makepad framework can set the activity instance.
//!
//! ## Usage
//! You probably want to use the [`robius-android-env`] crate instead of
//! using this crate directly.
//!
//! External users of this crate should only care about two functions:
//! 1. [`get_java_vm()`]: returns a pointer to the JavaVM instance,
//!    through which you can obtain the JNI environment.
//! 2. [`get_activity()`]: returns a pointer to the current Makepad Activity instance.
//!
//! The other functions are intended for Makepad-internal use only,
//! and will not be useful for external users.
//!
//! [`robius-android-env`]: https://github.com/project-robius/robius-android-env

use std::sync::Mutex;
use makepad_jni_sys as jni_sys;

static mut ACTIVITY: jni_sys::jobject = std::ptr::null_mut();
static mut VM: *mut jni_sys::JavaVM = std::ptr::null_mut();

static SET_ACTIVITY_FN: Mutex<Option<unsafe fn(jni_sys::jobject)>> = {
    unsafe fn set_activity(activity: jni_sys::jobject) {
        ACTIVITY = activity;
    }

    std::sync::Mutex::new(Some(set_activity))
};

/// Returns a function that can be used to set the current Makepad Activity instance.
///
/// This will return `Some` only once, which guarantees that only the
/// internal Makepad framework can obtain the function to set the activity instance.
#[doc(hidden)]
pub fn get_activity_setter_fn() -> Option<unsafe fn(jni_sys::jobject)> {
    SET_ACTIVITY_FN.lock().unwrap().take()
}

#[no_mangle]
#[doc(hidden)]
pub unsafe extern "C" fn JNI_OnLoad(
    vm: *mut jni_sys::JavaVM,
    _: std::ffi::c_void,
) -> jni_sys::jint {
    VM = vm as *mut _ as _;

    jni_sys::JNI_VERSION_1_6 as _
}

#[no_mangle]
extern "C" fn jni_on_load(vm: *mut std::ffi::c_void) {
    unsafe {
        VM = vm as _;
    }
}

/// Returns a raw pointer to the JavaVM instance initialized by the JNI layer.
///
/// If the JavaVM instance has not been initialized, this returns a null pointer.
#[inline(always)]
pub fn get_java_vm() -> *mut jni_sys::JavaVM {
    // SAFETY: just returning a raw pointer.
    unsafe { VM }
}

/// Returns a raw pointer to the main Makepad Activity instance.
///
/// Note that the caller should not cache or re-use the returned activity pointer,
/// but should instead re-call this function whenever the activity instance is needed.
/// This is because the activity instance may be destroyed and recreated behind the scenes
/// upon certain system actions, e.g., when the device is rotated,
/// the app is put into split screen, resized/moved, etc.
///
/// If the Activity instance has not been initialized, this returns a null pointer.
#[inline(always)]
pub fn get_activity() -> jni_sys::jobject {
    // SAFETY: just returning a raw pointer.
    unsafe { ACTIVITY }
}