UNPKG

7.9 kBTypeScriptView Raw
1import {
2 PermissionResponse,
3 PermissionStatus,
4 PermissionHookOptions,
5 createPermissionHook,
6 UnavailabilityError,
7} from 'expo-modules-core';
8import * as React from 'react';
9import { Platform, ViewProps } from 'react-native';
10
11import ExpoBarCodeScannerModule from './ExpoBarCodeScannerModule';
12import ExpoBarCodeScannerView from './ExpoBarCodeScannerView';
13
14const { BarCodeType, Type } = ExpoBarCodeScannerModule;
15
16const EVENT_THROTTLE_MS = 500;
17
18// @needsAudit
19/**
20 * Those coordinates are represented in the coordinate space of the barcode source (e.g. when you
21 * are using the barcode scanner view, these values are adjusted to the dimensions of the view).
22 */
23export type BarCodePoint = {
24 /**
25 * The `x` coordinate value.
26 */
27 x: number;
28 /**
29 * The `y` coordinate value.
30 */
31 y: number;
32};
33
34// @needsAudit
35export type BarCodeSize = {
36 /**
37 * The height value.
38 */
39 height: number;
40 /**
41 * The width value.
42 */
43 width: number;
44};
45
46// @needsAudit
47export type BarCodeBounds = {
48 /**
49 * The origin point of the bounding box.
50 */
51 origin: BarCodePoint;
52 /**
53 * The size of the bounding box.
54 */
55 size: BarCodeSize;
56};
57
58// @needsAudit
59/**
60 * > __Note:__ `bounds` and `cornerPoints` are not always available. On iOS, for `code39` and `pdf417`
61 * > you don't get those values. Moreover, on iOS, those values don't have to bounds the whole barcode.
62 * > For some types, they will represent the area used by the scanner.
63 */
64export type BarCodeScannerResult = {
65 /**
66 * The barcode type.
67 */
68 type: string;
69 /**
70 * The information encoded in the bar code.
71 */
72 data: string;
73 /**
74 * The [BarCodeBounds](#barcodebounds) object.
75 */
76 bounds?: BarCodeBounds;
77 /**
78 * Corner points of the bounding box.
79 */
80 cornerPoints?: BarCodePoint[];
81};
82
83// @docsMissing
84export type BarCodeEvent = BarCodeScannerResult & {
85 target?: number;
86};
87
88// @docsMissing
89export type BarCodeEventCallbackArguments = {
90 nativeEvent: BarCodeEvent;
91};
92
93// @docsMissing
94export type BarCodeScannedCallback = (params: BarCodeEvent) => void;
95
96// @needsAudit
97export type BarCodeScannerProps = ViewProps & {
98 /**
99 * Camera facing. Use one of `BarCodeScanner.Constants.Type`. Use either `Type.front` or `Type.back`.
100 * Same as `Camera.Constants.Type`.
101 * @default Type.back
102 */
103 type?: 'front' | 'back' | number;
104 /**
105 * An array of bar code types. Usage: `BarCodeScanner.Constants.BarCodeType.<codeType>` where
106 * `codeType` is one of these [listed above](#supported-formats). Defaults to all supported bar
107 * code types. It is recommended to provide only the bar code formats you expect to scan to
108 * minimize battery usage.
109 *
110 * For example: `barCodeTypes={[BarCodeScanner.Constants.BarCodeType.qr]}`.
111 */
112 barCodeTypes?: string[];
113 /**
114 * A callback that is invoked when a bar code has been successfully scanned. The callback is
115 * provided with an [BarCodeScannerResult](#barcodescannerresult).
116 * > __Note:__ Passing `undefined` to the `onBarCodeScanned` prop will result in no scanning. This
117 * > can be used to effectively "pause" the scanner so that it doesn't continually scan even after
118 * > data has been retrieved.
119 */
120 onBarCodeScanned?: BarCodeScannedCallback;
121};
122
123export class BarCodeScanner extends React.Component<BarCodeScannerProps> {
124 lastEvents: { [key: string]: any } = {};
125 lastEventsTimes: { [key: string]: any } = {};
126
127 static Constants = {
128 BarCodeType,
129 Type,
130 };
131
132 static ConversionTables = {
133 type: Type,
134 };
135
136 static defaultProps = {
137 type: Type.back,
138 barCodeTypes: Object.values(BarCodeType),
139 };
140
141 // @needsAudit
142 /**
143 * Checks user's permissions for accessing the camera.
144 * @return Return a promise that fulfills to an object of type [`PermissionResponse`](#permissionresponse).
145 */
146 static async getPermissionsAsync(): Promise<PermissionResponse> {
147 return ExpoBarCodeScannerModule.getPermissionsAsync();
148 }
149
150 // @needsAudit
151 /**
152 * Asks the user to grant permissions for accessing the camera.
153 *
154 * On iOS this will require apps to specify the `NSCameraUsageDescription` entry in the `Info.plist`.
155 * @return Return a promise that fulfills to an object of type [`PermissionResponse`](#permissionresponse).
156 */
157 static async requestPermissionsAsync(): Promise<PermissionResponse> {
158 return ExpoBarCodeScannerModule.requestPermissionsAsync();
159 }
160
161 // @needsAudit
162 /**
163 * Check or request permissions for the barcode scanner.
164 * This uses both `requestPermissionAsync` and `getPermissionsAsync` to interact with the permissions.
165 *
166 * @example
167 * ```ts
168 * const [status, requestPermission] = BarCodeScanner.usePermissions();
169 * ```
170 */
171 static usePermissions = createPermissionHook({
172 getMethod: BarCodeScanner.getPermissionsAsync,
173 requestMethod: BarCodeScanner.requestPermissionsAsync,
174 });
175
176 // @needsAudit
177 /**
178 * Scan bar codes from the image given by the URL.
179 * @param url URL to get the image from.
180 * @param barCodeTypes An array of bar code types. Defaults to all supported bar code types on
181 * the platform.
182 * > __Note:__ Only QR codes are supported on iOS.
183 * @return A possibly empty array of objects of the `BarCodeScannerResult` shape, where the type
184 * refers to the bar code type that was scanned and the data is the information encoded in the bar
185 * code.
186 */
187 static async scanFromURLAsync(
188 url: string,
189 barCodeTypes: string[] = Object.values(BarCodeType)
190 ): Promise<BarCodeScannerResult[]> {
191 if (!ExpoBarCodeScannerModule.scanFromURLAsync) {
192 throw new UnavailabilityError('expo-barcode-scanner', 'scanFromURLAsync');
193 }
194 if (Array.isArray(barCodeTypes) && !barCodeTypes.length) {
195 throw new Error('No barCodeTypes specified; provide at least one barCodeType for scanner');
196 }
197
198 if (Platform.OS === 'ios') {
199 if (Array.isArray(barCodeTypes) && !barCodeTypes.includes(BarCodeType.qr)) {
200 // Only QR type is supported on iOS, fail if one tries to use other types
201 throw new Error('Only QR type is supported by scanFromURLAsync() on iOS');
202 }
203 // on iOS use only supported QR type
204 return await ExpoBarCodeScannerModule.scanFromURLAsync(url, [BarCodeType.qr]);
205 }
206
207 // On other platforms, if barCodeTypes is not provided, use all available types
208 return await ExpoBarCodeScannerModule.scanFromURLAsync(url, barCodeTypes);
209 }
210
211 render() {
212 const nativeProps = this.convertNativeProps(this.props);
213 const { onBarCodeScanned } = this.props;
214 return (
215 <ExpoBarCodeScannerView
216 {...nativeProps}
217 onBarCodeScanned={this.onObjectDetected(onBarCodeScanned)}
218 />
219 );
220 }
221
222 onObjectDetected =
223 (callback?: BarCodeScannedCallback) =>
224 ({ nativeEvent }: BarCodeEventCallbackArguments) => {
225 const { type } = nativeEvent;
226 if (
227 this.lastEvents[type] &&
228 this.lastEventsTimes[type] &&
229 JSON.stringify(nativeEvent) === this.lastEvents[type] &&
230 Date.now() - this.lastEventsTimes[type] < EVENT_THROTTLE_MS
231 ) {
232 return;
233 }
234
235 if (callback) {
236 callback(nativeEvent);
237 this.lastEventsTimes[type] = new Date();
238 this.lastEvents[type] = JSON.stringify(nativeEvent);
239 }
240 };
241
242 convertNativeProps(props: BarCodeScannerProps) {
243 const nativeProps: BarCodeScannerProps = {};
244
245 for (const [key, value] of Object.entries(props)) {
246 if (typeof value === 'string' && BarCodeScanner.ConversionTables[key]) {
247 nativeProps[key] = BarCodeScanner.ConversionTables[key][value];
248 } else {
249 nativeProps[key] = value;
250 }
251 }
252
253 return nativeProps;
254 }
255}
256
257export { PermissionResponse, PermissionStatus, PermissionHookOptions };
258export const {
259 Constants,
260 getPermissionsAsync,
261 requestPermissionsAsync,
262 usePermissions,
263 scanFromURLAsync,
264} = BarCodeScanner;