diff --git a/src/js/controllers/tab-scan.js b/old/tab-scan.js
similarity index 98%
rename from src/js/controllers/tab-scan.js
rename to old/tab-scan.js
index 00f6045e7..423fc30d9 100644
--- a/src/js/controllers/tab-scan.js
+++ b/old/tab-scan.js
@@ -1,6 +1,6 @@
'use strict';
-angular.module('copayApp.controllers').controller('tabScanController', function($scope, $timeout, $ionicModal, $log, $ionicPopup, configService, gettextCatalog, platformInfo, bitcore, lodash, $state, walletService) {
+angular.module('copayApp.controllers').controller('tabScanController', function($scope, $timeout, $ionicModal, $log, $ionicPopup, configService, gettextCatalog, platformInfo, bitcore, lodash, $state, incomingData) {
var isCordova = platformInfo.isCordova;
var isWP = platformInfo.isWP;
@@ -22,7 +22,7 @@ angular.module('copayApp.controllers').controller('tabScanController', function(
var _dataScanned = function(data) {
$log.debug('Scanned:' + data);
- if (!walletService.redirFromUri(data)) {
+ if (!incomingData.redir(data)) {
$log.warn('Fail to process scanned data');
_showAlert('Bad bitcoin address', 'Could not recognize the bitcoin address', function(res) {
$scope.init();
@@ -84,7 +84,6 @@ angular.module('copayApp.controllers').controller('tabScanController', function(
}
};
-
// QR code Scanner
var video;
var canvas;
diff --git a/public/views/modals/addressbook.html b/public/views/modals/addressbook.html
index 0b2662b80..fd800ca42 100644
--- a/public/views/modals/addressbook.html
+++ b/public/views/modals/addressbook.html
@@ -63,7 +63,7 @@
ng-show="addressbookForm.address.$invalid && addressbookEntry.address">
-
+
+
QR-Scanner
-
+
diff --git a/public/views/tabs.html b/public/views/tabs.html
index bde3d6e96..d6a5c5ff9 100644
--- a/public/views/tabs.html
+++ b/public/views/tabs.html
@@ -3,7 +3,7 @@ Create tabs with an icon and label, using the tabs-positive style.
Each tab's child directive will have its own
navigation history that also transitions its views in and out.
-->
-
+
@@ -14,11 +14,13 @@ navigation history that also transitions its views in and out.
-
-
-
+
+
+
+
+
diff --git a/src/js/controllers/join.js b/src/js/controllers/join.js
index 3c1650d83..4afdbd963 100644
--- a/src/js/controllers/join.js
+++ b/src/js/controllers/join.js
@@ -1,7 +1,7 @@
'use strict';
angular.module('copayApp.controllers').controller('joinController',
- function($scope, $rootScope, $timeout, $state, profileService, configService, storageService, applicationService, gettext, lodash, ledger, trezor, platformInfo, derivationPathHelper, ongoingProcess, walletService, $log) {
+ function($scope, $rootScope, $timeout, $state, profileService, configService, storageService, applicationService, gettext, lodash, ledger, trezor, platformInfo, derivationPathHelper, ongoingProcess, walletService, $log, $stateParams) {
var isChromeApp = platformInfo.isChromeApp;
var isDevel = platformInfo.isDevel;
@@ -12,12 +12,22 @@ angular.module('copayApp.controllers').controller('joinController',
$scope.derivationPath = derivationPathHelper.default;
$scope.account = 1;
+
this.onQrCodeScanned = function(data) {
+console.log('[join.js.16:data:]',data); //TODO
$scope.secret = data;
- $scope.joinForm.secret.$setViewValue(data);
- $scope.joinForm.secret.$render();
+ if ($scope.joinForm) {
+ $scope.joinForm.secret.$setViewValue(data);
+ $scope.joinForm.secret.$render();
+ }
};
+ if ($stateParams.url) {
+ var data = $stateParams.url;
+ data = data.replace('copay:', '');
+ this.onQrCodeScanned(data);
+ }
+
var updateSeedSourceSelect = function() {
self.seedOptions = [{
id: 'new',
diff --git a/src/js/controllers/modals/scanner.js b/src/js/controllers/modals/scanner.js
new file mode 100644
index 000000000..c1bccb708
--- /dev/null
+++ b/src/js/controllers/modals/scanner.js
@@ -0,0 +1,102 @@
+'use strict';
+
+angular.module('copayApp.controllers').controller('scannerController', function($scope, $timeout) {
+
+ // 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() {
+ 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/src/js/controllers/tab-send.js b/src/js/controllers/tab-send.js
index 904b27c46..d73272e5a 100644
--- a/src/js/controllers/tab-send.js
+++ b/src/js/controllers/tab-send.js
@@ -1,6 +1,6 @@
'use strict';
-angular.module('copayApp.controllers').controller('tabSendController', function($scope, $ionicModal, $log, $timeout, addressbookService, profileService, lodash, $state, walletService, bitcore ) {
+angular.module('copayApp.controllers').controller('tabSendController', function($scope, $ionicModal, $log, $timeout, addressbookService, profileService, lodash, $state, walletService, incomingData ) {
var originalList;
@@ -55,16 +55,8 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
$scope.findContact = function(search, opts) {
opts = opts || {};
- if (search.indexOf('bitcoin:') === 0) {
- if (!walletService.redirFromUri(search)) {
- $log.error(err);
- }
- } else if (/^https?:\/\//.test(search)) {
- return $state.go('send.confirm', {paypro: search})
- } else if (bitcore.Address.isValid(search, 'livenet')) {
- return $state.go('send.amount', {toAddress: search})
- } else if (bitcore.Address.isValid(search, 'testnet')) {
- return $state.go('send.amount', {toAddress: search})
+ if (incomingData.redir(search)) {
+ return;
}
if (!search || search.length < 2) {
diff --git a/src/js/controllers/tabsController.js b/src/js/controllers/tabsController.js
new file mode 100644
index 000000000..b9114db36
--- /dev/null
+++ b/src/js/controllers/tabsController.js
@@ -0,0 +1,21 @@
+
+'use strict';
+
+angular.module('copayApp.controllers').controller('tabsController', function($log, $scope, $ionicModal, incomingData) {
+
+ $scope.onScan = function(data) {
+console.log('[tabsController.js.6:data:]',data); //TODO
+ if (!incomingData.redir(data)) {
+ $ionicPopup.alert({
+ title: 'Invalid data',
+ });
+ }
+ }
+
+ $scope.setScanFn = function(scanFn) {
+ $scope.scan = function() {
+ $log.debug('Scanning...');
+ scanFn();
+ };
+ };
+ });
diff --git a/src/js/directives/qrScanner.js b/src/js/directives/qrScanner.js
index b29290209..61fcde64c 100644
--- a/src/js/directives/qrScanner.js
+++ b/src/js/directives/qrScanner.js
@@ -1,16 +1,77 @@
'use strict';
angular.module('copayApp.directives')
- .directive('qrScanner', function() {
+ .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});
+ };
return {
restrict: 'E',
scope: {
onScan: "&",
+ setFn: "&",
beforeScan: "&"
},
- controller: 'tabScanController',
+ controller: controller,
replace: true,
- template: ''
+ template: ''
}
});
diff --git a/src/js/routes.js b/src/js/routes.js
index 2daf19559..04b4d043a 100644
--- a/src/js/routes.js
+++ b/src/js/routes.js
@@ -313,14 +313,6 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
}
})
- .state('tabs.scan', {
- url: '/scan',
- views: {
- 'tab-scan': {
- templateUrl: 'views/tab-scan.html',
- }
- }
- })
.state('tabs.send', {
url: '/send',
views: {
@@ -393,7 +385,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
}
})
.state('add.join', {
- url: '/join',
+ url: '/join/:url',
views: {
'add': {
templateUrl: 'views/join.html'
diff --git a/src/js/services/incomingData.js b/src/js/services/incomingData.js
new file mode 100644
index 000000000..6daa50306
--- /dev/null
+++ b/src/js/services/incomingData.js
@@ -0,0 +1,87 @@
+'use strict';
+
+angular.module('copayApp.services').factory('incomingData', function($log, $ionicModal, $state, bitcore) {
+
+ var root = {};
+
+ root.redir = function(data) {
+ $log.debug('Processing incoming data:' +data);
+
+ function sanitizeUri(data) {
+ // Fixes when a region uses comma to separate decimals
+ var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
+ var match = regex.exec(data);
+ if (!match || match.length === 0) {
+ return data;
+ }
+ var value = match[0].replace(',', '.');
+ var newUri = data.replace(regex, value);
+
+ // mobile devices, uris like copay://glidera
+ newUri.replace('://', ':');
+
+ return newUri;
+ };
+
+ // data extensions for Payment Protocol with non-backwards-compatible request
+ if ((/^bitcoin:\?r=[\w+]/).exec(data)) {
+ data = decodeURIComponent(data.replace('bitcoin:?r=', ''));
+ $state.go('send.confirm', {paypro: data})
+ }
+
+
+ data = sanitizeUri(data);
+
+ // BIP21
+ if (bitcore.URI.isValid(data)) {
+ var parsed = new bitcore.URI(data);
+
+ var addr = parsed.address ? parsed.address.toString() : '';
+ var message = parsed.message;
+
+ var amount = parsed.amount ? parsed.amount : '';
+
+ if (parsed.r) {
+ $state.go('send.confirm', {paypro: parsed.r});
+ } else {
+ if (amount) {
+ $state.go('send.confirm', {toAmount: amount, toAddress: addr, description:message})
+ } else {
+ $state.go('send.amount', {toAddress: addr})
+ }
+ }
+ return true;
+
+ // Plain URL
+ } else if (/^https?:\/\//.test(data)) {
+ return $state.go('send.confirm', {paypro: data})
+
+ // Plain Address
+ } else if (bitcore.Address.isValid(data, 'livenet')) {
+ return $state.go('send.amount', {toAddress: data})
+ } else if (bitcore.Address.isValid(data, 'testnet')) {
+ return $state.go('send.amount', {toAddress: data})
+
+
+ // copay: protocol
+ } else if (data.indexOf('copay:glidera')==0) {
+ return $state.go('send.uriglidera', {url: data})
+ } else if (data.indexOf('copay:coinbase')==0) {
+ return $state.go('send.uricoinbase', {url: data})
+
+ // Join
+ } else if (data.match(/^copay:[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
+ return $state.go('add.join', {url: data})
+
+ // Old join
+ } else if (data.match(/^[0-9A-HJ-NP-Za-km-z]{70,80}$/)) {
+ return $state.go('add.join', {url: data})
+ }
+
+
+ return false;
+
+ };
+
+ return root;
+});
diff --git a/src/js/services/openURL.js b/src/js/services/openURL.js
index 084126a57..a384214f3 100644
--- a/src/js/services/openURL.js
+++ b/src/js/services/openURL.js
@@ -1,37 +1,17 @@
'use strict';
-angular.module('copayApp.services').factory('openURLService', function($rootScope, $ionicHistory, $document, $log, $state, go, platformInfo, lodash, profileService) {
+angular.module('copayApp.services').factory('openURLService', function($rootScope, $ionicHistory, $document, $log, $state, go, platformInfo, lodash, profileService, incomingData) {
var root = {};
- root.registeredUriHandlers = [{
- name: 'Bitcoin BIP21 URL',
- startsWith: 'bitcoin:',
- transitionTo: 'uripayment',
- }, {
- name: 'Glidera Authentication Callback',
- startsWith: 'copay:glidera',
- transitionTo: 'uriglidera',
- }, {
- name: 'Coinbase Authentication Callback',
- startsWith: 'copay:coinbase',
- transitionTo: 'uricoinbase',
- }];
-
-
var handleOpenURL = function(args) {
$log.info('Handling Open URL: ' + JSON.stringify(args));
- if (!profileService.isBound) {
- $log.warn('Profile not bound yet. Waiting');
-
- return $rootScope.$on('Local/ProfileBound', function() {
- // Wait ux to settle
- setTimeout(function() {
- $log.warn('Profile ready, retrying...');
- handleOpenURL(args);
- }, 2000);
- });
- };
+ profileService.whenAvailable(function() {
+ // Wait ux to settle
+ setTimeout(function() {
+ handleOpenURL(args);
+ }, 2000);
+ });
// Stop it from caching the first view as one to return when the app opens
$ionicHistory.nextViewOptions({
@@ -55,19 +35,7 @@ angular.module('copayApp.services').factory('openURLService', function($rootScop
document.addEventListener('handleopenurl', handleOpenURL, false);
- var x = lodash.find(root.registeredUriHandlers, function(x) {
- return url.indexOf(x.startsWith) == 0 ||
- url.indexOf('web+' + x.startsWith) == 0 || // web protocols
- url.indexOf(x.startsWith.replace(':', '://')) == 0 // from mobile devices
- ;
- });
-
- if (x) {
- $log.debug('openURL GOT ' + x.name + ' URL');
- return $state.transitionTo(x.transitionTo, {
- url: url
- });
- } else {
+ if (!incomingData.redir(url)) {
$log.warn('Unknown URL! : ' + url);
}
};
diff --git a/src/js/services/walletService.js b/src/js/services/walletService.js
index 8cc238a31..c3c1c52cc 100644
--- a/src/js/services/walletService.js
+++ b/src/js/services/walletService.js
@@ -1123,50 +1123,7 @@ angular.module('copayApp.services').factory('walletService', function($log, $tim
return finale;
};
- // Here?
- root.redirFromUri = function(uri) {
- function sanitizeUri(uri) {
- // Fixes when a region uses comma to separate decimals
- var regex = /[\?\&]amount=(\d+([\,\.]\d+)?)/i;
- var match = regex.exec(uri);
- if (!match || match.length === 0) {
- return uri;
- }
- var value = match[0].replace(',', '.');
- var newUri = uri.replace(regex, value);
- return newUri;
- };
-
- // URI extensions for Payment Protocol with non-backwards-compatible request
- if ((/^bitcoin:\?r=[\w+]/).exec(uri)) {
- uri = decodeURIComponent(uri.replace('bitcoin:?r=', ''));
- $state.go('send.confirm', {paypro: uri})
- } else {
- uri = sanitizeUri(uri);
-
- if (!bitcore.URI.isValid(uri)) {
- return false;
- }
- var parsed = new bitcore.URI(uri);
-
- var addr = parsed.address ? parsed.address.toString() : '';
- var message = parsed.message;
-
- var amount = parsed.amount ? parsed.amount : '';
-
- if (parsed.r) {
- $state.go('send.confirm', {paypro: parsed.r});
- } else {
- if (amount) {
- $state.go('send.confirm', {toAmount: amount, toAddress: addr, description:message})
- } else {
- $state.go('send.amount', {toAddress: addr})
- }
- }
- return true;
- }
- };
return root;
});