diff --git a/src/common/android.cpp b/src/common/android.cpp index b53ce9735..0bf791a4d 100644 --- a/src/common/android.cpp +++ b/src/common/android.cpp @@ -266,6 +266,59 @@ bool hasBackgroundMusic() return result; } +bool hasRecordingPermission() +{ + JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv(); + jobject activity = (jobject) SDL_AndroidGetActivity(); + + jclass clazz(env->GetObjectClass(activity)); + jmethodID method_id = env->GetMethodID(clazz, "hasRecordAudioPermission", "()Z"); + jboolean result = false; + + if (methodID == nullptr) + env->ExceptionClear(); + else + result = env->CallBooleanMethod(activity, method_id); + + env->DeleteLocalRef(activity); + env->DeleteLocalRef(clazz); + + return result; +} + + +void showRecordingPermissionMissingDialog() +{ + JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv(); + jobject activity = (jobject) SDL_AndroidGetActivity(); + jclass clazz(env->GetObjectClass(activity)); + jmethodID methodID = env->GetMethodID(clazz, "requestRecordAudioPermission", "()V"); + + if (methodID == nullptr) + env->ExceptionClear(); + else + env->CallVoidMethod(activity, methodID); + + env->DeleteLocalRef(clazz); + env->DeleteLocalRef(activity); +} + +void showRecordingPermissionMissingDialog() +{ + JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv(); + jobject activity = (jobject) SDL_AndroidGetActivity(); + jclass clazz(env->GetObjectClass(activity)); + jmethodID methodID = env->GetMethodID(clazz, "showRecordingAudioPermissionMissingDialog", "()V"); + + if (methodID == nullptr) + env->ExceptionClear(); + else + env->CallVoidMethod(activity, methodID); + + env->DeleteLocalRef(clazz); + env->DeleteLocalRef(activity); +} + } // android } // love diff --git a/src/common/android.h b/src/common/android.h index a19e923fe..9773d201b 100644 --- a/src/common/android.h +++ b/src/common/android.h @@ -74,6 +74,12 @@ bool createStorageDirectories(); bool hasBackgroundMusic(); +bool hasRecordingPermission(); + +void requestRecordingPermission(); + +void showRecordingPermissionMissingDialog(); + } // android } // love diff --git a/src/modules/audio/Audio.cpp b/src/modules/audio/Audio.cpp index 5ed9021cf..86199a6d9 100644 --- a/src/modules/audio/Audio.cpp +++ b/src/modules/audio/Audio.cpp @@ -21,8 +21,10 @@ #include "Audio.h" #include "common/config.h" -#ifdef LOVE_IOS +#if defined(LOVE_IOS) #include "common/ios.h" +#elif defined(LOVE_ANDROID) +#include "common/android.h" #endif namespace love @@ -30,6 +32,42 @@ namespace love namespace audio { +static bool requestRecPermission = false; + +void setRequestRecordingPermission(bool rec) +{ + requestRecPermission = rec; +} + +bool getRequestRecordingPermission() +{ + return requestRecPermission; +} + +bool hasRecordingPermission() +{ +#if defined(LOVE_ANDROID) + return love::android::hasRecordingPermission(); +#else + // Always available(?) + return true; +#endif +} + +void requestRecordingPermission() +{ +#ifdef LOVE_ANDROID + love::android::requestRecordingPermission(); +#endif +} + +void _showRecordingPermissionMissingDialog() +{ +#ifdef LOVE_ANDROID + love::android::showRecordingPermissionMissingDialog(); +#endif +} + bool Audio::setMixWithSystem(bool mix) { #ifdef LOVE_IOS diff --git a/src/modules/audio/Audio.h b/src/modules/audio/Audio.h index 88a647d30..7df19404e 100644 --- a/src/modules/audio/Audio.h +++ b/src/modules/audio/Audio.h @@ -45,6 +45,34 @@ class SoundData; namespace audio { +/* + * In some platforms (notably Android), recording from mic + * requires user permission. This function sets whetever to + * request the permission later or not. + */ +void setRequestRecordingPermission(bool rec); + +/* + * Gets whetever recording permission will be requested. + */ +bool getRequestRecordingPermission(); + +/* + * Gets whetever recording permission is granted. + */ +bool hasRecordingPermission(); + +/* + * Request recording permission. This is blocking function. + */ +void requestRecordingPermission(); + +/* + * In case recording permission is not granted, this + * function shows the dialog about the recording permission. + */ +void showRecordingPermissionMissingDialog(); + /** * The Audio module is responsible for playing back raw sound samples. **/ diff --git a/src/modules/audio/openal/Audio.cpp b/src/modules/audio/openal/Audio.cpp index 2cdbc6371..1da25d076 100644 --- a/src/modules/audio/openal/Audio.cpp +++ b/src/modules/audio/openal/Audio.cpp @@ -101,6 +101,15 @@ Audio::Audio() love::thread::disableSignals(); #endif + // Before opening new device, check if recording + // is requested. + if (getRequestRecordingPermission()) + { + if (!hasRecordingPermission()) + // Request recording permission on some OSes. + requestRecordingPermission(); + } + // Passing null for default device. device = alcOpenDevice(nullptr); @@ -399,6 +408,14 @@ void Audio::setDistanceModel(DistanceModel distanceModel) const std::vector &Audio::getRecordingDevices() { + // If recording permission is not granted, inform user about it + // and return empty list. + if (!hasRecordingPermission()) + { + showRecordingPermissionMissingDialog(); + return {}; + } + std::vector devnames; std::vector devices;