Merge pull request #235 from bitjson/feature/new-qrscanner
Feature: New QRScanner
This commit is contained in:
commit
521e5f3618
27 changed files with 568 additions and 512 deletions
45
src/js/controllers/tab-scan.js
Normal file
45
src/js/controllers/tab-scan.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.controllers').controller('tabScanController', function($scope, $log, $timeout, scannerService, incomingData) {
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function() {
|
||||
$log.debug('Preparing to display available controls.');
|
||||
var capabilities = scannerService.getCapabilities();
|
||||
$scope.canEnableLight = capabilities.canEnableLight;
|
||||
$scope.canChangeCamera = capabilities.canChangeCamera;
|
||||
});
|
||||
|
||||
$scope.$on("$ionicView.afterEnter", function() {
|
||||
scannerService.activate(function(){
|
||||
scannerService.scan(function(err, contents){
|
||||
if(err){
|
||||
$log.debug('Scan canceled.');
|
||||
} else {
|
||||
incomingData.redir(contents);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
$scope.$on("$ionicView.afterLeave", function() {
|
||||
scannerService.deactivate();
|
||||
});
|
||||
|
||||
$scope.toggleLight = function(){
|
||||
scannerService.toggleLight(function(lightEnabled){
|
||||
$scope.lightActive = lightEnabled;
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.toggleCamera = function(){
|
||||
$scope.cameraToggleActive = true;
|
||||
scannerService.toggleCamera(function(status){
|
||||
// (a short delay for the user to see the visual feedback)
|
||||
$timeout(function(){
|
||||
$scope.cameraToggleActive = false;
|
||||
$log.debug('Camera toggle control deactivated.');
|
||||
}, 200);
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
|
@ -101,12 +101,6 @@ angular.module('copayApp.controllers').controller('tabSendController', function(
|
|||
});
|
||||
};
|
||||
|
||||
$scope.onQrCodeScanned = function(data) {
|
||||
if (!incomingData.redir(data)) {
|
||||
popupService.showAlert(null, gettextCatalog.getString('Invalid data'));
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$on("$ionicView.beforeEnter", function(event, data) {
|
||||
$scope.formData = {
|
||||
search: null
|
||||
|
|
|
|||
|
|
@ -208,6 +208,15 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.scan', {
|
||||
url: '/scan',
|
||||
views: {
|
||||
'tab-scan': {
|
||||
controller: 'tabScanController',
|
||||
templateUrl: 'views/tab-scan.html',
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('tabs.send', {
|
||||
url: '/send',
|
||||
views: {
|
||||
|
|
@ -875,7 +884,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
}
|
||||
});
|
||||
})
|
||||
.run(function($rootScope, $state, $location, $log, $timeout, $ionicHistory, $ionicPlatform, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService, storageService) {
|
||||
.run(function($rootScope, $state, $location, $log, $timeout, $ionicHistory, $ionicPlatform, lodash, platformInfo, profileService, uxLanguage, gettextCatalog, openURLService, storageService, scannerService) {
|
||||
|
||||
uxLanguage.init();
|
||||
openURLService.init();
|
||||
|
|
@ -982,7 +991,7 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr
|
|||
} else {
|
||||
profileService.storeProfileIfDirty();
|
||||
$log.debug('Profile loaded ... Starting UX.');
|
||||
|
||||
scannerService.gentleInitialize();
|
||||
$state.go('tabs.home');
|
||||
}
|
||||
});
|
||||
|
|
|
|||
188
src/js/services/scannerService.js
Normal file
188
src/js/services/scannerService.js
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('copayApp.services').service('scannerService', function($log, $timeout, platformInfo) {
|
||||
|
||||
var isDesktop = !platformInfo.isCordova;
|
||||
var QRScanner = window.QRScanner;
|
||||
var lightEnabled = false;
|
||||
var backCamera = true; // the plugin defaults to the back camera
|
||||
|
||||
// Initalize known capabilities
|
||||
var hasPermission = isDesktop? true: false;
|
||||
var canEnableLight = false;
|
||||
var canChangeCamera = 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;
|
||||
canEnableLight = status.canEnableLight? true : false;
|
||||
canChangeCamera = status.canChangeCamera? true : false;
|
||||
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.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately return known capabilities of the current platform.
|
||||
*/
|
||||
this.getCapabilities = function(){
|
||||
return {
|
||||
hasPermission: hasPermission,
|
||||
canEnableLight: canEnableLight,
|
||||
canChangeCamera: canChangeCamera
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$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();
|
||||
} else {
|
||||
$log.debug('QRScanner not authorized, waiting to initalize.');
|
||||
if(typeof callback === "function"){
|
||||
callback && callback(status);
|
||||
}
|
||||
}
|
||||
});
|
||||
} 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);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var nextHide = null;
|
||||
var nextDestroy = null;
|
||||
var hideAfterSeconds = 15;
|
||||
var destroyAfterSeconds = 5 * 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){
|
||||
_checkCapabilities(status);
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
};
|
||||
});
|
||||
|
|
@ -1,13 +1,28 @@
|
|||
/* Set ionic variables */
|
||||
$font-family-sans-serif: "Roboto", sans-serif;
|
||||
$font-family-light-sans-serif: "Roboto-Light", sans-serif-light;
|
||||
/* constants */
|
||||
$royal: #1e3186;
|
||||
$soft-blue: rgb(100,124,232);
|
||||
$base-background-color: #f5f5f5;
|
||||
$soft-blue: #647ce8;
|
||||
$subtle-gray: #f5f5f5;
|
||||
$roboto: "Roboto", sans-serif;
|
||||
$roboto-light: "Roboto-Light", sans-serif-light;
|
||||
|
||||
/* Ionic Workaround */
|
||||
/* Set ionic variables */
|
||||
$font-family-sans-serif: $roboto;
|
||||
$font-family-light-sans-serif: $roboto-light;
|
||||
$base-background-color: $subtle-gray;
|
||||
$item-default-active-bg: $subtle-gray;
|
||||
|
||||
$ios-transition-duration: 200ms;
|
||||
|
||||
/* Ionic Workarounds */
|
||||
// Please include a description of the problem solved by the workaround.
|
||||
|
||||
// class to dynamically hide the ion-nav-bar for v1 Amazon flow
|
||||
ion-nav-bar.hide { display: block !important; }
|
||||
|
||||
// the ion tabs element never needs it's own background (backgrounds are
|
||||
// rendered by the tabs), and the default background would cover the scanner
|
||||
ion-tabs.ion-tabs-transparent {
|
||||
background: none transparent;
|
||||
}
|
||||
|
||||
@import "../../bower_components/ionic/scss/ionic";
|
||||
|
|
|
|||
|
|
@ -438,363 +438,6 @@ ul.wallet-selection.wallets {
|
|||
}
|
||||
|
||||
// General purpose
|
||||
.dn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dni {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.pr {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.pa {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.m0 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.p0i {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.db {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dib {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.size-10 {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.size-12 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.size-14 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.size-16 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.size-18 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.size-21 {
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.size-24 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.size-28 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.size-32 {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.size-36 {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.size-42 {
|
||||
font-size: 42px;
|
||||
}
|
||||
|
||||
.size-48 {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.size-60 {
|
||||
font-size: 60px;
|
||||
}
|
||||
|
||||
.size-72 {
|
||||
font-size: 72px;
|
||||
}
|
||||
|
||||
.m5 {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.m5t {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.m8t {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.m5b {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.m5r {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.m10 {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.m10b {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.m3t {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.m10t {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.m15b {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.m15r {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.m20b {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.m30b {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.m40b {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.m50b {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.m10r {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.m40r {
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
.m55r {
|
||||
margin-right: 55px;
|
||||
}
|
||||
|
||||
.m25r {
|
||||
margin-right: 25px;
|
||||
}
|
||||
|
||||
.m10l {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.m5l {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.m15l {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.m15t {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.m20r {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.m20t {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.m20ti {
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
.m20tp {
|
||||
margin-top: 20%;
|
||||
}
|
||||
|
||||
.m30tp {
|
||||
margin-top: 30%;
|
||||
}
|
||||
|
||||
.m15 {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
.m15h {
|
||||
margin: 0 15px;
|
||||
}
|
||||
|
||||
.p10t {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.p10h {
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.p15h {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.p0r {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.p70r {
|
||||
padding-right: 70px;
|
||||
}
|
||||
|
||||
.p70l {
|
||||
padding-left: 70px;
|
||||
}
|
||||
|
||||
.p5h {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.p20h {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.p20v {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.p20b {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.p25b {
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
|
||||
.p25l {
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.p15l {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.p15 {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.p20 {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.p15t {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.p20t {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.p50t {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.p10 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.p10i {
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
.p10b {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.p45t {
|
||||
padding-top: 45px;
|
||||
}
|
||||
|
||||
.p60t {
|
||||
padding-top: 60px;
|
||||
}
|
||||
|
||||
.p60b {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
|
||||
.m60t {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.p45li {
|
||||
padding-left: 45px !important;
|
||||
}
|
||||
|
||||
.m30v {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.m15v {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.m10h {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.m10v {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.m20v {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.m30v {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.m30a {
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
.m-negative-l {
|
||||
margin-left: -0.9375rem;
|
||||
}
|
||||
|
||||
.br100 {
|
||||
border-radius: 100% !important;
|
||||
}
|
||||
|
||||
.lh {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.lh140 {
|
||||
line-height: 140%;
|
||||
}
|
||||
|
||||
.oh {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
|
@ -994,6 +637,7 @@ input[type=number] {
|
|||
@import "views/confirm";
|
||||
@import "views/tab-home";
|
||||
@import "views/tab-receive";
|
||||
@import "views/tab-scan";
|
||||
@import "views/tab-send";
|
||||
@import "views/tab-settings";
|
||||
@import "views/walletDetails";
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
#tab-home {
|
||||
.icon-create-wallet {
|
||||
background-image: url("../img/icon-bitcoin.svg");
|
||||
background-image: url("../img/icon-wallet.svg");
|
||||
background-color: #4A90E2; // default wallet color
|
||||
}
|
||||
.icon-buy-bitcoin {
|
||||
background-image: url("../img/icon-gift.svg");
|
||||
background-image: url("../img/icon-bitcoin.svg");
|
||||
}
|
||||
.icon-bitpay-card {
|
||||
background-image: url("../img/icon-bitpay.svg");
|
||||
background-image: url("../img/icon-card.svg");
|
||||
background-color: #1e3186;
|
||||
}
|
||||
.icon-gift {
|
||||
background-image: url("../img/icon-gift.svg");
|
||||
|
|
|
|||
54
src/sass/views/tab-scan.scss
Normal file
54
src/sass/views/tab-scan.scss
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
#tab-scan {
|
||||
// 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;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue