react-native-audio-recorder-player icon indicating copy to clipboard operation
react-native-audio-recorder-player copied to clipboard

Recording not possible in Android 13

Open thinkmobilede opened this issue 1 year ago • 19 comments

In Android 13 (Api Level 33) WRITE_EXTERNAL_STORAGE is not granted and the permission check will fail. The library defines these permissions in the AndroidManifest.xml and in RNAudioRecorderPlayerModule.kt startRecorder explicitly checks and asks for the permissions RECORD_AUDIO and WRITE_EXTERNAL_STORAGE. As the Storage-permission can not be granted it will fail. The library does not record with the exception "Try again after adding permission.".

If the permission check is removed it will need to be asked separately by PermissionsAndroid if writing to external storage (not in app sandbox) is needed.

If this is the case, you can use this patch-package fix:

react-native-audio-recorder-player+3.5.1.patch

diff --git a/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt b/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt
index 8bd601e..16b46da 100644
--- a/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt
+++ b/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt
@@ -38,11 +38,9 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont
     fun startRecorder(path: String, audioSet: ReadableMap?, meteringEnabled: Boolean, promise: Promise) {
         try {
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
-                    (ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED ||
-                    ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {
+                    (ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)) {
                 ActivityCompat.requestPermissions((currentActivity)!!, arrayOf(
-                        Manifest.permission.RECORD_AUDIO,
-                        Manifest.permission.WRITE_EXTERNAL_STORAGE), 0)
+                        Manifest.permission.RECORD_AUDIO), 0)
                 promise.reject("No permission granted.", "Try again after adding permission.")
                 return
             }

thinkmobilede avatar Sep 14 '22 10:09 thinkmobilede

Hey, thanks for making this, it helped me out.

I had to add \ No newline at end of file at the end of the file make it work.


Not sure if this note is actually relevant to this Android 13 update or if it was alway like this, but sharing none the less - I needed to set unique file names, so I had to define the path. The path that works is data/user/0/com.[your-app-name]/cache/${fileName}.mp4

For reference, here's the full function:

onStartRecord = async () => {
  const fileName = `record-${new Date().getTime()}`;

  const path = Platform.select({
    ios: `${fileName}.m4a`,
    android: `data/user/0/com.flow_app/cache/${fileName}.mp4`,
  });

  const result = await audioRecorderPlayer.startRecorder(path);
  audioRecorderPlayer.addRecordBackListener((e) => {
    return;
  });
};

JohnGoodman avatar Sep 29 '22 06:09 JohnGoodman

@thinkmobilede Is this change compatible with API levels < 33? In other words - will this change break the app on older versions of Android?

hrastnik avatar Oct 06 '22 23:10 hrastnik

@thinkmobilede Is this change compatible with API levels < 33? In other words - will this change break the app on older versions of Android?

As mentioned in the issue if you want to write the file to external storage (not app sandbox) you will need ask for the write permissions with PermissionsAndroid. You could also adapt the change above to still ask for the permissions by the library depending on the API level but then still you need to handle writing the file correctly on Android 11+ as the legacy storage system stops working there.

thinkmobilede avatar Oct 07 '22 08:10 thinkmobilede

I feel like react-native-audio-recorder-player is doing too much. The startRecording method should just try to record, and then, if the method fails it could warn the user about the possibility that permissions are not granted.

hrastnik avatar Oct 07 '22 08:10 hrastnik

I feel like react-native-audio-recorder-player is doing too much. The startRecording method should just try to record, and then, if the method fails it could warn the user about the possibility that permissions are not granted.

Many RN libraries want to simplify the usage this way, you can still handle the permissions yourself before using the library (e. g. in your user flow). The simplification here fails because the permission is not granted and the library not maintained for some time. You can also fork the library and remove the permissions handling for your usage.

thinkmobilede avatar Oct 07 '22 08:10 thinkmobilede

I feel like react-native-audio-recorder-player is doing too much. The startRecording method should just try to record, and then, if the method fails it could warn the user about the possibility that permissions are not granted.

Many RN libraries want to simplify the usage this way, you can still handle the permissions yourself before using the library (e. g. in your user flow). The simplification here fails because the permission is not granted and the library not maintained for some time. You can also fork the library and remove the permissions handling for your usage.

Hello, I am back here. Sorry that I was very busy. I am trying to follow up on issues today. Do you think the permission check should be removed from the module?

hyochan avatar Oct 15 '22 11:10 hyochan

IMO it should be at least moved to a separate fn like "requestPermissions".

hrastnik avatar Oct 15 '22 13:10 hrastnik

is there any news on this issue? Since this library cannot be used on android 13 as of now :/

pierroo avatar Jan 03 '23 12:01 pierroo

patch for 3.5.3 😢

diff --git a/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt b/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt
index 5cb9bbf..f2fb693 100644
--- a/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt
+++ b/node_modules/react-native-audio-recorder-player/android/src/main/java/com/dooboolab.audiorecorderplayer/RNAudioRecorderPlayerModule.kt
@@ -41,10 +41,8 @@ class RNAudioRecorderPlayerModule(private val reactContext: ReactApplicationCont
                 // TIRAMISU (33)
                 // https://github.com/hyochan/react-native-audio-recorder-player/issues/503
                 if (Build.VERSION.SDK_INT < 33 &&
-                        (ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED ||
-                        ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED))  {
+                       (ActivityCompat.checkSelfPermission(reactContext, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)) {
                     ActivityCompat.requestPermissions((currentActivity)!!, arrayOf(
-                            Manifest.permission.RECORD_AUDIO,
                             Manifest.permission.WRITE_EXTERNAL_STORAGE), 0)
                     promise.reject("No permission granted.", "Try again after adding permission.")
                     return

kenneth4896 avatar May 03 '23 08:05 kenneth4896

I was able to fix this issue for 3.5.3 without patching node_modules. I used the react-native-permissions library to ask for the missing WRITE_EXTERNAL_STORAGE permission.

Here's some sample code for Android:

import { request, PERMISSIONS, RESULTS } from 'react-native-permissions';

const micStatus = await request(PERMISSIONS.ANDROID.RECORD_AUDIO);
const mediaStatus = await request(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);

export const requestAllPermissions = async () => {
  const micStatus = await request(PERMISSIONS.ANDROID.RECORD_AUDIO);
  const mediaStatus = await request(PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE);
  return micStatus === RESULTS.GRANTED && mediaStatus === RESULTS.GRANTED;
};

After asking the user for these permissions, I was able to both record and replay audios.

Note: It goes without saying that these permissions must also be included in android/app/src/main/AndroidManifest.xml.

vkononov avatar May 12 '23 13:05 vkononov

I was able to fix this issue for 3.5.3 without patching node_modules. I used the react-native-permissions library to ask for the missing WRITE_EXTERNAL_STORAGE permission.

I doubt that you tested this on an Android 13 device. This issue is about running on an Android 13 device and this Android version does not grant WRITE_EXTERNAL_STORAGE anymore, so you will not be able to get it and it will fail.

thinkmobilede avatar May 12 '23 13:05 thinkmobilede

I doubt that you tested this on an Android 13 device. This issue is about running on an Android 13 device and this Android version does not grant WRITE_EXTERNAL_STORAGE anymore, so you will not be able to get it and it will fail.

@thinkmobilede Unfortunately, you're right. Although I did test on Android 13, it turns out I was running API level 32 - which worked fine. Once I tested on API level 33, it did not work, just as you said.

vkononov avatar May 12 '23 14:05 vkononov

hello any update on this one? i have the same issue.

AlkanV avatar Jul 10 '23 13:07 AlkanV

Hello, did someone solve the problem? I have the same problem.

XchHarutyunyan avatar Aug 28 '23 07:08 XchHarutyunyan

I solved it by removing the permission check via patch-package and did my own requesting using react-native-permissions

hrastnik avatar Aug 28 '23 07:08 hrastnik

I solved it by removing the permission check via patch-package and did my own requesting using react-native-permissions

It worked for me too, I also added these lines.

Android

NOTE: The problem is fixed in version 0.70.7.

/node_modules/react-native/Libraries/PermissionsAndroid/NativePermissionsAndroid.js

...
| 'android.permission.RECEIVE_WAP_PUSH'
| 'android.permission.RECEIVE_MMS'
| 'android.permission.WRITE_EXTERNAL_STORAGE'; <- removed this and
| 'android.permission.WRITE_EXTERNAL_STORAGE' <- added these 3
| 'android.permission.POST_NOTIFICATIONS'
| 'android.permission.READ_MEDIA_AUDIO';
...

/node_modules/react-native/Libraries/PermissionsAndroid/PermissionsAndroid.js

...
RECEIVE_MMS: 'android.permission.RECEIVE_MMS',
READ_EXTERNAL_STORAGE: 'android.permission.READ_EXTERNAL_STORAGE',
WRITE_EXTERNAL_STORAGE: 'android.permission.WRITE_EXTERNAL_STORAGE',
POST_NOTIFICATIONS: 'android.permission.POST_NOTIFICATIONS', <- added this
READ_MEDIA_AUDIO: 'android.permission.READ_MEDIA_AUDIO', <- added this
...
WRITE_CALL_LOG: string,
WRITE_CONTACTS: string,
WRITE_EXTERNAL_STORAGE: string,
POST_NOTIFICATIONS: string, <- added this
READ_MEDIA_AUDIO: string, <- added this
....

XchHarutyunyan avatar Sep 29 '23 08:09 XchHarutyunyan

Earlier my Android.Manifest is like this one

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="+++++++++++"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>

After the change it was like

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="++++++++++++"> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS'" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>

Now its working properly and then along with that i changed the version from 3.5.1 to 3.5.3 of this package react-native-audio-recorder-player

``

Bv2124 avatar Nov 16 '23 06:11 Bv2124

Please, I need help! It keeps warning that 'try again after adding permissions'

Android API 34

react-native: "0.71.8" react-native-audio-recorder-player: "^3.3.4"

const requestPermissions = async () => { const atLeastAndroid13 = Platform.OS === 'android' && Platform.Version >= 33;

  if (Platform.OS === 'android') {
    try {
      const grants = await PermissionsAndroid.requestMultiple(
        atLeastAndroid13
          ? [PermissionsAndroid.PERMISSIONS.RECORD_AUDIO, PermissionsAndroid.PERMISSIONS.READ_MEDIA_AUDIO]
          : [
            PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
            PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,
            PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,
          ],
      );

      console.log({grants});

      if (
        atLeastAndroid13
          ? grants['android.permission.RECORD_AUDIO'] === PermissionsAndroid.RESULTS.GRANTED &&
            grants['android.permission.READ_MEDIA_AUDIO'] === PermissionsAndroid.RESULTS.GRANTED
          : grants['android.permission.WRITE_EXTERNAL_STORAGE'] === PermissionsAndroid.RESULTS.GRANTED &&
          grants['android.permission.READ_EXTERNAL_STORAGE'] === PermissionsAndroid.RESULTS.GRANTED &&
          grants['android.permission.RECORD_AUDIO'] === PermissionsAndroid.RESULTS.GRANTED
      ) {
        console.log('permissions granted');
      } else {
        console.warn('All required permissions not granted');
        return false;
      }
    } catch (err) {
      console.warn(err);
      return false;
    }
  }
  return true;
}

const permissionsGranted = await requestPermissions();

if (!permissionsGranted) {
  Alert.alert('Permissions not granted', 'Please grant the necessary permissions in settings.');
  return;
}

if (status === 'start') {
  // let audioRecorderPlayer = new AudioRecorderPlayer();
  try {
    const uri = await audioRecorderPlayer.startRecorder(path);
    console.log('uri:', uri);
    NotificationService.create(order.userId, 'recording-started', I18n.locale === 'he' ? 'heb' : I18n.locale === 'en' ? 'en' : 'es');
    setRecordUri(uri);
    audioRecorderPlayer.addRecordBackListener((e) => {
      const timer = convertMilisecToTimer(e.currentPosition);
      setRecordTime(timer);
      console.log({timer});
      return
    });
    console.log("#########################TIEMPO#######################")
    BackgroundTimer.stopBackgroundTimer();
    setStatus('progress');
  } catch (err) {
    console.log("el error en el try...")
    console.error(err);
  }
} else if (status === 'pause') {
  try {
    await audioRecorderPlayer.resumeRecorder();
    BackgroundTimer.stopBackgroundTimer();
    setStatus('progress');
  } catch (err) {
    console.log(`Pause ::> status: ${status}, Record timer: ${recordTime}`)
    console.warn(err);
  }
} else if (status === 'progress') {
  try {
    await audioRecorderPlayer.pauseRecorder();
    setStatus('pause');
    // setTimeLeft(timeHold);
  } catch (err) {
    console.log(`Progress ::> status: ${status}, Record timer: ${recordTime}, recorder: ${JSON.stringify(audioRecorderPlayer)}`)
    console.warn(`Este es el null : ${err}`);
  }
}

ecastago avatar Jun 17 '24 21:06 ecastago

@ecastago What's the actual version of react-native-audio-recorder-player? Many months ago I got this message sometimes and had to apply the patch, but after upgrading to a more recent version, the patch isn't needed anymore ( pretty sure about that, feel free to correct me if I'm wrong ) and I stopped getting these errors. I'm using version 3.6.7.

in case it helps, i'm using RN 0.73.6

JohnGoodman avatar Jun 18 '24 17:06 JohnGoodman