From 81b3c2930db333e82bfa820314212c868da0239e Mon Sep 17 00:00:00 2001 From: Gabriel Masclef Date: Tue, 22 Aug 2017 17:52:31 -0300 Subject: [PATCH] Feat: QR Scanner implemented --- package.json | 223 +++++++++--------- src/app/app.module.ts | 9 +- .../img/bitpay-wallet-qr-scan-guides.svg | 37 +++ src/assets/img/icon-camera-toggle.svg | 16 ++ src/assets/img/icon-flash.svg | 9 + src/index.html | 6 +- src/pages/scan/scan.html | 33 +++ src/pages/scan/scan.scss | 86 +++++++ src/pages/scan/scan.ts | 44 ++++ src/pages/tabs/tabs.html | 1 + src/pages/tabs/tabs.ts | 2 + .../persistence/storage/chrome-storage.ts | 1 + src/providers/platform/platform.ts | 2 + src/providers/scan/scan.ts | 44 ++++ 14 files changed, 400 insertions(+), 113 deletions(-) create mode 100644 src/assets/img/bitpay-wallet-qr-scan-guides.svg create mode 100644 src/assets/img/icon-camera-toggle.svg create mode 100644 src/assets/img/icon-flash.svg create mode 100644 src/pages/scan/scan.html create mode 100644 src/pages/scan/scan.scss create mode 100644 src/pages/scan/scan.ts create mode 100644 src/providers/scan/scan.ts diff --git a/package.json b/package.json index 80b136dc9..36b66c055 100644 --- a/package.json +++ b/package.json @@ -1,111 +1,116 @@ { - "name": "copay", - "description": "Copay Bitcoin Wallet", - "version": "0.0.1", - "author": "BitPay", - "homepage": "https://copay.io/", - "private": true, - "scripts": { - "clean": "ionic-app-scripts clean", - "clean-all": "git clean -dfx", - "build": "ionic-app-scripts build", - "lint": "ionic-app-scripts lint", - "ionic:build": "ionic-app-scripts build", - "ionic:serve": "ionic-app-scripts serve", - "test": "karma start ./test-config/karma.conf.js", - "test-ci": "karma start ./test-config/karma.conf.js --single-run", - "extract": "ngx-translate-extract --input ./src --output ./src/assets/i18n/app.pot --clean --sort --format pot" - }, - "dependencies": { - "@angular/common": "4.1.3", - "@angular/compiler": "4.1.3", - "@angular/compiler-cli": "4.1.3", - "@angular/core": "4.1.3", - "@angular/forms": "4.1.3", - "@angular/http": "4.1.3", - "@angular/platform-browser": "4.1.3", - "@angular/platform-browser-dynamic": "4.1.3", - "@angular/tsc-wrapped": "4.1.3", - "@biesbjerg/ngx-translate-po-http-loader": "1.0.1", - "@ionic-native/clipboard": "4.1.0", - "@ionic-native/core": "3.12.1", - "@ionic-native/file": "4.1.0", - "@ionic-native/social-sharing": "4.1.0", - "@ionic-native/splash-screen": "3.12.1", - "@ionic-native/status-bar": "3.12.1", - "@ionic-native/toast": "4.1.0", - "@ionic/storage": "2.0.1", - "@ngx-translate/core": "6.0.1", - "@nsalaun/ng-logger": "2.0.1", - "ajv": "5.2.2", - "autoprefixer": "7.1.2", - "cordova-clipboard": "1.0.0", - "cordova-ios": "4.4.0", - "cordova-plugin-console": "1.0.5", - "cordova-plugin-device": "1.1.4", - "cordova-plugin-splashscreen": "4.0.3", - "cordova-plugin-statusbar": "2.2.2", - "cordova-plugin-whitelist": "1.3.1", - "cordova-plugin-x-socialsharing": "5.1.8", - "cordova-plugin-x-toast": "2.6.0", - "cordova-sqlite-storage": "2.0.4", - "es6-promise-plugin": "4.1.0", - "ionic-angular": "3.6.0", - "ionic-plugin-keyboard": "2.2.1", - "ionicons": "3.0.0", - "lodash": "4.17.4", - "ngx-clipboard": "8.0.3", - "ngx-qrcode2": "0.0.3", - "rxjs": "5.4.0", - "sw-toolbox": "3.6.0", - "zone.js": "0.8.12" - }, - "devDependencies": { - "@angular/cli": "1.3.0", - "@biesbjerg/ngx-translate-extract": "2.3.2", - "@ionic/app-scripts": "2.1.4", - "@types/chrome": "0.0.47", - "@types/jasmine": "2.5.53", - "@types/lodash": "4.14.71", - "@types/node": "8.0.19", - "angular2-template-loader": "0.6.2", - "codecov": "2.2.0", - "html-loader": "0.4.5", - "ionic": "3.9.2", - "jasmine-core": "2.6.4", - "jasmine-spec-reporter": "4.1.1", - "karma": "1.7.0", - "karma-chrome-launcher": "2.2.0", - "karma-jasmine": "1.1.0", - "karma-jasmine-html-reporter": "0.2.2", - "karma-mocha-reporter": "2.2.3", - "karma-phantomjs-launcher": "1.0.4", - "karma-remap-istanbul": "0.6.0", - "karma-sourcemap-loader": "0.3.7", - "karma-webpack": "2.0.4", - "null-loader": "0.1.1", - "ts-loader": "2.0.3", - "ts-node": "3.3.0", - "tslint": "5.5.0", - "tslint-eslint-rules": "4.1.1", - "typescript": "2.3.4", - "webpack": "3.4.1" - }, - "cordova": { - "plugins": { - "cordova-sqlite-storage": {}, - "cordova-plugin-console": {}, - "cordova-plugin-device": {}, - "cordova-plugin-splashscreen": {}, - "cordova-plugin-statusbar": {}, - "cordova-plugin-whitelist": {}, - "ionic-plugin-keyboard": {}, - "cordova-clipboard": {}, - "cordova-plugin-x-toast": {}, - "cordova-plugin-x-socialsharing": {} + "name": "copay", + "description": "Copay Bitcoin Wallet", + "version": "0.0.1", + "author": "BitPay", + "homepage": "https://copay.io/", + "private": true, + "scripts": { + "clean": "ionic-app-scripts clean", + "clean-all": "git clean -dfx", + "build": "ionic-app-scripts build", + "lint": "ionic-app-scripts lint", + "ionic:build": "ionic-app-scripts build", + "ionic:serve": "ionic-app-scripts serve", + "test": "karma start ./test-config/karma.conf.js", + "test-ci": "karma start ./test-config/karma.conf.js --single-run", + "extract": "ngx-translate-extract --input ./src --output ./src/assets/i18n/app.pot --clean --sort --format pot" }, - "platforms": [ - "ios" - ] - } -} + "dependencies": { + "@angular/common": "4.1.3", + "@angular/compiler": "4.1.3", + "@angular/compiler-cli": "4.1.3", + "@angular/core": "4.1.3", + "@angular/forms": "4.1.3", + "@angular/http": "4.1.3", + "@angular/platform-browser": "4.1.3", + "@angular/platform-browser-dynamic": "4.1.3", + "@angular/tsc-wrapped": "4.1.3", + "@biesbjerg/ngx-translate-po-http-loader": "1.0.1", + "@ionic-native/clipboard": "4.1.0", + "@ionic-native/core": "3.12.1", + "@ionic-native/file": "4.1.0", + "@ionic-native/qr-scanner": "^4.1.0", + "@ionic-native/social-sharing": "4.1.0", + "@ionic-native/splash-screen": "3.12.1", + "@ionic-native/status-bar": "3.12.1", + "@ionic-native/toast": "4.1.0", + "@ionic/storage": "2.0.1", + "@ngx-translate/core": "6.0.1", + "@nsalaun/ng-logger": "2.0.1", + "ajv": "5.2.2", + "autoprefixer": "7.1.2", + "cordova-clipboard": "1.0.0", + "cordova-ios": "4.4.0", + "cordova-plugin-console": "1.0.5", + "cordova-plugin-device": "1.1.4", + "cordova-plugin-qrscanner": "^2.5.0", + "cordova-plugin-splashscreen": "4.0.3", + "cordova-plugin-statusbar": "2.2.2", + "cordova-plugin-whitelist": "1.3.1", + "cordova-plugin-x-socialsharing": "5.1.8", + "cordova-plugin-x-toast": "2.6.0", + "cordova-sqlite-storage": "2.0.4", + "es6-promise-plugin": "4.1.0", + "ionic-angular": "3.6.0", + "ionic-plugin-keyboard": "2.2.1", + "ionicons": "3.0.0", + "lodash": "4.17.4", + "ngx-clipboard": "8.0.3", + "ngx-qrcode2": "0.0.3", + "rxjs": "5.4.0", + "sw-toolbox": "3.6.0", + "zone.js": "0.8.12", + "cordova-android": "^6.2.3" + }, + "devDependencies": { + "@angular/cli": "1.3.0", + "@biesbjerg/ngx-translate-extract": "2.3.2", + "@ionic/app-scripts": "2.1.4", + "@types/chrome": "0.0.47", + "@types/jasmine": "2.5.53", + "@types/lodash": "4.14.71", + "@types/node": "8.0.19", + "angular2-template-loader": "0.6.2", + "codecov": "2.2.0", + "html-loader": "0.4.5", + "ionic": "3.9.2", + "jasmine-core": "2.6.4", + "jasmine-spec-reporter": "4.1.1", + "karma": "1.7.0", + "karma-chrome-launcher": "2.2.0", + "karma-jasmine": "1.1.0", + "karma-jasmine-html-reporter": "0.2.2", + "karma-mocha-reporter": "2.2.3", + "karma-phantomjs-launcher": "1.0.4", + "karma-remap-istanbul": "0.6.0", + "karma-sourcemap-loader": "0.3.7", + "karma-webpack": "2.0.4", + "null-loader": "0.1.1", + "ts-loader": "2.0.3", + "ts-node": "3.3.0", + "tslint": "5.5.0", + "tslint-eslint-rules": "4.1.1", + "typescript": "2.3.4", + "webpack": "3.4.1" + }, + "cordova": { + "plugins": { + "cordova-sqlite-storage": {}, + "cordova-plugin-console": {}, + "cordova-plugin-device": {}, + "cordova-plugin-splashscreen": {}, + "cordova-plugin-statusbar": {}, + "cordova-plugin-whitelist": {}, + "ionic-plugin-keyboard": {}, + "cordova-clipboard": {}, + "cordova-plugin-x-toast": {}, + "cordova-plugin-x-socialsharing": {}, + "cordova-plugin-qrscanner": {} + }, + "platforms": [ + "android", + "ios" + ] + } +} \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b31c20b45..3fdcea498 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -9,6 +9,7 @@ import { SplashScreen } from '@ionic-native/splash-screen'; import { Toast } from '@ionic-native/toast'; import { Clipboard } from '@ionic-native/clipboard'; import { SocialSharing } from '@ionic-native/social-sharing'; +import { QRScanner } from '@ionic-native/qr-scanner'; /* Modules */ import { NgLoggerModule, Logger, Level } from '@nsalaun/ng-logger'; @@ -22,6 +23,7 @@ import { CopayApp } from './app.component'; /* Pages */ import { HomePage } from '../pages/home/home'; import { ReceivePage } from '../pages/receive/receive'; +import { ScanPage } from '../pages/scan/scan'; import { SendPage } from '../pages/send/send'; import { SettingPage } from '../pages/setting/setting'; import { TabsPage } from '../pages/tabs/tabs'; @@ -37,6 +39,7 @@ import { PlatformProvider } from '../providers/platform/platform'; import { ConfigProvider } from '../providers/config/config'; import { LanguageProvider } from '../providers/language/language'; import { UnitProvider } from '../providers/unit/unit'; +import { ScanProvider } from '../providers/scan/scan'; export function createTranslateLoader(http: Http) { return new TranslatePoHttpLoader(http, 'assets/i18n', '.po'); @@ -48,6 +51,7 @@ export function createTranslateLoader(http: Http) { HomePage, ReceivePage, SendPage, + ScanPage, SettingPage, AboutPage, TermsOfUsePage, @@ -72,6 +76,7 @@ export function createTranslateLoader(http: Http) { CopayApp, HomePage, ReceivePage, + ScanPage, SendPage, SettingPage, AboutPage, @@ -105,7 +110,9 @@ export function createTranslateLoader(http: Http) { PlatformProvider, ConfigProvider, LanguageProvider, - UnitProvider + UnitProvider, + QRScanner, + ScanProvider ] }) export class AppModule { } diff --git a/src/assets/img/bitpay-wallet-qr-scan-guides.svg b/src/assets/img/bitpay-wallet-qr-scan-guides.svg new file mode 100644 index 000000000..07a700688 --- /dev/null +++ b/src/assets/img/bitpay-wallet-qr-scan-guides.svg @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/img/icon-camera-toggle.svg b/src/assets/img/icon-camera-toggle.svg new file mode 100644 index 000000000..1e1f06503 --- /dev/null +++ b/src/assets/img/icon-camera-toggle.svg @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/src/assets/img/icon-flash.svg b/src/assets/img/icon-flash.svg new file mode 100644 index 000000000..72c726a69 --- /dev/null +++ b/src/assets/img/icon-flash.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/index.html b/src/index.html index ebea7ff7a..37b780fb5 100644 --- a/src/index.html +++ b/src/index.html @@ -20,11 +20,11 @@ navigator.serviceWorker.register('service-worker.js') .then(() => console.log('service worker installed')) .catch(err => console.error('Error', err)); - } + } --> - + - + diff --git a/src/pages/scan/scan.html b/src/pages/scan/scan.html new file mode 100644 index 000000000..b86d0bf16 --- /dev/null +++ b/src/pages/scan/scan.html @@ -0,0 +1,33 @@ + + + + + Scan + + + + + + +

