diff --git a/game-activity/Cargo.toml b/game-activity/Cargo.toml index b8f5714..6da41fa 100755 --- a/game-activity/Cargo.toml +++ b/game-activity/Cargo.toml @@ -12,6 +12,7 @@ jni-sys = "0.3" ndk = { version = "0.6" } ndk-sys = { version = "0.3" } ndk-context = { version = "0.1" } +android-properties = "0.2" lazy_static = "1.4.0" num_enum = "0.5" bitflags = "1.3" diff --git a/game-activity/src/lib.rs b/game-activity/src/lib.rs index feed342..d532cda 100755 --- a/game-activity/src/lib.rs +++ b/game-activity/src/lib.rs @@ -538,6 +538,52 @@ impl AndroidApp { callback(&InputEvent::MotionEvent(motion_event)); } } + + /// The user-visible SDK version of the framework + /// + /// Also referred to as [`Build.VERSION_CODES`](https://developer.android.com/reference/android/os/Build.VERSION_CODES) + pub fn sdk_version() -> i32 { + let mut prop = android_properties::getprop("ro.build.version.sdk"); + if let Some(val) = prop.value() { + i32::from_str_radix(&val, 10).expect("Failed to parse ro.build.version.sdk property") + } else { + panic!("Couldn't read ro.build.version.sdk system property"); + } + } + + fn try_get_path_from_ptr(path: *const u8) -> Option { + if path == ptr::null() { return None; } + let cstr = unsafe { + let cstr_slice = CStr::from_ptr(path); + cstr_slice.to_str().ok()? + }; + if cstr.len() == 0 { return None; } + Some(std::path::PathBuf::from(cstr)) + } + + /// Path to this application's internal data directory + pub fn internal_data_path(&self) -> Option { + unsafe { + let app_ptr = self.ptr.as_ptr(); + Self::try_get_path_from_ptr((*(*app_ptr).activity).internalDataPath) + } + } + + /// Path to this application's external data directory + pub fn external_data_path(&self) -> Option { + unsafe { + let app_ptr = self.ptr.as_ptr(); + Self::try_get_path_from_ptr((*(*app_ptr).activity).externalDataPath) + } + } + + /// Path to the directory containing the application's OBB files (if any). + pub fn obb_path(&self) -> Option { + unsafe { + let app_ptr = self.ptr.as_ptr(); + Self::try_get_path_from_ptr((*(*app_ptr).activity).obbPath) + } + } } struct MotionEventsIterator<'a> { diff --git a/native-activity/Cargo.toml b/native-activity/Cargo.toml index fa6bf78..9a182d0 100644 --- a/native-activity/Cargo.toml +++ b/native-activity/Cargo.toml @@ -12,6 +12,7 @@ jni-sys = "0.3" ndk = { version = "0.6" } ndk-sys = { version = "0.3" } ndk-context = { version = "0.1" } +android-properties = "0.2" lazy_static = "1.4.0" num_enum = "0.5" bitflags = "1.3" diff --git a/native-activity/src/lib.rs b/native-activity/src/lib.rs index 792194b..0448899 100644 --- a/native-activity/src/lib.rs +++ b/native-activity/src/lib.rs @@ -3,7 +3,6 @@ use ndk::asset::AssetManager; use ndk::configuration::Configuration; use ndk::input_queue::InputQueue; use ndk::looper::{FdEvent}; -use ndk::native_activity::NativeActivity; use ndk::native_window::NativeWindow; use ndk_sys::ALooper_wake; use ndk_sys::{ALooper, ALooper_pollAll}; @@ -302,6 +301,13 @@ impl AndroidApp { } } + pub(crate) fn native_activity(&self) -> *const ndk_sys::ANativeActivity { + unsafe { + let app_ptr = self.ptr.as_ptr(); + (*app_ptr).activity.cast() + } + } + /// Calls [`ALooper_pollAll`] on the looper associated with this AndroidApp as well /// as processing any events (such as lifecycle events) via the given `callback`. /// @@ -536,6 +542,46 @@ impl AndroidApp { } } } + + /// The user-visible SDK version of the framework + /// + /// Also referred to as [`Build.VERSION_CODES`](https://developer.android.com/reference/android/os/Build.VERSION_CODES) + pub fn sdk_version() -> i32 { + let mut prop = android_properties::getprop("ro.build.version.sdk"); + if let Some(val) = prop.value() { + i32::from_str_radix(&val, 10).expect("Failed to parse ro.build.version.sdk property") + } else { + panic!("Couldn't read ro.build.version.sdk system property"); + } + } + + fn try_get_path_from_ptr(path: *const u8) -> Option { + if path == ptr::null() { return None; } + let cstr = unsafe { + let cstr_slice = CStr::from_ptr(path); + cstr_slice.to_str().ok()? + }; + if cstr.len() == 0 { return None; } + Some(std::path::PathBuf::from(cstr)) + } + + /// Path to this application's internal data directory + pub fn internal_data_path(&self) -> Option { + let na = self.native_activity(); + unsafe { Self::try_get_path_from_ptr((*na).internalDataPath.cast()) } + } + + /// Path to this application's external data directory + pub fn external_data_path(&self) -> Option { + let na = self.native_activity(); + unsafe { Self::try_get_path_from_ptr((*na).externalDataPath.cast()) } + } + + /// Path to the directory containing the application's OBB files (if any). + pub fn obb_path(&self) -> Option { + let na = self.native_activity(); + unsafe { Self::try_get_path_from_ptr((*na).obbPath.cast()) } + } } /// Gets the global [`AndroidApp`] for this process @@ -621,12 +667,13 @@ pub unsafe extern "C" fn _rust_glue_entry(app: *mut ffi::android_app) { } }); - let activity = NonNull::::new((*app).activity.cast()).unwrap(); - let activity = NativeActivity::from_ptr(activity); - ndk_context::initialize_android_context(activity.vm().cast(), activity.activity().cast()); - let app = AndroidApp::from_ptr(NonNull::new(app).unwrap()); + let na = app.native_activity(); + let jvm = (*na).vm; + let activity = (*na).clazz; // Completely bogus name; this is the _instance_ not class pointer + ndk_context::initialize_android_context(jvm.cast(), activity.cast()); + ANDROID_APP = Some(app.clone()); android_main();