1import React from 'react';
2import { NavigationEvents } from 'react-navigation';
3import { Button, Platform, StyleSheet, Text, View } from 'react-native';
4
5import { BarCodeScanner, Permissions, Svg, ScreenOrientation } from 'expo';
6
7const BUTTON_COLOR = Platform.OS === 'ios' ? '#fff' : '#666';
8
9interface State {
10  isPermissionsGranted: boolean;
11  type: any;
12  cornerPoints?: any[];
13  alerting: boolean;
14  haveDimensions: boolean;
15  canvasHeight?: number;
16  canvasWidth?: number;
17  boundingBox?: {
18    origin: {
19      x: number;
20      y: number;
21    };
22    size: {
23      width: number;
24      height: number;
25    };
26  };
27}
28
29export default class BarcodeScannerExample extends React.Component<{}, State> {
30  static navigationOptions = {
31    title: '<BarCodeScanner />',
32  };
33
34  canChangeOrientation = false;
35
36  readonly state: State = {
37    isPermissionsGranted: false,
38    type: BarCodeScanner.Constants.Type.back,
39    alerting: false,
40    haveDimensions: false,
41  };
42
43  componentDidFocus = async () => {
44    const { status } = await Permissions.askAsync(Permissions.CAMERA);
45    this.setState({ isPermissionsGranted: status === 'granted' });
46  }
47
48  toggleAlertingAboutResult = () => {
49    this.setState({ alerting: !this.state.alerting });
50  }
51
52  toggleScreenOrientationState = () => {
53    if (this.canChangeOrientation) {
54      ScreenOrientation.allowAsync(ScreenOrientation.OrientationLock.PORTRAIT_UP);
55    } else {
56      ScreenOrientation.allowAsync(ScreenOrientation.OrientationLock.ALL);
57    }
58    this.canChangeOrientation = !this.canChangeOrientation;
59  }
60
61  setCanvasDimensions = (e: any) => {
62    this.setState({
63      canvasWidth: e.nativeEvent.layout.width,
64      canvasHeight: e.nativeEvent.layout.height,
65      haveDimensions: true,
66    });
67  }
68
69  render() {
70    if (!this.state.isPermissionsGranted) {
71      return (
72        <View style={styles.container}>
73          <NavigationEvents onDidFocus={this.componentDidFocus} />
74          <Text>You have not granted permission to use the camera on this device!</Text>
75        </View>
76      );
77    }
78
79    const circles = [];
80
81    if (this.state.cornerPoints) {
82      for (const point of this.state.cornerPoints) {
83        circles.push(
84          <Svg.Circle
85            cx={point.x}
86            cy={point.y}
87            r={2}
88            strokeWidth={0.1}
89            stroke="gray"
90            fill="green"
91          />
92        );
93      }
94    }
95
96    return (
97      <View style={styles.container}>
98        <BarCodeScanner
99          onLayout={this.setCanvasDimensions}
100          onBarCodeScanned={this.handleBarCodeScanned}
101          barCodeTypes={[
102            BarCodeScanner.Constants.BarCodeType.qr,
103            BarCodeScanner.Constants.BarCodeType.pdf417,
104            BarCodeScanner.Constants.BarCodeType.code128,
105          ]}
106          type={this.state.type}
107          style={styles.preview}
108        />
109
110        {this.state.haveDimensions && (
111          <Svg height={this.state.canvasHeight} width={this.state.canvasWidth} style={styles.svg}>
112            <Svg.Circle
113              cx={this.state.canvasWidth! / 2}
114              cy={this.state.canvasHeight! / 2}
115              r={2}
116              strokeWidth={2.5}
117              stroke="#e74c3c"
118              fill="#f1c40f"
119            />
120            {this.state.boundingBox && (
121              <Svg.Rect
122                x={this.state.boundingBox.origin.x}
123                y={this.state.boundingBox.origin.y}
124                width={this.state.boundingBox.size.width}
125                height={this.state.boundingBox.size.height}
126                strokeWidth={2}
127                stroke="#9b59b6"
128                fill="none"
129              />
130            )}
131            {circles}
132          </Svg>
133        )}
134
135        <View style={styles.toolbar}>
136          <Button color={BUTTON_COLOR} title="Direction" onPress={this.toggleType} />
137          <Button
138            color={BUTTON_COLOR}
139            title="Orientation"
140            onPress={this.toggleScreenOrientationState}
141          />
142          <Button color={BUTTON_COLOR} title="Alerting" onPress={this.toggleAlertingAboutResult} />
143        </View>
144      </View>
145    );
146  }
147
148  toggleType = () =>
149    this.setState({
150      type:
151        this.state.type === BarCodeScanner.Constants.Type.back
152          ? BarCodeScanner.Constants.Type.front
153          : BarCodeScanner.Constants.Type.back,
154    })
155
156  handleBarCodeScanned = (data: any) => {
157    if (this.state.alerting) {
158      requestAnimationFrame(() => {
159        alert(JSON.stringify(data));
160      });
161    }
162    this.setState({ cornerPoints: data.cornerPoints, boundingBox: data.bounds });
163  }
164}
165
166const styles = StyleSheet.create({
167  container: {
168    flex: 1,
169  },
170  preview: {
171    ...StyleSheet.absoluteFillObject,
172    backgroundColor: 'black',
173  },
174  toolbar: {
175    position: 'absolute',
176    bottom: 0,
177    left: 0,
178    right: 0,
179    paddingVertical: 10,
180    paddingHorizontal: 10,
181    flexDirection: 'row',
182    justifyContent: 'space-between',
183    backgroundColor: 'rgba(255,255,255,0.2)',
184  },
185  svg: {
186    position: 'absolute',
187    borderWidth: 2,
188    borderColor: 'red',
189  },
190});
191