//! Provides a stable api to rust crates for interfacing with the Android platform. It is //! initialized by the runtime, usually [__ndk-glue__](https://crates.io/crates/ndk-glue), //! but could also be initialized by Java or Kotlin code when embedding in an existing Android //! project. //! //! ```no_run //! let ctx = ndk_context::android_context(); //! let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?; //! let env = vm.attach_current_thread(); //! let class_ctx = env.find_class("android/content/Context")?; //! let audio_service = env.get_static_field(class_ctx, "AUDIO_SERVICE", "Ljava/lang/String;")?; //! let audio_manager = env //! .call_method( //! ctx.context() as jni::sys::jobject, //! "getSystemService", //! "(Ljava/lang/String;)Ljava/lang/Object;", //! &[audio_service], //! )? //! .l()?; //! ``` use std::ffi::c_void; static mut ANDROID_CONTEXT: Option = None; /// [`AndroidContext`] provides the pointers required to interface with the jni on Android /// platforms. #[derive(Clone, Copy, Debug)] pub struct AndroidContext { java_vm: *mut c_void, context_jobject: *mut c_void, } impl AndroidContext { /// A handle to the `JavaVM` object. /// /// Usage with [__jni__](https://crates.io/crates/jni) crate: /// ```no_run /// let ctx = ndk_context::android_context(); /// let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?; /// let env = vm.attach_current_thread(); /// ``` pub fn vm(self) -> *mut c_void { self.java_vm } /// A handle to an [android.content.Context](https://developer.android.com/reference/android/content/Context). /// In most cases this will be a ptr to an `Activity`, but this isn't guaranteed. /// /// Usage with [__jni__](https://crates.io/crates/jni) crate: /// ```no_run /// let ctx = ndk_context::android_context(); /// let vm = unsafe { jni::JavaVM::from_raw(ctx.vm().cast()) }?; /// let env = vm.attach_current_thread(); /// let class_ctx = env.find_class("android/content/Context")?; /// let audio_service = env.get_static_field(class_ctx, "AUDIO_SERVICE", "Ljava/lang/String;")?; /// let audio_manager = env /// .call_method( /// ctx.context() as jni::sys::jobject, /// "getSystemService", /// "(Ljava/lang/String;)Ljava/lang/Object;", /// &[audio_service], /// )? /// .l()?; /// ``` pub fn context(self) -> *mut c_void { self.context_jobject } } /// Main entry point to this crate. Returns an [`AndroidContext`]. pub fn android_context() -> AndroidContext { unsafe { ANDROID_CONTEXT.expect("android context was not initialized") } } /// Initializes the [`AndroidContext`]. [`AndroidContext`] is initialized by [__ndk-glue__](https://crates.io/crates/ndk-glue) /// before `main` is called. /// /// # Safety /// /// The pointers must be valid and this function must be called exactly once before `main` is /// called. pub unsafe fn initialize_android_context(java_vm: *mut c_void, context_jobject: *mut c_void) { let previous = ANDROID_CONTEXT.replace(AndroidContext { java_vm, context_jobject, }); assert!(previous.is_none()); } /// Removes the [`AndroidContext`]. It is released by [__ndk-glue__](https://crates.io/crates/ndk-glue) /// when the activity is finished and destroyed. /// /// # Safety /// /// This function must only be called after [`initialize_android_context()`], /// when the activity is subsequently destroyed according to Android. pub unsafe fn release_android_context() { let previous = ANDROID_CONTEXT.take(); assert!(previous.is_some()); }