diff --git a/crossbundle/tools/src/tools/android_sdk.rs b/crossbundle/tools/src/tools/android_sdk.rs index 2324253d..5ce28334 100644 --- a/crossbundle/tools/src/tools/android_sdk.rs +++ b/crossbundle/tools/src/tools/android_sdk.rs @@ -54,7 +54,7 @@ impl AndroidSdk { .filter_map(|path| path.ok()) .filter(|path| path.path().is_dir()) .filter_map(|path| path.file_name().into_string().ok()) - .filter(|name| name.chars().next().unwrap().is_digit(10)) + .filter(|name| name.chars().next().unwrap().is_ascii_digit()) .max() .ok_or(AndroidError::BuildToolsNotFound)?; let platforms_path = sdk_path.join("platforms"); diff --git a/docs/crossbow-plugins.md b/docs/crossbow-plugins.md new file mode 100644 index 00000000..34d43069 --- /dev/null +++ b/docs/crossbow-plugins.md @@ -0,0 +1,28 @@ +# Crossbow plugins +## Crossbow permissions + +You can import `crossbow permissions` features as follows: + +```rust +use crossbow::crossbow_permissions::prelude::*; +``` + +Invoke request_permission function. This function provides checking permission status in the application and will request permission if it is denied. + +```rust +request_permission(permission: AndroidPermission) +``` + +See usage [example](https://github.com/dodorare/crossbow/blob/main/examples/macroquad-permissions/src/main.rs). + +Useful commands to debug permission status in the application using [adb](https://developer.android.com/studio/command-line/adb). + +```sh +adb shell pm grant +adb shell pm revoke +``` +```sh +adb shell pm reset-permissions +adb shell pm list permission-groups +adb shell pm list permissions +``` \ No newline at end of file diff --git a/plugins/permissions/Cargo.toml b/plugins/permissions/Cargo.toml index dc5fd6c1..ffe4a981 100644 --- a/plugins/permissions/Cargo.toml +++ b/plugins/permissions/Cargo.toml @@ -17,7 +17,7 @@ anyhow = "1.0" [target.'cfg(target_os = "android")'.dependencies] ndk-context = "0.1" ndk-glue = "0.6.0" -jni = "0.19" +jni = "0.19.0" ndk = "0.6" [features] diff --git a/plugins/permissions/src/android/check_permission.rs b/plugins/permissions/src/android/check_permission.rs deleted file mode 100644 index 3f6845da..00000000 --- a/plugins/permissions/src/android/check_permission.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::{error::*, types::android::*}; - -/// Check whether permission was granted or not -pub fn check_permission(permission: AndroidPermission) -> Result { - let (ctx, vm) = super::create_java_vm()?; - let java_env = vm.attach_current_thread()?; - - let string_permission = super::get_permission_from_manifest(permission, &java_env)?; - - let class_package_manager = java_env.find_class("android/content/pm/PackageManager")?; - let field_permission_granted = - java_env.get_static_field_id(class_package_manager, "PERMISSION_GRANTED", "I")?; - let int_permission_granted = java_env.get_static_field_unchecked( - class_package_manager, - field_permission_granted, - jni::signature::JavaType::Primitive(jni::signature::Primitive::Int), - )?; - - // Determine whether you have been granted a particular permission. - let class_context = java_env.find_class("android/content/Context")?; - let method_check_self_permission = java_env.get_method_id( - class_context, - "checkSelfPermission", - "(Ljava/lang/String;)I", - )?; - let ret = java_env.call_method_unchecked( - ctx.context().cast(), - method_check_self_permission, - jni::signature::JavaType::Primitive(jni::signature::Primitive::Int), - &[string_permission], - )?; - Ok(ret.i()? == int_permission_granted.i()?) -} diff --git a/plugins/permissions/src/android/mod.rs b/plugins/permissions/src/android/mod.rs index 07219234..8c27c1ea 100644 --- a/plugins/permissions/src/android/mod.rs +++ b/plugins/permissions/src/android/mod.rs @@ -1,8 +1,11 @@ -mod check_permission; mod request_permission; +mod show_text; -pub use check_permission::*; pub use request_permission::*; +pub use show_text::*; + +use crate::types::android::*; +use jni::signature as Signature; /// Create a java VM for executing Java calls fn create_java_vm() -> crate::error::Result<(ndk_context::AndroidContext, jni::JavaVM)> { @@ -13,21 +16,56 @@ fn create_java_vm() -> crate::error::Result<(ndk_context::AndroidContext, jni::J /// Find declared permissions in AndroidManifest.xml and return it as JValue type fn get_permission_from_manifest<'a>( - permission: crate::types::android::AndroidPermission, + permission: AndroidPermission, java_env: &jni::AttachGuard<'a>, ) -> crate::error::Result> { - let class_manifest_permission = java_env.find_class("android/Manifest$permission")?; + // Find the android manifest class and get the permission + let class_manifest_permission = java_env.find_class(ANDROID_MANIFEST_PERMISSION)?; let field_permission = java_env.get_static_field_id( class_manifest_permission, permission.to_string(), - "Ljava/lang/String;", + MANIFEST_PERMISSION_SIGNATURE, )?; + + // Convert the permission to the JValue type let string_permission = java_env .get_static_field_unchecked( class_manifest_permission, field_permission, - jni::signature::JavaType::Object("java/lang/String".to_owned()), + Signature::JavaType::Object(JAVA_STRING_SIGNATURE.to_owned()), )? .to_owned(); Ok(string_permission) } + +/// Get `PERMISSION_GRANTED` and `PERMISSION_DENIED` status +pub fn permission_status<'a>( + java_env: &jni::AttachGuard<'a>, +) -> crate::error::Result<(jni::objects::JValue<'a>, jni::objects::JValue<'a>)> { + let class_package_manager = java_env.find_class(ANDROID_PACKAGE_MANAGER)?; + let field_permission_granted = java_env.get_static_field_id( + class_package_manager, + PERMISSIONS_GRANTED, + PRIMITIVE_INT_SIGNATURE, + )?; + + let field_permission_denied = java_env.get_static_field_id( + class_package_manager, + PERMISSION_DENIED, + PRIMITIVE_INT_SIGNATURE, + )?; + + let permission_denied = java_env.get_static_field_unchecked( + class_package_manager, + field_permission_denied, + Signature::JavaType::Primitive(Signature::Primitive::Int), + )?; + + let permission_granted = java_env.get_static_field_unchecked( + class_package_manager, + field_permission_granted, + Signature::JavaType::Primitive(Signature::Primitive::Int), + )?; + + Ok((permission_granted, permission_denied)) +} diff --git a/plugins/permissions/src/android/request_permission.rs b/plugins/permissions/src/android/request_permission.rs index 2230324c..2be537e9 100644 --- a/plugins/permissions/src/android/request_permission.rs +++ b/plugins/permissions/src/android/request_permission.rs @@ -1,38 +1,59 @@ +use super::*; use crate::{error::*, types::android::*}; +use jni::signature as Signature; -/// Request permission +/// Provides checking permission status in the application and will request permission if it is denied. pub fn request_permission(permission: AndroidPermission) -> Result { - if super::check_permission(permission)? { - return Ok(true); - } - - let (ctx, vm) = super::create_java_vm()?; + let (ctx, vm) = create_java_vm()?; let java_env = vm.attach_current_thread()?; - let array_permissions = java_env.new_object_array( - 1, - java_env.find_class("java/lang/String")?, - java_env.new_string("")?, - )?; + let string_permission = get_permission_from_manifest(permission, &java_env)?; - let string_permission = super::get_permission_from_manifest(permission, &java_env)?; + let (_permission_granted, permission_denied) = permission_status(&java_env)?; - java_env.set_object_array_element(array_permissions, 0, string_permission.l()?)?; - let class_activity = java_env.find_class("android/app/Activity")?; - let method_request_permissions = java_env.get_method_id( - class_activity, - "requestPermissions", - "([Ljava/lang/String;I)V", + // Determine whether you have been granted a particular permission. + let class_context = java_env.find_class(ANDROID_CONTEXT)?; + let method_check_self_permission = java_env.get_method_id( + class_context, + CHECK_SELF_PERMISSION_METHOD, + CHECK_SELF_PERMISSION_SIGNATURE, )?; - java_env.call_method_unchecked( + let ret = java_env.call_method_unchecked( ctx.context().cast(), - method_request_permissions, - jni::signature::JavaType::Primitive(jni::signature::Primitive::Void), - &[array_permissions.into(), jni::objects::JValue::Int(0)], + method_check_self_permission, + Signature::JavaType::Primitive(Signature::Primitive::Int), + &[string_permission], )?; - // /* TODO: How to create a native callback for a Java class for last argument (0) */ - // env->CallVoidMethod(mApp->activity->clazz, MethodrequestPermissions, ArrayPermissions, 0); + + if ret.i()? == permission_denied.i()? { + let array_permissions = java_env.new_object_array( + ARRAY_LENGTH.into(), + java_env.find_class(JAVA_STRING_SIGNATURE)?, + java_env.new_string(String::new())?, + )?; + + let string_permission = get_permission_from_manifest(permission, &java_env)?; + + java_env.set_object_array_element( + array_permissions, + OBJECT_INDEX.into(), + string_permission.l()?, + )?; + let class_activity = java_env.find_class(ANDROID_ACTIVITY)?; + let method_request_permissions = java_env.get_method_id( + class_activity, + REQUEST_PERMISSIONS_METHOD, + REQUEST_PERMISSIONS_SIGNATURE, + )?; + + java_env.call_method_unchecked( + ctx.context().cast(), + method_request_permissions, + Signature::JavaType::Primitive(Signature::Primitive::Void), + &[array_permissions.into(), jni::objects::JValue::Int(0)], + )?; + } Ok(true) } diff --git a/plugins/permissions/src/lib.rs b/plugins/permissions/src/lib.rs index 984d8f5b..feccc7fe 100644 --- a/plugins/permissions/src/lib.rs +++ b/plugins/permissions/src/lib.rs @@ -21,23 +21,10 @@ pub fn request_permission(permission: Permission) -> error::Result { } } -pub fn check_permission(permission: Permission) -> error::Result { - match permission { - Permission::AndroidPermission(_p) => { - #[cfg(target_os = "android")] - return android::check_permission(_p); - - #[cfg(not(target_os = "android"))] - Err(error::PermissionError::PermissionWrongPlatform) - } - Permission::ApplePermission => Ok(false), - } -} - pub mod prelude { #[cfg(target_os = "android")] pub use super::android::*; pub use super::types::android::*; - pub use super::{check_permission, request_permission, Permission}; + pub use super::{request_permission, Permission}; } diff --git a/plugins/permissions/src/types/android_permission.rs b/plugins/permissions/src/types/android_permission.rs index b19c8b01..9b58cade 100644 --- a/plugins/permissions/src/types/android_permission.rs +++ b/plugins/permissions/src/types/android_permission.rs @@ -1111,7 +1111,7 @@ impl AndroidPermission { impl std::fmt::Display for AndroidPermission { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match *self { + match self { Self::AcceptHandover => write!(f, "ACCEPT_HANDOVER"), Self::AccessBackgroundLocation => write!(f, "ACCESS_BACKGROUND_LOCATION"), Self::AccessBlobsAcrossUsers => write!(f, "ACCESS_BLOBS_ACROSS_USERS"), diff --git a/plugins/permissions/src/types/consts.rs b/plugins/permissions/src/types/consts.rs new file mode 100644 index 00000000..7d7cb78e --- /dev/null +++ b/plugins/permissions/src/types/consts.rs @@ -0,0 +1,24 @@ +// JNI classes +pub const ANDROID_MANIFEST_PERMISSION: &str = "android/Manifest$permission"; +pub const ANDROID_CONTEXT: &str = "android/content/Context"; +pub const ANDROID_PACKAGE_MANAGER: &str = "android/content/pm/PackageManager"; +pub const ANDROID_ACTIVITY: &str = "android/app/Activity"; + +// JNI methods +pub const REQUEST_PERMISSIONS_METHOD: &str = "requestPermissions"; +pub const CHECK_SELF_PERMISSION_METHOD: &str = "checkSelfPermission"; + +// JNI signatures +pub const JAVA_STRING_SIGNATURE: &str = "java/lang/String"; +pub const MANIFEST_PERMISSION_SIGNATURE: &str = "Ljava/lang/String;"; +pub const REQUEST_PERMISSIONS_SIGNATURE: &str = "([Ljava/lang/String;I)V"; +pub const CHECK_SELF_PERMISSION_SIGNATURE: &str = "(Ljava/lang/String;)I"; +pub const PRIMITIVE_INT_SIGNATURE: &str = "I"; + +// JNI static fields +pub const PERMISSIONS_GRANTED: &str = "PERMISSION_GRANTED"; +pub const PERMISSION_DENIED: &str = "PERMISSION_DENIED"; + +// JNI types +pub const ARRAY_LENGTH: i32 = 1; +pub const OBJECT_INDEX: i32 = 0; diff --git a/plugins/permissions/src/types/mod.rs b/plugins/permissions/src/types/mod.rs index cccd2878..87dea2e6 100644 --- a/plugins/permissions/src/types/mod.rs +++ b/plugins/permissions/src/types/mod.rs @@ -1,9 +1,11 @@ mod android_permission; mod android_permission_group; +mod consts; pub mod android { use super::*; pub use android_permission::*; pub use android_permission_group::*; + pub use consts::*; }