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 18export type BarCodePoint = { 19 x: number; 20 y: number; 21}; 22 23export type BarCodeSize = { 24 height: number; 25 width: number; 26}; 27 28export type BarCodeBounds = { 29 origin: BarCodePoint; 30 size: BarCodeSize; 31}; 32 33export type BarCodeScannerResult = { 34 type: string; 35 data: string; 36 bounds?: BarCodeBounds; 37 cornerPoints?: BarCodePoint[]; 38}; 39 40export type BarCodeEvent = BarCodeScannerResult & { 41 target?: number; 42}; 43 44export type BarCodeEventCallbackArguments = { 45 nativeEvent: BarCodeEvent; 46}; 47 48export { PermissionResponse, PermissionStatus, PermissionHookOptions }; 49 50export type BarCodeScannedCallback = (params: BarCodeEvent) => void; 51 52export interface BarCodeScannerProps extends ViewProps { 53 type?: 'front' | 'back' | number; 54 barCodeTypes?: string[]; 55 onBarCodeScanned?: BarCodeScannedCallback; 56} 57 58export class BarCodeScanner extends React.Component<BarCodeScannerProps> { 59 lastEvents: { [key: string]: any } = {}; 60 lastEventsTimes: { [key: string]: any } = {}; 61 62 static Constants = { 63 BarCodeType, 64 Type, 65 }; 66 67 static ConversionTables = { 68 type: Type, 69 }; 70 71 static defaultProps = { 72 type: Type.back, 73 barCodeTypes: Object.values(BarCodeType), 74 }; 75 76 static async getPermissionsAsync(): Promise<PermissionResponse> { 77 return ExpoBarCodeScannerModule.getPermissionsAsync(); 78 } 79 80 static async requestPermissionsAsync(): Promise<PermissionResponse> { 81 return ExpoBarCodeScannerModule.requestPermissionsAsync(); 82 } 83 84 // @needsAudit 85 /** 86 * Check or request permissions for the barcode scanner. 87 * This uses both `requestPermissionAsync` and `getPermissionsAsync` to interact with the permissions. 88 * 89 * @example 90 * ```ts 91 * const [status, requestPermission] = BarCodeScanner.usePermissions(); 92 * ``` 93 */ 94 static usePermissions = createPermissionHook({ 95 getMethod: BarCodeScanner.getPermissionsAsync, 96 requestMethod: BarCodeScanner.requestPermissionsAsync, 97 }); 98 99 static async scanFromURLAsync( 100 url: string, 101 barCodeTypes: string[] = Object.values(BarCodeType) 102 ): Promise<BarCodeScannerResult[]> { 103 if (!ExpoBarCodeScannerModule.scanFromURLAsync) { 104 throw new UnavailabilityError('expo-barcode-scanner', 'scanFromURLAsync'); 105 } 106 if (Array.isArray(barCodeTypes) && !barCodeTypes.length) { 107 throw new Error('No barCodeTypes specified; provide at least one barCodeType for scanner'); 108 } 109 110 if (Platform.OS === 'ios') { 111 if (Array.isArray(barCodeTypes) && !barCodeTypes.includes(BarCodeType.qr)) { 112 // Only QR type is supported on iOS, fail if one tries to use other types 113 throw new Error('Only QR type is supported by scanFromURLAsync() on iOS'); 114 } 115 // on iOS use only supported QR type 116 return await ExpoBarCodeScannerModule.scanFromURLAsync(url, [BarCodeType.qr]); 117 } 118 119 // On other platforms, if barCodeTypes is not provided, use all available types 120 return await ExpoBarCodeScannerModule.scanFromURLAsync(url, barCodeTypes); 121 } 122 123 render() { 124 const nativeProps = this.convertNativeProps(this.props); 125 const { onBarCodeScanned } = this.props; 126 return ( 127 <ExpoBarCodeScannerView 128 {...nativeProps} 129 onBarCodeScanned={this.onObjectDetected(onBarCodeScanned)} 130 /> 131 ); 132 } 133 134 onObjectDetected = 135 (callback?: BarCodeScannedCallback) => 136 ({ nativeEvent }: BarCodeEventCallbackArguments) => { 137 const { type } = nativeEvent; 138 if ( 139 this.lastEvents[type] && 140 this.lastEventsTimes[type] && 141 JSON.stringify(nativeEvent) === this.lastEvents[type] && 142 Date.now() - this.lastEventsTimes[type] < EVENT_THROTTLE_MS 143 ) { 144 return; 145 } 146 147 if (callback) { 148 callback(nativeEvent); 149 this.lastEventsTimes[type] = new Date(); 150 this.lastEvents[type] = JSON.stringify(nativeEvent); 151 } 152 }; 153 154 convertNativeProps(props: BarCodeScannerProps) { 155 const nativeProps: BarCodeScannerProps = {}; 156 157 for (const [key, value] of Object.entries(props)) { 158 if (typeof value === 'string' && BarCodeScanner.ConversionTables[key]) { 159 nativeProps[key] = BarCodeScanner.ConversionTables[key][value]; 160 } else { 161 nativeProps[key] = value; 162 } 163 } 164 165 return nativeProps; 166 } 167} 168 169export const { Constants, getPermissionsAsync, requestPermissionsAsync, usePermissions } = 170 BarCodeScanner; 171