From ce3812b2204a0e65d33ee2b80fb2123793977579 Mon Sep 17 00:00:00 2001 From: Jason Dreyzehner Date: Mon, 10 Oct 2016 22:57:20 -0400 Subject: [PATCH 1/5] feat(scan): scaffold permission priming in scan view --- src/sass/views/tab-scan.scss | 139 ++++++++++++++++++++++++----------- www/views/tab-scan.html | 49 ++++++++---- 2 files changed, 133 insertions(+), 55 deletions(-) diff --git a/src/sass/views/tab-scan.scss b/src/sass/views/tab-scan.scss index 41d9e7ef1..ad6898c02 100644 --- a/src/sass/views/tab-scan.scss +++ b/src/sass/views/tab-scan.scss @@ -1,55 +1,110 @@ +$scannerBackgroundColor: #060d2d; + #tab-scan { - // view background is transparent to show video preview - background: none transparent; - .scanner-controls { - width: 100%; - text-align: center; - bottom: 0; - position: absolute; + color: #fff; + text-align: center; + background: transparent none; + &-has-problems, + &-loading-camera { + background-color: $scannerBackgroundColor; } - .guides { - display: flex; - position: absolute; + &-has-problems { height: 100%; - width: 100%; + display: flex; + flex-direction: column; align-items: center; justify-content: center; - top: 0; - left: 0; - } - .qr-scan-guides { - width: 60%; - max-width: 400px; - margin-bottom: 8em; - max-height: 50%; - } - .icon-flash, .icon-camera-toggle { - border-radius: 50%; - width: 4em; - height: 4em; - background-color: rgba(13, 13, 13, 0.79); - background-repeat: no-repeat; - background-clip: padding-box; - background-size: 100%; - display: inline-block; - margin: 2em 1em; - cursor: pointer; - // hover for desktop only - body:not(.platform-cordova) &:hover { - background-color: rgba(31, 40, 78, 0.79); - } - &.active, &:active { - background-color: rgba(100, 124, 232, 0.79); + + .zero-state { + &-icon { + display: inline-block; + width: 50px; + height: 50px; + border-radius: 50%; + padding: 13px; + box-shadow: $subtle-box-shadow; + background-color: #fff; + } + &-heading { + font-size: 20px; + margin: 1rem; + } + &-description { + margin: 0 2rem 120px; + opacity: .6; + max-width: 300px; + } + &-tldr { + margin: 1rem auto; + } + &-description, + &-tldr { + max-width: 300px; + } + &-cta { + position: absolute; + bottom: 0; + width: 100%; + padding-bottom: 6vh; + } } } - .icon-flash { - background-image: url("../img/icon-flash.svg"); + &-loading-camera { + } - .icon-camera-toggle { - background-image: url("../img/icon-camera-toggle.svg"); + &-camera-ready { + // view background is transparent to show video preview + background: none transparent; + .scanner-controls { + width: 100%; + text-align: center; + bottom: 0; + position: absolute; + } + .guides { + display: flex; + position: absolute; + height: 100%; + width: 100%; + align-items: center; + justify-content: center; + top: 0; + left: 0; + } + .qr-scan-guides { + width: 60%; + max-width: 400px; + margin-bottom: 8em; + max-height: 50%; + } + .icon-flash, .icon-camera-toggle { + border-radius: 50%; + width: 4em; + height: 4em; + background-color: rgba(13, 13, 13, 0.79); + background-repeat: no-repeat; + background-clip: padding-box; + background-size: 100%; + display: inline-block; + margin: 2em 1em; + cursor: pointer; + // hover for desktop only + body:not(.platform-cordova) &:hover { + background-color: rgba(31, 40, 78, 0.79); + } + &.active, &:active { + background-color: rgba(100, 124, 232, 0.79); + } + } + .icon-flash { + background-image: url("../img/icon-flash.svg"); + } + .icon-camera-toggle { + background-image: url("../img/icon-camera-toggle.svg"); + } } } #cordova-plugin-qrscanner-still, #cordova-plugin-qrscanner-video-preview { - background-color: #060d2d !important; + background-color: $scannerBackgroundColor !important; } diff --git a/www/views/tab-scan.html b/www/views/tab-scan.html index 27c9cb9c5..288e15915 100644 --- a/www/views/tab-scan.html +++ b/www/views/tab-scan.html @@ -1,22 +1,45 @@ {{'Scan' | translate}} + + + -
- +
+ + + +
Scan QR Codes
+
You can scan bitcoin addresses, payment requests, paper wallets, and more.
+
+
Enable the camera to get started.
+
Enable camera access in your device settings to get started.
+
Please connect a camera to get started.
+ + + +
-
- - -
-
-
- - -
-
-
+
+
+
+ +
+
From f41c56ba040de17e3af4f496b007f0fe2be8c711 Mon Sep 17 00:00:00 2001 From: Jason Dreyzehner Date: Mon, 10 Oct 2016 22:59:35 -0400 Subject: [PATCH 2/5] feat(scan): implement permission priming in scanService --- src/js/controllers/tab-scan.js | 79 +++++++++++++++++++++++++++--- src/js/routes.js | 8 +++ src/js/services/scannerService.js | 81 +++++++++++++++++++++++-------- 3 files changed, 143 insertions(+), 25 deletions(-) diff --git a/src/js/controllers/tab-scan.js b/src/js/controllers/tab-scan.js index 3f444ac3c..0f16ea580 100644 --- a/src/js/controllers/tab-scan.js +++ b/src/js/controllers/tab-scan.js @@ -1,29 +1,89 @@ 'use strict'; -angular.module('copayApp.controllers').controller('tabScanController', function($scope, $log, $timeout, scannerService, incomingData) { +angular.module('copayApp.controllers').controller('tabScanController', function($scope, $log, $timeout, scannerService, incomingData, $state, $ionicHistory, $rootScope) { - $scope.$on("$ionicView.beforeEnter", function() { - $log.debug('Preparing to display available controls.'); + var scannerStates = { + unauthorized: 'unauthorized', + denied: 'denied', + unavailable: 'unavailable', + loading: 'loading', + visible: 'visible' + }; + $scope.scannerStates = scannerStates; + + function _updateCapabilities(){ var capabilities = scannerService.getCapabilities(); + $scope.scannerIsAvailable = capabilities.isAvailable; + $scope.scannerHasPermission = capabilities.hasPermission; + $scope.scannerIsDenied = capabilities.isDenied; + $scope.scannerIsRestricted = capabilities.isRestricted; $scope.canEnableLight = capabilities.canEnableLight; $scope.canChangeCamera = capabilities.canChangeCamera; + $scope.canOpenSettings = capabilities.canOpenSettings; + } + + function _handleCapabilities(){ + if(!$scope.scannerIsAvailable){ + $scope.currentState = scannerStates.unavailable; + } else if($scope.scannerIsDenied){ + $scope.currentState = scannerStates.denied; + } else if($scope.scannerIsRestricted){ + $scope.currentState = scannerStates.denied; + } else if(!$scope.scannerHasPermission){ + $scope.currentState = scannerStates.unauthorized; + } else if($scope.scannerHasPermission){ + activate(); + } + } + + function _initScanView(){ + _updateCapabilities(); + _handleCapabilities(); + } + + $scope.$on("$ionicView.beforeEnter", function() { + $scope.currentState = scannerStates.loading; + }); + + // This could be much cleaner with a Promise API + // (needs a polyfill for some platforms) + $rootScope.$on('scannerServiceInitialized', function(){ + $log.debug('Scanner initialization finished, reinitializing scan view...'); + _initScanView(); }); $scope.$on("$ionicView.afterEnter", function() { + if(scannerService.isInitialized()){ + _initScanView(); + } + }); + + function activate(){ scannerService.activate(function(){ + $log.debug('Scanner activated, setting to visible...'); + $scope.currentState = scannerStates.visible; scannerService.scan(function(err, contents){ if(err){ $log.debug('Scan canceled.'); + } else if ($state.params.passthroughMode) { + $rootScope.scanResult = contents; + goBack(); } else { - incomingData.redir(contents); + handleSuccessfulScan(contents); } }); }); - }); + } + $scope.$on("$ionicView.afterLeave", function() { scannerService.deactivate(); }); + function handleSuccessfulScan(contents){ + $log.debug('Scan returned: "' + contents + '"'); + incomingData.redir(contents); + } + $scope.toggleLight = function(){ scannerService.toggleLight(function(lightEnabled){ $scope.lightActive = lightEnabled; @@ -38,8 +98,15 @@ angular.module('copayApp.controllers').controller('tabScanController', function( $timeout(function(){ $scope.cameraToggleActive = false; $log.debug('Camera toggle control deactivated.'); - }, 200); + }, 600); }); }; + $scope.canGoBack = function(){ + return $state.params.passthroughMode; + } + function goBack(){ + $ionicHistory.backView().go(); + } + $scope.goBack = goBack; }); diff --git a/src/js/routes.js b/src/js/routes.js index a69f00072..e75079d2b 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -217,6 +217,14 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr } } }) + .state('scanner', { + url: '/scanner', + params: { + passthroughMode: null, + }, + controller: 'tabScanController', + templateUrl: 'views/tab-scan.html' + }) .state('tabs.send', { url: '/send', views: { diff --git a/src/js/services/scannerService.js b/src/js/services/scannerService.js index b46e5e604..09df4dfee 100644 --- a/src/js/services/scannerService.js +++ b/src/js/services/scannerService.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.services').service('scannerService', function($log, $timeout, platformInfo) { +angular.module('copayApp.services').service('scannerService', function($log, $timeout, platformInfo, $rootScope) { var isDesktop = !platformInfo.isCordova; var QRScanner = window.QRScanner; @@ -8,21 +8,40 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti var backCamera = true; // the plugin defaults to the back camera // Initalize known capabilities + var isAvailable = false; var hasPermission = isDesktop? true: false; + var isAuthorized = 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; - function orIsNot(bool){ + canOpenSettings = status.canOpenSettings? true : false; + _logCapabilities(); + } + + function _logCapabilities(){ + function _orIsNot(bool){ return bool? '' : 'not '; } - $log.debug('A light is ' + orIsNot(canEnableLight) + 'available on this platform.'); - $log.debug('A second camera is ' + orIsNot(canChangeCamera) + 'available on this platform.'); + $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.'); } /** @@ -30,9 +49,13 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti */ this.getCapabilities = function(){ return { + isAvailable: isAvailable, hasPermission: hasPermission, + isDenied: isDenied, + isRestricted: isRestricted, canEnableLight: canEnableLight, - canChangeCamera: canChangeCamera + canChangeCamera: canChangeCamera, + canOpenSettings: canOpenSettings } } @@ -50,7 +73,7 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti _checkCapabilities(status); if(status.authorized){ $log.debug('Camera permission already granted.'); - _initalize(); + _initalize(callback); } else { $log.debug('QRScanner not authorized, waiting to initalize.'); if(typeof callback === "function"){ @@ -60,23 +83,43 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti }); } else { $log.debug('Camera permission assumed on desktop.'); - _initalize(); - } - function _initalize(){ - $log.debug('Preparing scanner...'); - QRScanner.prepare(function(err, status){ - if(err){ - $log.error(err); - } - _checkCapabilities(status); - callback && callback(status); - }); + _initalize(callback); } }; + function _initalize(callback){ + $log.debug('Initializing scanner...'); + QRScanner.prepare(function(err, status){ + if(err){ + $log.error(err); + // does not return `status` if there is an error + QRScanner.getStatus(function(status){ + _completeInitialization(status, callback); + + }); + } else { + isAvailable = true; + _completeInitialization(status, callback); + } + }); + } + + // This could be much cleaner with a Promise API + // (needs a polyfill for some platforms) + var initializeCompleted = false; + function _completeInitialization(status, callback){ + _checkCapabilities(status); + $rootScope.$emit('scannerServiceInitialized'); + initializeCompleted = true; + callback && callback(status); + } + this.isInitialized = function(){ + return initializeCompleted; + } + var nextHide = null; var nextDestroy = null; - var hideAfterSeconds = 15; + var hideAfterSeconds = 10; var destroyAfterSeconds = 5 * 60; /** @@ -121,7 +164,7 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti */ this.deactivate = function(callback) { $log.debug('Deactivating scanner...'); - QRScanner.cancelScan(); + // QRScanner.cancelScan(); nextHide = $timeout(_hide, hideAfterSeconds * 1000); nextDestroy = $timeout(_destroy, destroyAfterSeconds * 1000); }; From 581622f83de9f28987c39bb4f58d55b219706387 Mon Sep 17 00:00:00 2001 From: Nick Cardin Date: Mon, 10 Oct 2016 23:02:19 -0400 Subject: [PATCH 3/5] feat(qr-scanner): update directive to use the new scan view --- src/js/directives/qrScanner.js | 83 +++++++--------------------------- 1 file changed, 17 insertions(+), 66 deletions(-) diff --git a/src/js/directives/qrScanner.js b/src/js/directives/qrScanner.js index 5e199d7c1..0a23819a5 100644 --- a/src/js/directives/qrScanner.js +++ b/src/js/directives/qrScanner.js @@ -1,77 +1,28 @@ 'use strict'; angular.module('copayApp.directives') - .directive('qrScanner', function($rootScope, $timeout, $ionicModal, gettextCatalog, platformInfo) { - - var isCordova = platformInfo.isCordova; - var isWP = platformInfo.isWP; - var isIOS = platformInfo.isIOS; - - var controller = function($scope) { - - var onSuccess = function(result) { - $timeout(function() { - window.plugins.spinnerDialog.hide(); - }, 100); - if (isWP && result.cancelled) return; - - $timeout(function() { - var data = isIOS ? result : result.text; - $scope.onScan({ - data: data - }); - }, 1000); - }; - - var onError = function(error) { - $timeout(function() { - window.plugins.spinnerDialog.hide(); - }, 100); - }; - - $scope.cordovaOpenScanner = function() { - window.plugins.spinnerDialog.show(null, gettextCatalog.getString('Preparing camera...'), true); - $timeout(function() { - if (isIOS) { - cloudSky.zBar.scan({}, onSuccess, onError); - } else { - cordova.plugins.barcodeScanner.scan(onSuccess, onError); - } - if ($scope.beforeScan) { - $scope.beforeScan(); - } - }, 100); - }; - - $scope.modalOpenScanner = function() { - $ionicModal.fromTemplateUrl('views/modals/scanner.html', { - scope: $scope, - animation: 'slide-in-up' - }).then(function(modal) { - $scope.scannerModal = modal; - $scope.scannerModal.show(); - }); - }; - - $scope.openScanner = function() { - if (isCordova) { - $scope.cordovaOpenScanner(); - } else { - $scope.modalOpenScanner(); - } - }; - $scope.setFn({theScanFn: $scope.openScanner}); - }; + .directive('qrScanner', function($state, $rootScope, $log) { return { restrict: 'E', scope: { - onScan: "&", - setFn: "&", - beforeScan: "&" + onScan: "&" }, - controller: controller, replace: true, - template: '' + template: '', + link: function(scope, el, attrs) { + + scope.openScanner = function() { + $log.debug('Opening scanner by directive...'); + $state.go('scanner', { passthroughMode: 1 }); + }; + + $rootScope.$on('$ionicView.afterEnter', function() { + if($rootScope.scanResult) { + scope.onScan({ data: $rootScope.scanResult }); + $rootScope.scanResult = null; + } + }); + } } }); From 845534c727e4841d9c7ec47460e0027d4dbe3324 Mon Sep 17 00:00:00 2001 From: Jason Dreyzehner Date: Mon, 10 Oct 2016 23:09:27 -0400 Subject: [PATCH 4/5] refactor(scan): remove dead code around old desktop scanner --- Gruntfile.js | 1 - src/js/controllers/modals/scanner.js | 131 --------------------------- www/views/modals/scanner.html | 12 --- 3 files changed, 144 deletions(-) delete mode 100644 src/js/controllers/modals/scanner.js delete mode 100644 www/views/modals/scanner.html diff --git a/Gruntfile.js b/Gruntfile.js index 52c0f1338..347f7809c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -117,7 +117,6 @@ module.exports = function(grunt) { angular: { src: [ 'bower_components/qrcode-generator/js/qrcode.js', - 'bower_components/qrcode-decoder-js/lib/qrcode-decoder.js', 'bower_components/moment/min/moment-with-locales.js', 'bower_components/angular-moment/angular-moment.js', 'bower_components/ng-lodash/build/ng-lodash.js', diff --git a/src/js/controllers/modals/scanner.js b/src/js/controllers/modals/scanner.js deleted file mode 100644 index 6616d505a..000000000 --- a/src/js/controllers/modals/scanner.js +++ /dev/null @@ -1,131 +0,0 @@ -'use strict'; - -angular.module('copayApp.controllers').controller('scannerController', function($scope, $timeout, storageService, $ionicModal, platformInfo) { - - // QR code Scanner - var video; - var canvas; - var $video; - var context; - var localMediaStream; - var prevResult; - var scanTimer; - - var _scan = function(evt) { - if (localMediaStream) { - context.drawImage(video, 0, 0, 300, 225); - try { - qrcode.decode(); - } catch (e) { - //qrcodeError(e); - } - } - scanTimer = $timeout(_scan, 800); - }; - - var _scanStop = function() { - $timeout.cancel(scanTimer); - if (localMediaStream && localMediaStream.active) { - var localMediaStreamTrack = localMediaStream.getTracks(); - for (var i = 0; i < localMediaStreamTrack.length; i++) { - localMediaStreamTrack[i].stop(); - } - } else { - try { - localMediaStream.stop(); - } catch (e) { - // Older Chromium not support the STOP function - }; - } - localMediaStream = null; - video.src = ''; - }; - - qrcode.callback = function(data) { - if (prevResult != data) { - prevResult = data; - return; - } - _scanStop(); - $scope.cancel(); - $scope.onScan({ - data: data - }); - }; - - var _successCallback = function(stream) { - video.src = (window.URL && window.URL.createObjectURL(stream)) || stream; - localMediaStream = stream; - video.play(); - $timeout(_scan, 1000); - }; - - var _videoError = function(err) { - $scope.cancel(); - }; - - var setScanner = function() { - navigator.getUserMedia = navigator.getUserMedia || - navigator.webkitGetUserMedia || navigator.mozGetUserMedia || - navigator.msGetUserMedia; - window.URL = window.URL || window.webkitURL || - window.mozURL || window.msURL; - }; - - $scope.init = function() { - if (platformInfo.isCordova) scannerInit(); - else checkTips(); - }; - - function checkTips() { - //TODO addapt tips to the new QR plugin (mobile) - storageService.getScanTipsAccepted(function(err, accepted) { - if (err) $log.warn(err); - if (accepted) { - scannerInit(); - return; - } - - $timeout(function() { - $ionicModal.fromTemplateUrl('views/modals/scan-tips.html', { - scope: $scope - }).then(function(modal) { - $scope.scanTipsModal = modal; - $scope.scanTipsModal.show(); - }); - }, 1000); - }); - }; - - $scope.$on('TipsModalClosed', function(event) { - scannerInit(); - }); - - function scannerInit() { - setScanner(); - $timeout(function() { - if ($scope.beforeScan) { - $scope.beforeScan(); - } - canvas = document.getElementById('qr-canvas'); - context = canvas.getContext('2d'); - - video = document.getElementById('qrcode-scanner-video'); - $video = angular.element(video); - canvas.width = 300; - canvas.height = 225; - context.clearRect(0, 0, 300, 225); - - navigator.getUserMedia({ - video: true - }, _successCallback, _videoError); - }, 500); - }; - - $scope.cancel = function() { - _scanStop(); - $scope.scannerModal.hide(); - $scope.scannerModal.remove(); - }; - -}); diff --git a/www/views/modals/scanner.html b/www/views/modals/scanner.html deleted file mode 100644 index 1a92705c6..000000000 --- a/www/views/modals/scanner.html +++ /dev/null @@ -1,12 +0,0 @@ - - - -

QR-Scanner

-
- - - - -
From c0d4fbe5bb11a5c22c8d5f0e0ddc307c3589cdcb Mon Sep 17 00:00:00 2001 From: Jason Dreyzehner Date: Tue, 11 Oct 2016 02:59:21 -0400 Subject: [PATCH 5/5] feat(scan): first iteration of full scan view implementation --- src/js/controllers/tab-scan.js | 91 ++++++++++++++++++++----------- src/js/directives/qrScanner.js | 7 ++- src/js/services/scannerService.js | 48 ++++++++++++---- src/sass/views/tab-scan.scss | 6 +- www/views/tab-scan.html | 8 +-- 5 files changed, 108 insertions(+), 52 deletions(-) diff --git a/src/js/controllers/tab-scan.js b/src/js/controllers/tab-scan.js index 0f16ea580..3d95c3a25 100644 --- a/src/js/controllers/tab-scan.js +++ b/src/js/controllers/tab-scan.js @@ -23,57 +23,71 @@ angular.module('copayApp.controllers').controller('tabScanController', function( } function _handleCapabilities(){ - if(!$scope.scannerIsAvailable){ - $scope.currentState = scannerStates.unavailable; - } else if($scope.scannerIsDenied){ - $scope.currentState = scannerStates.denied; - } else if($scope.scannerIsRestricted){ - $scope.currentState = scannerStates.denied; - } else if(!$scope.scannerHasPermission){ - $scope.currentState = scannerStates.unauthorized; - } else if($scope.scannerHasPermission){ + // always update the view + $timeout(function(){ + if(!scannerService.isInitialized()){ + $scope.currentState = scannerStates.loading; + } else if(!$scope.scannerIsAvailable){ + $scope.currentState = scannerStates.unavailable; + } else if($scope.scannerIsDenied){ + $scope.currentState = scannerStates.denied; + } 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(); } } - function _initScanView(){ - _updateCapabilities(); - _handleCapabilities(); - } - - $scope.$on("$ionicView.beforeEnter", function() { - $scope.currentState = scannerStates.loading; - }); - // This could be much cleaner with a Promise API // (needs a polyfill for some platforms) $rootScope.$on('scannerServiceInitialized', function(){ $log.debug('Scanner initialization finished, reinitializing scan view...'); - _initScanView(); + _refreshScanView(); }); $scope.$on("$ionicView.afterEnter", function() { - if(scannerService.isInitialized()){ - _initScanView(); - } + // try initializing and refreshing status any time the view is entered + scannerService.gentleInitialize(); }); function activate(){ scannerService.activate(function(){ + _updateCapabilities(); + _handleCapabilities(); $log.debug('Scanner activated, setting to visible...'); $scope.currentState = scannerStates.visible; - scannerService.scan(function(err, contents){ - if(err){ - $log.debug('Scan canceled.'); - } else if ($state.params.passthroughMode) { - $rootScope.scanResult = contents; - goBack(); - } else { - handleSuccessfulScan(contents); - } - }); + // pause to update the view + $timeout(function(){ + scannerService.scan(function(err, contents){ + if(err){ + $log.debug('Scan canceled.'); + } else if ($state.params.passthroughMode) { + $rootScope.scanResult = contents; + goBack(); + } else { + handleSuccessfulScan(contents); + } + }); + }); }); } + $scope.activate = activate; + + $scope.authorize = function(){ + scannerService.initialize(function(){ + _refreshScanView(); + }); + }; $scope.$on("$ionicView.afterLeave", function() { scannerService.deactivate(); @@ -84,6 +98,14 @@ angular.module('copayApp.controllers').controller('tabScanController', function( incomingData.redir(contents); } + $scope.openSettings = function(){ + scannerService.openSettings(); + }; + + $scope.attemptToReactivate = function(){ + scannerService.reinitialize(); + }; + $scope.toggleLight = function(){ scannerService.toggleLight(function(lightEnabled){ $scope.lightActive = lightEnabled; @@ -98,7 +120,7 @@ angular.module('copayApp.controllers').controller('tabScanController', function( $timeout(function(){ $scope.cameraToggleActive = false; $log.debug('Camera toggle control deactivated.'); - }, 600); + }, 200); }); }; @@ -106,6 +128,9 @@ angular.module('copayApp.controllers').controller('tabScanController', function( return $state.params.passthroughMode; } function goBack(){ + $ionicHistory.nextViewOptions({ + disableAnimate: true + }); $ionicHistory.backView().go(); } $scope.goBack = goBack; diff --git a/src/js/directives/qrScanner.js b/src/js/directives/qrScanner.js index 0a23819a5..131e48fde 100644 --- a/src/js/directives/qrScanner.js +++ b/src/js/directives/qrScanner.js @@ -1,7 +1,7 @@ 'use strict'; angular.module('copayApp.directives') - .directive('qrScanner', function($state, $rootScope, $log) { + .directive('qrScanner', function($state, $rootScope, $log, $ionicHistory) { return { restrict: 'E', @@ -9,11 +9,14 @@ angular.module('copayApp.directives') onScan: "&" }, replace: true, - template: '', + template: '', link: function(scope, el, attrs) { scope.openScanner = function() { $log.debug('Opening scanner by directive...'); + $ionicHistory.nextViewOptions({ + disableAnimate: true + }); $state.go('scanner', { passthroughMode: 1 }); }; diff --git a/src/js/services/scannerService.js b/src/js/services/scannerService.js index 09df4dfee..3baff576a 100644 --- a/src/js/services/scannerService.js +++ b/src/js/services/scannerService.js @@ -8,9 +8,8 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti var backCamera = true; // the plugin defaults to the back camera // Initalize known capabilities - var isAvailable = false; - var hasPermission = isDesktop? true: false; - var isAuthorized = false; + var isAvailable = isDesktop? false: true; // assume camera exists on mobile + var hasPermission = isDesktop? true: false; // assume desktop has permission var isDenied = false; var isRestricted = 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 * 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. */ this.gentleInitialize = function(callback) { + if(initializeStarted){ + 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.'); - _initalize(callback); + initialize(callback); } else { $log.debug('QRScanner not authorized, waiting to initalize.'); - if(typeof callback === "function"){ - callback && callback(status); - } + _completeInitialization(status, callback); } }); } else { $log.debug('Camera permission assumed on desktop.'); - _initalize(callback); + initialize(callback); } }; - function _initalize(callback){ + function initialize(callback){ $log.debug('Initializing scanner...'); QRScanner.prepare(function(err, status){ 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 // (needs a polyfill for some platforms) var initializeCompleted = false; function _completeInitialization(status, callback){ _checkCapabilities(status); - $rootScope.$emit('scannerServiceInitialized'); initializeCompleted = true; - callback && callback(status); + $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; @@ -132,7 +143,9 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti $log.debug('Activating scanner...'); QRScanner.show(function(status){ _checkCapabilities(status); - callback(status); + if(typeof callback === "function"){ + callback(status); + } }); if(nextHide !== null){ $timeout.cancel(nextHide); @@ -183,6 +196,12 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti QRScanner.destroy(); } + this.reinitialize = function(callback){ + initializeCompleted = false; + QRScanner.destroy(); + initialize(callback); + } + /** * Toggle the device light (if available). * @@ -228,4 +247,9 @@ angular.module('copayApp.services').service('scannerService', function($log, $ti callback(status); }); }; + + this.openSettings = function() { + $log.debug('Attempting to open device settings...'); + QRScanner.openSettings(); + }; }); diff --git a/src/sass/views/tab-scan.scss b/src/sass/views/tab-scan.scss index ad6898c02..9fe049233 100644 --- a/src/sass/views/tab-scan.scss +++ b/src/sass/views/tab-scan.scss @@ -4,6 +4,9 @@ $scannerBackgroundColor: #060d2d; color: #fff; text-align: center; background: transparent none; + .bar-header { + opacity: .9; + } &-has-problems, &-loading-camera { background-color: $scannerBackgroundColor; @@ -50,7 +53,8 @@ $scannerBackgroundColor: #060d2d; } } &-loading-camera { - + height: 100%; + width: 100% } &-camera-ready { // view background is transparent to show video preview diff --git a/www/views/tab-scan.html b/www/views/tab-scan.html index 288e15915..d75188b4d 100644 --- a/www/views/tab-scan.html +++ b/www/views/tab-scan.html @@ -10,7 +10,7 @@
- +
Scan QR Codes
You can scan bitcoin addresses, payment requests, paper wallets, and more.
@@ -18,9 +18,9 @@
Enable the camera to get started.
Enable camera access in your device settings to get started.
Please connect a camera to get started.
- - - + + +