feat(scan): first iteration of full scan view implementation

This commit is contained in:
Jason Dreyzehner 2016-10-11 02:59:21 -04:00
commit c0d4fbe5bb
5 changed files with 106 additions and 50 deletions

View file

@ -23,57 +23,71 @@ angular.module('copayApp.controllers').controller('tabScanController', function(
} }
function _handleCapabilities(){ function _handleCapabilities(){
if(!$scope.scannerIsAvailable){ // always update the view
$scope.currentState = scannerStates.unavailable; $timeout(function(){
} else if($scope.scannerIsDenied){ if(!scannerService.isInitialized()){
$scope.currentState = scannerStates.denied; $scope.currentState = scannerStates.loading;
} else if($scope.scannerIsRestricted){ } else if(!$scope.scannerIsAvailable){
$scope.currentState = scannerStates.denied; $scope.currentState = scannerStates.unavailable;
} else if(!$scope.scannerHasPermission){ } else if($scope.scannerIsDenied){
$scope.currentState = scannerStates.unauthorized; $scope.currentState = scannerStates.denied;
} else if($scope.scannerHasPermission){ } else if($scope.scannerIsRestricted){
$scope.currentState = scannerStates.denied;
} else if(!$scope.scannerHasPermission){
$scope.currentState = scannerStates.unauthorized;
}
$log.debug('Scan view state set to: ' + $scope.currentState);
});
}
function _refreshScanView(){
_updateCapabilities();
_handleCapabilities();
if($scope.scannerHasPermission){
activate(); activate();
} }
} }
function _initScanView(){
_updateCapabilities();
_handleCapabilities();
}
$scope.$on("$ionicView.beforeEnter", function() {
$scope.currentState = scannerStates.loading;
});
// This could be much cleaner with a Promise API // This could be much cleaner with a Promise API
// (needs a polyfill for some platforms) // (needs a polyfill for some platforms)
$rootScope.$on('scannerServiceInitialized', function(){ $rootScope.$on('scannerServiceInitialized', function(){
$log.debug('Scanner initialization finished, reinitializing scan view...'); $log.debug('Scanner initialization finished, reinitializing scan view...');
_initScanView(); _refreshScanView();
}); });
$scope.$on("$ionicView.afterEnter", function() { $scope.$on("$ionicView.afterEnter", function() {
if(scannerService.isInitialized()){ // try initializing and refreshing status any time the view is entered
_initScanView(); scannerService.gentleInitialize();
}
}); });
function activate(){ function activate(){
scannerService.activate(function(){ scannerService.activate(function(){
_updateCapabilities();
_handleCapabilities();
$log.debug('Scanner activated, setting to visible...'); $log.debug('Scanner activated, setting to visible...');
$scope.currentState = scannerStates.visible; $scope.currentState = scannerStates.visible;
scannerService.scan(function(err, contents){ // pause to update the view
if(err){ $timeout(function(){
$log.debug('Scan canceled.'); scannerService.scan(function(err, contents){
} else if ($state.params.passthroughMode) { if(err){
$rootScope.scanResult = contents; $log.debug('Scan canceled.');
goBack(); } else if ($state.params.passthroughMode) {
} else { $rootScope.scanResult = contents;
handleSuccessfulScan(contents); goBack();
} } else {
}); handleSuccessfulScan(contents);
}
});
});
}); });
} }
$scope.activate = activate;
$scope.authorize = function(){
scannerService.initialize(function(){
_refreshScanView();
});
};
$scope.$on("$ionicView.afterLeave", function() { $scope.$on("$ionicView.afterLeave", function() {
scannerService.deactivate(); scannerService.deactivate();
@ -84,6 +98,14 @@ angular.module('copayApp.controllers').controller('tabScanController', function(
incomingData.redir(contents); incomingData.redir(contents);
} }
$scope.openSettings = function(){
scannerService.openSettings();
};
$scope.attemptToReactivate = function(){
scannerService.reinitialize();
};
$scope.toggleLight = function(){ $scope.toggleLight = function(){
scannerService.toggleLight(function(lightEnabled){ scannerService.toggleLight(function(lightEnabled){
$scope.lightActive = lightEnabled; $scope.lightActive = lightEnabled;
@ -98,7 +120,7 @@ angular.module('copayApp.controllers').controller('tabScanController', function(
$timeout(function(){ $timeout(function(){
$scope.cameraToggleActive = false; $scope.cameraToggleActive = false;
$log.debug('Camera toggle control deactivated.'); $log.debug('Camera toggle control deactivated.');
}, 600); }, 200);
}); });
}; };
@ -106,6 +128,9 @@ angular.module('copayApp.controllers').controller('tabScanController', function(
return $state.params.passthroughMode; return $state.params.passthroughMode;
} }
function goBack(){ function goBack(){
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$ionicHistory.backView().go(); $ionicHistory.backView().go();
} }
$scope.goBack = goBack; $scope.goBack = goBack;

View file

@ -1,7 +1,7 @@
'use strict'; 'use strict';
angular.module('copayApp.directives') angular.module('copayApp.directives')
.directive('qrScanner', function($state, $rootScope, $log) { .directive('qrScanner', function($state, $rootScope, $log, $ionicHistory) {
return { return {
restrict: 'E', restrict: 'E',
@ -9,11 +9,14 @@ angular.module('copayApp.directives')
onScan: "&" onScan: "&"
}, },
replace: true, replace: true,
template: '<a on-tap="openScanner()"><i class="icon ion-qr-scanner"></i></a>', template: '<a on-tap="openScanner()" nav-transition="none"><i class="icon ion-qr-scanner"></i></a>',
link: function(scope, el, attrs) { link: function(scope, el, attrs) {
scope.openScanner = function() { scope.openScanner = function() {
$log.debug('Opening scanner by directive...'); $log.debug('Opening scanner by directive...');
$ionicHistory.nextViewOptions({
disableAnimate: true
});
$state.go('scanner', { passthroughMode: 1 }); $state.go('scanner', { passthroughMode: 1 });
}; };

View file

@ -8,9 +8,8 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti
var backCamera = true; // the plugin defaults to the back camera var backCamera = true; // the plugin defaults to the back camera
// Initalize known capabilities // Initalize known capabilities
var isAvailable = false; var isAvailable = isDesktop? false: true; // assume camera exists on mobile
var hasPermission = isDesktop? true: false; var hasPermission = isDesktop? true: false; // assume desktop has permission
var isAuthorized = false;
var isDenied = false; var isDenied = false;
var isRestricted = false; var isRestricted = false;
var canEnableLight = false; var canEnableLight = false;
@ -59,6 +58,7 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti
} }
} }
var initializeStarted = false;
/** /**
* If camera access has been granted, pre-initialize the QRScanner. This method * If camera access has been granted, pre-initialize the QRScanner. This method
* can be safely called before the scanner is visible to improve perceived * can be safely called before the scanner is visible to improve perceived
@ -67,27 +67,32 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti
* The `status` of QRScanner is returned to the callback. * The `status` of QRScanner is returned to the callback.
*/ */
this.gentleInitialize = function(callback) { this.gentleInitialize = function(callback) {
if(initializeStarted){
QRScanner.getStatus(function(status){
_completeInitialization(status, callback);
});
return;
}
initializeStarted = true;
$log.debug('Trying to pre-initialize QRScanner.'); $log.debug('Trying to pre-initialize QRScanner.');
if(!isDesktop){ if(!isDesktop){
QRScanner.getStatus(function(status){ QRScanner.getStatus(function(status){
_checkCapabilities(status); _checkCapabilities(status);
if(status.authorized){ if(status.authorized){
$log.debug('Camera permission already granted.'); $log.debug('Camera permission already granted.');
_initalize(callback); initialize(callback);
} else { } else {
$log.debug('QRScanner not authorized, waiting to initalize.'); $log.debug('QRScanner not authorized, waiting to initalize.');
if(typeof callback === "function"){ _completeInitialization(status, callback);
callback && callback(status);
}
} }
}); });
} else { } else {
$log.debug('Camera permission assumed on desktop.'); $log.debug('Camera permission assumed on desktop.');
_initalize(callback); initialize(callback);
} }
}; };
function _initalize(callback){ function initialize(callback){
$log.debug('Initializing scanner...'); $log.debug('Initializing scanner...');
QRScanner.prepare(function(err, status){ QRScanner.prepare(function(err, status){
if(err){ if(err){
@ -103,19 +108,25 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti
} }
}); });
} }
this.initialize = initialize;
// This could be much cleaner with a Promise API // This could be much cleaner with a Promise API
// (needs a polyfill for some platforms) // (needs a polyfill for some platforms)
var initializeCompleted = false; var initializeCompleted = false;
function _completeInitialization(status, callback){ function _completeInitialization(status, callback){
_checkCapabilities(status); _checkCapabilities(status);
$rootScope.$emit('scannerServiceInitialized');
initializeCompleted = true; initializeCompleted = true;
callback && callback(status); $rootScope.$emit('scannerServiceInitialized');
if(typeof callback === "function"){
callback(status);
}
} }
this.isInitialized = function(){ this.isInitialized = function(){
return initializeCompleted; return initializeCompleted;
} }
this.initializeStarted = function(){
return initializeStarted;
}
var nextHide = null; var nextHide = null;
var nextDestroy = null; var nextDestroy = null;
@ -132,7 +143,9 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti
$log.debug('Activating scanner...'); $log.debug('Activating scanner...');
QRScanner.show(function(status){ QRScanner.show(function(status){
_checkCapabilities(status); _checkCapabilities(status);
callback(status); if(typeof callback === "function"){
callback(status);
}
}); });
if(nextHide !== null){ if(nextHide !== null){
$timeout.cancel(nextHide); $timeout.cancel(nextHide);
@ -183,6 +196,12 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti
QRScanner.destroy(); QRScanner.destroy();
} }
this.reinitialize = function(callback){
initializeCompleted = false;
QRScanner.destroy();
initialize(callback);
}
/** /**
* Toggle the device light (if available). * Toggle the device light (if available).
* *
@ -228,4 +247,9 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti
callback(status); callback(status);
}); });
}; };
this.openSettings = function() {
$log.debug('Attempting to open device settings...');
QRScanner.openSettings();
};
}); });

View file

@ -4,6 +4,9 @@ $scannerBackgroundColor: #060d2d;
color: #fff; color: #fff;
text-align: center; text-align: center;
background: transparent none; background: transparent none;
.bar-header {
opacity: .9;
}
&-has-problems, &-has-problems,
&-loading-camera { &-loading-camera {
background-color: $scannerBackgroundColor; background-color: $scannerBackgroundColor;
@ -50,7 +53,8 @@ $scannerBackgroundColor: #060d2d;
} }
} }
&-loading-camera { &-loading-camera {
height: 100%;
width: 100%
} }
&-camera-ready { &-camera-ready {
// view background is transparent to show video preview // view background is transparent to show video preview

View file

@ -10,7 +10,7 @@
<ion-content scroll="false"> <ion-content scroll="false">
<div class="ng-hide" id="tab-scan-has-problems" ng-show="currentState === scannerStates.unauthorized || currentState === scannerStates.denied || currentState === scannerStates.unavailable"> <div class="ng-hide" id="tab-scan-has-problems" ng-show="currentState === scannerStates.unauthorized || currentState === scannerStates.denied || currentState === scannerStates.unavailable">
<i class="icon zero-state-icon"> <i class="icon zero-state-icon">
<img src="img/tab-icons/ico-receive.svg" /> <img src="img/tab-icons/ico-receive.svg"/>
</i> </i>
<div class="zero-state-heading" translate>Scan QR Codes</div> <div class="zero-state-heading" translate>Scan QR Codes</div>
<div class="zero-state-description" translate>You can scan bitcoin addresses, payment requests, paper wallets, and more.</div> <div class="zero-state-description" translate>You can scan bitcoin addresses, payment requests, paper wallets, and more.</div>
@ -18,9 +18,9 @@
<div class="ng-hide zero-state-tldr" ng-show="currentState === scannerStates.unauthorized" translate>Enable the camera to get started.</div> <div class="ng-hide zero-state-tldr" ng-show="currentState === scannerStates.unauthorized" translate>Enable the camera to get started.</div>
<div class="ng-hide zero-state-tldr" ng-show="currentState === scannerStates.denied" translate>Enable camera access in your device settings to get started.</div> <div class="ng-hide zero-state-tldr" ng-show="currentState === scannerStates.denied" translate>Enable camera access in your device settings to get started.</div>
<div class="ng-hide zero-state-tldr" ng-show="currentState === scannerStates.unavailable" translate>Please connect a camera to get started.</div> <div class="ng-hide zero-state-tldr" ng-show="currentState === scannerStates.unavailable" translate>Please connect a camera to get started.</div>
<button ng-show="currentState === scannerStates.unauthorized" class="ng-hide button button-standard button-primary" ng-click="attemptActivate">Allow Camera Access</button> <button ng-show="currentState === scannerStates.unauthorized" class="ng-hide button button-standard button-primary" ng-click="authorize()">Allow Camera Access</button>
<button ng-show="currentState === scannerStates.denied && canOpenSettings" class="ng-hide button button-standard button-primary" ng-click="openSettings">Open Settings</button> <button ng-show="currentState === scannerStates.denied && canOpenSettings" class="ng-hide button button-standard button-primary" ng-click="openSettings()">Open Settings</button>
<button ng-show="currentState === scannerStates.unavailable" class="ng-hide button button-standard button-primary">Retry Camera</button> <button ng-show="currentState === scannerStates.unavailable" class="ng-hide button button-standard button-primary" ng-click="attemptToReactivate()">Retry Camera</button>
</div> </div>
</div> </div>
<div class="ng-show" id="tab-scan-loading-camera" ng-show="currentState === scannerStates.loading"></div> <div class="ng-show" id="tab-scan-loading-camera" ng-show="currentState === scannerStates.loading"></div>