Fix issue causing the Android editor to crash when creating a new AudioStreamMicrophone

Fixes https://github.com/godotengine/godot/issues/73801
This commit is contained in:
Fredia Huya-Kouadio 2023-05-31 00:11:53 -07:00
parent b71701714f
commit 9c334fa242
6 changed files with 86 additions and 17 deletions

View File

@ -276,7 +276,8 @@ Error AudioDriverOpenSL::capture_start() {
return capture_init_device(); return capture_init_device();
} }
return OK; WARN_PRINT("Unable to start audio capture - No RECORD_AUDIO permission");
return ERR_UNAUTHORIZED;
} }
Error AudioDriverOpenSL::capture_stop() { Error AudioDriverOpenSL::capture_stop() {

View File

@ -21,6 +21,8 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/> android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />
<application <application
android:allowBackup="false" android:allowBackup="false"

View File

@ -81,7 +81,9 @@ open class GodotEditor : FullScreenGodotApp() {
private val commandLineParams = ArrayList<String>() private val commandLineParams = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) { 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 = intent.getStringArrayExtra(COMMAND_LINE_PARAMS) val params = intent.getStringArrayExtra(COMMAND_LINE_PARAMS)
updateCommandLineParams(params) updateCommandLineParams(params)
@ -98,6 +100,8 @@ open class GodotEditor : FullScreenGodotApp() {
val longPressEnabled = enableLongPressGestures() val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures() val panScaleEnabled = enablePanAndScaleGestures()
checkForProjectPermissionsToEnable()
runOnUiThread { runOnUiThread {
// Enable long press, panning and scaling gestures // Enable long press, panning and scaling gestures
godotFragment?.renderView?.inputHandler?.apply { godotFragment?.renderView?.inputHandler?.apply {
@ -107,6 +111,17 @@ open class GodotEditor : FullScreenGodotApp() {
} }
} }
/**
* Check for project permissions to enable
*/
protected open fun checkForProjectPermissionsToEnable() {
// Check for RECORD_AUDIO permission
val audioInputEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("audio/enable_audio_input"));
if (audioInputEnabled) {
PermissionsUtil.requestPermission(Manifest.permission.RECORD_AUDIO, this)
}
}
private fun updateCommandLineParams(args: Array<String>?) { private fun updateCommandLineParams(args: Array<String>?) {
// Update the list of command line params with the new args // Update the list of command line params with the new args
commandLineParams.clear() commandLineParams.clear()

View File

@ -39,4 +39,9 @@ class GodotGame : GodotEditor() {
override fun enableLongPressGestures() = false override fun enableLongPressGestures() = false
override fun enablePanAndScaleGestures() = 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.
}
} }

View File

@ -37,4 +37,8 @@ package org.godotengine.editor
* Upon selection of a project, this activity (via its parent logic) starts the * Upon selection of a project, this activity (via its parent logic) starts the
* [GodotEditor] activity. * [GodotEditor] activity.
*/ */
class GodotProjectManager : GodotEditor() class GodotProjectManager : GodotEditor() {
override fun checkForProjectPermissionsToEnable() {
// Nothing to do here.. we have yet to select a project to load.
}
}

View File

@ -42,10 +42,12 @@ import android.os.Environment;
import android.provider.Settings; import android.provider.Settings;
import android.util.Log; import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* This class includes utility functions for Android permissions related operations. * This class includes utility functions for Android permissions related operations.
@ -58,6 +60,7 @@ public final class PermissionsUtil {
static final int REQUEST_CAMERA_PERMISSION = 2; static final int REQUEST_CAMERA_PERMISSION = 2;
static final int REQUEST_VIBRATE_PERMISSION = 3; 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; public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002;
private PermissionsUtil() { private PermissionsUtil() {
@ -65,31 +68,57 @@ public final class 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> * 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 permissionName the name of the requested permission.
* @param activity the caller activity for this method. * @param activity the caller activity for this method.
* @return true/false. "true" if permission was granted otherwise returns "false". * @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) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Not necessary, asked on install already // Not necessary, asked on install already
return true; return true;
} }
if (name.equals("RECORD_AUDIO") && ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { 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); activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION);
return false; return false;
} }
return true;
if (name.equals("CAMERA") && ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { 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); activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION);
return false; return false;
} }
return true;
if (name.equals("VIBRATE") && ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) { 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); activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
return false; return false;
} }
return true; return true;
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;
}
} }
/** /**
@ -98,6 +127,16 @@ public final class PermissionsUtil {
* @return true/false. "true" if all permissions were granted otherwise returns "false". * @return true/false. "true" if all permissions were granted otherwise returns "false".
*/ */
public static boolean requestManifestPermissions(Activity activity) { 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<String> excludes) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true; return true;
} }
@ -115,6 +154,9 @@ public final class PermissionsUtil {
List<String> requestedPermissions = new ArrayList<>(); List<String> requestedPermissions = new ArrayList<>();
for (String manifestPermission : manifestPermissions) { for (String manifestPermission : manifestPermissions) {
if (excludes != null && excludes.contains(manifestPermission)) {
continue;
}
try { 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()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {