expo-av bare Error: Missing audio recording permissions - react-native

Was looking to use the recently available expo packages for detached or "bare" apps in react-native.
Running into the following error:
Error: Missing audio recording permissions.
however I have already called the expo-permissions library to ensure RecordAudio permissions are obtained.
See https://github.com/Glorifundel/bareaudio for full example project
App.js
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View, Button } from 'react-native';
import * as Permissions from 'expo-permissions';
import { Audio } from 'expo-av';
export default class App extends Component {
render() {
return (
<View style={{ flex: 1 }}>
<Text style={styles.welcome}>Welcome to React Native!</Text>
<Button title="Get Permission" onPress={this.onPressPermission} />
<Button title="Record" onPress={this.onPressRecord} />
</View>
);
}
onPressPermission = async () => {
const { status, expires, permissions } = await Permissions.askAsync(
Permissions.CAMERA_ROLL,
Permissions.AUDIO_RECORDING
);
alert(`permission: ${status}`);
};
onPressRecord = async () => {
const recording = new Audio.Recording();
try {
await recording.prepareToRecordAsync(
Audio.RECORDING_OPTIONS_PRESET_LOW_QUALITY
);
await recording.startAsync();
alert(`onPressRecord recording!`);
} catch (error) {
alert(`onPressRecord error: ${error}`);
}
};
}
Pressing the "Get Permission" button reports an alert "permissions: granted", following that up with pressing the "Record" button results in an alert "onPressRecord error: Error: Missing audio recording permissions."
Any insight is appreciated,
Environment details: I am running on windows 10, on an android emulator running android API 27 (8.1 Oreo). Did a fresh Node v10.15.3 install as well as a fresh react-native-cli expo-cli install and generated the project with expo init --template bare-minimum. followed up with yarn add expo-av and yarn add expo-permissions, followed the instructions found on the readme for the two packages.

async function startRecording() {
try {
console.log('Requesting permissions..');
await Audio.requestPermissionsAsync().then(() => {
console.log('Permission granted!');
})
.catch(error => {
console.log(error);
});
await Audio.setAudioModeAsync({
allowsRecordingIOS: true,
playsInSilentModeIOS: true,
});
console.log('Starting recording..');
const { recording } = await Audio.Recording.createAsync(
Audio.RECORDING_OPTIONS_PRESET_HIGH_QUALITY
);
setRecording(recording);
console.log('Recording started');
} catch (err) {
console.error('Failed to start recording', err);
}
}

Related

ConnectyCube expo react native support for RNChat

