Skip to main content

android_context/
lib.rs

1//! WIP successor to `ndk-context`
2
3use jni::{JavaVM, jni_sig, jni_str, objects::JObject};
4use std::{ffi::c_void, sync::Mutex};
5
6static mut ANDROID_APPLICATION_CONTEXT: Option<AndroidContext> = None;
7// TODO: Android should always call its Activity_onCreate() on the same thread, no locking needed if
8// the user calls it on the same thread directly
9static ANDROID_CONTEXTS: Mutex<Vec<AndroidContext>> = Mutex::new(vec![]);
10
11/// [`AndroidContext`] provides the pointers required to interface with the jni on Android
12/// platforms.
13#[derive(Clone, Copy, Debug)]
14pub struct AndroidContext {
15    java_vm: *mut c_void,
16    context_jobject: *mut c_void,
17}
18unsafe impl Send for AndroidContext {}
19unsafe impl Sync for AndroidContext {}
20
21pub fn add_activity(java_vm: *mut c_void, activity_ptr: *mut c_void) {
22    let vm = unsafe { JavaVM::from_raw(java_vm.cast()) };
23    vm.attach_current_thread(|env| -> jni::errors::Result<()> {
24        let activity = unsafe { JObject::from_raw(env, activity_ptr.cast()) };
25        let app_context = env
26            .call_method(
27                activity,
28                jni_str!("getApplicationContext"),
29                jni_sig!(() -> android.context.Context),
30                &[],
31            )?
32            .l()?;
33
34        if let Some(c) = unsafe { ANDROID_APPLICATION_CONTEXT } {
35            assert_eq!(c.java_vm, java_vm);
36            assert_eq!(c.context_jobject, app_context.as_raw().cast());
37        } else {
38            unsafe {
39                ANDROID_APPLICATION_CONTEXT = Some(AndroidContext {
40                    java_vm,
41                    context_jobject: app_context.as_raw().cast(),
42                })
43            };
44        }
45
46        Ok(())
47    })
48    .unwrap();
49
50    // TODO: Assert against duplicates
51    ANDROID_CONTEXTS.lock().unwrap().push(AndroidContext {
52        java_vm,
53        context_jobject: activity_ptr,
54    });
55}
56
57pub fn remove_activity(activity_ptr: *mut c_void) {
58    let mut contexts = ANDROID_CONTEXTS.lock().unwrap();
59    let index = contexts
60        .iter()
61        .position(|c| c.context_jobject == activity_ptr)
62        .expect("Android activity does not exist");
63    contexts.remove(index);
64}
65
66/// Returns the application context as acquired from [`getApplicationContext()`] on the first
67///
68/// [`getApplicationContext()`]: https://developer.android.com/reference/android/content/Context#getApplicationContext()
69pub fn android_app_context() -> AndroidContext {
70    unsafe { ANDROID_APPLICATION_CONTEXT }.expect("No Android applications were ever registered")
71}
72
73/// All registered Android activities.  Most apps should **not** use this, instead use either
74/// [`android_app_context()`] which is global for the entire application or receive an explicit
75/// `Context` handle for the `Activity` that is relevant (i.e. via `raw-window-handle`).
76pub fn activities() -> Vec<AndroidContext> {
77    ANDROID_CONTEXTS.lock().unwrap().clone()
78}