camerja
camerja copied to clipboard
Camera mount error
Hi, when i navigate to my Camera component for the first time, the onCameraReady
method is correctly fired, but when i navigate there again, the onMountError
method is fired. The docs say that this method returns an object containing a message, but when i try to log the object, i get undefined:
onMountError={(obj) => console.log('Camera mount error', JSON.stringify(obj))
.
This means that i can see the buttons in my render, but the camera screen is black.
I thought the issue might be that the Cam component would still be mounted, but I checked and whenever i leave it, componentWillUnmount
is correctly fired.
I based my code on the doc's example and removed a lot of things. Here it is:
export class CameraModule extends Component<IProps, IState> {
camera: any;
constructor(props: IProps) {
super(props);
this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
this.state = {
type: Camera.Constants.Type.back,
flash: 'off',
zoom: 0,
autoFocus: 'on',
depth: 0,
whiteBalance: 'auto',
ratio: '16:9',
ratios: [],
showGallery: false,
permissionsGranted: false,
};
}
async componentWillMount() {
BackHandler.addEventListener(C.HARDWARE_BACK_PRESS, this.handleBackButtonClick);
const { status } = await Permissions.askAsync(Permissions.CAMERA);
this.setState({ permissionsGranted: status === 'granted' });
}
componentWillUnmount() {
BackHandler.removeEventListener(C.HARDWARE_BACK_PRESS, this.handleBackButtonClick);
}
handleBackButtonClick() {
this.props.navigation.goBack(null);
return true;
}
toggleFacing() {
this.setState({ type: this.state.type === 'back' ? 'front' : 'back' });
}
toggleFlash() {
this.setState({ flash: flashModeOrder[this.state.flash] });
}
setRatio(ratio: string) {
this.setState({ ratio });
}
toggleWB() {
this.setState({ whiteBalance: wbOrder[this.state.whiteBalance] });
}
toggleFocus() {
this.setState({ autoFocus: this.state.autoFocus === 'on' ? 'off' : 'on' });
}
zoomOut = () => {
const { zoom } = this.state;
if (zoom > 0) {
this.setState({ zoom: zoom - 0.01 });
}
}
zoomIn = () => {
const { zoom } = this.state;
if (zoom < 0.5) {
this.setState({ zoom: zoom + 0.01 });
}
}
setFocusDepth(depth: number) {
this.setState({ depth });
}
resize = async (url: string) => {
const manipResult = await ImageManipulator.manipulate(url,
[{ resize: { height: 512 } }],
{ format: 'jpeg', compress: 1, base64: true },
);
return manipResult.base64;
}
takePicture = async () => {
if (this.camera) {
const { auth: { sessionId }, appReducer: { currentJob: { id } } } = this.props;
this.props.setIsTakingPicture(true);
this.camera.takePictureAsync({ base64: true, quality: 0.5 }).then(data => {
console.log('Photo taken');
this.resize(data.uri).then(base64 => {
this.props.setPictureId(this.props.appReducer.pictureId + 1);
setTimeout(() => {
const newPic = {
id: this.props.appReducer.pictureId,
jobId: id,
captureDate: moment().valueOf(),
picture: base64,
};
const { appReducer: { localPictures = [] } } = this.props;
if (isValid(localPictures)) {
this.props.setLocalPictures([...localPictures, newPic]);
} else {
this.props.setLocalPictures([newPic]);
}
this.props.setIsTakingPicture(false);
}, 0);
});
});
}
}
renderNoPermissions() {
const { t } = this.props;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center', padding: 10 }}>
<Text>{t('aidooTechnician:noPermission')}</Text>
</View>
);
}
renderCamera() {
const { navigation: { navigate }, appReducer: { theme = null, shape = C.SHAPE_ROUND, isTakingPicture }, pictureReducer: { isAddingPicture } } = this.props;
const { colorMain = C.COLOR_THEME } = theme;
const { type, flash, autoFocus, zoom, whiteBalance, ratio, depth } = this.state;
const borderRadius = shape === C.SHAPE_ROUND ? 25 : 0;
const pictureButtonBorderRadius = shape === C.SHAPE_ROUND ? 40 : 0;
return (
<Camera
ref={ref => this.camera = ref}
style={{ flex: 1 }}
type={type}
flashMode={flash}
autoFocus={autoFocus}
zoom={zoom}
whiteBalance={whiteBalance}
ratio={ratio}
focusDepth={depth}
onCameraReady={() => console.log('Camera ready')}
onMountError={(message) => console.log('Camera mount error', JSON.stringify(message))}>
<View style={{ flex: 1, backgroundColor: 'transparent' }}>
<View
style={{
backgroundColor: 'transparent',
flexDirection: 'row',
justifyContent: 'space-around',
paddingTop: Constants.statusBarHeight / 2,
}}>
<TouchableOpacity style={[s.camButton, { borderRadius, backgroundColor: 'white' }]} onPress={() => this.handleBackButtonClick()}>
<Icon name={C.ICON_BACK_BUTTON} />
</TouchableOpacity>
<TouchableOpacity style={[s.camButton, { borderRadius }]} onPress={() => this.toggleFlash()}>
{flash === 'on' && <Image source={require('../../../../assets/camera/flash_on.png')} style={{ height: 30, width: 30 }} />}
{flash === 'off' && <Image source={require('../../../../assets/camera/flash_off.png')} style={{ height: 30, width: 30 }} />}
{flash === 'auto' && <Image source={require('../../../../assets/camera/flash_auto.png')} style={{ height: 30, width: 30 }} />}
{flash === 'torch' && <Icon name={C.ICON_BULB} style={{ fontSize: 30, color: 'white' }} />}
</TouchableOpacity>
<TouchableOpacity
style={[s.camButton, { borderRadius }]}
onPress={() => this.toggleFocus()}>
<Text style={{ fontWeight: C.NUMBER_BOLD, color: 'white' }}>AF</Text>
<Text style={{ color: 'white' }}>{autoFocus} </Text>
</TouchableOpacity>
</View>
{autoFocus !== 'on' ? (
<Slider
style={{ width: 150, marginTop: 15, marginRight: 15, alignSelf: 'flex-end' }}
onValueChange={value => this.setFocusDepth(value)}
step={0.1}
/>
) : null}
</View>
<View
style={{
paddingBottom: isIPhoneX ? 20 : 0,
backgroundColor: 'transparent',
flexDirection: 'row',
alignSelf: 'flex-end',
}}>
<View style={{ flex: 0.4, alignSelf: 'flex-end' }}>
<TouchableOpacity
style={[s.camButton, { borderRadius, marginLeft: 10 }]}
onPress={() => this.toggleFacing()}>
<Icon name={C.ICON_REVERSE_CAMERA} style={{ color: 'white' }} />
</TouchableOpacity>
</View>
<View style={{ flex: 0.2, alignSelf: 'flex-end' }}>
<TouchableOpacity
style={[s.pictureButton, { borderRadius: pictureButtonBorderRadius }]}
onPress={() => this.takePicture()}>
<Icon name={C.ICON_CAMERA} style={{ color: 'white' }} />
</TouchableOpacity>
</View>
<View style={{ flex: 0.4, alignItems: 'flex-end' }}>
<TouchableOpacity
style={[s.camButton, { marginRight: 10, borderRadius }]}
onPress={this.zoomIn}>
<Text style={{ color: 'white', fontSize: 22 }}> + </Text>
</TouchableOpacity>
<TouchableOpacity
style={[s.camButton, { marginRight: 10, borderRadius }]}
onPress={this.zoomOut}>
<Text style={{ color: 'white', fontSize: 22 }}> - </Text>
</TouchableOpacity>
<TouchableOpacity
style={[s.camButton, { marginRight: 10, borderRadius, backgroundColor: colorMain }]}
onPress={() => navigate(C.SCREEN_GALLERY)}>
<Icon name={C.ICON_FORWARD_BUTTON} style={{ color: 'white' }} />
</TouchableOpacity>
</View>
</View>
</Camera >
);
}
render() {
const { permissionsGranted } = this.state;
return <View style={{ flex: 1 }}>{permissionsGranted ? this.renderCamera() : this.renderNoPermissions()}</View>;
}
}
const flashModeOrder = {
off: 'on',
on: 'auto',
auto: 'torch',
torch: 'off',
};
const wbOrder = {
auto: 'sunny',
sunny: 'cloudy',
cloudy: 'shadow',
shadow: 'fluorescent',
fluorescent: 'incandescent',
incandescent: 'auto',
};
const mapStateToProps = ({ appReducer, auth, pictureReducer }) => ({
appReducer,
auth,
pictureReducer,
});
const mapDispatchToProps = (dispatch: Function) => ({
setPictureId: (pictureId: number) => dispatch(setPictureId(pictureId)),
setIsTakingPicture: (isTakingPicture: boolean) => dispatch(setIsTakingPicture(isTakingPicture)),
setLocalPictures: (localPictures: ILocalPicture[]) => dispatch(setLocalPictures(localPictures)),
});
export default connect(mapStateToProps, mapDispatchToProps)(CameraModule);
We're a few hours later and right now the Camera is failing to mount 100% of the time. My only solution right now is to restart my phone.
I've noticed that when I place my app in the background and in the foreground again while on the Camera component, the Camera mounts successfully.