summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/bind/core_bind.cpp12
-rw-r--r--core/bind/core_bind.h2
-rw-r--r--core/os/main_loop.cpp2
-rw-r--r--core/os/os.h2
-rw-r--r--doc/classes/MainLoop.xml11
-rw-r--r--doc/classes/OS.xml14
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.java37
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java157
-rw-r--r--platform/android/java_godot_lib_jni.cpp4
-rw-r--r--platform/android/java_godot_wrapper.cpp30
-rw-r--r--platform/android/java_godot_wrapper.h4
-rw-r--r--platform/android/os_android.cpp10
-rw-r--r--platform/android/os_android.h2
13 files changed, 258 insertions, 29 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index d07ba44788..1d451b2982 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -1136,6 +1136,16 @@ bool _OS::request_permission(const String &p_name) {
return OS::get_singleton()->request_permission(p_name);
}
+bool _OS::request_permissions() {
+
+ return OS::get_singleton()->request_permissions();
+}
+
+Vector<String> _OS::get_granted_permissions() const {
+
+ return OS::get_singleton()->get_granted_permissions();
+}
+
_OS *_OS::singleton = NULL;
void _OS::_bind_methods() {
@@ -1319,6 +1329,8 @@ void _OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_power_percent_left"), &_OS::get_power_percent_left);
ClassDB::bind_method(D_METHOD("request_permission", "name"), &_OS::request_permission);
+ ClassDB::bind_method(D_METHOD("request_permissions"), &_OS::request_permissions);
+ ClassDB::bind_method(D_METHOD("get_granted_permissions"), &_OS::get_granted_permissions);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "clipboard"), "set_clipboard", "get_clipboard");
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_screen"), "set_current_screen", "get_current_screen");
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 693b85710a..1a4fd1d5cb 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -349,6 +349,8 @@ public:
bool has_feature(const String &p_feature) const;
bool request_permission(const String &p_name);
+ bool request_permissions();
+ Vector<String> get_granted_permissions() const;
static _OS *get_singleton() { return singleton; }
diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp
index 5587e827ba..146a301995 100644
--- a/core/os/main_loop.cpp
+++ b/core/os/main_loop.cpp
@@ -65,6 +65,8 @@ void MainLoop::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_OS_IME_UPDATE);
BIND_CONSTANT(NOTIFICATION_APP_RESUMED);
BIND_CONSTANT(NOTIFICATION_APP_PAUSED);
+
+ ADD_SIGNAL(MethodInfo("on_request_permissions_result", PropertyInfo(Variant::STRING, "permission"), PropertyInfo(Variant::BOOL, "granted")));
};
void MainLoop::set_init_script(const Ref<Script> &p_init_script) {
diff --git a/core/os/os.h b/core/os/os.h
index 9b46b43081..b5224c4f63 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -530,6 +530,8 @@ public:
List<String> get_restart_on_exit_arguments() const;
virtual bool request_permission(const String &p_name) { return true; }
+ virtual bool request_permissions() { return true; }
+ virtual Vector<String> get_granted_permissions() const { return Vector<String>(); }
virtual void process_and_drop_events() {}
OS();
diff --git a/doc/classes/MainLoop.xml b/doc/classes/MainLoop.xml
index 9e65da8eea..9457800825 100644
--- a/doc/classes/MainLoop.xml
+++ b/doc/classes/MainLoop.xml
@@ -167,6 +167,17 @@
</description>
</method>
</methods>
+ <signals>
+ <signal name="on_request_permissions_result">
+ <argument index="0" name="permission" type="String">
+ </argument>
+ <argument index="1" name="granted" type="bool">
+ </argument>
+ <description>
+ Emitted when an user responds to permission request.
+ </description>
+ </signal>
+ </signals>
<constants>
<constant name="NOTIFICATION_WM_MOUSE_ENTER" value="1002">
Notification received from the OS when the mouse enters the game window.
diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml
index 9e1b25abe1..13a2fc2602 100644
--- a/doc/classes/OS.xml
+++ b/doc/classes/OS.xml
@@ -217,6 +217,13 @@
Returns the path to the current engine executable.
</description>
</method>
+ <method name="get_granted_permissions">
+ <return type="PoolStringArray">
+ </return>
+ <description>
+ With this function you can get the list of dangerous permissions that have been granted to the Android application.
+ </description>
+ </method>
<method name="get_ime_selection" qualifiers="const">
<return type="Vector2">
</return>
@@ -744,6 +751,13 @@
At the moment this function is only used by [code]AudioDriverOpenSL[/code] to request permission for [code]RECORD_AUDIO[/code] on Android.
</description>
</method>
+ <method name="request_permissions">
+ <return type="bool">
+ </return>
+ <description>
+ With this function you can request dangerous permissions since normal permissions are automatically granted at install time in Android application.
+ </description>
+ </method>
<method name="set_icon">
<return type="void">
</return>
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
index 247f006a69..4dae2dcc53 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java
@@ -30,7 +30,6 @@
package org.godotengine.godot;
-import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
@@ -64,7 +63,6 @@ import android.os.Vibrator;
import android.provider.Settings.Secure;
import android.support.annotation.Keep;
import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -98,14 +96,12 @@ import java.util.Locale;
import javax.microedition.khronos.opengles.GL10;
import org.godotengine.godot.input.GodotEditText;
import org.godotengine.godot.payments.PaymentsManager;
+import org.godotengine.godot.utils.PermissionsUtil;
import org.godotengine.godot.xr.XRMode;
public abstract class Godot extends Activity implements SensorEventListener, IDownloaderClient {
static final int MAX_SINGLETONS = 64;
- static final int REQUEST_RECORD_AUDIO_PERMISSION = 1;
- static final int REQUEST_CAMERA_PERMISSION = 2;
- static final int REQUEST_VIBRATE_PERMISSION = 3;
private IStub mDownloaderClientStub;
private TextView mStatusText;
private TextView mProgressFraction;
@@ -1007,32 +1003,15 @@ public abstract class Godot extends Activity implements SensorEventListener, IDo
}
public boolean requestPermission(String p_name) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
- // Not necessary, asked on install already
- return true;
- }
-
- if (p_name.equals("RECORD_AUDIO")) {
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION);
- return false;
- }
- }
+ return PermissionsUtil.requestPermission(p_name, this);
+ }
- if (p_name.equals("CAMERA")) {
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION);
- return false;
- }
- }
+ public boolean requestPermissions() {
+ return PermissionsUtil.requestManifestPermissions(this);
+ }
- if (p_name.equals("VIBRATE")) {
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) {
- requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
- return false;
- }
- }
- return true;
+ public String[] getGrantedPermissions() {
+ return PermissionsUtil.getGrantedPermissions(this);
}
/**
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
new file mode 100644
index 0000000000..2c4a444e5a
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
@@ -0,0 +1,157 @@
+package org.godotengine.godot.utils;
+
+import android.Manifest;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.os.Build;
+import android.support.v4.content.ContextCompat;
+import java.util.ArrayList;
+import java.util.List;
+import org.godotengine.godot.Godot;
+
+/**
+ * This class includes utility functions for Android permissions related operations.
+ * @author Cagdas Caglak <cagdascaglak@gmail.com>
+ */
+public final class PermissionsUtil {
+
+ static final int REQUEST_RECORD_AUDIO_PERMISSION = 1;
+ static final int REQUEST_CAMERA_PERMISSION = 2;
+ static final int REQUEST_VIBRATE_PERMISSION = 3;
+ static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001;
+
+ private PermissionsUtil() {
+ }
+
+ /**
+ * Request a dangerous permission. name must be specified in <a href="https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/res/AndroidManifest.xml">this</a>
+ * @param name the name of the requested permission.
+ * @param activity the caller activity for this method.
+ * @return true/false. "true" if permission was granted otherwise returns "false".
+ */
+ public static boolean requestPermission(String name, Godot activity) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ // Not necessary, asked on install already
+ return true;
+ }
+
+ if (name.equals("RECORD_AUDIO") && ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
+ activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION);
+ return false;
+ }
+
+ if (name.equals("CAMERA") && ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
+ activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION);
+ return false;
+ }
+
+ if (name.equals("VIBRATE") && ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) {
+ activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Request dangerous permissions which are defined in the Android manifest file from the user.
+ * @param activity the caller activity for this method.
+ * @return true/false. "true" if all permissions were granted otherwise returns "false".
+ */
+ public static boolean requestManifestPermissions(Godot activity) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ return true;
+ }
+
+ String[] manifestPermissions;
+ try {
+ manifestPermissions = getManifestPermissions(activity);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ if (manifestPermissions == null || manifestPermissions.length == 0)
+ return true;
+
+ List<String> dangerousPermissions = new ArrayList<>();
+ for (String manifestPermission : manifestPermissions) {
+ try {
+ PermissionInfo permissionInfo = getPermissionInfo(activity, manifestPermission);
+ int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
+ if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, manifestPermission) != PackageManager.PERMISSION_GRANTED) {
+ dangerousPermissions.add(manifestPermission);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ if (dangerousPermissions.isEmpty()) {
+ // If list is empty, all of dangerous permissions were granted.
+ return true;
+ }
+
+ String[] requestedPermissions = dangerousPermissions.toArray(new String[0]);
+ activity.requestPermissions(requestedPermissions, REQUEST_ALL_PERMISSION_REQ_CODE);
+ return false;
+ }
+
+ /**
+ * With this function you can get the list of dangerous permissions that have been granted to the Android application.
+ * @param activity the caller activity for this method.
+ * @return granted permissions list
+ */
+ public static String[] getGrantedPermissions(Godot activity) {
+ String[] manifestPermissions;
+ try {
+ manifestPermissions = getManifestPermissions(activity);
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ return new String[0];
+ }
+ if (manifestPermissions == null || manifestPermissions.length == 0)
+ return new String[0];
+
+ List<String> dangerousPermissions = new ArrayList<>();
+ for (String manifestPermission : manifestPermissions) {
+ try {
+ PermissionInfo permissionInfo = getPermissionInfo(activity, manifestPermission);
+ int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
+ if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, manifestPermission) == PackageManager.PERMISSION_GRANTED) {
+ dangerousPermissions.add(manifestPermission);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ return new String[0];
+ }
+ }
+
+ return dangerousPermissions.toArray(new String[0]);
+ }
+
+ /**
+ * Returns the permissions defined in the AndroidManifest.xml file.
+ * @param activity the caller activity for this method.
+ * @return manifest permissions list
+ * @throws PackageManager.NameNotFoundException the exception is thrown when a given package, application, or component name cannot be found.
+ */
+ private static String[] getManifestPermissions(Godot activity) throws PackageManager.NameNotFoundException {
+ PackageManager packageManager = activity.getPackageManager();
+ PackageInfo packageInfo = packageManager.getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);
+ return packageInfo.requestedPermissions;
+ }
+
+ /**
+ * Returns the information of the desired permission.
+ * @param activity the caller activity 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.
+ */
+ private static PermissionInfo getPermissionInfo(Godot activity, String permission) throws PackageManager.NameNotFoundException {
+ PackageManager packageManager = activity.getPackageManager();
+ return packageManager.getPermissionInfo(permission, 0);
+ }
+}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 7daea19961..a14e0a1960 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -1393,6 +1393,10 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResu
if (permission == "android.permission.RECORD_AUDIO" && p_result) {
AudioDriver::get_singleton()->capture_start();
}
+
+ if (os_android->get_main_loop()) {
+ os_android->get_main_loop()->emit_signal("on_request_permissions_result", permission, p_result == JNI_TRUE);
+ }
}
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz) {
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 8194ee6ecf..e3e613d30b 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -59,6 +59,8 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) {
_get_clipboard = p_env->GetMethodID(cls, "getClipboard", "()Ljava/lang/String;");
_set_clipboard = p_env->GetMethodID(cls, "setClipboard", "(Ljava/lang/String;)V");
_request_permission = p_env->GetMethodID(cls, "requestPermission", "(Ljava/lang/String;)Z");
+ _request_permissions = p_env->GetMethodID(cls, "requestPermissions", "()Z");
+ _get_granted_permissions = p_env->GetMethodID(cls, "getGrantedPermissions", "()[Ljava/lang/String;");
_init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V");
_get_surface = p_env->GetMethodID(cls, "getSurface", "()Landroid/view/Surface;");
_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
@@ -199,6 +201,34 @@ bool GodotJavaWrapper::request_permission(const String &p_name) {
}
}
+bool GodotJavaWrapper::request_permissions() {
+ if (_request_permissions) {
+ JNIEnv *env = ThreadAndroid::get_env();
+ return env->CallBooleanMethod(godot_instance, _request_permissions);
+ } else {
+ return false;
+ }
+}
+
+Vector<String> GodotJavaWrapper::get_granted_permissions() const {
+ Vector<String> permissions_list;
+ if (_get_granted_permissions) {
+ JNIEnv *env = ThreadAndroid::get_env();
+ jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);
+ jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);
+
+ int i = 0;
+ jsize len = env->GetArrayLength(*arr);
+ for (i = 0; i < len; i++) {
+ jstring jstr = (jstring)env->GetObjectArrayElement(*arr, i);
+ String str = jstring_to_string(jstr, env);
+ permissions_list.push_back(str);
+ env->DeleteLocalRef(jstr);
+ }
+ }
+ return permissions_list;
+}
+
void GodotJavaWrapper::init_input_devices() {
if (_init_input_devices) {
JNIEnv *env = ThreadAndroid::get_env();
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index b1bd9b7f48..d23ff273cb 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -54,6 +54,8 @@ private:
jmethodID _get_clipboard = 0;
jmethodID _set_clipboard = 0;
jmethodID _request_permission = 0;
+ jmethodID _request_permissions = 0;
+ jmethodID _get_granted_permissions = 0;
jmethodID _init_input_devices = 0;
jmethodID _get_surface = 0;
jmethodID _is_activity_resumed = 0;
@@ -81,6 +83,8 @@ public:
bool has_set_clipboard();
void set_clipboard(const String &p_text);
bool request_permission(const String &p_name);
+ bool request_permissions();
+ Vector<String> get_granted_permissions() const;
void init_input_devices();
jobject get_surface();
bool is_activity_resumed();
diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp
index 91bd6cbdd2..defee8f1f1 100644
--- a/platform/android/os_android.cpp
+++ b/platform/android/os_android.cpp
@@ -220,6 +220,16 @@ bool OS_Android::request_permission(const String &p_name) {
return godot_java->request_permission(p_name);
}
+bool OS_Android::request_permissions() {
+
+ return godot_java->request_permissions();
+}
+
+Vector<String> OS_Android::get_granted_permissions() const {
+
+ return godot_java->get_granted_permissions();
+}
+
Error OS_Android::open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path) {
p_library_handle = dlopen(p_path.utf8().get_data(), RTLD_NOW);
ERR_FAIL_COND_V_MSG(!p_library_handle, ERR_CANT_OPEN, "Can't open dynamic library: " + p_path + ", error: " + dlerror() + ".");
diff --git a/platform/android/os_android.h b/platform/android/os_android.h
index 9bad9b2e01..a290c0cedd 100644
--- a/platform/android/os_android.h
+++ b/platform/android/os_android.h
@@ -125,6 +125,8 @@ public:
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
virtual bool request_permission(const String &p_name);
+ virtual bool request_permissions();
+ virtual Vector<String> get_granted_permissions() const;
virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false);