Is there a chance to get an RNChat example which runs on latest expo react native for supporting the great expo plugins like expo av player or expo haptics?
Edit:
Build fails
=== BUILD TARGET React-Codegen OF PROJECT Pods WITH CONFIGURATION Debug ===
Check dependencies
** BUILD FAILED **
The following build commands failed:
CompileSwiftSources normal x86_64 com.apple.xcode.tools.swift.compiler
CompileSwift normal x86_64
(2 failures)
I get tons of errors using the expo and expo-modules-core like:
expo-modules-core/ Class Components Factories swift:71:52: error: unknown attribute '_implicitSelfCapture'
OR
expo-modules-core/ios/Swift/DynamicTypes/DynamicEnumType.swift:7:22: error: expected declaration
let innerType: any Enumerable.Type
Unfortunately, we don't have the chat app example for Expo. Errors you've provided aren't related to ConnectyCube's services.
You can make chat app using our ConnectyCube SDK. The SDK supports Expo except WebRTC (video/audio calls).
See docs how to use ConnectyCube's API and chat for React Native here
Try simple code how to use ConnectyCube SDK with Expo:
npx create-expo-app ExpoConnectyCube
cd ExpoConnectyCube
yarn add react-native-connectycube
yarn ios
import { StatusBar } from "expo-status-bar";
import { useEffect, useState } from "react";
import { StyleSheet, Text, View } from "react-native";
import ConnectyCube from "react-native-connectycube";
const creds = {
appId: 385,
authKey: "DFBMs5-dKBBCXcd",
authSecret: "SkCW-ThdnmRg9Za",
};
const conf = {
debug: { mode: 1 },
};
const user = {
id: 72780,
login: "videouser1",
password: "videouser1",
};
export default function App() {
const [session, setSession] = useState("empty");
const [chat, setChat] = useState(false);
const [message, setMessage] = useState("");
const initConnectyCube = () => {
ConnectyCube.init(creds, conf);
};
const createAuthSession = () => {
ConnectyCube.createSession({ login: user.login, password: user.password })
.then((result) => {
setSession(result);
})
.catch((sessionError) => setChat(sessionError));
};
const chatConnect = () => {
ConnectyCube.chat.onMessageListener = (user, message) => {
setMessage({ user, message });
};
ConnectyCube.chat
.connect({ userId: user.id, password: user.password })
.then(() => {
setChat(true);
ConnectyCube.chat.send(user.id, { body: "Hello", type: "chat" });
})
.catch((chatError) => setChat(chatError));
};
useEffect(() => {
initConnectyCube();
createAuthSession();
chatConnect();
}, []);
return (
<View style={styles.container}>
<Text>{`Session: ${JSON.stringify(session)} \n`}</Text>
<Text>{`Chat connected: ${chat} \n`}</Text>
<Text>{`onMessage: ${JSON.stringify(message)} \n`}</Text>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
justifyContent: "center",
},
});

Data lost with last version of react-native and AsyncStorage

