@ -11,15 +11,16 @@ export class CapacitorQRScanner implements QRScannerService {
private scanListener : ScanListener | null = null ;
private scanListener : ScanListener | null = null ;
private isScanning = false ;
private isScanning = false ;
private listenerHandles : Array < ( ) = > Promise < void > > = [ ] ;
private listenerHandles : Array < ( ) = > Promise < void > > = [ ] ;
private cleanupPromise : Promise < void > | null = null ;
async checkPermissions ( ) : Promise < boolean > {
async checkPermissions ( ) : Promise < boolean > {
try {
try {
logger . debug ( "Checking camera permissions" ) ;
const { camera } = await BarcodeScanner . checkPermissions ( ) ;
const { camera } = await BarcodeScanner . checkPermissions ( ) ;
return camera === "granted" ;
return camera === "granted" ;
} catch ( error ) {
} catch ( error ) {
const wrappedError =
const wrappedError = error instanceof Error ? error : new Error ( String ( error ) ) ;
error instanceof Error ? error : new Error ( String ( error ) ) ;
logger . error ( "Error checking camera permissions:" , { error : wrappedError.message } ) ;
logger . error ( "Error checking camera permissions:" , wrappedError ) ;
return false ;
return false ;
}
}
}
}
@ -28,42 +29,50 @@ export class CapacitorQRScanner implements QRScannerService {
try {
try {
// First check if we already have permissions
// First check if we already have permissions
if ( await this . checkPermissions ( ) ) {
if ( await this . checkPermissions ( ) ) {
logger . debug ( "Camera permissions already granted" ) ;
return true ;
return true ;
}
}
// Request permissions if we don't have them
logger . debug ( "Requesting camera permissions" ) ;
const { camera } = await BarcodeScanner . requestPermissions ( ) ;
const { camera } = await BarcodeScanner . requestPermissions ( ) ;
return camera === "granted" ;
const granted = camera === "granted" ;
logger . debug ( ` Camera permissions ${ granted ? "granted" : "denied" } ` ) ;
return granted ;
} catch ( error ) {
} catch ( error ) {
const wrappedError =
const wrappedError = error instanceof Error ? error : new Error ( String ( error ) ) ;
error instanceof Error ? error : new Error ( String ( error ) ) ;
logger . error ( "Error requesting camera permissions:" , { error : wrappedError.message } ) ;
logger . error ( "Error requesting camera permissions:" , wrappedError ) ;
return false ;
return false ;
}
}
}
}
async isSupported ( ) : Promise < boolean > {
async isSupported ( ) : Promise < boolean > {
try {
try {
logger . debug ( "Checking scanner support" ) ;
const { supported } = await BarcodeScanner . isSupported ( ) ;
const { supported } = await BarcodeScanner . isSupported ( ) ;
logger . debug ( ` Scanner support: ${ supported } ` ) ;
return supported ;
return supported ;
} catch ( error ) {
} catch ( error ) {
const wrappedError =
const wrappedError = error instanceof Error ? error : new Error ( String ( error ) ) ;
error instanceof Error ? error : new Error ( String ( error ) ) ;
logger . error ( "Error checking scanner support:" , { error : wrappedError.message } ) ;
logger . error ( "Error checking scanner support:" , wrappedError ) ;
return false ;
return false ;
}
}
}
}
async startScan ( options? : QRScannerOptions ) : Promise < void > {
async startScan ( options? : QRScannerOptions ) : Promise < void > {
if ( this . isScanning ) {
if ( this . isScanning ) {
logger . debug ( "Scanner already running" ) ;
return ;
return ;
}
}
if ( this . cleanupPromise ) {
logger . debug ( "Waiting for previous cleanup to complete" ) ;
await this . cleanupPromise ;
}
try {
try {
// Ensure we have permissions before starting
// Ensure we have permissions before starting
logger . log ( "Checking camera permissions..." ) ;
if ( ! ( await this . checkPermissions ( ) ) ) {
if ( ! ( await this . checkPermissions ( ) ) ) {
logger . log ( "Requesting camera permissions..." ) ;
logger . debu g( "Requesting camera permissions" ) ;
const granted = await this . requestPermissions ( ) ;
const granted = await this . requestPermissions ( ) ;
if ( ! granted ) {
if ( ! granted ) {
throw new Error ( "Camera permission denied" ) ;
throw new Error ( "Camera permission denied" ) ;
@ -71,39 +80,39 @@ export class CapacitorQRScanner implements QRScannerService {
}
}
// Check if scanning is supported
// Check if scanning is supported
logger . log ( "Checking scanner support..." ) ;
if ( ! ( await this . isSupported ( ) ) ) {
if ( ! ( await this . isSupported ( ) ) ) {
throw new Error ( "QR scanning not supported on this device" ) ;
throw new Error ( "QR scanning not supported on this device" ) ;
}
}
logger . log ( "Starting MLKit scanner... " ) ;
logger . info ( "Starting MLKit scanner" ) ;
this . isScanning = true ;
this . isScanning = true ;
const scanOptions : StartScanOptions = {
const scanOptions : StartScanOptions = {
formats : [ BarcodeFormat . QrCode ] ,
formats : [ BarcodeFormat . QrCode ] ,
lensFacing :
lensFacing : options?.camera === "front" ? LensFacing.Front : LensFacing.Back ,
options ? . camera === "front" ? LensFacing.Front : LensFacing.Back ,
} ;
} ;
logger . lo g( "Scanner options:" , scanOptions ) ;
logger . debu g( "Scanner options:" , scanOptions ) ;
// Add listener for barcode scans
// Add listener for barcode scans
const handle = await BarcodeScanner . addListener (
const handle = await BarcodeScanner . addListener ( "barcodeScanned" , ( result ) = > {
"barcodeScanned" ,
if ( this . scanListener && result . barcode ? . rawValue ) {
( result ) = > {
this . scanListener . onScan ( result . barcode . rawValue ) ;
if ( this . scanListener ) {
}
this . scanListener . onScan ( result . barcode . rawValue ) ;
} ) ;
}
} ,
) ;
this . listenerHandles . push ( handle . remove ) ;
this . listenerHandles . push ( handle . remove ) ;
// Start continuous scanning
// Start continuous scanning
await BarcodeScanner . startScan ( scanOptions ) ;
await BarcodeScanner . startScan ( scanOptions ) ;
logger . info ( "MLKit scanner started successfully" ) ;
} catch ( error ) {
} catch ( error ) {
const wrappedError =
const wrappedError = error instanceof Error ? error : new Error ( String ( error ) ) ;
error instanceof Error ? error : new Error ( String ( error ) ) ;
logger . error ( "Error during QR scan:" , {
logger . error ( "Error during QR scan:" , wrappedError ) ;
error : wrappedError.message ,
stack : wrappedError.stack
} ) ;
this . isScanning = false ;
await this . cleanup ( ) ;
this . scanListener ? . onError ? . ( wrappedError ) ;
this . scanListener ? . onError ? . ( wrappedError ) ;
throw wrappedError ;
throw wrappedError ;
}
}
@ -111,15 +120,20 @@ export class CapacitorQRScanner implements QRScannerService {
async stopScan ( ) : Promise < void > {
async stopScan ( ) : Promise < void > {
if ( ! this . isScanning ) {
if ( ! this . isScanning ) {
logger . debug ( "Scanner not running" ) ;
return ;
return ;
}
}
try {
try {
logger . debug ( "Stopping QR scanner" ) ;
await BarcodeScanner . stopScan ( ) ;
await BarcodeScanner . stopScan ( ) ;
logger . info ( "QR scanner stopped successfully" ) ;
} catch ( error ) {
} catch ( error ) {
const wrappedError =
const wrappedError = error instanceof Error ? error : new Error ( String ( error ) ) ;
error instanceof Error ? error : new Error ( String ( error ) ) ;
logger . error ( "Error stopping QR scan:" , {
logger . error ( "Error stopping QR scan:" , wrappedError ) ;
error : wrappedError.message ,
stack : wrappedError.stack
} ) ;
this . scanListener ? . onError ? . ( wrappedError ) ;
this . scanListener ? . onError ? . ( wrappedError ) ;
throw wrappedError ;
throw wrappedError ;
} finally {
} finally {
@ -132,19 +146,44 @@ export class CapacitorQRScanner implements QRScannerService {
}
}
async cleanup ( ) : Promise < void > {
async cleanup ( ) : Promise < void > {
try {
// Prevent multiple simultaneous cleanup attempts
await this . stopScan ( ) ;
if ( this . cleanupPromise ) {
for ( const handle of this . listenerHandles ) {
return this . cleanupPromise ;
await handle ( ) ;
}
} catch ( error ) {
const wrappedError =
error instanceof Error ? error : new Error ( String ( error ) ) ;
logger . error ( "Error during cleanup:" , wrappedError ) ;
throw wrappedError ;
} finally {
this . listenerHandles = [ ] ;
this . scanListener = null ;
}
}
this . cleanupPromise = ( async ( ) = > {
try {
logger . debug ( "Starting QR scanner cleanup" ) ;
// Stop scanning if active
if ( this . isScanning ) {
await this . stopScan ( ) ;
}
// Remove all listeners
for ( const handle of this . listenerHandles ) {
try {
await handle ( ) ;
} catch ( error ) {
logger . warn ( "Error removing listener:" , error ) ;
}
}
logger . info ( "QR scanner cleanup completed" ) ;
} catch ( error ) {
const wrappedError = error instanceof Error ? error : new Error ( String ( error ) ) ;
logger . error ( "Error during cleanup:" , {
error : wrappedError.message ,
stack : wrappedError.stack
} ) ;
throw wrappedError ;
} finally {
this . listenerHandles = [ ] ;
this . scanListener = null ;
this . cleanupPromise = null ;
}
} ) ( ) ;
return this . cleanupPromise ;
}
}
}
}