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