When I try to update my application (android/ios) with react-native and expo my data is lost and i don't know why :
old version :
"react-native": "0.51.0",
"redux-persist": "4.8.2",
new version :
"redux-persist": "4.8.2",
"react-native": "~0.61.5",
"expo": "^37.0.12",
Build with expo for the newer version
How I persist the data. The code hasn't changed between versions.
useEffect(() => {
persistStore(
store,
{
storage: AsyncStorage,
whitelist: ['data', 'data2' ],
transforms: [
createTransform(
state => state,
state => ({
...state,
infosOpened: false,
}),
{
whitelist: 'app',
}
),
],
},
() => {
setRehydrated(true)
}
)
}, [])
AsyncStorage is imported from react-native and I keep react-native with expo. #react-native-community/async-storage isn't compatible.
As what I saw inside the older version data is persisted with SQLlite format 3 inside "data user". Data appears to be always here with the newer version but it is no longer recovered.
I don't know if there is some braking changes react-native from version 51 to 61. I see nothing inside github release tags.
Edit1: OK so it seems that the problem is that RN use RCTAsyncLocalStorage_V1 folder and expo RCTAsyncLocalStorage. How I can fix that with the easiest way for IOS and Android ? Is it better to eject expo app or to add migration code ?
Edit2: Finally I opted for an RN version with react-native-unimodules like that I have no problem with path storage and I can use some dependencies from expo. (https://blog.expo.io/you-can-now-use-expo-apis-in-any-react-native-app-7c3a93041331)
Maybe the solution under works... I didn't test it
Another possible solution here. I didn't test it too :
https://github.com/expo/expo/issues/8220#issuecomment-656300244
You can find the sample code for migration in the snippet. This snippet is migration from Expo to pure RN. You will only have to reverse migration.
import React, { Component } from 'react';
import { View, ActivityIndicator, AsyncStorage } from 'react-native';
import PropTypes from 'prop-types';
import RNFS from 'react-native-fs';
export default class WalletMigrate extends Component {
componentDidMount() {
this.migrateDataFromExpo();
}
migrationComplete() {
console.log('Migration was successful. Exiting migration...')
this.props.onComplete();
}
// Migrate Document directory from Expo
async migrateDataFromExpo() {
const expoDirectoryExists = await RNFS.exists(RNFS.DocumentDirectoryPath + '/ExponentExperienceData');
if (!expoDirectoryExists) {
console.log('Expo data was previously migrated. Exiting migration...');
this.props.onComplete();
return;
}
try {
await RNFS.unlink(RNFS.DocumentDirectoryPath + '/RCTAsyncLocalStorage_V1')
console.log('/RCTAsyncLocalStorage_V1 has been deleted. Continuing...')
} catch {
console.log('/RCTAsyncLocalStorage_V1 does not exist. Continuing...')
}
RNFS.copyFile(
RNFS.DocumentDirectoryPath + '/ExponentExperienceData/%40USERNAME%2FAPPNAME/RCTAsyncLocalStorage',
RNFS.DocumentDirectoryPath + '/RCTAsyncLocalStorage_V1',
)
.then(() => {
RNFS.readDir(RNFS.DocumentDirectoryPath + '/RCTAsyncLocalStorage_V1').then(files => {
files.forEach(file => {
if (file.name !== 'manifest.json') {
RNFS.readFile(file.path).then(fileContents => {
AsyncStorage.setItem('data', fileContents)
.then(() => {
RNFS.unlink(RNFS.DocumentDirectoryPath + '/ExponentExperienceData').then(() => this.migrationComplete());
})
.catch(() => {
console.log('An error was encountered when trying to delete /ExponentExperienceData. Exiting migration...');
this.props.onComplete();
})
.then(() => this.migrationComplete())
});
}
});
})
.catch(error => {
console.log('An error was encountered when trying to read the /RTCAsyncLocalStorage_V1 directory. Exiting migration...');
console.log(error);
this.props.onComplete();
});
})
.catch(_error => this.props.onComplete());
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
}
WalletMigrate.propTypes = {
onComplete: PropTypes.func,
};

Works in debug but not in release | expo-av | react-native-unimodules

Environment
Expo CLI 3.11.1 environment info:
System:
OS: Windows 10
Binaries:
Yarn: 1.22.4 - C:\Users\user\AppData\Roaming\npm\yarn.CMD
npm: 6.12.0 - C:\Program Files\nodejs\npm.CMD
IDEs:
Android Studio: Version 3.6.0.0 AI-192.7142.36.36.6308749
React-native android on device
Step to repro :
1.
npx react-native init ReactRelease --version 0.62.2
npm install react-native-unimodules#0.9.0 --save
expo install expo-av
2.
Configure your files according to the doc in each library
3.
Edit App.js
import React, { Component } from 'react';
import { Text, StyleSheet, View, Button, Alert } from 'react-native';
import { Audio } from 'expo-av';
import * as Permissions from 'expo-permissions';
var audioObjectActions = new Audio.Sound();
export default class App extends Component {
constructor(props) {
super(props);
this.state = {};
}
async componentDidMount() {
await Permissions.askAsync(Permissions.AUDIO_RECORDING);
await Audio.setAudioModeAsync({
staysActiveInBackground: true,
allowsRecordingIOS: false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DUCK_OTHERS,
playsInSilentModeIOS: true,
playThroughEarpieceAndroid: false,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DUCK_OTHERS,
shouldDuckAndroid: true
});
}
async _playRandomAction() {
try {
audioObjectActions.setOnPlaybackStatusUpdate(this._onPlaybackStatusUpdateAction);
await audioObjectActions.loadAsync(require('./assets/song_sound.m4a'));
await audioObjectActions.playAsync();
} catch (error) {
Alert.alert('ERROR : ', '' + JSON.stringify(error));
}
}
_onPlaybackStatusUpdateAction = async (playbackStatus) => {
if (!playbackStatus.isLoaded) {
// Update your UI for the unloaded state
if (playbackStatus.error) {
console.log(`Encountered a fatal error during playback: ${playbackStatus.error}`);
// Send Expo team the error on Slack or the forums so we can help you debug!
}
} else {
if (playbackStatus.didJustFinish && !playbackStatus.isLooping) {
await audioObjectActions.stopAsync();
await audioObjectActions.unloadAsync();
}
}
};
render() {
return (
<View style={styles.container}>
<Text> Test Audio </Text>
<Button title="On joue le morceau" onPress={async () => this._playRandomAction()} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#555555'
}
});
4.
Create assets folder and add song_sound.m4a like this :
5
Connect your android device via USB
Test in debug :
//POWERSHELL 1
npx react-native run-android
//POWERSHELL 2
npm start
Behavior
Works perfectly : the sound is loaded and is played
Test in release
cd android
./gradlew assembleRelease
./gradlew installRelease
Behavior
The sound doesn't load and the error displayed is :
{
"nativeStackAndroid":[],
"userInfo":null,
"message":"com.google.android.exoplayer2.upstream.FileDataSource$FileDataSourceException: java.io.FileNotFoundException: assets_song_sound: open fialed: ENOENT (No such file or directory)",
"code":"E_LOAD_ERROR",
"line": 18,
"column": 1111,
"sourceURL":"index.android.bundle"
}
What I want ?
Just load and play an audio in release juste like in debug.
I finally found the answer.
How it works
require()
not works well in release.
I don't know why require() works well in release with Image but not with Audio
So you need to use
{ uri: 'asset:/my_sound.m4a' }
and put your sound file (like my_sound.m4a) in android/app/src/main/assets:
And it will work.

React Native Expo “AppLoading threw an unexpected error when loading” error

I have a React Native project with Expo, I installed Expo client on my Android phone. It used to work well so far. But even though I didn't change any code, I now get the following error when I scan the QR code from my phone. This error is shown on terminal and phone screen keeps blank.
import React from 'react';
import { Image } from 'react-native';
import { AppLoading } from 'expo';
import { Asset } from 'expo-asset';
import { Block, GalioProvider } from 'galio-framework';
import Screens from './navigation/Screens';
import { Images, articles, ditsTheme } from './constants';
// cache app images
const assetImages = [
Images.Onboarding,
Images.LogoOnboarding,
Images.Logo,
Images.Pro,
Images.DITSLogo,
Images.iOSLogo,
Images.androidLogo
];
// cache product images
articles.map(article => assetImages.push(article.image));
function cacheImages(images) {
return images.map(image => {
if (typeof image === 'string') {
return Image.prefetch(image);
} else {
return Asset.fromModule(image).downloadAsync();
}
});
}
export default class App extends React.Component {
state = {
isLoadingComplete: false,
}
render() {
if(!this.state.isLoadingComplete) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<GalioProvider theme={ditsTheme}>
<Block flex>
<Screens />
</Block>
</GalioProvider>
);
}
}
_loadResourcesAsync = async () => {
return Promise.all([
...cacheImages(assetImages),
]);
};
_handleLoadingError = error => {
// In this case, you might want to report the error to your error
// reporting service, for example Sentry
warn(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
}
How can I solve this error?
In your _handleLoadingError method, you're using warn. While you should be using console.warn. This is what's breaking your app.
I was getting this error, the problem was with AppLoading. The issue turned out to be incorrect function name that I was calling from <AppLoading .. />
Note below, I was using Font.loadAsync (with missing c), fixing it made the font load correctly.
const getFonts = () => Font.loadAsyn({
'nanitu-regular' : require('./assets/fonts/EastSeaDokdo-Regular.ttf'),
'nanitu-bold' : require('./assets/fonts/NanumBrushScript-Regular.ttf')
})
;
You should add onError to AppLoading. As a like:
<AppLoading startAsync={getFonts} onFinish={() => {
setFontLoaded(true)
}} onError={console.warn} />

undefined is not an object (evaluating '_expo.Permission.askAsync')

i don't know what's the problem exactly but when i click on the button to choose image that erreur fire in the console
here's my code
_checkPermissions = async () => {
try {
const { status } = await Permission.askAsync(Permission.CAMERA);
this.setState({ camera: status });
const { statusRoll } = await Permission.askAsync(Permission.CAMERA_ROLL);
this.setState({ cameraRoll: statusRoll });
} catch (err) {
console.log(err);
}
};
findNewImage = async () => {
try {
this._checkPermissions();
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: "Images",
allowsEditing: true,
quality: 1
});
if (!result.cancelled) {
this.setState({
image: result.uri
});
} else {
console.log("cancel");
}
} catch (err) {
// console.log(err);
}
};
to me what solved it was importing the permissions and imagePicker like this:
import * as Permissions from 'expo-permissions';
import * as ImagePicker from 'expo-image-picker';
instead of this:
import Permissions from 'expo-permissions';
import ImagePicker from 'expo-image-picker';
And that's basically because there is no default export
It is getAsync(), not askAsync()
https://docs.expo.io/versions/latest/sdk/permissions/
I know I'm a little late to the party, but I feel it's important to show a way that is currently working (as of 2022) and also askAsync is deprecated ...
Getting image from (native) CAMERA
TL;DR: Even though we want "camera", we will actually use expo-image-picker FOR THE CAMERA (yes, you read right!)
I REPEAT: DO NOT USE expo-camera FOR CAMERA!
REMEMBER: USE ImagePickerExpo.requestCameraPermissionsAsync()AND ImagePickerExpo.launchCameraAsync() NOT Camera....!
So install it first: expo install expo-image-picker
Then import everything, from it under 1 alias, I like to use ImagePickerExpo because ImagePicker itself is confusing since it can mean more libraries, + import all needed for this code - you can replace Button with any other button/pressable that supports onPress (to use react-native-elements, you need to install it with yarn add react-native-elements)
Create displaying component
Create a state & setter to save current image source
Create a function that requests the permissions and opens the camera
Return the coponent with button binding onPress on function from 5. and Image that is displayed from the state from 4. but only when available.
working & tested(so far on android in expo go) code:
import React, { useState } from 'react';
import { View, Image, Alert, StyleSheet } from 'react-native';
import { Button } from 'react-native-elements';
import * as ImagePickerExpo from 'expo-image-picker';
const MyCameraComponent = () => {
const [selectedImage, setSelectedImage] = useState(null);
const openCameraWithPermission = async () => {
let permissionResult = await ImagePickerExpo.requestCameraPermissionsAsync();
if (permissionResult.granted === false) {
Alert.alert("For this to work app needs camera roll permissions...");
return;
}
let cameraResult = await ImagePickerExpo.launchCameraAsync({
// ...
});
console.log(cameraResult);
if (cameraResult.cancelled === true) {
return;
}
setSelectedImage({ localUri: cameraResult.uri });
};
return (
<View>
<Button title='Take a photo' onPress={openCameraWithPermission}></Button>
{(selectedImage !== null) && <Image
source={{ uri: selectedImage.localUri }}
style={styles.thumbnail}
/>}
</View>
);
}
const styles = StyleSheet.create({
thumbnail: {
width: 300,
height: 300,
resizeMode: "contain"
}
});
export default MyCameraComponent;
Note that I had to style the Image for it to display, it didn't display to me without proper styling which I find misleading, but I guess that's the react native way...
BTW: This also works in Android emulator (besides expo go in real Android device)
It also works on snack on desktop but only when you choose android (or Web) - https://snack.expo.dev/#jave.web/expo-camera-from-expo-image-picker
Getting image from (native) gallery (not camera)
In case you're wondering how to do the same for gallery, the code is basically the same, you just need a different callback function for the button that uses requestMediaLibraryPermissionsAsync / launchImageLibraryAsync instead of the camera ones.
let openImagePickerAsync = async () => {
let permissionResult = await ImagePickerExpo.requestMediaLibraryPermissionsAsync();
if (permissionResult.granted === false) {
Alert.alert("For this to work app needs media library/gallery permissions...");
return;
}
let pickerResult = await ImagePickerExpo.launchImageLibraryAsync({
presentationStyle: 0, // without this iOS was crashing
});
console.log(pickerResult);
if (pickerResult.cancelled === true) {
return;
}
setSelectedImage({ localUri: pickerResult.uri });
}