Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crossbundle/tools/src/tools/android_sdk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
28 changes: 28 additions & 0 deletions docs/crossbow-plugins.md
Original file line number Diff line number Diff line change
@@ -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 <app package> <permission name>
adb shell pm revoke <app package> <permission name>
```
```sh
adb shell pm reset-permissions
adb shell pm list permission-groups
adb shell pm list permissions
```
2 changes: 1 addition & 1 deletion plugins/permissions/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
33 changes: 0 additions & 33 deletions plugins/permissions/src/android/check_permission.rs

This file was deleted.

50 changes: 44 additions & 6 deletions plugins/permissions/src/android/mod.rs
Original file line number Diff line number Diff line change
@@ -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)> {
Expand All @@ -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<jni::objects::JValue<'a>> {
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))
}
69 changes: 45 additions & 24 deletions plugins/permissions/src/android/request_permission.rs
Original file line number Diff line number Diff line change
@@ -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<bool> {
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)
}
15 changes: 1 addition & 14 deletions plugins/permissions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,10 @@ pub fn request_permission(permission: Permission) -> error::Result<bool> {
}
}

pub fn check_permission(permission: Permission) -> error::Result<bool> {
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};
}
2 changes: 1 addition & 1 deletion plugins/permissions/src/types/android_permission.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand Down
24 changes: 24 additions & 0 deletions plugins/permissions/src/types/consts.rs
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 2 additions & 0 deletions plugins/permissions/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -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::*;
}