Make RNCamera check for permissions.

Migrate permission check to different file to be shared by RNCamera and RCTCamera
This commit is contained in:
Joao Fidelis 2018-01-30 16:11:24 -02:00
parent 0f1cdf5819
commit 177cf00775
6 changed files with 126 additions and 39 deletions

View File

@ -196,6 +196,8 @@ export default class CameraScreen extends React.Component {
onFacesDetected={this.onFacesDetected}
onFaceDetectionError={this.onFaceDetectionError}
focusDepth={this.state.depth}
permissionDialogTitle={'Permission to use camera'}
permissionDialogMessage={'We need your permission to use your camera phone'}
>
<View
style={{

View File

@ -39,6 +39,8 @@
<string>Used to take photos</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string></string>
<key>NSMicrophoneUsageDescription</key>
<string>Used to record audio in videos</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>

View File

@ -226,5 +226,22 @@ RCT_REMAP_METHOD(stopRecording, reactTag:(nonnull NSNumber *)reactTag)
}];
}
RCT_EXPORT_METHOD(checkDeviceAuthorizationStatus:(RCTPromiseResolveBlock)resolve
reject:(__unused RCTPromiseRejectBlock)reject) {
__block NSString *mediaType = AVMediaTypeVideo;
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
if (!granted) {
resolve(@(granted));
}
else {
mediaType = AVMediaTypeAudio;
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
resolve(@(granted));
}];
}
}];
}
@end

View File

@ -16,6 +16,8 @@ import {
UIManager,
} from 'react-native';
import { requestPermissions } from './handlePermissions';
const CameraManager = NativeModules.CameraManager || NativeModules.CameraModule;
function convertNativeProps(props) {
@ -190,34 +192,8 @@ export default class Camera extends Component {
let hasVideoAndAudio =
this.props.captureAudio && captureMode === Camera.constants.CaptureMode.video;
if (Platform.OS === 'ios') {
let check = hasVideoAndAudio
? Camera.checkDeviceAuthorizationStatus
: Camera.checkVideoAuthorizationStatus;
if (check) {
const isAuthorized = await check();
this.setState({ isAuthorized, isAuthorizationChecked: true });
}
} else if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA, {
title: this.props.permissionDialogTitle,
message: this.props.permissionDialogMessage,
});
// On devices before SDK version 23, the permissions are automatically granted if they appear in the manifest,
// so check and request should always be true.
// https://github.com/facebook/react-native-website/blob/master/docs/permissionsandroid.md
const isAuthorized =
Platform.Version >= 23 ? granted === PermissionsAndroid.RESULTS.GRANTED : granted === true;
this.setState({
isAuthorized,
isAuthorizationChecked: true,
});
} else {
this.setState({ isAuthorized: true, isAuthorizationChecked: true });
}
const isAuthorized = await requestPermissions(hasVideoAndAudio, Camera, Platform.OS, this.props.permissionDialogTitle, this.props.permissionDialogMessage);
this.setState({ isAuthorized, isAuthorizationChecked: true });
}
componentWillUnmount() {

View File

@ -2,10 +2,21 @@
import React from 'react';
import PropTypes from 'prop-types';
import { mapValues } from 'lodash';
import { findNodeHandle, Platform, NativeModules, ViewPropTypes, requireNativeComponent } from 'react-native';
import {
findNodeHandle,
Platform,
NativeModules,
ViewPropTypes,
requireNativeComponent,
View,
ActivityIndicator,
Text,
} from 'react-native';
import type { FaceFeature } from './FaceDetector';
import { requestPermissions } from './handlePermissions';
type PictureOptions = {
quality?: number,
};
@ -108,6 +119,10 @@ export default class Camera extends React.Component<PropsType> {
flashMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
whiteBalance: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
autoFocus: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
permissionDialogTitle: PropTypes.string,
permissionDialogMessage: PropTypes.string,
notAuthorizedView: PropTypes.element,
pendingAuthorizationView: PropTypes.element,
};
static defaultProps: Object = {
@ -122,6 +137,37 @@ export default class Camera extends React.Component<PropsType> {
barCodeTypes: Object.values(CameraManager.BarCodeType),
faceDetectionLandmarks: CameraManager.FaceDetection.Landmarks.none,
faceDetectionClassifications: CameraManager.FaceDetection.Classifications.none,
permissionDialogTitle: '',
permissionDialogMessage: '',
notAuthorizedView: (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}
>
<Text
style={{
textAlign: 'center',
fontSize: 16,
}}
>
Camera not authorized
</Text>
</View>
),
pendingAuthorizationView: (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
}}
>
<ActivityIndicator size="small" />
</View>
),
};
_cameraRef: ?Object;
@ -133,6 +179,10 @@ export default class Camera extends React.Component<PropsType> {
super(props);
this._lastEvents = {};
this._lastEventsTimes = {};
this.state = {
isAuthorized: false,
isAuthorizationChecked: false,
};
}
async takePictureAsync(options?: PictureOptions) {
@ -207,19 +257,31 @@ export default class Camera extends React.Component<PropsType> {
}
};
async componentWillMount() {
const hasVideoAndAudio = true; //TODO implement capture mode for camera / video like RCTCamera. Now, infer always video.
const isAuthorized = await requestPermissions(hasVideoAndAudio, CameraManager, Platform.OS, this.props.permissionDialogTitle, this.props.permissionDialogMessage);
this.setState({ isAuthorized, isAuthorizationChecked: true });
}
render() {
const nativeProps = this._convertNativeProps(this.props);
return (
<RNCamera
{...nativeProps}
ref={this._setReference}
onMountError={this._onMountError}
onCameraReady={this._onCameraReady}
onBarCodeRead={this._onObjectDetected(this.props.onBarCodeRead)}
onFacesDetected={this._onObjectDetected(this.props.onFacesDetected)}
/>
);
if (this.state.isAuthorized) {
return (
<RNCamera
{...nativeProps}
ref={this._setReference}
onMountError={this._onMountError}
onCameraReady={this._onCameraReady}
onBarCodeRead={this._onObjectDetected(this.props.onBarCodeRead)}
onFacesDetected={this._onObjectDetected(this.props.onFacesDetected)}
/>
);
} else if (!this.state.isAuthorizationChecked) {
return this.props.pendingAuthorizationView;
} else {
return this.props.notAuthorizedView;
}
}
_convertNativeProps(props: PropsType) {

28
src/handlePermissions.js Normal file
View File

@ -0,0 +1,28 @@
import { PermissionsAndroid } from 'react-native';
export const requestPermissions = async (hasVideoAndAudio, CameraManager, osType, permissionDialogTitle, permissionDialogMessage) => {
if (osType === 'ios') {
let check = hasVideoAndAudio
? CameraManager.checkDeviceAuthorizationStatus
: CameraManager.checkVideoAuthorizationStatus;
if (check) {
const isAuthorized = await check();
return isAuthorized;
}
} else if (osType === 'android') {
const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA, {
title: permissionDialogTitle,
message: permissionDialogMessage,
});
// On devices before SDK version 23, the permissions are automatically granted if they appear in the manifest,
// so check and request should always be true.
// https://github.com/facebook/react-native-website/blob/master/docs/permissionsandroid.md
const isAuthorized =
Platform.Version >= 23 ? granted === PermissionsAndroid.RESULTS.GRANTED : granted === true;
return isAuthorized;
}
return true;
}