Scanner text: {{ text }}

+

Scanner visible: {{ scannerVisible }}

+
+ +
+ +
+
+ + + + + + +
+
+
diff --git a/src/pages/scan/scan.scss b/src/pages/scan/scan.scss new file mode 100644 index 000000000..d820cf02e --- /dev/null +++ b/src/pages/scan/scan.scss @@ -0,0 +1,86 @@ +$v-scanner-background-color: #1e3186; +$v-scanner-guide-color: #647ce8; + +html , body, .back-trasnparent, ion-app { + background: none !important; +} + +page-scan { + color: #fff; + text-align: center; + background: transparent none; + .header { + opacity: .9; + } + svg#QR-scanner-guides path.st1 { + stroke: $v-scanner-guide-color; + } + .zero-state-cta { + padding-bottom: 6vh; + } + #page-scan-has-problems, + #page-scan-loading-camera { + background-color: $v-scanner-background-color; + } + #page-scan-loading-camera { + height: 100%; + width: 100% + } + #page-scan-camera-ready { + // view background is transparent to show video preview + background: none transparent; + .scanner-controls { + width: 100%; + text-align: center; + bottom: 0; + position: absolute; + left: 0; + right: 0; + } + .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("assets/img/icon-flash.svg"); + } + .icon-camera-toggle { + background-image: url("assets/img/icon-camera-toggle.svg"); + } + } +} + +#cordova-plugin-qrscanner-still, #cordova-plugin-qrscanner-video-preview { + background-color: $v-scanner-background-color !important; +} diff --git a/src/pages/scan/scan.ts b/src/pages/scan/scan.ts new file mode 100644 index 000000000..e3b41c3f9 --- /dev/null +++ b/src/pages/scan/scan.ts @@ -0,0 +1,44 @@ +import { Component, OnInit } from '@angular/core'; +import { NavController, NavParams } from 'ionic-angular'; +import { QRScanner, QRScannerStatus } from '@ionic-native/qr-scanner'; +import { PlatformProvider } from '../../providers/platform/platform'; +import { ScanProvider } from '../../providers/scan/scan'; +//import { QRScanner as QRScannerBrowser } from 'cordova-plugin-qrscanner/src/browser/src/library' + +@Component({ + selector: 'page-scan', + templateUrl: 'scan.html', + providers: [ScanProvider] +}) +export class ScanPage implements OnInit { + + public text: string; + public scannerVisible: boolean; + public canEnableLight: boolean; + public canChangeCamera: boolean; + + // private qrScannerBrowser: QRScannerBrowser (inside constructor) + constructor(public navCtrl: NavController, public navParams: NavParams, private scanProvider: ScanProvider, private platform: PlatformProvider) { + this.text = "Codigo QR"; + this.canEnableLight = true; + this.canChangeCamera = true; + this.scannerVisible = this.platform.isCordova ? true : false; + } + + ionViewDidLoad() { + console.log('ionViewDidLoad ScanPage'); + } + + ngOnInit () { + this.scanProvider.activate() + .then(resp => { + console.log("resp"); + console.log(resp); + this.text = resp; + }) + .catch(error => { + console.log("error: " + error); + }); + } + +} diff --git a/src/pages/tabs/tabs.html b/src/pages/tabs/tabs.html index 7d7e56024..bcb5104fc 100644 --- a/src/pages/tabs/tabs.html +++ b/src/pages/tabs/tabs.html @@ -1,6 +1,7 @@ + diff --git a/src/pages/tabs/tabs.ts b/src/pages/tabs/tabs.ts index 65d36f2a2..163b393b4 100644 --- a/src/pages/tabs/tabs.ts +++ b/src/pages/tabs/tabs.ts @@ -2,6 +2,7 @@ import { Component } from '@angular/core'; import { HomePage } from '../home/home'; import { ReceivePage } from '../receive/receive'; +import { ScanPage } from '../scan/scan'; import { SendPage } from '../send/send'; import { SettingPage } from '../setting/setting'; @@ -12,6 +13,7 @@ export class TabsPage { homeRoot = HomePage; receiveRoot = ReceivePage; + scanRoot = ScanPage; sendRoot = SendPage; settingRoot = SettingPage; diff --git a/src/providers/persistence/storage/chrome-storage.ts b/src/providers/persistence/storage/chrome-storage.ts index e8abb004e..ce0cf0d6a 100644 --- a/src/providers/persistence/storage/chrome-storage.ts +++ b/src/providers/persistence/storage/chrome-storage.ts @@ -8,6 +8,7 @@ import { IStorage, KeyAlreadyExistsError } from './istorage'; export class ChromeStorage implements IStorage { ls: chrome.storage.StorageArea; constructor(private log: Logger) { + let chrome: any; if (!chrome.storage || !chrome.storage.local) throw new Error('Chrome storage not supported'); this.ls = chrome.storage.local; } diff --git a/src/providers/platform/platform.ts b/src/providers/platform/platform.ts index 88284a584..1bc3a2176 100644 --- a/src/providers/platform/platform.ts +++ b/src/providers/platform/platform.ts @@ -20,6 +20,7 @@ export class PlatformProvider { supportsIntelTEE: boolean; constructor(private platform: Platform, private log: Logger) { + let chrome: any; var ua = navigator ? navigator.userAgent : null; if (!ua) { @@ -46,6 +47,7 @@ export class PlatformProvider { } getBrowserName(): string { + let chrome: any; let userAgent = window.navigator.userAgent; let browsers = { chrome: /chrome/i, safari: /safari/i, firefox: /firefox/i, ie: /internet explorer/i }; diff --git a/src/providers/scan/scan.ts b/src/providers/scan/scan.ts new file mode 100644 index 000000000..afba70819 --- /dev/null +++ b/src/providers/scan/scan.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; +import { Http } from '@angular/http'; +import { QRScanner, QRScannerStatus } from '@ionic-native/qr-scanner'; +import { PlatformProvider } from '../platform/platform'; +import 'rxjs/add/operator/map'; + +/* + Generated class for the ScanProvider provider. + + See https://angular.io/docs/ts/latest/guide/dependency-injection.html + for more info on providers and Angular DI. +*/ +@Injectable() +export class ScanProvider { + + public text: string; + public scannerVisible: boolean; + + constructor(public http: Http, private qrScanner: QRScanner, private platform: PlatformProvider) { + this.scannerVisible = false; + } + + activate(): Promise { + return new Promise(resolve => { + if (this.platform.isCordova) { + console.log("Cordova device"); + this.qrScanner.show(); + this.scannerVisible = true; + // start scanning + let scanSub = this.qrScanner.scan().subscribe((text: string) => { + console.log('Scanned something' + text); + this.text = text; + resolve(this.text); + this.scannerVisible = false; + this.qrScanner.hide(); // hide camera preview + scanSub.unsubscribe(); // stop scanning + }); + } else { + resolve("ERROR - No Cordova device"); + } + }); + } + +}