diff --git a/ReactNativeClient/lib/components/CameraView.js b/ReactNativeClient/lib/components/CameraView.js
index f15e29f29..ed9f29565 100644
--- a/ReactNativeClient/lib/components/CameraView.js
+++ b/ReactNativeClient/lib/components/CameraView.js
@@ -1,22 +1,39 @@
const React = require('react');
const Component = React.Component;
-const { View, TouchableOpacity } = require('react-native');
+const { connect } = require('react-redux');
+const { View, TouchableOpacity, Text, Dimensions } = require('react-native');
import { RNCamera } from 'react-native-camera';
const Icon = require('react-native-vector-icons/Ionicons').default;
const { _ } = require('lib/locale.js');
+const { shim } = require('lib/shim');
+const Setting = require('lib/models/Setting');
class CameraView extends Component {
constructor() {
super();
+ const dimensions = Dimensions.get('window');
+
this.state = {
snapping: false,
- camera: RNCamera.Constants.Type.back,
+ ratios: [],
+ screenWidth: dimensions.width,
+ screenHeight: dimensions.height,
};
this.back_onPress = this.back_onPress.bind(this);
this.photo_onPress = this.photo_onPress.bind(this);
this.reverse_onPress = this.reverse_onPress.bind(this);
+ this.ratio_onPress = this.ratio_onPress.bind(this);
+ this.onCameraReady = this.onCameraReady.bind(this);
+ this.onLayout = this.onLayout.bind(this);
+ }
+
+ onLayout(event) {
+ this.setState({
+ screenWidth: event.nativeEvent.layout.width,
+ screenHeight: event.nativeEvent.layout.height,
+ });
}
back_onPress() {
@@ -24,17 +41,23 @@ class CameraView extends Component {
}
reverse_onPress() {
- if (this.state.camera == RNCamera.Constants.Type.back) {
- this.setState({
- camera: RNCamera.Constants.Type.front,
- });
- } else if (this.state.camera == RNCamera.Constants.Type.front) {
- this.setState({
- camera: RNCamera.Constants.Type.back,
- });
+ if (this.props.cameraType === RNCamera.Constants.Type.back) {
+ Setting.setValue('camera.type', RNCamera.Constants.Type.front);
+ } else {
+ Setting.setValue('camera.type', RNCamera.Constants.Type.back);
}
}
+ ratio_onPress() {
+ if (this.state.ratios.length <= 1) return;
+
+ let index = this.state.ratios.indexOf(this.props.cameraRatio);
+ index++;
+ if (index >= this.state.ratios.length) index = 0;
+ Setting.setValue('camera.ratio', this.state.ratios[index]);
+ this.forceUpdate();
+ }
+
async photo_onPress() {
if (!this.camera || !this.props.onPhoto) return;
@@ -51,29 +74,120 @@ class CameraView extends Component {
this.setState({ snapping: false });
}
+ async onCameraReady() {
+ const ratios = await this.camera.getSupportedRatiosAsync();
+ this.setState({ ratios: ratios });
+ }
+
+ renderButton(onPress, iconName, style) {
+ let icon = null;
+
+ if (typeof iconName === 'string') {
+ icon = (
+
+ );
+ } else {
+ icon = iconName;
+ }
+
+ return (
+
+
+ { icon }
+
+
+ );
+ }
+
+ fitRectIntoBounds(rect, bounds) {
+ var rectRatio = rect.width / rect.height;
+ var boundsRatio = bounds.width / bounds.height;
+
+ var newDimensions = {};
+
+ // Rect is more landscape than bounds - fit to width
+ if (rectRatio > boundsRatio) {
+ newDimensions.width = bounds.width;
+ newDimensions.height = rect.height * (bounds.width / rect.width);
+ } else { // Rect is more portrait than bounds - fit to height
+ newDimensions.width = rect.width * (bounds.height / rect.height);
+ newDimensions.height = bounds.height;
+ }
+
+ return newDimensions;
+ }
+
+ cameraRect(ratio) {
+ // To keep the calculations simpler, it's assumed that the phone is in
+ // portrait orientation. Then at the end we swap the values if needed.
+ const splitted = ratio.split(':');
+
+ const output = this.fitRectIntoBounds({
+ width: Number(splitted[1]),
+ height: Number(splitted[0]),
+ }, {
+ width: Math.min(this.state.screenWidth, this.state.screenHeight),
+ height: Math.max(this.state.screenWidth, this.state.screenHeight),
+ });
+
+ if (this.state.screenWidth > this.state.screenHeight) {
+ const w = output.width;
+ output.width = output.height;
+ output.height = w;
+ }
+
+ return output;
+ }
+
render() {
const photoIcon = this.state.snapping ? 'md-checkmark' : 'md-camera';
+ const displayRatios = shim.mobilePlatform() === 'android' && this.state.ratios.length > 1;
+
+ const reverseCameraButton = this.renderButton(this.reverse_onPress, 'md-reverse-camera', { flex: 1, flexDirection: 'row', justifyContent: 'flex-start', marginLeft: 20 });
+ const ratioButton = !displayRatios ? : this.renderButton(this.ratio_onPress, {Setting.value('camera.ratio')}, { flex: 1, flexDirection: 'row', justifyContent: 'flex-end', marginRight: 20 });
+
+ let cameraRatio = '4:3';
+ const cameraProps = {};
+ if (displayRatios) {
+ cameraProps.ratio = this.props.cameraRatio;
+ cameraRatio = this.props.cameraRatio;
+ }
+
+ const cameraRect = this.cameraRect(cameraRatio);
+ cameraRect.left = (this.state.screenWidth - cameraRect.width) / 2;
+ cameraRect.top = (this.state.screenHeight - cameraRect.height) / 2;
+
return (
-
+
+
{
this.camera = ref;
}}
- type={this.state.camera}
+ type={this.props.cameraType}
captureAudio={false}
+ onCameraReady={this.onCameraReady}
androidCameraPermissionOptions={{
title: _('Permission to use camera'),
message: _('Your permission to use your camera is required.'),
buttonPositive: _('OK'),
buttonNegative: _('Cancel'),
}}
+
+ { ...cameraProps }
>
-
+
-
-
-
-
-
-
-
-
-
+
+
+ { reverseCameraButton }
+
+
+ { ratioButton }
@@ -117,4 +222,12 @@ class CameraView extends Component {
}
}
-module.exports = CameraView;
+const mapStateToProps = state => {
+ return {
+ cameraRatio: state.settings['camera.ratio'],
+ cameraType: state.settings['camera.type'],
+ };
+};
+
+
+module.exports = connect(mapStateToProps)(CameraView);
diff --git a/ReactNativeClient/lib/models/Setting.js b/ReactNativeClient/lib/models/Setting.js
index 4cdfb7d9e..00755d73c 100644
--- a/ReactNativeClient/lib/models/Setting.js
+++ b/ReactNativeClient/lib/models/Setting.js
@@ -480,6 +480,9 @@ class Setting extends BaseModel {
'welcome.wasBuilt': { value: false, type: Setting.TYPE_BOOL, public: false },
'welcome.enabled': { value: true, type: Setting.TYPE_BOOL, public: false },
+
+ 'camera.type': { value: 0, type: Setting.TYPE_INT, public: false, appTypes: ['mobile'] },
+ 'camera.ratio': { value: '4:3', type: Setting.TYPE_STRING, public: false, appTypes: ['mobile'] },
};
return this.metadata_;
diff --git a/ReactNativeClient/lib/shim.js b/ReactNativeClient/lib/shim.js
index 44ae2c8d6..e27f1aca8 100644
--- a/ReactNativeClient/lib/shim.js
+++ b/ReactNativeClient/lib/shim.js
@@ -42,6 +42,7 @@ shim.platformName = function() {
throw new Error('Cannot determine platform');
};
+// "ios" or "android", or "" if not on mobile
shim.mobilePlatform = function() {
return ''; // Default if we're not on mobile (React Native)
};