diff --git a/platform/android/audio_driver_opensl.cpp b/platform/android/audio_driver_opensl.cpp
index fcc288789..5bf310cf6 100644
--- a/platform/android/audio_driver_opensl.cpp
+++ b/platform/android/audio_driver_opensl.cpp
@@ -284,7 +284,8 @@ Error AudioDriverOpenSL::capture_start() {
return capture_init_device();
}
- return OK;
+ WARN_PRINT("Unable to start audio capture - No RECORD_AUDIO permission");
+ return ERR_UNAUTHORIZED;
}
Error AudioDriverOpenSL::capture_stop() {
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index 37c3adbd0..84dbf6419 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -21,6 +21,8 @@
+
+
()
override fun onCreate(savedInstanceState : Bundle?) {
- PermissionsUtil.requestManifestPermissions(this);
+ // We exclude certain permissions from the set we request at startup, as they'll be
+ // requested on demand based on use-cases.
+ PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
val params : Array? = getIntent().getStringArrayExtra(COMMAND_LINE_PARAMS);
@@ -110,6 +112,7 @@ open class PandemoniumEditor : FullScreenPandemoniumApp() {
val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures()
+ checkForProjectPermissionsToEnable()
runOnUiThread {
// Enable long press, panning and scaling gestures
@@ -120,6 +123,17 @@ open class PandemoniumEditor : FullScreenPandemoniumApp() {
}
}
+ /**
+ * Check for project permissions to enable
+ */
+ protected open fun checkForProjectPermissionsToEnable() {
+ // Check for RECORD_AUDIO permission
+ val audioInputEnabled = java.lang.Boolean.parseBoolean(pandemoniumLib.getGlobal("audio/enable_audio_input"));
+ if (audioInputEnabled) {
+ PermissionsUtil.requestPermission(Manifest.permission.RECORD_AUDIO, this)
+ }
+ }
+
private fun updateCommandLineParams(args: Array?) {
// Update the list of command line params with the new args
commandLineParams.clear()
diff --git a/platform/android/java/editor/src/main/java/net/relintai/pandemonium/editor/PandemoniumGame.kt b/platform/android/java/editor/src/main/java/net/relintai/pandemonium/editor/PandemoniumGame.kt
index 82d5cccca..35b1baee1 100644
--- a/platform/android/java/editor/src/main/java/net/relintai/pandemonium/editor/PandemoniumGame.kt
+++ b/platform/android/java/editor/src/main/java/net/relintai/pandemonium/editor/PandemoniumGame.kt
@@ -39,4 +39,9 @@ class PandemoniumGame : PandemoniumEditor() {
override fun enableLongPressGestures() = false
override fun enablePanAndScaleGestures() = false
+
+ override fun checkForProjectPermissionsToEnable() {
+ // Nothing to do.. by the time we get here, the project permissions will have already
+ // been requested by the Editor window.
+ }
}
diff --git a/platform/android/java/editor/src/main/java/net/relintai/pandemonium/editor/PandemoniumProjectManager.kt b/platform/android/java/editor/src/main/java/net/relintai/pandemonium/editor/PandemoniumProjectManager.kt
index 3cd44d770..85baf22d8 100644
--- a/platform/android/java/editor/src/main/java/net/relintai/pandemonium/editor/PandemoniumProjectManager.kt
+++ b/platform/android/java/editor/src/main/java/net/relintai/pandemonium/editor/PandemoniumProjectManager.kt
@@ -38,5 +38,9 @@ package net.relintai.pandemonium.editor
* [PandemoniumEditor] activity.
*/
-class PandemoniumProjectManager : PandemoniumEditor()
+class PandemoniumProjectManager : PandemoniumEditor() {
+ override fun checkForProjectPermissionsToEnable() {
+ // Nothing to do here.. we have yet to select a project to load.
+ }
+}
diff --git a/platform/android/java/lib/src/net/relintai/pandemonium/pandemonium/utils/PermissionsUtil.java b/platform/android/java/lib/src/net/relintai/pandemonium/pandemonium/utils/PermissionsUtil.java
index 520abae28..c4be96a9b 100644
--- a/platform/android/java/lib/src/net/relintai/pandemonium/pandemonium/utils/PermissionsUtil.java
+++ b/platform/android/java/lib/src/net/relintai/pandemonium/pandemonium/utils/PermissionsUtil.java
@@ -42,10 +42,12 @@ import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* This class includes utility functions for Android permissions related operations.
@@ -57,7 +59,8 @@ 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;
- public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001;
+ public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001;
+ public static final int REQUEST_SINGLE_PERMISSION_REQ_CODE = 1002;
public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002;
private PermissionsUtil() {
@@ -65,31 +68,55 @@ public final class PermissionsUtil {
/**
* Request a dangerous permission. name must be specified in this
- * @param name the name of the requested permission.
+ * @param permissionName 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, Activity activity) {
+ public static boolean requestPermission(String permissionName, Activity 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;
- }
+ switch (permissionName) {
+ case "RECORD_AUDIO":
+ case Manifest.permission.RECORD_AUDIO:
+ if (ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
+ activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION);
+ return false;
+ }
+ return true;
+ case "CAMERA":
+ case Manifest.permission.CAMERA:
+ if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
+ activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION);
+ return false;
+ }
+ return true;
+ case "VIBRATE":
+ case Manifest.permission.VIBRATE:
+ if (ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) {
+ activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
+ return false;
+ }
+ return true;
- 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;
+ default:
+ // Check if the given permission is a dangerous permission
+ try {
+ PermissionInfo permissionInfo = getPermissionInfo(activity, permissionName);
+ int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
+ if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permissionName) != PackageManager.PERMISSION_GRANTED) {
+ activity.requestPermissions(new String[] { permissionName }, REQUEST_SINGLE_PERMISSION_REQ_CODE);
+ return false;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Unknown permission - return false as it can't be granted.
+ Log.w(TAG, "Unable to identify permission " + permissionName, e);
+ return false;
+ }
+ return true;
}
-
- 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;
}
/**
@@ -98,6 +125,16 @@ public final class PermissionsUtil {
* @return true/false. "true" if all permissions were granted otherwise returns "false".
*/
public static boolean requestManifestPermissions(Activity activity) {
+ return requestManifestPermissions(activity, null);
+ }
+
+ /**
+ * Request dangerous permissions which are defined in the Android manifest file from the user.
+ * @param activity the caller activity for this method.
+ * @param excludes Set of permissions to exclude from the request
+ * @return true/false. "true" if all permissions were granted otherwise returns "false".
+ */
+ public static boolean requestManifestPermissions(Activity activity, @Nullable Set excludes) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
}
@@ -115,8 +152,12 @@ public final class PermissionsUtil {
List requestedPermissions = new ArrayList<>();
for (String manifestPermission : manifestPermissions) {
+ if (excludes != null && excludes.contains(manifestPermission)) {
+ continue;
+ }
+
try {
- if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
+ if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);