diff --git a/godotopenxrmeta/src/main/java/org/godotengine/openxr/vendors/meta/GodotOpenXRMeta.kt b/godotopenxrmeta/src/main/java/org/godotengine/openxr/vendors/meta/GodotOpenXRMeta.kt index 5f6dcb7f..9cff6e12 100644 --- a/godotopenxrmeta/src/main/java/org/godotengine/openxr/vendors/meta/GodotOpenXRMeta.kt +++ b/godotopenxrmeta/src/main/java/org/godotengine/openxr/vendors/meta/GodotOpenXRMeta.kt @@ -29,7 +29,16 @@ package org.godotengine.openxr.vendors.meta +import android.Manifest import android.app.Activity +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.pm.PermissionInfo +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.provider.Settings import android.util.Log import android.view.View import org.godotengine.godot.Godot @@ -55,6 +64,77 @@ class GodotOpenXRMeta(godot: Godot?) : GodotPlugin(godot) { Log.e(TAG, "Unable to load godotopenxrvendors shared library") } } + + /** + * Returns the information of the desired permission. + * @param context the caller context for this method. + * @param permission the name of the permission. + * @return permission info object + * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found. + * + * TODO: remove this when Godot 4.3 is out with the new `PermissionsUtil.requestPermissions(...)` api + */ + @Throws(PackageManager.NameNotFoundException::class) + private fun getPermissionInfo(context: Context, permission: String): PermissionInfo { + val packageManager = context.packageManager + return packageManager.getPermissionInfo(permission, 0) + } + + /** + * Request a list of dangerous permissions. The requested permissions must be included in the app's AndroidManifest + * @param permissions list of the permissions to request. + * @param activity the caller activity for this method. + * @return true/false. "true" if permissions are already granted, "false" if a permissions request was dispatched. + * + * TODO: remove this when Godot 4.3 is out with the new `PermissionsUtil.requestPermissions(...)` api + */ + private fun requestPermissions(activity: Activity?, permissions: List): Boolean { + if (activity == null) { + return false + } + if (permissions.isEmpty()) { + return true + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + // Not necessary, asked on install already + return true + } + + val requestedPermissions: MutableSet = HashSet() + for (permission in permissions) { + try { + if (permission == Manifest.permission.MANAGE_EXTERNAL_STORAGE) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) { + Log.d(TAG, "Requesting permission $permission") + try { + val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) + intent.setData(Uri.parse(String.format("package:%s", activity.packageName))) + activity.startActivityForResult(intent, PermissionsUtil.REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE) + } catch (ignored: Exception) { + val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) + activity.startActivityForResult(intent, PermissionsUtil.REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE) + } + } + } else { + val permissionInfo = getPermissionInfo(activity, permission) + val protectionLevel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) permissionInfo.protection else permissionInfo.protectionLevel + if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && activity.checkSelfPermission(permission) !== PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Requesting permission $permission") + requestedPermissions.add(permission) + } + } + } catch (e: PackageManager.NameNotFoundException) { + // Skip this permission and continue. + Log.w(TAG, "Unable to identify permission $permission", e) + } + } + if (requestedPermissions.isEmpty()) { + // If list is empty, all of dangerous permissions were granted. + return true + } + activity.requestPermissions(requestedPermissions.toTypedArray(), PermissionsUtil.REQUEST_ALL_PERMISSION_REQ_CODE) + return true + } } override fun getPluginName(): String { @@ -64,20 +144,22 @@ class GodotOpenXRMeta(godot: Godot?) : GodotPlugin(godot) { override fun getPluginGDExtensionLibrariesPaths() = setOf("res://addons/godotopenxrvendors/plugin.gdextension") override fun onMainCreate(activity: Activity): View? { + val permissionsToRequest = ArrayList() // Request the eye tracking permission if it's included in the manifest if (PermissionsUtil.hasManifestPermission(activity, EYE_TRACKING_PERMISSION)) { - Log.d(TAG, "Requesting permission '${EYE_TRACKING_PERMISSION}'") - PermissionsUtil.requestPermission(EYE_TRACKING_PERMISSION, activity) + permissionsToRequest.add(EYE_TRACKING_PERMISSION) } // Request the face tracking permission if it's included in the manifest if (PermissionsUtil.hasManifestPermission(activity, FACE_TRACKING_PERMISSION)) { - Log.d(TAG, "Requesting permission '${FACE_TRACKING_PERMISSION}'") - PermissionsUtil.requestPermission(FACE_TRACKING_PERMISSION, activity) + permissionsToRequest.add(FACE_TRACKING_PERMISSION) } // Request the scene API permission if it's included in the manifest if (PermissionsUtil.hasManifestPermission(activity, SCENE_PERMISSION)) { - Log.d(TAG, "Requesting permission '${SCENE_PERMISSION}'") - PermissionsUtil.requestPermission(SCENE_PERMISSION, activity) + permissionsToRequest.add(SCENE_PERMISSION) + } + + if (permissionsToRequest.isNotEmpty()) { + requestPermissions(activity, permissionsToRequest) } return null }