UNPKG

copay-crown

Version:

A Secure Crown Wallet

300 lines (272 loc) 9.28 kB
import { Injectable } from '@angular/core'; import { QRScanner } from '@ionic-native/qr-scanner'; import { Events } from 'ionic-angular'; import { Logger } from '../../providers/logger/logger'; import { PlatformProvider } from '../platform/platform'; @Injectable() export class ScanProvider { public text: string; public scannerVisible: boolean; public lightEnabled: boolean; public frontCameraEnabled: boolean; public isDesktop = !this.platform.isCordova; public isAvailable: boolean = true; public hasPermission: boolean = false; public isDenied: boolean = false; public isRestricted: boolean = false; public canEnableLight: boolean = false; public canChangeCamera: boolean = false; public canOpenSettings: boolean = false; public backCamera: boolean = true; public initializeStarted: boolean = false; public initializeCompleted: boolean = false; constructor( private qrScanner: QRScanner, private platform: PlatformProvider, private logger: Logger, private events: Events ) { this.scannerVisible = false; this.lightEnabled = false; this.frontCameraEnabled = false; } private checkCapabilities(status) { this.logger.debug('scannerService is reviewing platform capabilities...'); // Permission can be assumed on the desktop builds this.hasPermission = (this.isDesktop || status.authorized) ? true : false; this.isDenied = status.denied ? true : false; this.isRestricted = status.restricted ? true : false; this.canEnableLight = status.canEnableLight ? true : false; this.canChangeCamera = status.canChangeCamera ? true : false; this.canOpenSettings = status.canOpenSettings ? true : false; this.logCapabilities(); } private orIsNot(bool: boolean): string { return bool ? '' : 'not '; } private logCapabilities() { this.logger.debug('A camera is ' + this.orIsNot(this.isAvailable) + 'available to this app.'); var access = 'not authorized'; if (this.hasPermission) access = 'authorized'; if (this.isDenied) access = 'denied'; if (this.isRestricted) access = 'restricted'; this.logger.debug('Camera access is ' + access + '.'); this.logger.debug('Support for opening device settings is ' + this.orIsNot(this.canOpenSettings) + 'available on this platform.'); this.logger.debug('A light is ' + this.orIsNot(this.canEnableLight) + 'available on this platform.'); this.logger.debug('A second camera is ' + this.orIsNot(this.canChangeCamera) + 'available on this platform.'); } /** * Immediately return known capabilities of the current platform. */ public getCapabilities(): any { return { isAvailable: this.isAvailable, hasPermission: this.hasPermission, isDenied: this.isDenied, isRestricted: this.isRestricted, canEnableLight: this.canEnableLight, canChangeCamera: this.canChangeCamera, canOpenSettings: this.canOpenSettings }; }; /** * If camera access has been granted, pre-initialize the QRScanner. This method * can be safely called before the scanner is visible to improve perceived * scanner loading times. * * The `status` of QRScanner is returned to the callback. */ public gentleInitialize(): Promise<any> { return new Promise((resolve, reject) => { if (this.initializeStarted && !this.isDesktop) { this.qrScanner.getStatus().then((status) => { this.completeInitialization(status); }); return resolve(); } this.initializeStarted = true; this.logger.debug('Trying to pre-initialize QRScanner.'); if (!this.isDesktop) { this.qrScanner.getStatus().then((status) => { this.checkCapabilities(status); if (status.authorized) { this.logger.debug('Camera permission already granted.'); this.initialize().then(() => { return resolve(); }); } else { this.logger.debug('QRScanner not authorized, waiting to initalize.'); this.completeInitialization(status); return resolve(); } }); } else { this.logger.debug('To avoid flashing the privacy light, we do not pre-initialize the camera on desktop.'); return resolve(); } }); } public reinitialize(): void { this.initializeCompleted = false; this.qrScanner.destroy(); this.initialize(); }; public initialize(): Promise<any> { return new Promise((resolve, reject) => { this.logger.debug('Initializing scanner...'); this.qrScanner.prepare().then((status: any) => { this.completeInitialization(status); return resolve(); }).catch((err) => { this.isAvailable = false; this.logger.error(err); // does not return `status` if there is an error this.qrScanner.getStatus().then((status) => { this.completeInitialization(status); return resolve(); }); }); }); } private completeInitialization(status: any): void { this.checkCapabilities(status); this.initializeCompleted = true; this.events.publish('scannerServiceInitialized'); } public isInitialized(): boolean { return this.initializeCompleted; }; public isInitializeStarted(): boolean { return this.initializeStarted; }; /** * (Re)activate the QRScanner, and cancel the timeouts if present. * * The `status` of QRScanner is passed to the callback when activation * is complete. */ public activate(): Promise<any> { return new Promise((resolve, reject) => { this.logger.debug('Activating scanner...'); this.qrScanner.show().then((status) => { this.initializeCompleted = true; this.checkCapabilities(status); return resolve(); }); }); } /** * Start a new scan. */ public scan(): Promise<any> { return new Promise((resolve, reject) => { this.logger.debug('Scanning...'); let scanSub = this.qrScanner.scan().subscribe((text: string) => { this.logger.debug('Scanned something', text); this.qrScanner.hide(); // hide camera preview scanSub.unsubscribe(); // stop scanning return resolve(text); }); }); } public pausePreview(): void { this.qrScanner.pausePreview(); } public resumePreview(): void { this.qrScanner.resumePreview(); } /** * Deactivate the QRScanner. To balance user-perceived performance and power * consumption, this kicks off a countdown which will "sleep" the scanner * after a certain amount of time. * * The `status` of QRScanner is passed to the callback when deactivation * is complete. */ public deactivate(): void { this.logger.debug('Deactivating scanner...'); if (this.lightEnabled) { this.qrScanner.disableLight(); this.lightEnabled = false; } this.hide(); this.destroy(); } // Natively hide the QRScanner's preview // On mobile platforms, this can reduce GPU/power usage // On desktop, this fully turns off the camera (and any associated privacy lights) private hide() { this.qrScanner.hide(); } // Reduce QRScanner power/processing consumption by the maximum amount private destroy() { this.qrScanner.destroy(); } /** * Toggle the device light (if available). * * The callback receives a boolean which is `true` if the light is enabled. */ public toggleLight(): Promise<any> { return new Promise((resolve, reject) => { this.logger.info('Toggling light...'); if (this.lightEnabled) { this.qrScanner.disableLight() .then(resp => { this.lightEnabled = false; return resolve(this.lightEnabled); }) .catch(err => { this.logger.warn("Error: ", err); return reject(err); }); } else { this.qrScanner.enableLight() .then(resp => { this.lightEnabled = true; return resolve(this.lightEnabled); }) .catch(err => { this.logger.warn("Error: ", err); return reject(err); }); } }); }; /** * Switch cameras (if a second camera is available). * * The `status` of QRScanner is passed to the callback when activation * is complete. */ public toggleCamera(): Promise<any> { return new Promise((resolve, reject) => { this.logger.info('Toggling camera...'); if (this.frontCameraEnabled) { this.qrScanner.useBackCamera() .then(resp => { this.frontCameraEnabled = false; return resolve(this.frontCameraEnabled); }) .catch(err => { this.logger.warn("Error: ", err); return reject(err); }); } else { this.qrScanner.useFrontCamera() .then(resp => { this.frontCameraEnabled = true; return resolve(this.frontCameraEnabled); }) .catch(err => { this.logger.warn("Error: ", err); return reject(err); }); } }); } public openSettings(): void { this.logger.debug('Attempting to open device settings...'); this.qrScanner.openSettings(); } }