Wallet/src/js/services/scannerService.js
2017-06-22 15:47:50 -03:00

277 lines
8.3 KiB
JavaScript

'use strict';
angular.module('copayApp.services').service('scannerService', function($log, $timeout, platformInfo, $rootScope, $window) {
var isDesktop = !platformInfo.isCordova;
var QRScanner = $window.QRScanner;
var lightEnabled = false;
var backCamera = true; // the plugin defaults to the back camera
// Initalize known capabilities
// Assume camera is available. If init fails, we'll set this to false.
var isAvailable = true;
var hasPermission = false;
var isDenied = false;
var isRestricted = false;
var canEnableLight = false;
var canChangeCamera = false;
var canOpenSettings = false;
function _checkCapabilities(status) {
$log.debug('scannerService is reviewing platform capabilities...');
// Permission can be assumed on the desktop builds
hasPermission = (isDesktop || status.authorized) ? true : false;
isDenied = status.denied ? true : false;
isRestricted = status.restricted ? true : false;
canEnableLight = status.canEnableLight ? true : false;
canChangeCamera = status.canChangeCamera ? true : false;
canOpenSettings = status.canOpenSettings ? true : false;
_logCapabilities();
}
function _logCapabilities() {
function _orIsNot(bool) {
return bool ? '' : 'not ';
}
$log.debug('A camera is ' + _orIsNot(isAvailable) + 'available to this app.');
var access = 'not authorized';
if (hasPermission) access = 'authorized';
if (isDenied) access = 'denied';
if (isRestricted) access = 'restricted';
$log.debug('Camera access is ' + access + '.');
$log.debug('Support for opening device settings is ' + _orIsNot(canOpenSettings) + 'available on this platform.');
$log.debug('A light is ' + _orIsNot(canEnableLight) + 'available on this platform.');
$log.debug('A second camera is ' + _orIsNot(canChangeCamera) + 'available on this platform.');
}
/**
* Immediately return known capabilities of the current platform.
*/
this.getCapabilities = function() {
return {
isAvailable: isAvailable,
hasPermission: hasPermission,
isDenied: isDenied,
isRestricted: isRestricted,
canEnableLight: canEnableLight,
canChangeCamera: canChangeCamera,
canOpenSettings: canOpenSettings
};
};
var initializeStarted = false;
/**
* 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.
*/
this.gentleInitialize = function(callback) {
if (initializeStarted && !isDesktop) {
QRScanner.getStatus(function(status) {
_completeInitialization(status, callback);
});
return;
}
initializeStarted = true;
$log.debug('Trying to pre-initialize QRScanner.');
if (!isDesktop) {
QRScanner.getStatus(function(status) {
_checkCapabilities(status);
if (status.authorized) {
$log.debug('Camera permission already granted.');
initialize(callback);
} else {
$log.debug('QRScanner not authorized, waiting to initalize.');
_completeInitialization(status, callback);
}
});
} else {
$log.debug('To avoid flashing the privacy light, we do not pre-initialize the camera on desktop.');
}
};
function initialize(callback) {
$log.debug('Initializing scanner...');
QRScanner.prepare(function(err, status) {
if (err) {
isAvailable = false;
$log.error(err);
// does not return `status` if there is an error
QRScanner.getStatus(function(status) {
_completeInitialization(status, callback);
});
} else {
_completeInitialization(status, callback);
}
});
}
this.initialize = initialize;
// This could be much cleaner with a Promise API
// (needs a polyfill for some platforms)
var initializeCompleted = false;
function _completeInitialization(status, callback) {
_checkCapabilities(status);
initializeCompleted = true;
$rootScope.$emit('scannerServiceInitialized');
if (typeof callback === "function") {
callback(status);
}
}
this.isInitialized = function() {
return initializeCompleted;
};
this.initializeStarted = function() {
return initializeStarted;
};
var nextHide = null;
var nextDestroy = null;
var hideAfterSeconds = 5;
var destroyAfterSeconds = 60;
/**
* (Re)activate the QRScanner, and cancel the timeouts if present.
*
* The `status` of QRScanner is passed to the callback when activation
* is complete.
*/
this.activate = function(callback) {
$log.debug('Activating scanner...');
QRScanner.show(function(status) {
initializeCompleted = true;
_checkCapabilities(status);
if (typeof callback === "function") {
callback(status);
}
});
if (nextHide !== null) {
$timeout.cancel(nextHide);
nextHide = null;
}
if (nextDestroy !== null) {
$timeout.cancel(nextDestroy);
nextDestroy = null;
}
};
/**
* Start a new scan.
*
* The callback receives: (err, contents)
*/
this.scan = function(callback) {
$log.debug('Scanning...');
QRScanner.scan(callback);
};
this.pausePreview = function() {
QRScanner.pausePreview();
};
this.resumePreview = function() {
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.
*/
this.deactivate = function(callback) {
$log.debug('Deactivating scanner...');
QRScanner.cancelScan();
nextHide = $timeout(_hide, hideAfterSeconds * 1000);
nextDestroy = $timeout(_destroy, destroyAfterSeconds * 1000);
};
// 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)
function _hide() {
$log.debug('Scanner not in use for ' + hideAfterSeconds + ' seconds, hiding...');
QRScanner.hide();
}
// Reduce QRScanner power/processing consumption by the maximum amount
function _destroy() {
$log.debug('Scanner not in use for ' + destroyAfterSeconds + ' seconds, destroying...');
QRScanner.destroy();
}
this.reinitialize = function(callback) {
initializeCompleted = false;
QRScanner.destroy();
initialize(callback);
};
/**
* Toggle the device light (if available).
*
* The callback receives a boolean which is `true` if the light is enabled.
*/
this.toggleLight = function(callback) {
$log.debug('Toggling light...');
if (lightEnabled) {
QRScanner.disableLight(_handleResponse);
} else {
QRScanner.enableLight(_handleResponse);
}
function _handleResponse(err, status) {
if (err) {
$log.error(err);
} else {
lightEnabled = status.lightEnabled;
var state = lightEnabled ? 'enabled' : 'disabled';
$log.debug('Light ' + state + '.');
}
callback(lightEnabled);
}
};
/**
* Switch cameras (if a second camera is available).
*
* The `status` of QRScanner is passed to the callback when activation
* is complete.
*/
this.toggleCamera = function(callback) {
var nextCamera = backCamera ? 1 : 0;
function cameraToString(index) {
return index === 1 ? 'front' : 'back'; // front = 1, back = 0
}
$log.debug('Toggling to the ' + cameraToString(nextCamera) + ' camera...');
QRScanner.useCamera(nextCamera, function(err, status) {
if (err) {
$log.error(err);
}
backCamera = status.currentCamera === 1 ? false : true;
$log.debug('Camera toggled. Now using the ' + cameraToString(backCamera) + ' camera.');
callback(status);
});
};
this.openSettings = function() {
$log.debug('Attempting to open device settings...');
QRScanner.openSettings();
};
this.useOldScanner = function(callback) {
cordova.plugins.barcodeScanner.scan(
function(result) {
callback(null, result.text);
},
function(error) {
callback(error);
}
);
}
});