import { PermissionResponse, PermissionStatus, PermissionHookOptions, createPermissionHook, UnavailabilityError, } from 'expo-modules-core'; import * as React from 'react'; import { Platform, ViewProps } from 'react-native'; import ExpoBarCodeScannerModule from './ExpoBarCodeScannerModule'; import ExpoBarCodeScannerView from './ExpoBarCodeScannerView'; const { BarCodeType, Type } = ExpoBarCodeScannerModule; const EVENT_THROTTLE_MS = 500; // @needsAudit /** * Those coordinates are represented in the coordinate space of the barcode source (e.g. when you * are using the barcode scanner view, these values are adjusted to the dimensions of the view). */ export type BarCodePoint = { /** * The `x` coordinate value. */ x: number; /** * The `y` coordinate value. */ y: number; }; // @needsAudit export type BarCodeSize = { /** * The height value. */ height: number; /** * The width value. */ width: number; }; // @needsAudit export type BarCodeBounds = { /** * The origin point of the bounding box. */ origin: BarCodePoint; /** * The size of the bounding box. */ size: BarCodeSize; }; // @needsAudit /** * > __Note:__ `bounds` and `cornerPoints` are not always available. On iOS, for `code39` and `pdf417` * > you don't get those values. Moreover, on iOS, those values don't have to bounds the whole barcode. * > For some types, they will represent the area used by the scanner. */ export type BarCodeScannerResult = { /** * The barcode type. */ type: string; /** * The information encoded in the bar code. */ data: string; /** * The [BarCodeBounds](#barcodebounds) object. */ bounds?: BarCodeBounds; /** * Corner points of the bounding box. */ cornerPoints?: BarCodePoint[]; }; // @docsMissing export type BarCodeEvent = BarCodeScannerResult & { target?: number; }; // @docsMissing export type BarCodeEventCallbackArguments = { nativeEvent: BarCodeEvent; }; // @docsMissing export type BarCodeScannedCallback = (params: BarCodeEvent) => void; // @needsAudit export type BarCodeScannerProps = ViewProps & { /** * Camera facing. Use one of `BarCodeScanner.Constants.Type`. Use either `Type.front` or `Type.back`. * Same as `Camera.Constants.Type`. * @default Type.back */ type?: 'front' | 'back' | number; /** * An array of bar code types. Usage: `BarCodeScanner.Constants.BarCodeType.` where * `codeType` is one of these [listed above](#supported-formats). Defaults to all supported bar * code types. It is recommended to provide only the bar code formats you expect to scan to * minimize battery usage. * * For example: `barCodeTypes={[BarCodeScanner.Constants.BarCodeType.qr]}`. */ barCodeTypes?: string[]; /** * A callback that is invoked when a bar code has been successfully scanned. The callback is * provided with an [BarCodeScannerResult](#barcodescannerresult). * > __Note:__ Passing `undefined` to the `onBarCodeScanned` prop will result in no scanning. This * > can be used to effectively "pause" the scanner so that it doesn't continually scan even after * > data has been retrieved. */ onBarCodeScanned?: BarCodeScannedCallback; }; export class BarCodeScanner extends React.Component { lastEvents: { [key: string]: any } = {}; lastEventsTimes: { [key: string]: any } = {}; static Constants = { BarCodeType, Type, }; static ConversionTables = { type: Type, }; static defaultProps = { type: Type.back, barCodeTypes: Object.values(BarCodeType), }; // @needsAudit /** * Checks user's permissions for accessing the camera. * @return Return a promise that fulfills to an object of type [`PermissionResponse`](#permissionresponse). */ static async getPermissionsAsync(): Promise { return ExpoBarCodeScannerModule.getPermissionsAsync(); } // @needsAudit /** * Asks the user to grant permissions for accessing the camera. * * On iOS this will require apps to specify the `NSCameraUsageDescription` entry in the `Info.plist`. * @return Return a promise that fulfills to an object of type [`PermissionResponse`](#permissionresponse). */ static async requestPermissionsAsync(): Promise { return ExpoBarCodeScannerModule.requestPermissionsAsync(); } // @needsAudit /** * Check or request permissions for the barcode scanner. * This uses both `requestPermissionAsync` and `getPermissionsAsync` to interact with the permissions. * * @example * ```ts * const [status, requestPermission] = BarCodeScanner.usePermissions(); * ``` */ static usePermissions = createPermissionHook({ getMethod: BarCodeScanner.getPermissionsAsync, requestMethod: BarCodeScanner.requestPermissionsAsync, }); // @needsAudit /** * Scan bar codes from the image given by the URL. * @param url URL to get the image from. * @param barCodeTypes An array of bar code types. Defaults to all supported bar code types on * the platform. * > __Note:__ Only QR codes are supported on iOS. * @return A possibly empty array of objects of the `BarCodeScannerResult` shape, where the type * refers to the bar code type that was scanned and the data is the information encoded in the bar * code. */ static async scanFromURLAsync( url: string, barCodeTypes: string[] = Object.values(BarCodeType) ): Promise { if (!ExpoBarCodeScannerModule.scanFromURLAsync) { throw new UnavailabilityError('expo-barcode-scanner', 'scanFromURLAsync'); } if (Array.isArray(barCodeTypes) && !barCodeTypes.length) { throw new Error('No barCodeTypes specified; provide at least one barCodeType for scanner'); } if (Platform.OS === 'ios') { if (Array.isArray(barCodeTypes) && !barCodeTypes.includes(BarCodeType.qr)) { // Only QR type is supported on iOS, fail if one tries to use other types throw new Error('Only QR type is supported by scanFromURLAsync() on iOS'); } // on iOS use only supported QR type return await ExpoBarCodeScannerModule.scanFromURLAsync(url, [BarCodeType.qr]); } // On other platforms, if barCodeTypes is not provided, use all available types return await ExpoBarCodeScannerModule.scanFromURLAsync(url, barCodeTypes); } render() { const nativeProps = this.convertNativeProps(this.props); const { onBarCodeScanned } = this.props; return ( ); } onObjectDetected = (callback?: BarCodeScannedCallback) => ({ nativeEvent }: BarCodeEventCallbackArguments) => { const { type } = nativeEvent; if ( this.lastEvents[type] && this.lastEventsTimes[type] && JSON.stringify(nativeEvent) === this.lastEvents[type] && Date.now() - this.lastEventsTimes[type] < EVENT_THROTTLE_MS ) { return; } if (callback) { callback(nativeEvent); this.lastEventsTimes[type] = new Date(); this.lastEvents[type] = JSON.stringify(nativeEvent); } }; convertNativeProps(props: BarCodeScannerProps) { const nativeProps: BarCodeScannerProps = {}; for (const [key, value] of Object.entries(props)) { if (typeof value === 'string' && BarCodeScanner.ConversionTables[key]) { nativeProps[key] = BarCodeScanner.ConversionTables[key][value]; } else { nativeProps[key] = value; } } return nativeProps; } } export { PermissionResponse, PermissionStatus, PermissionHookOptions }; export const { Constants, getPermissionsAsync, requestPermissionsAsync, usePermissions, scanFromURLAsync, } = BarCodeScanner;