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