1import React from 'react';
2import { NavigationEvents } from 'react-navigation';
3import { Button, Platform, StyleSheet, Text, View } from 'react-native';
4
5import * as ScreenOrientation from 'expo-screen-orientation';
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            BarCodeScanner.Constants.BarCodeType.code39,
110          ]}
111          type={this.state.type}
112          style={styles.preview}
113        />
114
115        {this.state.haveDimensions && (
116          <Svg.Svg height={this.state.canvasHeight} width={this.state.canvasWidth} style={styles.svg}>
117            <Svg.Circle
118              cx={this.state.canvasWidth! / 2}
119              cy={this.state.canvasHeight! / 2}
120              r={2}
121              strokeWidth={2.5}
122              stroke="#e74c3c"
123              fill="#f1c40f"
124            />
125            {this.state.boundingBox && (
126              <Svg.Rect
127                x={this.state.boundingBox.origin.x}
128                y={this.state.boundingBox.origin.y}
129                width={this.state.boundingBox.size.width}
130                height={this.state.boundingBox.size.height}
131                strokeWidth={2}
132                stroke="#9b59b6"
133                fill="none"
134              />
135            )}
136            {circles}
137          </Svg.Svg>
138        )}
139
140        <View style={styles.toolbar}>
141          <Button color={BUTTON_COLOR} title="Direction" onPress={this.toggleType} />
142          <Button
143            color={BUTTON_COLOR}
144            title="Orientation"
145            onPress={this.toggleScreenOrientationState}
146          />
147          <Button color={BUTTON_COLOR} title="Alerting" onPress={this.toggleAlertingAboutResult} />
148        </View>
149      </View>
150    );
151  }
152
153  toggleType = () =>
154    this.setState({
155      type:
156        this.state.type === BarCodeScanner.Constants.Type.back
157          ? BarCodeScanner.Constants.Type.front
158          : BarCodeScanner.Constants.Type.back,
159    })
160
161  handleBarCodeScanned = (data: any) => {
162    if (this.state.alerting) {
163      requestAnimationFrame(() => {
164        alert(JSON.stringify(data));
165      });
166    }
167    this.setState({ cornerPoints: data.cornerPoints, boundingBox: data.bounds });
168  }
169}
170
171const styles = StyleSheet.create({
172  container: {
173    flex: 1,
174  },
175  preview: {
176    ...StyleSheet.absoluteFillObject,
177    backgroundColor: 'black',
178  },
179  toolbar: {
180    position: 'absolute',
181    bottom: 0,
182    left: 0,
183    right: 0,
184    paddingVertical: 10,
185    paddingHorizontal: 10,
186    flexDirection: 'row',
187    justifyContent: 'space-between',
188    backgroundColor: 'rgba(255,255,255,0.2)',
189  },
190  svg: {
191    position: 'absolute',
192    borderWidth: 2,
193    borderColor: 'red',
194  },
195});
196