Fix Conflicts:
.gitignore index.html js/controllers/backup.js js/controllers/sidebar.js
8
.gitignore
vendored
|
|
@ -51,6 +51,14 @@ version.js
|
||||||
|
|
||||||
android/package
|
android/package
|
||||||
android/*.apk
|
android/*.apk
|
||||||
|
android/*.keystore
|
||||||
|
|
||||||
|
mobile/*.keystore
|
||||||
|
mobile/assets/www
|
||||||
|
mobile/bin/*
|
||||||
|
mobile/gen/*
|
||||||
|
mobile/cordova/*
|
||||||
|
mobile/CordovaLib/*
|
||||||
|
|
||||||
coverage/
|
coverage/
|
||||||
|
|
||||||
|
|
|
||||||
26
README.md
|
|
@ -106,6 +106,32 @@ One solution is to use Copay with a Python version manager for 2.6.
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
|
||||||
|
## Android APK
|
||||||
|
|
||||||
|
System Requirements
|
||||||
|
|
||||||
|
* Download [Android SDK](http://developer.android.com/sdk/index.html)
|
||||||
|
* Download and install [Crosswalk 8](https://crosswalk-project.org/#documentation/getting_started) (Use Linux setup for OSX)
|
||||||
|
|
||||||
|
Add to your ~/.bash_profile or ~/.bashrc
|
||||||
|
|
||||||
|
```
|
||||||
|
export CROSSWALK="<path to Crosswalk directory>"
|
||||||
|
```
|
||||||
|
|
||||||
|
To build the APK run the script:
|
||||||
|
|
||||||
|
```
|
||||||
|
sh android/build.sh [-d]
|
||||||
|
```
|
||||||
|
- The -d flag will package the apk in debug mode, allowing [remote debugging chrome](https://developer.chrome.com/devtools/docs/remote-debugging)
|
||||||
|
- The APK file is in **android/Copay_VERSION_arm.apk**
|
||||||
|
|
||||||
|
To install the APK in your device run:
|
||||||
|
|
||||||
|
```
|
||||||
|
adb install -r Copay_VERSION_arm.apk
|
||||||
|
```
|
||||||
|
|
||||||
## Google Chrome Extension
|
## Google Chrome Extension
|
||||||
|
|
||||||
|
|
|
||||||
27
TODO.md
|
|
@ -30,33 +30,6 @@ It was developed to be run on OSX. The outputs are copied to the dist directory
|
||||||
DMG is created with hdiutil
|
DMG is created with hdiutil
|
||||||
EXE is created with makensis (brew install makensis)
|
EXE is created with makensis (brew install makensis)
|
||||||
|
|
||||||
## Android APK
|
|
||||||
|
|
||||||
System Requirements
|
|
||||||
|
|
||||||
* Download [Android SDK](http://developer.android.com/sdk/index.html)
|
|
||||||
* Download and install [Crosswalk](https://crosswalk-project.org/#documentation/getting_started) (Use Linux setup for OSX)
|
|
||||||
|
|
||||||
Add to your ~/.bash_profile or ~/.bashrc
|
|
||||||
|
|
||||||
```
|
|
||||||
export CROSSWALK="<path to Crosswalk directory>"
|
|
||||||
```
|
|
||||||
|
|
||||||
To build the APK run the script:
|
|
||||||
|
|
||||||
```
|
|
||||||
sh android/build.sh [-d]
|
|
||||||
```
|
|
||||||
- The -d flag will package the apk in debug mode, allowing [remote debugging chrome](https://developer.chrome.com/devtools/docs/remote-debugging)
|
|
||||||
- The APK file is in **android/Copay_VERSION_arm.apk**
|
|
||||||
|
|
||||||
To install the APK in your device run:
|
|
||||||
|
|
||||||
```
|
|
||||||
adb install -r Copay_VERSION_arm.apk
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,16 @@ then
|
||||||
DEBUG="--enable-remote-debugging"
|
DEBUG="--enable-remote-debugging"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ $# -eq 1 && ! $1 = "-d" ]]
|
||||||
|
then
|
||||||
|
if [ ! -f $BUILDDIR/copay.keystore ]
|
||||||
|
then
|
||||||
|
echo "${OpenColor}${Red}* Can't build production app without a keystore${CloseColor}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
PRODUCTION="--keystore-path=$BUILDDIR/copay.keystore --keystore-alias=copay_play --keystore-passcode=$1"
|
||||||
|
fi
|
||||||
|
|
||||||
# Move to the build directory
|
# Move to the build directory
|
||||||
cd $BUILDDIR
|
cd $BUILDDIR
|
||||||
|
|
||||||
|
|
@ -48,13 +58,13 @@ checkOK
|
||||||
echo "${OpenColor}${Green}* Copying all app files...${CloseColor}"
|
echo "${OpenColor}${Green}* Copying all app files...${CloseColor}"
|
||||||
sed "s/APP_VERSION/$VERSION/g" manifest.json > $APPDIR/manifest.json
|
sed "s/APP_VERSION/$VERSION/g" manifest.json > $APPDIR/manifest.json
|
||||||
cd $BUILDDIR/..
|
cd $BUILDDIR/..
|
||||||
cp -af {css,font,img,js,lib,sound,config.js,version.js,index.html,./android/icon.png} $APPDIR
|
cp -af {css,font,img,js,lib,sound,config.js,version.js,index.html,./android/icon.png,./android/logo.png} $APPDIR
|
||||||
checkOK
|
checkOK
|
||||||
|
|
||||||
# Building the APK
|
# Building the APK
|
||||||
echo "${OpenColor}${Green}* Building APK file...${CloseColor}"
|
echo "${OpenColor}${Green}* Building APK file...${CloseColor}"
|
||||||
cd $CROSSWALK
|
cd $CROSSWALK
|
||||||
python make_apk.py --manifest=$APPDIR/manifest.json --target-dir=$BUILDDIR --arch=arm $DEBUG
|
python make_apk.py --manifest=$APPDIR/manifest.json --package=com.bitpay.copay --arch=arm --target-dir=$BUILDDIR $DEBUG $PRODUCTION
|
||||||
checkOK
|
checkOK
|
||||||
cd $BUILDDIR
|
cd $BUILDDIR
|
||||||
|
|
||||||
|
|
|
||||||
BIN
android/icon.png
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 8 KiB |
BIN
android/logo.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
|
|
@ -1,12 +1,21 @@
|
||||||
{
|
{
|
||||||
"name": "Copay",
|
"name": "Copay",
|
||||||
"version": "APP_VERSION",
|
"version": "APP_VERSION",
|
||||||
"app": {
|
"start_url": "index.html",
|
||||||
"launch":{
|
"permissions": [
|
||||||
"local_path": "index.html"
|
"Vibration"
|
||||||
}
|
],
|
||||||
|
"xwalk_launch_screen": {
|
||||||
|
"ready_when": "complete",
|
||||||
|
"default": {
|
||||||
|
"background_color": "#2C3E50",
|
||||||
|
"image": "logo.png"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"icons": {
|
"icons": {
|
||||||
"128": "icon.png"
|
"128": "icon.png"
|
||||||
}
|
},
|
||||||
|
"xwalk_hosts": [
|
||||||
|
"https://*"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
"sjcl": "1.0.0",
|
"sjcl": "1.0.0",
|
||||||
"file-saver": "*",
|
"file-saver": "*",
|
||||||
"qrcode-decoder-js": "*",
|
"qrcode-decoder-js": "*",
|
||||||
"bitcore": "0.1.25",
|
"bitcore": "0.1.34",
|
||||||
"angular-moment": "~0.7.1",
|
"angular-moment": "~0.7.1",
|
||||||
"socket.io-client": ">=1.0.0",
|
"socket.io-client": ">=1.0.0",
|
||||||
"mousetrap": "1.4.6"
|
"mousetrap": "1.4.6"
|
||||||
|
|
|
||||||
BIN
img/notification.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
|
|
@ -33,6 +33,13 @@ var copayApp = window.copayApp = angular.module('copayApp', [
|
||||||
'copayApp.directives',
|
'copayApp.directives',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
copayApp.config(function($sceDelegateProvider) {
|
||||||
|
$sceDelegateProvider.resourceUrlWhitelist([
|
||||||
|
'self',
|
||||||
|
'mailto:**'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
angular.module('copayApp.filters', []);
|
angular.module('copayApp.filters', []);
|
||||||
angular.module('copayApp.services', []);
|
angular.module('copayApp.services', []);
|
||||||
angular.module('copayApp.controllers', []);
|
angular.module('copayApp.controllers', []);
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ angular.module('copayApp.controllers').controller('ImportController',
|
||||||
updateStatus('Importing wallet - Setting things up...');
|
updateStatus('Importing wallet - Setting things up...');
|
||||||
var w, errMsg;
|
var w, errMsg;
|
||||||
|
|
||||||
|
// try to import encrypted wallet with passphrase
|
||||||
try {
|
try {
|
||||||
w = walletFactory.import(encryptedObj, passphrase);
|
w = walletFactory.import(encryptedObj, passphrase);
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errMsg = e.message;
|
errMsg = e.message;
|
||||||
}
|
}
|
||||||
|
|
@ -31,12 +31,14 @@ angular.module('copayApp.controllers').controller('ImportController',
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if wallet was never used, we're done
|
||||||
if (!w.isReady()) {
|
if (!w.isReady()) {
|
||||||
$rootScope.wallet = w;
|
$rootScope.wallet = w;
|
||||||
controllerUtils.startNetwork($rootScope.wallet, $scope);
|
controllerUtils.startNetwork($rootScope.wallet, $scope);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if it was used, we need to scan for indices
|
||||||
w.updateIndexes(function(err) {
|
w.updateIndexes(function(err) {
|
||||||
updateStatus('Importing wallet - We are almost there...');
|
updateStatus('Importing wallet - We are almost there...');
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
|
|
||||||
// Detect protocol
|
// Detect protocol
|
||||||
$scope.isHttp = ($window.location.protocol.indexOf('http') === 0);
|
$scope.isHttp = ($window.location.protocol.indexOf('http') === 0);
|
||||||
|
$scope.isCordova = typeof(window.cordova) != 'undefined';
|
||||||
|
|
||||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||||
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
|
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
|
||||||
|
|
@ -72,8 +73,8 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
});
|
});
|
||||||
|
|
||||||
// reset fields
|
// reset fields
|
||||||
$scope.address = $scope.amount = $scope.comment = null;
|
$scope.address = $scope.amount = $scope.commentText = null;
|
||||||
form.address.$pristine = form.amount.$pristine = form.comment.$pristine = true;
|
form.address.$pristine = form.amount.$pristine = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// QR code Scanner
|
// QR code Scanner
|
||||||
|
|
@ -194,6 +195,25 @@ angular.module('copayApp.controllers').controller('SendController',
|
||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.scannerIntent = function() {
|
||||||
|
cordova.plugins.barcodeScanner.scan(
|
||||||
|
function onSuccess(result) {
|
||||||
|
if (result.cancelled) return;
|
||||||
|
|
||||||
|
var bip21 = copay.Structure.parseBitcoinURI(result.text);
|
||||||
|
$scope.address = bip21.address;
|
||||||
|
|
||||||
|
if (bip21.amount) {
|
||||||
|
$scope.amount = bip21.amount * bitcore.util.COIN * satToUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$rootScope.$digest();
|
||||||
|
},
|
||||||
|
function onError(error) {
|
||||||
|
alert('Scanning error');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$scope.toggleAddressBookEntry = function(key) {
|
$scope.toggleAddressBookEntry = function(key) {
|
||||||
var w = $rootScope.wallet;
|
var w = $rootScope.wallet;
|
||||||
w.toggleAddressBookEntry(key);
|
w.toggleAddressBookEntry(key);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ angular.module('copayApp.controllers').controller('SidebarController',
|
||||||
'icon': 'fi-arrow-right',
|
'icon': 'fi-arrow-right',
|
||||||
'link': 'send'
|
'link': 'send'
|
||||||
}, {
|
}, {
|
||||||
'title': 'More...',
|
'title': 'More',
|
||||||
'icon': 'fi-download',
|
'icon': 'fi-download',
|
||||||
'link': 'backup'
|
'link': 'backup'
|
||||||
}];
|
}];
|
||||||
|
|
|
||||||
|
|
@ -156,63 +156,55 @@ angular.module('copayApp.directives')
|
||||||
restrict: 'EACM',
|
restrict: 'EACM',
|
||||||
require: 'ngModel',
|
require: 'ngModel',
|
||||||
link: function(scope, element, attrs) {
|
link: function(scope, element, attrs) {
|
||||||
var strength = {
|
|
||||||
messages: ['very weak', 'weak', 'weak', 'medium', 'strong'],
|
|
||||||
colors: ['#c0392b', '#e74c3c', '#d35400', '#f39c12', '#27ae60'],
|
|
||||||
mesureStrength: function(p) {
|
|
||||||
var force = 0;
|
|
||||||
var regex = /[$-/:-?{-~!"^_`\[\]]/g;
|
|
||||||
var lowerLetters = /[a-z]+/.test(p);
|
|
||||||
var upperLetters = /[A-Z]+/.test(p);
|
|
||||||
var numbers = /[0-9]+/.test(p);
|
|
||||||
var symbols = regex.test(p);
|
|
||||||
var flags = [lowerLetters, upperLetters, numbers, symbols];
|
|
||||||
var passedMatches = flags.filter(function(el) {
|
|
||||||
return !!el;
|
|
||||||
}).length;
|
|
||||||
|
|
||||||
force = 2 * p.length + (p.length >= 10 ? 1 : 0);
|
var MIN_LENGTH = 8;
|
||||||
force += passedMatches * 10;
|
var MESSAGES = ['Very Weak', 'Very Weak', 'Weak', 'Medium', 'Strong', 'Very Strong'];
|
||||||
|
var COLOR = ['#dd514c', '#dd514c', '#faa732', '#faa732', '#5eb95e', '#5eb95e'];
|
||||||
|
|
||||||
// penality (short password)
|
function evaluateMeter(password) {
|
||||||
force = (p.length <= 6) ? Math.min(force, 10) : force;
|
var passwordStrength = 0;
|
||||||
|
var text;
|
||||||
// penality (poor variety of characters)
|
if (password.length > 0) passwordStrength = 1;
|
||||||
force = (passedMatches == 1) ? Math.min(force, 10) : force;
|
if (password.length >= MIN_LENGTH) {
|
||||||
force = (passedMatches == 2) ? Math.min(force, 20) : force;
|
if ((password.match(/[a-z]/)) && (password.match(/[A-Z]/))) {
|
||||||
force = (passedMatches == 3) ? Math.min(force, 40) : force;
|
passwordStrength++;
|
||||||
return force;
|
|
||||||
},
|
|
||||||
getColor: function(s) {
|
|
||||||
var idx = 0;
|
|
||||||
|
|
||||||
if (s <= 10) {
|
|
||||||
idx = 0;
|
|
||||||
} else if (s <= 20) {
|
|
||||||
idx = 1;
|
|
||||||
} else if (s <= 30) {
|
|
||||||
idx = 2;
|
|
||||||
} else if (s <= 40) {
|
|
||||||
idx = 3;
|
|
||||||
} else {
|
} else {
|
||||||
idx = 4;
|
text = ', add mixed case';
|
||||||
}
|
}
|
||||||
|
if (password.match(/\d+/)) {
|
||||||
return {
|
passwordStrength++;
|
||||||
idx: idx + 1,
|
} else {
|
||||||
col: this.colors[idx],
|
if (!text) text = ', add numerals';
|
||||||
message: this.messages[idx]
|
}
|
||||||
};
|
if (password.match(/.[!,@,#,$,%,^,&,*,?,_,~,-,(,)]/)) {
|
||||||
|
passwordStrength++;
|
||||||
|
} else {
|
||||||
|
if (!text) text = ', add punctuation';
|
||||||
|
}
|
||||||
|
if (password.length > 12) {
|
||||||
|
passwordStrength++;
|
||||||
|
} else {
|
||||||
|
if (!text) text = ', add characters';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text = ', that\'s short';
|
||||||
}
|
}
|
||||||
};
|
if (!text) text = '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
strength: passwordStrength,
|
||||||
|
message: MESSAGES[passwordStrength] + text,
|
||||||
|
color: COLOR[passwordStrength]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
|
scope.$watch(attrs.ngModel, function(newValue, oldValue) {
|
||||||
if (newValue && newValue !== '') {
|
if (newValue && newValue !== '') {
|
||||||
var c = strength.getColor(strength.mesureStrength(newValue));
|
var info = evaluateMeter(newValue);
|
||||||
element.css({
|
element.css({
|
||||||
'border-color': c.col
|
'border-color': info.color
|
||||||
});
|
});
|
||||||
scope[attrs.checkStrength] = c.message;
|
scope[attrs.checkStrength] = info.message;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,18 @@ angular.module('copayApp.filters', [])
|
||||||
.filter('removeEmpty', function() {
|
.filter('removeEmpty', function() {
|
||||||
return function(elements) {
|
return function(elements) {
|
||||||
elements = elements || [];
|
elements = elements || [];
|
||||||
// Hide empty addresses from other copayers
|
// Hide empty change addresses from other copayers
|
||||||
return elements.filter(function(e) {
|
return elements.filter(function(e) {
|
||||||
return e.owned || e.balance > 0;
|
return !e.isChange || e.balance > 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter('limitAddress', function() {
|
.filter('limitAddress', function() {
|
||||||
return function(elements, showAll) {
|
return function(elements, showAll) {
|
||||||
|
var elements = elements.sort(function(a, b) {
|
||||||
|
return (+b.owned) - (+a.owned);
|
||||||
|
});
|
||||||
|
|
||||||
if (elements.length <= 1 || showAll) {
|
if (elements.length <= 1 || showAll) {
|
||||||
return elements;
|
return elements;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
js/models/core/BuilderMockV0.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var Transaction = bitcore.Transaction;
|
||||||
|
|
||||||
|
function BuilderMockV0 (data) {
|
||||||
|
this.vanilla = data;
|
||||||
|
this.tx = new Transaction();
|
||||||
|
this.tx.parse(new Buffer(data.tx, 'hex'));
|
||||||
|
};
|
||||||
|
|
||||||
|
BuilderMockV0.prototype.build = function() {
|
||||||
|
return this.tx;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
BuilderMockV0.prototype.getSelectedUnspent = function() {
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
BuilderMockV0.prototype.toObj = function() {
|
||||||
|
return this.vanilla;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = BuilderMockV0;
|
||||||
|
|
@ -59,13 +59,16 @@ Structure.parseBitcoinURI = function(uri) {
|
||||||
data = splitDots[1];
|
data = splitDots[1];
|
||||||
var splitQuestion = data.split('?');
|
var splitQuestion = data.split('?');
|
||||||
ret.address = splitQuestion[0];
|
ret.address = splitQuestion[0];
|
||||||
var search = splitQuestion[1];
|
|
||||||
data = JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g, '":"') + '"}',
|
if (splitQuestion.length > 1) {
|
||||||
function(key, value) {
|
var search = splitQuestion[1];
|
||||||
return key === "" ? value : decodeURIComponent(value);
|
data = JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g, '":"') + '"}',
|
||||||
});
|
function(key, value) {
|
||||||
ret.amount = parseFloat(data.amount);
|
return key === "" ? value : decodeURIComponent(value);
|
||||||
ret.message = data.message;
|
});
|
||||||
|
ret.amount = parseFloat(data.amount);
|
||||||
|
ret.message = data.message;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@ var imports = require('soop').imports();
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var util = bitcore.util;
|
var util = bitcore.util;
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var Builder = bitcore.TransactionBuilder;
|
var BuilderMockV0 = require('./BuilderMockV0');;
|
||||||
|
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||||
var Script = bitcore.Script;
|
var Script = bitcore.Script;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
|
var preconditions = require('preconditions').instance();
|
||||||
|
|
||||||
function TxProposal(opts) {
|
function TxProposal(opts) {
|
||||||
this.creator = opts.creator;
|
this.creator = opts.creator;
|
||||||
|
|
@ -23,8 +25,7 @@ function TxProposal(opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TxProposal.prototype.getID = function() {
|
TxProposal.prototype.getID = function() {
|
||||||
var ntxid = this.builder.build().getNormalizedHash().toString('hex');
|
return this.builder.build().getNormalizedHash().toString('hex');
|
||||||
return ntxid;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposal.prototype.toObj = function() {
|
TxProposal.prototype.toObj = function() {
|
||||||
|
|
@ -40,13 +41,41 @@ TxProposal.prototype.setSent = function(sentTxid) {
|
||||||
this.sentTs = Date.now();
|
this.sentTs = Date.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposal.fromObj = function(o) {
|
TxProposal.fromObj = function(o, forceOpts) {
|
||||||
var t = new TxProposal(o);
|
var t = new TxProposal(o);
|
||||||
var b = new Builder.fromObj(o.builderObj);
|
|
||||||
t.builder = b;
|
try {
|
||||||
|
// force opts is requested.
|
||||||
|
for (var k in forceOpts) {
|
||||||
|
o.builderObj.opts[k] = forceOpts[k];
|
||||||
|
}
|
||||||
|
t.builder = TransactionBuilder.fromObj(o.builderObj);
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
if (!o.version) {
|
||||||
|
t.builder = new BuilderMockV0(o.builderObj);
|
||||||
|
t.readonly = 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TxProposal.prototype.isValid = function() {
|
||||||
|
if (this.builder.signhash && this.builder.signhash !== Transaction.SIGHASH_ALL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var tx = this.builder.build();
|
||||||
|
for (var i = 0; i < tx.ins.length; i++) {
|
||||||
|
var hashType = tx.getHashType(i);
|
||||||
|
if (hashType && hashType !== Transaction.SIGHASH_ALL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
TxProposal.getSentTs = function() {
|
TxProposal.getSentTs = function() {
|
||||||
return this.sentTs;
|
return this.sentTs;
|
||||||
};
|
};
|
||||||
|
|
@ -127,6 +156,17 @@ TxProposal.prototype.mergeMetadata = function(v1, author) {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//This should be on bitcore / Transaction
|
||||||
|
TxProposal.prototype.countSignatures = function() {
|
||||||
|
var tx = this.builder.build();
|
||||||
|
|
||||||
|
var ret = 0;
|
||||||
|
for (var i in tx.ins) {
|
||||||
|
ret += tx.countInputSignatures(i);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = require('soop')(TxProposal);
|
module.exports = require('soop')(TxProposal);
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -138,15 +178,18 @@ function TxProposals(opts) {
|
||||||
this.txps = {};
|
this.txps = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
TxProposals.fromObj = function(o) {
|
TxProposals.fromObj = function(o, forceOpts) {
|
||||||
var ret = new TxProposals({
|
var ret = new TxProposals({
|
||||||
networkName: o.networkName,
|
networkName: o.networkName,
|
||||||
walletId: o.walletId,
|
walletId: o.walletId,
|
||||||
});
|
});
|
||||||
|
|
||||||
o.txps.forEach(function(o2) {
|
o.txps.forEach(function(o2) {
|
||||||
var t = TxProposal.fromObj(o2);
|
var t = TxProposal.fromObj(o2, forceOpts);
|
||||||
var id = t.builder.build().getNormalizedHash().toString('hex');
|
if (t.builder) {
|
||||||
ret.txps[id] = t;
|
var id = t.getID();
|
||||||
|
ret.txps[id] = t;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
@ -198,7 +241,6 @@ TxProposals.prototype.merge = function(inTxp, author) {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
var preconditions = require('preconditions').instance();
|
|
||||||
TxProposals.prototype.add = function(data) {
|
TxProposals.prototype.add = function(data) {
|
||||||
preconditions.checkArgument(data.inputChainPaths);
|
preconditions.checkArgument(data.inputChainPaths);
|
||||||
preconditions.checkArgument(data.signedBy);
|
preconditions.checkArgument(data.signedBy);
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ function Wallet(opts) {
|
||||||
self[k] = opts[k];
|
self[k] = opts[k];
|
||||||
});
|
});
|
||||||
if (copayConfig.forceNetwork && this.getNetworkName() !== copayConfig.networkName)
|
if (copayConfig.forceNetwork && this.getNetworkName() !== copayConfig.networkName)
|
||||||
throw new Error('Network forced to '+copayConfig.networkName+
|
throw new Error('Network forced to ' + copayConfig.networkName +
|
||||||
' and tried to create a Wallet with network '+ this.getNetworkName());
|
' and tried to create a Wallet with network ' + this.getNetworkName());
|
||||||
|
|
||||||
this.log('creating ' + opts.requiredCopayers + ' of ' + opts.totalCopayers + ' wallet');
|
this.log('creating ' + opts.requiredCopayers + ' of ' + opts.totalCopayers + ' wallet');
|
||||||
|
|
||||||
|
|
@ -57,6 +57,14 @@ function Wallet(opts) {
|
||||||
this.network.setHexNonces(opts.networkNonces);
|
this.network.setHexNonces(opts.networkNonces);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Wallet.builderOpts = {
|
||||||
|
lockTime: null,
|
||||||
|
signhash: bitcore.Transaction.SIGNHASH_ALL,
|
||||||
|
fee: null,
|
||||||
|
feeSat: null,
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.parent = EventEmitter;
|
Wallet.parent = EventEmitter;
|
||||||
Wallet.prototype.log = function() {
|
Wallet.prototype.log = function() {
|
||||||
if (!this.verbose) return;
|
if (!this.verbose) return;
|
||||||
|
|
@ -121,11 +129,20 @@ Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype._handleTxProposal = function(senderId, data) {
|
|
||||||
preconditions.checkArgument(senderId);
|
|
||||||
this.log('RECV TXPROPOSAL:', data);
|
|
||||||
|
|
||||||
var inTxp = TxProposals.TxProposal.fromObj(data.txProposal);
|
Wallet.prototype._handleTxProposal = function(senderId, data) {
|
||||||
|
this.log('RECV TXPROPOSAL: ', data);
|
||||||
|
|
||||||
|
var inTxp = TxProposals.TxProposal.fromObj(data.txProposal, Wallet.builderOpts);
|
||||||
|
var valid = inTxp.isValid();
|
||||||
|
if (!valid) {
|
||||||
|
var corruptEvent = {
|
||||||
|
type: 'corrupt',
|
||||||
|
cId: inTxp.creator
|
||||||
|
};
|
||||||
|
this.emit('txProposalEvent', corruptEvent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
var mergeInfo = this.txProposals.merge(inTxp, senderId);
|
var mergeInfo = this.txProposals.merge(inTxp, senderId);
|
||||||
var added = this.addSeenToTxProposals();
|
var added = this.addSeenToTxProposals();
|
||||||
|
|
||||||
|
|
@ -370,7 +387,7 @@ Wallet.fromObj = function(o, storage, network, blockchain) {
|
||||||
opts.addressBook = o.addressBook;
|
opts.addressBook = o.addressBook;
|
||||||
|
|
||||||
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
|
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
|
||||||
opts.txProposals = TxProposals.fromObj(o.txProposals);
|
opts.txProposals = TxProposals.fromObj(o.txProposals, Wallet.builderOpts);
|
||||||
opts.privateKey = PrivateKey.fromObj(o.privateKey);
|
opts.privateKey = PrivateKey.fromObj(o.privateKey);
|
||||||
|
|
||||||
opts.storage = storage;
|
opts.storage = storage;
|
||||||
|
|
@ -489,7 +506,9 @@ Wallet.prototype.getTxProposals = function() {
|
||||||
txp.finallyRejected = true;
|
txp.finallyRejected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.push(txp);
|
if (txp.readonly && !txp.finallyRejected && !txp.sentTs) {} else {
|
||||||
|
ret.push(txp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
@ -509,6 +528,7 @@ Wallet.prototype.reject = function(ntxid) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Wallet.prototype.sign = function(ntxid, cb) {
|
Wallet.prototype.sign = function(ntxid, cb) {
|
||||||
preconditions.checkState(typeof this.getMyCopayerId() !== 'undefined');
|
preconditions.checkState(typeof this.getMyCopayerId() !== 'undefined');
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
@ -522,11 +542,11 @@ Wallet.prototype.sign = function(ntxid, cb) {
|
||||||
var keys = self.privateKey.getForPaths(txp.inputChainPaths);
|
var keys = self.privateKey.getForPaths(txp.inputChainPaths);
|
||||||
|
|
||||||
var b = txp.builder;
|
var b = txp.builder;
|
||||||
var before = b.signaturesAdded;
|
var before = txp.countSignatures();
|
||||||
b.sign(keys);
|
b.sign(keys);
|
||||||
|
|
||||||
var ret = false;
|
var ret = false;
|
||||||
if (b.signaturesAdded > before) {
|
if (txp.countSignatures() > before) {
|
||||||
txp.signedBy[myId] = Date.now();
|
txp.signedBy[myId] = Date.now();
|
||||||
self.sendTxProposal(ntxid);
|
self.sendTxProposal(ntxid);
|
||||||
self.store();
|
self.store();
|
||||||
|
|
@ -697,15 +717,9 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
||||||
var priv = this.privateKey;
|
var priv = this.privateKey;
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
|
||||||
var amountSat = bignum(amountSatStr);
|
|
||||||
preconditions.checkArgument(new Address(toAddress).network().name === this.getNetworkName());
|
preconditions.checkArgument(new Address(toAddress).network().name === this.getNetworkName());
|
||||||
if (!pkr.isComplete()) {
|
preconditions.checkState(pkr.isComplete());
|
||||||
throw new Error('publicKeyRing is not complete');
|
if (comment) preconditions.checkArgument(comment.length <= 100);
|
||||||
}
|
|
||||||
|
|
||||||
if (comment && comment.length > 100) {
|
|
||||||
throw new Error("comment can't be longer that 100 characters");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!opts.remainderOut) {
|
if (!opts.remainderOut) {
|
||||||
opts.remainderOut = {
|
opts.remainderOut = {
|
||||||
|
|
@ -713,11 +727,15 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (var k in Wallet.builderOpts){
|
||||||
|
opts[k] = Wallet.builderOpts[k];
|
||||||
|
}
|
||||||
|
|
||||||
var b = new Builder(opts)
|
var b = new Builder(opts)
|
||||||
.setUnspent(utxos)
|
.setUnspent(utxos)
|
||||||
.setOutputs([{
|
.setOutputs([{
|
||||||
address: toAddress,
|
address: toAddress,
|
||||||
amountSat: amountSat
|
amountSatStr: amountSatStr,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
var selectedUtxos = b.getSelectedUnspent();
|
var selectedUtxos = b.getSelectedUnspent();
|
||||||
|
|
@ -735,7 +753,9 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
|
||||||
var me = {};
|
var me = {};
|
||||||
if (priv && b.signaturesAdded) me[myId] = now;
|
|
||||||
|
var tx = b.build();
|
||||||
|
if (priv && tx.countInputSignatures(0)) me[myId] = now;
|
||||||
|
|
||||||
var meSeen = {};
|
var meSeen = {};
|
||||||
if (priv) meSeen[myId] = now;
|
if (priv) meSeen[myId] = now;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,18 @@ BackupService.prototype.download = function(wallet) {
|
||||||
wallet: ew
|
wallet: ew
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// throw an email intent if we are in the mobile version
|
||||||
|
if (window.cordova) {
|
||||||
|
var name = wallet.name ? wallet.name + ' ' : '';
|
||||||
|
var partial = partial ? 'Partial ' : '';
|
||||||
|
return window.plugin.email.open({
|
||||||
|
subject: 'Copay - ' + name + 'Wallet ' + partial + 'Backup',
|
||||||
|
body: 'Here is the encrypted backup of the wallet ' + wallet.id,
|
||||||
|
attachments: ['base64:' + filename + '//' + btoa(ew)]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// otherwise lean on the browser implementation
|
// otherwise lean on the browser implementation
|
||||||
saveAs(blob, filename);
|
saveAs(blob, filename);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -149,15 +149,17 @@ angular.module('copayApp.services')
|
||||||
}, 3000);
|
}, 3000);
|
||||||
});
|
});
|
||||||
w.on('txProposalEvent', function(e) {
|
w.on('txProposalEvent', function(e) {
|
||||||
|
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case 'signed':
|
case 'signed':
|
||||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
|
||||||
notification.info('Transaction Update', 'A transaction was signed by ' + user);
|
notification.info('Transaction Update', 'A transaction was signed by ' + user);
|
||||||
break;
|
break;
|
||||||
case 'rejected':
|
case 'rejected':
|
||||||
var user = w.publicKeyRing.nicknameForCopayer(e.cId);
|
|
||||||
notification.info('Transaction Update', 'A transaction was rejected by ' + user);
|
notification.info('Transaction Update', 'A transaction was rejected by ' + user);
|
||||||
break;
|
break;
|
||||||
|
case 'corrupt':
|
||||||
|
notification.error('Transaction Error', 'Received corrupt transaction from '+user);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
w.on('addressBookUpdated', function(dontDigest) {
|
w.on('addressBookUpdated', function(dontDigest) {
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,12 @@ factory('notification', ['$timeout',
|
||||||
$timeout(function removeFromQueueTimeout() {
|
$timeout(function removeFromQueueTimeout() {
|
||||||
queue.splice(queue.indexOf(notification), 1);
|
queue.splice(queue.indexOf(notification), 1);
|
||||||
}, settings[type].duration);
|
}, settings[type].duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Movile notification
|
||||||
|
window.navigator.vibrate([200,100,200]);
|
||||||
|
if (document.hidden && (type == 'info' || type == 'funds')) {
|
||||||
|
new window.Notification(title, {body: content, icon:'img/notification.png'});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
|
|
|
||||||
39
mobile/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<manifest android:hardwareAccelerated="true" android:versionCode="3" android:versionName="0.4.1" android:windowSoftInputMode="adjustPan" package="com.bitpay.copay" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<application android:hardwareAccelerated="true" android:icon="@drawable/icon" android:label="@string/app_name">
|
||||||
|
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/app_name" android:launchMode="singleTop" android:name="Copay" android:theme="@android:style/Theme.Black.NoTitleBar">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:clearTaskOnLaunch="true" android:configChanges="orientation|keyboardHidden" android:exported="false" android:name="com.google.zxing.client.android.CaptureActivity" android:screenOrientation="landscape" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.phonegap.plugins.barcodescanner.SCAN" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:label="@string/share_name" android:name="com.google.zxing.client.android.encode.EncodeActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.phonegap.plugins.barcodescanner.ENCODE" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity android:label="@string/share_name" android:name="com.google.zxing.client.android.HelpActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<provider android:authorities="com.bitpay.copay.plugin.emailcomposer.attachmentprovider" android:name="de.appplant.cordova.plugin.emailcomposer.AttachmentProvider" />
|
||||||
|
</application>
|
||||||
|
<uses-sdk android:minSdkVersion="10" android:targetSdkVersion="19" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.FLASHLIGHT" />
|
||||||
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
|
</manifest>
|
||||||
76
mobile/build.sh
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
# Description: This script compiles and copy the needed files to later package the application for Android
|
||||||
|
|
||||||
|
OpenColor="\033["
|
||||||
|
Red="1;31m"
|
||||||
|
Yellow="1;33m"
|
||||||
|
Green="1;32m"
|
||||||
|
CloseColor="\033[0m"
|
||||||
|
|
||||||
|
# Check function OK
|
||||||
|
checkOK() {
|
||||||
|
if [ $? != 0 ]; then
|
||||||
|
echo "${OpenColor}${Red}* ERROR. Exiting...${CloseColor}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configs
|
||||||
|
BUILDDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
|
APPDIR="$BUILDDIR/assets/www"
|
||||||
|
VERSION=`cut -d '"' -f2 $BUILDDIR/../version.js`
|
||||||
|
RELEASE=false
|
||||||
|
RUN=false
|
||||||
|
|
||||||
|
if [[ $1 = "--release" ]]
|
||||||
|
then
|
||||||
|
RELEASE=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $1 = "-r" ]]
|
||||||
|
then
|
||||||
|
RUN=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move to the build directory
|
||||||
|
cd $BUILDDIR
|
||||||
|
|
||||||
|
[ -z "$CROSSWALK" ] && { echo "${OpenColor}${Red}* Need to set CROSSWALK environment variable${CloseColor}"; exit 1; }
|
||||||
|
|
||||||
|
# Create/Clean temp dir
|
||||||
|
echo "${OpenColor}${Green}* Checking temp dir...${CloseColor}"
|
||||||
|
if [ -d $APPDIR ]; then
|
||||||
|
rm -rf $APPDIR
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p $APPDIR
|
||||||
|
|
||||||
|
# Re-compile copayBundle.js
|
||||||
|
echo "${OpenColor}${Green}* Generating copay bundle...${CloseColor}"
|
||||||
|
grunt --target=dev shell
|
||||||
|
checkOK
|
||||||
|
|
||||||
|
# Copy all app files
|
||||||
|
echo "${OpenColor}${Green}* Copying all app files...${CloseColor}"
|
||||||
|
cd $BUILDDIR/..
|
||||||
|
cp -af {css,font,img,js,lib,sound,config.js,version.js,$BUILDDIR/cordova.js,$BUILDDIR/cordova_plugins.js,$BUILDDIR/plugins} $APPDIR
|
||||||
|
checkOK
|
||||||
|
sed "s/<\!-- PLACEHOLDER: CORDOVA SRIPT -->/<script type='text\/javascript' charset='utf-8' src='cordova.js'><\/script>/g" index.html > $APPDIR/index.html
|
||||||
|
checkOK
|
||||||
|
|
||||||
|
# Building the APK
|
||||||
|
echo "${OpenColor}${Green}* Building APK file...${CloseColor}"
|
||||||
|
cd $BUILDDIR
|
||||||
|
if [[ $RUN == true ]]
|
||||||
|
then
|
||||||
|
./cordova/run
|
||||||
|
elif [[ $RELEASE == true ]]
|
||||||
|
then
|
||||||
|
./cordova/build --release
|
||||||
|
else
|
||||||
|
./cordova/build
|
||||||
|
fi
|
||||||
|
checkOK
|
||||||
|
|
||||||
|
echo "${OpenColor}${Yellow}\nAwesome! We have a brand new APK, enjoy it!${CloseColor}"
|
||||||
92
mobile/build.xml
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project name="Copay" default="help">
|
||||||
|
|
||||||
|
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||||
|
It contains the path to the SDK. It should *NOT* be checked into
|
||||||
|
Version Control Systems. -->
|
||||||
|
<property file="local.properties" />
|
||||||
|
|
||||||
|
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||||
|
'android' tool to add properties to it.
|
||||||
|
This is the place to change some Ant specific build properties.
|
||||||
|
Here are some properties you may want to change/update:
|
||||||
|
|
||||||
|
source.dir
|
||||||
|
The name of the source directory. Default is 'src'.
|
||||||
|
out.dir
|
||||||
|
The name of the output directory. Default is 'bin'.
|
||||||
|
|
||||||
|
For other overridable properties, look at the beginning of the rules
|
||||||
|
files in the SDK, at tools/ant/build.xml
|
||||||
|
|
||||||
|
Properties related to the SDK location or the project target should
|
||||||
|
be updated using the 'android' tool with the 'update' action.
|
||||||
|
|
||||||
|
This file is an integral part of the build system for your
|
||||||
|
application and should be checked into Version Control Systems.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<property file="ant.properties" />
|
||||||
|
|
||||||
|
<!-- if sdk.dir was not set from one of the property file, then
|
||||||
|
get it from the ANDROID_HOME env var.
|
||||||
|
This must be done before we load project.properties since
|
||||||
|
the proguard config can use sdk.dir -->
|
||||||
|
<property environment="env" />
|
||||||
|
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||||
|
<isset property="env.ANDROID_HOME" />
|
||||||
|
</condition>
|
||||||
|
|
||||||
|
<!-- The project.properties file is created and updated by the 'android'
|
||||||
|
tool, as well as ADT.
|
||||||
|
|
||||||
|
This contains project specific properties such as project target, and library
|
||||||
|
dependencies. Lower level build properties are stored in ant.properties
|
||||||
|
(or in .classpath for Eclipse projects).
|
||||||
|
|
||||||
|
This file is an integral part of the build system for your
|
||||||
|
application and should be checked into Version Control Systems. -->
|
||||||
|
<loadproperties srcFile="project.properties" />
|
||||||
|
|
||||||
|
<!-- quick check on sdk.dir -->
|
||||||
|
<fail
|
||||||
|
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||||
|
unless="sdk.dir"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Import per project custom build rules if present at the root of the project.
|
||||||
|
This is the place to put custom intermediary targets such as:
|
||||||
|
-pre-build
|
||||||
|
-pre-compile
|
||||||
|
-post-compile (This is typically used for code obfuscation.
|
||||||
|
Compiled code location: ${out.classes.absolute.dir}
|
||||||
|
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||||
|
-post-package
|
||||||
|
-post-build
|
||||||
|
-pre-clean
|
||||||
|
-->
|
||||||
|
<import file="custom_rules.xml" optional="true" />
|
||||||
|
|
||||||
|
<!-- Import the actual build file.
|
||||||
|
|
||||||
|
To customize existing targets, there are two options:
|
||||||
|
- Customize only one target:
|
||||||
|
- copy/paste the target into this file, *before* the
|
||||||
|
<import> task.
|
||||||
|
- customize it to your needs.
|
||||||
|
- Customize the whole content of build.xml
|
||||||
|
- copy/paste the content of the rules files (minus the top node)
|
||||||
|
into this file, replacing the <import> task.
|
||||||
|
- customize to your needs.
|
||||||
|
|
||||||
|
***********************
|
||||||
|
****** IMPORTANT ******
|
||||||
|
***********************
|
||||||
|
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||||
|
in order to avoid having your file be overridden by tools such as "android update project"
|
||||||
|
-->
|
||||||
|
<!-- version-tag: 1 -->
|
||||||
|
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||||
|
|
||||||
|
</project>
|
||||||
1879
mobile/cordova.js
vendored
Normal file
33
mobile/cordova_plugins.js
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
cordova.define('cordova/plugin_list', function(require, exports, module) {
|
||||||
|
module.exports = [
|
||||||
|
{
|
||||||
|
"file": "plugins/de.appplant.cordova.plugin.email-composer/www/email_composer.js",
|
||||||
|
"id": "de.appplant.cordova.plugin.email-composer.EmailComposer",
|
||||||
|
"clobbers": [
|
||||||
|
"plugin.email"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "plugins/com.phonegap.plugins.barcodescanner/www/barcodescanner.js",
|
||||||
|
"id": "com.phonegap.plugins.barcodescanner.BarcodeScanner",
|
||||||
|
"clobbers": [
|
||||||
|
"cordova.plugins.barcodeScanner"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "plugins/org.apache.cordova.splashscreen/www/splashscreen.js",
|
||||||
|
"id": "org.apache.cordova.splashscreen.SplashScreen",
|
||||||
|
"clobbers": [
|
||||||
|
"navigator.splashscreen"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
module.exports.metadata =
|
||||||
|
// TOP OF METADATA
|
||||||
|
{
|
||||||
|
"de.appplant.cordova.plugin.email-composer": "0.8.2dev",
|
||||||
|
"com.phonegap.plugins.barcodescanner": "1.0.1",
|
||||||
|
"org.apache.cordova.splashscreen": "0.3.0"
|
||||||
|
}
|
||||||
|
// BOTTOM OF METADATA
|
||||||
|
});
|
||||||
BIN
mobile/libs/com.google.zxing.client.android.captureactivity.jar
Normal file
10
mobile/local.properties
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must *NOT* be checked into Version Control Systems,
|
||||||
|
# as it contains information specific to your local configuration.
|
||||||
|
|
||||||
|
# location of the SDK. This is only used by Ant
|
||||||
|
# For customization when using a Version Control System, please read the
|
||||||
|
# header note.
|
||||||
|
sdk.dir=/Users/yemeljardi/Documents/sdk
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
cordova.define("com.phonegap.plugins.barcodescanner.BarcodeScanner", function(require, exports, module) { /**
|
||||||
|
* cordova is available under *either* the terms of the modified BSD license *or* the
|
||||||
|
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||||
|
*
|
||||||
|
* Copyright (c) Matt Kane 2010
|
||||||
|
* Copyright (c) 2011, IBM Corporation
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
var exec = require("cordova/exec");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @returns {BarcodeScanner}
|
||||||
|
*/
|
||||||
|
function BarcodeScanner() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encoding constants.
|
||||||
|
*
|
||||||
|
* @type Object
|
||||||
|
*/
|
||||||
|
this.Encode = {
|
||||||
|
TEXT_TYPE: "TEXT_TYPE",
|
||||||
|
EMAIL_TYPE: "EMAIL_TYPE",
|
||||||
|
PHONE_TYPE: "PHONE_TYPE",
|
||||||
|
SMS_TYPE: "SMS_TYPE"
|
||||||
|
// CONTACT_TYPE: "CONTACT_TYPE", // TODO: not implemented, requires passing a Bundle class from Javascript to Java
|
||||||
|
// LOCATION_TYPE: "LOCATION_TYPE" // TODO: not implemented, requires passing a Bundle class from Javascript to Java
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read code from scanner.
|
||||||
|
*
|
||||||
|
* @param {Function} successCallback This function will recieve a result object: {
|
||||||
|
* text : '12345-mock', // The code that was scanned.
|
||||||
|
* format : 'FORMAT_NAME', // Code format.
|
||||||
|
* cancelled : true/false, // Was canceled.
|
||||||
|
* }
|
||||||
|
* @param {Function} errorCallback
|
||||||
|
*/
|
||||||
|
BarcodeScanner.prototype.scan = function (successCallback, errorCallback) {
|
||||||
|
if (errorCallback == null) {
|
||||||
|
errorCallback = function () {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof errorCallback != "function") {
|
||||||
|
console.log("BarcodeScanner.scan failure: failure parameter not a function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof successCallback != "function") {
|
||||||
|
console.log("BarcodeScanner.scan failure: success callback parameter must be a function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exec(successCallback, errorCallback, 'BarcodeScanner', 'scan', []);
|
||||||
|
};
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------
|
||||||
|
BarcodeScanner.prototype.encode = function (type, data, successCallback, errorCallback, options) {
|
||||||
|
if (errorCallback == null) {
|
||||||
|
errorCallback = function () {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof errorCallback != "function") {
|
||||||
|
console.log("BarcodeScanner.encode failure: failure parameter not a function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof successCallback != "function") {
|
||||||
|
console.log("BarcodeScanner.encode failure: success callback parameter must be a function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exec(successCallback, errorCallback, 'BarcodeScanner', 'encode', [
|
||||||
|
{"type": type, "data": data, "options": options}
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
var barcodeScanner = new BarcodeScanner();
|
||||||
|
module.exports = barcodeScanner;
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
cordova.define("de.appplant.cordova.plugin.email-composer.EmailComposer", function(require, exports, module) { /*
|
||||||
|
Copyright 2013-2014 appPlant UG
|
||||||
|
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var EmailComposer = function () {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
EmailComposer.prototype = {
|
||||||
|
/**
|
||||||
|
* Displays the email composer pre-filled with data.
|
||||||
|
*
|
||||||
|
* @param {Object} options
|
||||||
|
* Different properties of the email like the body, subject
|
||||||
|
* @param {Function} callback
|
||||||
|
* A callback function to be called with the result
|
||||||
|
* @param {Object?} scope
|
||||||
|
* The scope of the callback
|
||||||
|
*/
|
||||||
|
open: function (options, callback, scope) {
|
||||||
|
var callbackFn = this.createCallbackFn(callback, scope),
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
subject: null,
|
||||||
|
body: null,
|
||||||
|
to: null,
|
||||||
|
cc: null,
|
||||||
|
bcc: null,
|
||||||
|
attachments: null,
|
||||||
|
isHtml: true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var key in defaults) {
|
||||||
|
if (options[key] !== undefined) {
|
||||||
|
defaults[key] = options[key];
|
||||||
|
} else {
|
||||||
|
console.log('EmailComposer plugin: unknown property "' + key + '"');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cordova.exec(callbackFn, null, 'EmailComposer', 'open', [options]);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias für `open()`.
|
||||||
|
*/
|
||||||
|
openDraft: function () {
|
||||||
|
this.open.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies if sending emails is supported on the device.
|
||||||
|
*
|
||||||
|
* @param {Function} callback
|
||||||
|
* A callback function to be called with the result
|
||||||
|
* @param {Object} scope
|
||||||
|
* The scope of the callback
|
||||||
|
*/
|
||||||
|
isServiceAvailable: function (callback, scope) {
|
||||||
|
var callbackFn = this.createCallbackFn(callback, scope);
|
||||||
|
|
||||||
|
cordova.exec(callbackFn, null, 'EmailComposer', 'isServiceAvailable', []);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*
|
||||||
|
* Creates a callback, which will be executed within a specific scope.
|
||||||
|
*
|
||||||
|
* @param {Function} callbackFn
|
||||||
|
* The callback function
|
||||||
|
* @param {Object} scope
|
||||||
|
* The scope for the function
|
||||||
|
*
|
||||||
|
* @return {Function}
|
||||||
|
* The new callback function
|
||||||
|
*/
|
||||||
|
createCallbackFn: function (callbackFn, scope) {
|
||||||
|
return function () {
|
||||||
|
if (typeof callbackFn == 'function') {
|
||||||
|
callbackFn.apply(scope || this, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var plugin = new EmailComposer();
|
||||||
|
|
||||||
|
module.exports = plugin;
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
cordova.define("org.apache.cordova.splashscreen.SplashScreen", function(require, exports, module) { /*
|
||||||
|
*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
var exec = require('cordova/exec');
|
||||||
|
|
||||||
|
var splashscreen = {
|
||||||
|
show:function() {
|
||||||
|
exec(null, null, "SplashScreen", "show", []);
|
||||||
|
},
|
||||||
|
hide:function() {
|
||||||
|
exec(null, null, "SplashScreen", "hide", []);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = splashscreen;
|
||||||
|
|
||||||
|
});
|
||||||
20
mobile/proguard-project.txt
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# To enable ProGuard in your project, edit project.properties
|
||||||
|
# to define the proguard.config property as described in that file.
|
||||||
|
#
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||||
|
# You can edit the include path and order by changing the ProGuard
|
||||||
|
# include property in project.properties.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# Add any project specific keep options here:
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
17
mobile/project.properties
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system edit
|
||||||
|
# "ant.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
#
|
||||||
|
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||||
|
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||||
|
|
||||||
|
android.library.reference.1=CordovaLib
|
||||||
|
# Project target.
|
||||||
|
target=android-19
|
||||||
|
key.store=copay.keystore
|
||||||
|
key.alias=copay_play
|
||||||
BIN
mobile/res/drawable/icon.png
Normal file
|
After Width: | Height: | Size: 8 KiB |
BIN
mobile/res/drawable/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
mobile/res/drawable/screen.9.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
mobile/res/drawable/share_via_barcode.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
mobile/res/drawable/shopper_icon.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
48
mobile/res/layout-land/encode.xml
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:background="@color/encode_view"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/image_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:scaleType="center"/>
|
||||||
|
|
||||||
|
<ScrollView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/contents_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/contents_text"
|
||||||
|
android:paddingRight="8dip"
|
||||||
|
android:paddingTop="8dip"
|
||||||
|
android:paddingBottom="8dip"/>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
90
mobile/res/layout-land/share.xml
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="8dip">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/msg_share_explanation"
|
||||||
|
android:paddingBottom="12dip"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingBottom="12dip">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/share_via_barcode"
|
||||||
|
android:paddingRight="24dip"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_app_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/button_share_app"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_contact_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/button_share_contact"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_bookmark_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/button_share_bookmark"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_clipboard_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/button_share_clipboard"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText android:id="@+id/share_text_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/msg_share_text"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:selectAllOnFocus="true"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
219
mobile/res/layout-ldpi/capture.xml
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
|
||||||
|
<SurfaceView android:id="@+id/preview_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"/>
|
||||||
|
|
||||||
|
<com.google.zxing.client.android.ViewfinderView
|
||||||
|
android:id="@+id/viewfinder_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"/>
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/result_view"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:background="@color/result_view"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="top"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:gravity="right">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/barcode_image_view"
|
||||||
|
android:layout_width="150dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxWidth="150dip"
|
||||||
|
android:maxHeight="150dip"
|
||||||
|
android:layout_marginBottom="@dimen/half_padding"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="centerInside"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_format"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/format_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_type"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/type_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_time"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/time_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/meta_text_view_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_meta"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/meta_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/contents_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_text"
|
||||||
|
android:textColorLink="@color/result_text"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:paddingLeft="12dip"
|
||||||
|
android:autoLink="web"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/contents_supplement_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_text"
|
||||||
|
android:textColorLink="@color/result_text"
|
||||||
|
android:paddingLeft="12dip"
|
||||||
|
android:autoLink="web"
|
||||||
|
android:clickable="true"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/result_button_view"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<Button android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/shopper_button"
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:drawableLeft="@drawable/shopper_icon"
|
||||||
|
android:text="@string/button_google_shopper"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/status_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|center_horizontal"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:text="@string/msg_default_status"
|
||||||
|
android:textColor="@color/status_text"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
34
mobile/res/layout/bookmark_picker_list_item.xml
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/half_padding">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/bookmark_title"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/bookmark_url"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="false"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
218
mobile/res/layout/capture.xml
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
|
||||||
|
<SurfaceView android:id="@+id/preview_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"/>
|
||||||
|
|
||||||
|
<com.google.zxing.client.android.ViewfinderView
|
||||||
|
android:id="@+id/viewfinder_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"/>
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/result_view"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:background="@color/result_view"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:gravity="right|center_vertical">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/barcode_image_view"
|
||||||
|
android:layout_width="160dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:maxWidth="160dip"
|
||||||
|
android:maxHeight="160dip"
|
||||||
|
android:layout_marginBottom="@dimen/half_padding"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="centerInside"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_format"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/format_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_type"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/type_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_time"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/time_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/meta_text_view_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/msg_default_meta"
|
||||||
|
android:textColor="@color/result_minor_text"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:paddingRight="@dimen/half_padding"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/meta_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_minor_text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ScrollView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/contents_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_text"
|
||||||
|
android:textColorLink="@color/result_text"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:paddingLeft="12dip"
|
||||||
|
android:autoLink="web"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/contents_supplement_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/result_text"
|
||||||
|
android:textColorLink="@color/result_text"
|
||||||
|
android:paddingLeft="12dip"
|
||||||
|
android:autoLink="web"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout android:id="@+id/result_button_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<Button android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/shopper_button"
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:drawableLeft="@drawable/shopper_icon"
|
||||||
|
android:text="@string/button_google_shopper"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/status_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|center_horizontal"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:text="@string/msg_default_status"
|
||||||
|
android:textColor="@color/status_text"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
49
mobile/res/layout/encode.xml
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:background="@color/encode_view"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/image_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:scaleType="center"/>
|
||||||
|
|
||||||
|
<ScrollView android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/contents_text_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/contents_text"
|
||||||
|
android:paddingBottom="@dimen/standard_padding"
|
||||||
|
android:paddingLeft="@dimen/standard_padding"
|
||||||
|
android:paddingRight="@dimen/standard_padding"/>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
50
mobile/res/layout/help.xml
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<WebView android:id="@+id/help_contents"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:paddingTop="@dimen/half_padding"
|
||||||
|
android:weightSum="1">
|
||||||
|
|
||||||
|
<Button android:id="@+id/back_button"
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0.5"
|
||||||
|
android:text="@string/button_back"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/done_button"
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0.5"
|
||||||
|
android:text="@string/button_done"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
22
mobile/res/layout/history_list_item.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/history_title"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:singleLine="true"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/history_detail"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:singleLine="false"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
51
mobile/res/layout/search_book_contents.xml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0">
|
||||||
|
|
||||||
|
<EditText android:id="@+id/query_text_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left|center_vertical"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:selectAllOnFocus="true"
|
||||||
|
android:inputType="text"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/query_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="0"
|
||||||
|
android:text="@string/button_search_book_contents"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
<ListView android:id="@+id/result_list_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
23
mobile/res/layout/search_book_contents_header.xml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="@dimen/standard_padding"
|
||||||
|
android:paddingBottom="@dimen/half_padding"
|
||||||
|
android:enabled="false"
|
||||||
|
android:singleLine="true"/>
|
||||||
38
mobile/res/layout/search_book_contents_list_item.xml
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<com.google.zxing.client.android.book.SearchBookContentsListItem
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<TextView android:id="@+id/page_number_view"
|
||||||
|
android:layout_width="75dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left|top"
|
||||||
|
android:layout_marginRight="@dimen/standard_padding"
|
||||||
|
android:singleLine="false"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<TextView android:id="@+id/snippet_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="left|top"
|
||||||
|
android:singleLine="false"/>
|
||||||
|
|
||||||
|
</com.google.zxing.client.android.book.SearchBookContentsListItem>
|
||||||
96
mobile/res/layout/share.xml
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- ScrollView wrapper is to accommodate small screens. -->
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<!-- Must wrap the rest in one layout -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/msg_share_explanation"
|
||||||
|
android:paddingBottom="@dimen/standard_padding"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/share_via_barcode"
|
||||||
|
android:paddingBottom="@dimen/standard_padding"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingBottom="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_app_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/button_share_app"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_bookmark_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/button_share_bookmark"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingBottom="@dimen/standard_padding">
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_contact_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/button_share_contact"/>
|
||||||
|
|
||||||
|
<Button android:id="@+id/share_clipboard_button"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/button_share_clipboard"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText android:id="@+id/share_text_view"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/msg_share_text"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:selectAllOnFocus="true"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
19
mobile/res/menu/capture.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2012 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- removed nonfunctional menu - see file history to retrieve -->
|
||||||
|
</menu>
|
||||||
19
mobile/res/menu/encode.xml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2012 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- removed nonfunctional menu - see file history to retrieve -->
|
||||||
|
</menu>
|
||||||
26
mobile/res/menu/history.xml
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2012 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@+id/menu_history_send"
|
||||||
|
android:title="@string/history_send"
|
||||||
|
android:icon="@android:drawable/ic_menu_share"
|
||||||
|
android:orderInCategory="1"/>
|
||||||
|
<item android:id="@+id/menu_history_clear_text"
|
||||||
|
android:title="@string/history_clear_text"
|
||||||
|
android:icon="@android:drawable/ic_menu_delete"
|
||||||
|
android:orderInCategory="2"/>
|
||||||
|
</menu>
|
||||||
BIN
mobile/res/raw/beep.ogg
Normal file
35
mobile/res/values/arrays.xml
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<resources>
|
||||||
|
<string-array name="country_codes">
|
||||||
|
<item>-</item>
|
||||||
|
<item>AR</item>
|
||||||
|
<item>AU</item>
|
||||||
|
<item>BR</item>
|
||||||
|
<item>BG</item>
|
||||||
|
<item>CA</item>
|
||||||
|
<item>CN</item>
|
||||||
|
<item>CZ</item>
|
||||||
|
<item>DE</item>
|
||||||
|
<item>DK</item>
|
||||||
|
<item>ES</item>
|
||||||
|
<item>FI</item>
|
||||||
|
<item>FR</item>
|
||||||
|
<item>GB</item>
|
||||||
|
<item>GR</item>
|
||||||
|
<item>HU</item>
|
||||||
|
<item>ID</item>
|
||||||
|
<item>IT</item>
|
||||||
|
<item>JP</item>
|
||||||
|
<item>KR</item>
|
||||||
|
<item>NL</item>
|
||||||
|
<item>PL</item>
|
||||||
|
<item>PT</item>
|
||||||
|
<item>RU</item>
|
||||||
|
<item>SE</item>
|
||||||
|
<item>SK</item>
|
||||||
|
<item>SI</item>
|
||||||
|
<item>TR</item>
|
||||||
|
<item>TW</item>
|
||||||
|
<item>US</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
||||||
29
mobile/res/values/colors.xml
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
<color name="contents_text">#ff000000</color>
|
||||||
|
<color name="encode_view">#ffffffff</color>
|
||||||
|
<color name="possible_result_points">#c0ffbd21</color> <!-- Android standard ICS color -->
|
||||||
|
<color name="result_minor_text">#ffc0c0c0</color>
|
||||||
|
<color name="result_points">#c099cc00</color> <!-- Android standard ICS color -->
|
||||||
|
<color name="result_text">#ffffffff</color>
|
||||||
|
<color name="result_view">#b0000000</color>
|
||||||
|
<color name="status_text">#ffffffff</color>
|
||||||
|
<color name="transparent">#00000000</color>
|
||||||
|
<color name="viewfinder_laser">#ffcc0000</color> <!-- Android standard ICS color -->
|
||||||
|
<color name="viewfinder_mask">#60000000</color>
|
||||||
|
</resources>
|
||||||
5
mobile/res/values/dimens.xml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<resources>
|
||||||
|
<dimen name="standard_padding">8dip</dimen>
|
||||||
|
<dimen name="half_padding">4dip</dimen>
|
||||||
|
</resources>
|
||||||
25
mobile/res/values/ids.xml
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<resources>
|
||||||
|
<item type="id" name="decode"/>
|
||||||
|
<item type="id" name="decode_failed"/>
|
||||||
|
<item type="id" name="decode_succeeded"/>
|
||||||
|
<item type="id" name="launch_product_query"/>
|
||||||
|
<item type="id" name="quit"/>
|
||||||
|
<item type="id" name="restart_preview"/>
|
||||||
|
<item type="id" name="return_scan_result"/>
|
||||||
|
</resources>
|
||||||
123
mobile/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<resources>
|
||||||
|
<string name="app_name">Copay</string>
|
||||||
|
<string name="app_picker_name">Applications</string>
|
||||||
|
<string name="bookmark_picker_name">Bookmarks</string>
|
||||||
|
<string name="button_add_calendar">Add to calendar</string>
|
||||||
|
<string name="button_add_contact">Add contact</string>
|
||||||
|
<string name="button_back">Back</string>
|
||||||
|
<string name="button_book_search">Book Search</string>
|
||||||
|
<string name="button_cancel">Cancel</string>
|
||||||
|
<string name="button_custom_product_search">Custom search</string>
|
||||||
|
<string name="button_dial">Dial number</string>
|
||||||
|
<string name="button_done">Done</string>
|
||||||
|
<string name="button_email">Send email</string>
|
||||||
|
<string name="button_get_directions">Get directions</string>
|
||||||
|
<string name="button_google_shopper">Shopper</string>
|
||||||
|
<string name="button_mms">Send MMS</string>
|
||||||
|
<string name="button_ok">OK</string>
|
||||||
|
<string name="button_open_browser">Open browser</string>
|
||||||
|
<string name="button_product_search">Product Search</string>
|
||||||
|
<string name="button_search_book_contents">Search contents</string>
|
||||||
|
<string name="button_share_app">Application</string>
|
||||||
|
<string name="button_share_bookmark">Bookmark</string>
|
||||||
|
<string name="button_share_by_email">Share via email</string>
|
||||||
|
<string name="button_share_by_sms">Share via SMS</string>
|
||||||
|
<string name="button_share_clipboard">Clipboard</string>
|
||||||
|
<string name="button_share_contact">Contact</string>
|
||||||
|
<string name="button_show_map">Show map</string>
|
||||||
|
<string name="button_sms">Send SMS</string>
|
||||||
|
<string name="button_web_search">Web search</string>
|
||||||
|
<string name="button_wifi">Connect to Network</string>
|
||||||
|
<string name="contents_contact">Contact info</string>
|
||||||
|
<string name="contents_email">Email address</string>
|
||||||
|
<string name="contents_location">Geographic coordinates</string>
|
||||||
|
<string name="contents_phone">Phone number</string>
|
||||||
|
<string name="contents_sms">SMS address</string>
|
||||||
|
<string name="contents_text">Plain text</string>
|
||||||
|
<string name="history_clear_text">Clear history</string>
|
||||||
|
<string name="history_clear_one_history_text">Clear</string>
|
||||||
|
<string name="history_email_title">Barcode Scanner history</string>
|
||||||
|
<string name="history_empty">Empty</string>
|
||||||
|
<string name="history_empty_detail">No barcode scans have been recorded</string>
|
||||||
|
<string name="history_send">Send history</string>
|
||||||
|
<string name="history_title">History</string>
|
||||||
|
<string name="menu_encode_mecard">Use MECARD</string>
|
||||||
|
<string name="menu_encode_vcard">Use vCard</string>
|
||||||
|
<string name="menu_help">Help</string>
|
||||||
|
<string name="menu_history">History</string>
|
||||||
|
<string name="menu_share">Share</string>
|
||||||
|
<string name="msg_bulk_mode_scanned">Bulk mode: barcode scanned and saved</string>
|
||||||
|
<string name="msg_camera_framework_bug">Sorry, the Android camera encountered a problem. You may need to restart the device.</string>
|
||||||
|
<string name="msg_default_format">Format</string>
|
||||||
|
<string name="msg_default_meta">Metadata</string>
|
||||||
|
<string name="msg_default_mms_subject">Hi</string>
|
||||||
|
<string name="msg_default_status">Place a barcode inside the viewfinder rectangle to scan it.</string>
|
||||||
|
<string name="msg_default_time">Time</string>
|
||||||
|
<string name="msg_default_type">Type</string>
|
||||||
|
<string name="msg_encode_contents_failed">Could not encode a barcode from the data provided.</string>
|
||||||
|
<string name="msg_google_books">Google Books</string>
|
||||||
|
<string name="msg_google_product">Google Product Search</string>
|
||||||
|
<string name="msg_google_shopper_missing">Google Shopper is not installed</string>
|
||||||
|
<string name="msg_install_google_shopper">Google Shopper combines barcode scanning with online and local prices, reviews and more without opening the browser. Would you like to try it?</string>
|
||||||
|
<string name="msg_intent_failed">Sorry, the requested application could not be launched. The barcode contents may be invalid.</string>
|
||||||
|
<string name="msg_redirect">Redirect</string>
|
||||||
|
<string name="msg_sbc_book_not_searchable">Sorry, this book is not searchable.</string>
|
||||||
|
<string name="msg_sbc_failed">Sorry, the search encountered a problem.</string>
|
||||||
|
<string name="msg_sbc_no_page_returned">No page returned</string>
|
||||||
|
<string name="msg_sbc_page">Page</string>
|
||||||
|
<string name="msg_sbc_results">Results</string>
|
||||||
|
<string name="msg_sbc_searching_book">Searching book\u2026</string>
|
||||||
|
<string name="msg_sbc_snippet_unavailable">Snippet not available</string>
|
||||||
|
<string name="msg_sbc_unknown_page">Unknown page</string>
|
||||||
|
<string name="msg_share_explanation">You can share data by displaying a barcode on your screen and scanning it with another phone.</string>
|
||||||
|
<string name="msg_share_subject_line">Here are the contents of a barcode I scanned</string>
|
||||||
|
<string name="msg_share_text">Or type some text and press Enter</string>
|
||||||
|
<string name="msg_sure">Are you sure?</string>
|
||||||
|
<string name="msg_unmount_usb">Sorry, the SD card is not accessible.</string>
|
||||||
|
<string name="preferences_actions_title">When a barcode is found\u2026</string>
|
||||||
|
<string name="preferences_auto_focus_title">Use auto focus</string>
|
||||||
|
<string name="preferences_bulk_mode_summary">Scan and save many barcodes continuously</string>
|
||||||
|
<string name="preferences_bulk_mode_title">Bulk scan mode</string>
|
||||||
|
<string name="preferences_copy_to_clipboard_title">Copy to clipboard</string>
|
||||||
|
<string formatted="false" name="preferences_custom_product_search_summary">Substitutions: %s = contents, %f = format, %t = type</string>
|
||||||
|
<string name="preferences_custom_product_search_title">Custom search URL</string>
|
||||||
|
<string name="preferences_decode_1D_title">1D barcodes</string>
|
||||||
|
<string name="preferences_decode_Data_Matrix_title">Data Matrix</string>
|
||||||
|
<string name="preferences_decode_QR_title">QR Codes</string>
|
||||||
|
<string name="preferences_device_bug_workarounds_title">Device Bug Workarounds</string>
|
||||||
|
<string name="preferences_disable_continuous_focus_summary">Use only standard focus mode</string>
|
||||||
|
<string name="preferences_disable_continuous_focus_title">No continuous focus</string>
|
||||||
|
<string name="preferences_disable_exposure_title">No exposure</string>
|
||||||
|
<string name="preferences_front_light_summary">Improves scanning in low light on some phones, but may cause glare. Does not work on all phones.</string>
|
||||||
|
<string name="preferences_front_light_title">Use front light</string>
|
||||||
|
<string name="preferences_general_title">General settings</string>
|
||||||
|
<string name="preferences_name">Settings</string>
|
||||||
|
<string name="preferences_play_beep_title">Beep</string>
|
||||||
|
<string name="preferences_remember_duplicates_summary">Store multiple scans of the same barcode in History</string>
|
||||||
|
<string name="preferences_remember_duplicates_title">Remember duplicates</string>
|
||||||
|
<string name="preferences_result_title">Result settings</string>
|
||||||
|
<string name="preferences_scanning_title">When scanning for barcodes, decode\u2026</string>
|
||||||
|
<string name="preferences_search_country">Search country</string>
|
||||||
|
<string name="preferences_try_bsplus">Try Barcode Scanner+</string>
|
||||||
|
<string name="preferences_try_bsplus_summary">Enhanced with new features and interface</string>
|
||||||
|
<string name="preferences_supplemental_summary">Try to retrieve more information about the barcode contents</string>
|
||||||
|
<string name="preferences_supplemental_title">Retrieve more info</string>
|
||||||
|
<string name="preferences_vibrate_title">Vibrate</string>
|
||||||
|
<string name="result_address_book">Found contact info</string>
|
||||||
|
<string name="result_calendar">Found calendar event</string>
|
||||||
|
<string name="result_email_address">Found email address</string>
|
||||||
|
<string name="result_geo">Found geographic coordinates</string>
|
||||||
|
<string name="result_isbn">Found book</string>
|
||||||
|
<string name="result_product">Found product</string>
|
||||||
|
<string name="result_sms">Found SMS address</string>
|
||||||
|
<string name="result_tel">Found phone number</string>
|
||||||
|
<string name="result_text">Found plain text</string>
|
||||||
|
<string name="result_uri">Found URL</string>
|
||||||
|
<string name="result_wifi">Found WLAN Configuration</string>
|
||||||
|
<string name="sbc_name">Google Book Search</string>
|
||||||
|
<string name="share_name">Share via barcode</string>
|
||||||
|
<string name="wifi_changing_network">Requesting connection to network\u2026</string>
|
||||||
|
<string name="wifi_ssid_label">Network Name</string>
|
||||||
|
<string name="wifi_type_label">Type</string>
|
||||||
|
</resources>
|
||||||
31
mobile/res/xml/config.xml
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<widget id="com.bitpay.copay" version="2.0.0" xmlns="http://www.w3.org/ns/widgets">
|
||||||
|
<name>Copay</name>
|
||||||
|
<description>
|
||||||
|
A secure bitcoin wallet for friends and companies
|
||||||
|
</description>
|
||||||
|
<author email="support@bitpay.com" href="http://copay.io">
|
||||||
|
Bitpay Inc.
|
||||||
|
</author>
|
||||||
|
<access origin="*" />
|
||||||
|
<content src="index.html" />
|
||||||
|
<preference name="loglevel" value="DEBUG" />
|
||||||
|
<preference name="splashscreen" value="screen" />
|
||||||
|
<preference name="splashScreenDelay" value="10000" />
|
||||||
|
<preference name="backgroundColor" value="0x2C3E50" />
|
||||||
|
<feature name="App">
|
||||||
|
<param name="android-package" value="com.bitpay.copay" />
|
||||||
|
</feature>
|
||||||
|
<feature name="Vibration">
|
||||||
|
<param name="android-package" value="org.apache.cordova.vibration.Vibration" />
|
||||||
|
</feature>
|
||||||
|
<feature name="BarcodeScanner">
|
||||||
|
<param name="android-package" value="com.phonegap.plugins.barcodescanner.BarcodeScanner" />
|
||||||
|
</feature>
|
||||||
|
<feature name="EmailComposer">
|
||||||
|
<param name="android-package" value="de.appplant.cordova.plugin.emailcomposer.EmailComposer" />
|
||||||
|
</feature>
|
||||||
|
<feature name="SplashScreen">
|
||||||
|
<param name="android-package" value="org.apache.cordova.splashscreen.SplashScreen" />
|
||||||
|
</feature>
|
||||||
|
</widget>
|
||||||
102
mobile/res/xml/preferences.xml
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2008 ZXing authors
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<PreferenceCategory android:title="@string/preferences_scanning_title">
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_decode_1D"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:title="@string/preferences_decode_1D_title"/>
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_decode_QR"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:title="@string/preferences_decode_QR_title"/>
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_decode_Data_Matrix"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:title="@string/preferences_decode_Data_Matrix_title"/>
|
||||||
|
</PreferenceCategory>
|
||||||
|
<PreferenceCategory android:title="@string/preferences_actions_title">
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_play_beep"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:title="@string/preferences_play_beep_title"/>
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_vibrate"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:title="@string/preferences_vibrate_title"/>
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_copy_to_clipboard"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:title="@string/preferences_copy_to_clipboard_title"/>
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_remember_duplicates"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:title="@string/preferences_remember_duplicates_title"
|
||||||
|
android:summary="@string/preferences_remember_duplicates_summary"/>
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_supplemental"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:title="@string/preferences_supplemental_title"
|
||||||
|
android:summary="@string/preferences_supplemental_summary"/>
|
||||||
|
</PreferenceCategory>
|
||||||
|
<PreferenceCategory android:title="@string/preferences_result_title">
|
||||||
|
<EditTextPreference
|
||||||
|
android:key="preferences_custom_product_search"
|
||||||
|
android:title="@string/preferences_custom_product_search_title"
|
||||||
|
android:summary="@string/preferences_custom_product_search_summary"/>
|
||||||
|
</PreferenceCategory>
|
||||||
|
<PreferenceCategory android:title="@string/preferences_general_title">
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_front_light"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:title="@string/preferences_front_light_title"
|
||||||
|
android:summary="@string/preferences_front_light_summary"/>
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_auto_focus"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:title="@string/preferences_auto_focus_title"/>
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_bulk_mode"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:title="@string/preferences_bulk_mode_title"
|
||||||
|
android:summary="@string/preferences_bulk_mode_summary"/>
|
||||||
|
<ListPreference
|
||||||
|
android:key="preferences_search_country"
|
||||||
|
android:defaultValue="-"
|
||||||
|
android:entries="@array/country_codes"
|
||||||
|
android:entryValues="@array/country_codes"
|
||||||
|
android:title="@string/preferences_search_country"/>
|
||||||
|
</PreferenceCategory>
|
||||||
|
<PreferenceCategory android:title="@string/preferences_device_bug_workarounds_title">
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_disable_continuous_focus"
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:title="@string/preferences_disable_continuous_focus_title"
|
||||||
|
android:summary="@string/preferences_disable_continuous_focus_summary"/>
|
||||||
|
<!--
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="preferences_disable_exposure"
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:title="@string/preferences_disable_exposure_title"/>
|
||||||
|
-->
|
||||||
|
</PreferenceCategory>
|
||||||
|
<PreferenceCategory android:title="@string/preferences_try_bsplus">
|
||||||
|
<com.google.zxing.client.android.pref.BSPlusPreference
|
||||||
|
android:title="@string/preferences_try_bsplus"
|
||||||
|
android:summary="@string/preferences_try_bsplus_summary"/>
|
||||||
|
</PreferenceCategory>
|
||||||
|
</PreferenceScreen>
|
||||||
37
mobile/src/com/bitpay/copay/Copay.java
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.bitpay.copay;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
|
public class Copay extends CordovaActivity
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
super.init();
|
||||||
|
// Set by <content src="index.html" /> in config.xml
|
||||||
|
super.loadUrl(Config.getStartUrl());
|
||||||
|
//super.loadUrl("file:///android_asset/www/index.html");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
/**
|
||||||
|
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||||
|
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||||
|
*
|
||||||
|
* Copyright (c) Matt Kane 2010
|
||||||
|
* Copyright (c) 2011, IBM Corporation
|
||||||
|
* Copyright (c) 2013, Maciej Nux Jaros
|
||||||
|
*/
|
||||||
|
package com.phonegap.plugins.barcodescanner;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.apache.cordova.CordovaPlugin;
|
||||||
|
import org.apache.cordova.CallbackContext;
|
||||||
|
import org.apache.cordova.PluginResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This calls out to the ZXing barcode reader and returns the result.
|
||||||
|
*
|
||||||
|
* @sa https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java
|
||||||
|
*/
|
||||||
|
public class BarcodeScanner extends CordovaPlugin {
|
||||||
|
public static final int REQUEST_CODE = 0x0ba7c0de;
|
||||||
|
|
||||||
|
private static final String SCAN = "scan";
|
||||||
|
private static final String ENCODE = "encode";
|
||||||
|
private static final String CANCELLED = "cancelled";
|
||||||
|
private static final String FORMAT = "format";
|
||||||
|
private static final String TEXT = "text";
|
||||||
|
private static final String DATA = "data";
|
||||||
|
private static final String TYPE = "type";
|
||||||
|
private static final String SCAN_INTENT = "com.phonegap.plugins.barcodescanner.SCAN";
|
||||||
|
private static final String ENCODE_DATA = "ENCODE_DATA";
|
||||||
|
private static final String ENCODE_TYPE = "ENCODE_TYPE";
|
||||||
|
private static final String ENCODE_INTENT = "com.phonegap.plugins.barcodescanner.ENCODE";
|
||||||
|
private static final String TEXT_TYPE = "TEXT_TYPE";
|
||||||
|
private static final String EMAIL_TYPE = "EMAIL_TYPE";
|
||||||
|
private static final String PHONE_TYPE = "PHONE_TYPE";
|
||||||
|
private static final String SMS_TYPE = "SMS_TYPE";
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "BarcodeScanner";
|
||||||
|
|
||||||
|
private CallbackContext callbackContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public BarcodeScanner() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the request.
|
||||||
|
*
|
||||||
|
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
|
||||||
|
* cordova.getThreadPool().execute(runnable);
|
||||||
|
*
|
||||||
|
* To run on the UI thread, use:
|
||||||
|
* cordova.getActivity().runOnUiThread(runnable);
|
||||||
|
*
|
||||||
|
* @param action The action to execute.
|
||||||
|
* @param args The exec() arguments.
|
||||||
|
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||||
|
* @return Whether the action was valid.
|
||||||
|
*
|
||||||
|
* @sa https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||||
|
this.callbackContext = callbackContext;
|
||||||
|
|
||||||
|
if (action.equals(ENCODE)) {
|
||||||
|
JSONObject obj = args.optJSONObject(0);
|
||||||
|
if (obj != null) {
|
||||||
|
String type = obj.optString(TYPE);
|
||||||
|
String data = obj.optString(DATA);
|
||||||
|
|
||||||
|
// If the type is null then force the type to text
|
||||||
|
if (type == null) {
|
||||||
|
type = TEXT_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data == null) {
|
||||||
|
callbackContext.error("User did not specify data to encode");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
encode(type, data);
|
||||||
|
} else {
|
||||||
|
callbackContext.error("User did not specify data to encode");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (action.equals(SCAN)) {
|
||||||
|
scan();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an intent to scan and decode a barcode.
|
||||||
|
*/
|
||||||
|
public void scan() {
|
||||||
|
Intent intentScan = new Intent(SCAN_INTENT);
|
||||||
|
intentScan.addCategory(Intent.CATEGORY_DEFAULT);
|
||||||
|
|
||||||
|
this.cordova.startActivityForResult((CordovaPlugin) this, intentScan, REQUEST_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the barcode scanner intent completes.
|
||||||
|
*
|
||||||
|
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||||
|
* allowing you to identify who this result came from.
|
||||||
|
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||||
|
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
|
if (requestCode == REQUEST_CODE) {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
try {
|
||||||
|
obj.put(TEXT, intent.getStringExtra("SCAN_RESULT"));
|
||||||
|
obj.put(FORMAT, intent.getStringExtra("SCAN_RESULT_FORMAT"));
|
||||||
|
obj.put(CANCELLED, false);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.d(LOG_TAG, "This should never happen");
|
||||||
|
}
|
||||||
|
//this.success(new PluginResult(PluginResult.Status.OK, obj), this.callback);
|
||||||
|
this.callbackContext.success(obj);
|
||||||
|
} else if (resultCode == Activity.RESULT_CANCELED) {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
try {
|
||||||
|
obj.put(TEXT, "");
|
||||||
|
obj.put(FORMAT, "");
|
||||||
|
obj.put(CANCELLED, true);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.d(LOG_TAG, "This should never happen");
|
||||||
|
}
|
||||||
|
//this.success(new PluginResult(PluginResult.Status.OK, obj), this.callback);
|
||||||
|
this.callbackContext.success(obj);
|
||||||
|
} else {
|
||||||
|
//this.error(new PluginResult(PluginResult.Status.ERROR), this.callback);
|
||||||
|
this.callbackContext.error("Unexpected error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiates a barcode encode.
|
||||||
|
*
|
||||||
|
* @param type Endoiding type.
|
||||||
|
* @param data The data to encode in the bar code.
|
||||||
|
*/
|
||||||
|
public void encode(String type, String data) {
|
||||||
|
Intent intentEncode = new Intent(ENCODE_INTENT);
|
||||||
|
intentEncode.putExtra(ENCODE_TYPE, type);
|
||||||
|
intentEncode.putExtra(ENCODE_DATA, data);
|
||||||
|
|
||||||
|
this.cordova.getActivity().startActivity(intentEncode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
Copyright 2013-2014 appPlant UG
|
||||||
|
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.appplant.cordova.plugin.emailcomposer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.UriMatcher;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
public class AttachmentProvider extends ContentProvider {
|
||||||
|
|
||||||
|
public static final String AUTHORITY = ".plugin.emailcomposer.attachmentprovider";
|
||||||
|
|
||||||
|
private UriMatcher uriMatcher;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
String pkgName = this.getContext().getPackageName();
|
||||||
|
|
||||||
|
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
|
|
||||||
|
uriMatcher.addURI(pkgName + AUTHORITY, "*", 1);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||||
|
switch(uriMatcher.match(uri)) {
|
||||||
|
case 1:
|
||||||
|
String storage = getContext().getCacheDir() + EmailComposer.STORAGE_FOLDER;
|
||||||
|
String path = storage + File.separator + uri.getLastPathSegment();
|
||||||
|
|
||||||
|
File file = new File(path);
|
||||||
|
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
||||||
|
|
||||||
|
return pfd;
|
||||||
|
default:
|
||||||
|
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(Uri arg0, String arg1, String[] arg2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri insert(Uri arg0, ContentValues arg1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType(Uri arg0) {
|
||||||
|
String fileExtension = MimeTypeMap.getFileExtensionFromUrl(arg0.getPath());
|
||||||
|
String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
|
MatrixCursor result = new MatrixCursor(projection);
|
||||||
|
Object[] row = new Object[projection.length];
|
||||||
|
long fileSize = 0;
|
||||||
|
|
||||||
|
String fileLocation = getContext().getCacheDir() + File.separator + uri.getLastPathSegment();
|
||||||
|
File tempFile = new File(fileLocation);
|
||||||
|
fileSize = tempFile.length();
|
||||||
|
|
||||||
|
for (int i=0; i<projection.length; i++) {
|
||||||
|
if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.DISPLAY_NAME) == 0) {
|
||||||
|
row[i] = uri.getLastPathSegment();
|
||||||
|
} else if (projection[i].compareToIgnoreCase(MediaStore.MediaColumns.SIZE) == 0) {
|
||||||
|
row[i] = fileSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.addRow(row);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,413 @@
|
||||||
|
/*
|
||||||
|
Copyright 2013-2014 appPlant UG
|
||||||
|
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package de.appplant.cordova.plugin.emailcomposer;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.Html;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
|
import org.apache.cordova.CordovaPlugin;
|
||||||
|
import org.apache.cordova.CallbackContext;
|
||||||
|
import org.apache.cordova.PluginResult;
|
||||||
|
|
||||||
|
public class EmailComposer extends CordovaPlugin {
|
||||||
|
|
||||||
|
static protected final String STORAGE_FOLDER = File.separator + "email_composer";
|
||||||
|
|
||||||
|
private CallbackContext command;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute (String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||||
|
|
||||||
|
this.command = callbackContext;
|
||||||
|
|
||||||
|
// Eine E-Mail soll versendet werden
|
||||||
|
if ("open".equals(action)) {
|
||||||
|
open(args);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Es soll überprüft werden, ob ein Dienst zum Versenden der E-Mail zur Verfügung steht
|
||||||
|
if ("isServiceAvailable".equals(action)) {
|
||||||
|
isServiceAvailable();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returning false results in a "MethodNotFound" error.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Überprüft, ob Emails versendet werden können.
|
||||||
|
*/
|
||||||
|
private void isServiceAvailable () {
|
||||||
|
Boolean available = isEmailAccountConfigured();
|
||||||
|
PluginResult result = new PluginResult(PluginResult.Status.OK, available);
|
||||||
|
|
||||||
|
command.sendPluginResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Öffnet den Email-Kontroller mit vorausgefüllten Daten.
|
||||||
|
*/
|
||||||
|
private void open (JSONArray args) throws JSONException {
|
||||||
|
JSONObject properties = args.getJSONObject(0);
|
||||||
|
Intent draft = getDraftWithProperties(properties);
|
||||||
|
|
||||||
|
openDraft(draft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt den ViewController für Mails und fügt die übergebenen Eigenschaften ein.
|
||||||
|
*
|
||||||
|
* @param {JSONObject} params (Subject, Body, Recipients, ...)
|
||||||
|
*/
|
||||||
|
private Intent getDraftWithProperties (JSONObject params) throws JSONException {
|
||||||
|
Intent mail = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);
|
||||||
|
|
||||||
|
if (params.has("subject"))
|
||||||
|
setSubject(params.getString("subject"), mail);
|
||||||
|
if (params.has("body"))
|
||||||
|
setBody(params.getString("body"), params.optBoolean("isHtml"), mail);
|
||||||
|
if (params.has("to"))
|
||||||
|
setRecipients(params.getJSONArray("to"), mail);
|
||||||
|
if (params.has("cc"))
|
||||||
|
setCcRecipients(params.getJSONArray("cc"), mail);
|
||||||
|
if (params.has("bcc"))
|
||||||
|
setBccRecipients(params.getJSONArray("bcc"), mail);
|
||||||
|
if (params.has("attachments"))
|
||||||
|
setAttachments(params.getJSONArray("attachments"), mail);
|
||||||
|
|
||||||
|
mail.setType("application/octet-stream");
|
||||||
|
|
||||||
|
return mail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeigt den ViewController zum Versenden/Bearbeiten der Mail an.
|
||||||
|
*/
|
||||||
|
private void openDraft (final Intent draft) {
|
||||||
|
final EmailComposer plugin = this;
|
||||||
|
|
||||||
|
cordova.getThreadPool().execute( new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
cordova.startActivityForResult(plugin, Intent.createChooser(draft, "Select Email App"), 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Subject der Mail.
|
||||||
|
*/
|
||||||
|
private void setSubject (String subject, Intent draft) {
|
||||||
|
draft.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Body der Mail.
|
||||||
|
*/
|
||||||
|
private void setBody (String body, Boolean isHTML, Intent draft) {
|
||||||
|
if (isHTML) {
|
||||||
|
draft.putExtra(android.content.Intent.EXTRA_TEXT, Html.fromHtml(body));
|
||||||
|
draft.setType("text/html");
|
||||||
|
} else {
|
||||||
|
draft.putExtra(android.content.Intent.EXTRA_TEXT, body);
|
||||||
|
draft.setType("text/plain");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Empfänger der Mail.
|
||||||
|
*/
|
||||||
|
private void setRecipients (JSONArray recipients, Intent draft) throws JSONException {
|
||||||
|
String[] receivers = new String[recipients.length()];
|
||||||
|
|
||||||
|
for (int i = 0; i < recipients.length(); i++) {
|
||||||
|
receivers[i] = recipients.getString(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
draft.putExtra(android.content.Intent.EXTRA_EMAIL, receivers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die CC-Empfänger der Mail.
|
||||||
|
*/
|
||||||
|
private void setCcRecipients (JSONArray ccRecipients, Intent draft) throws JSONException {
|
||||||
|
String[] receivers = new String[ccRecipients.length()];
|
||||||
|
|
||||||
|
for (int i = 0; i < ccRecipients.length(); i++) {
|
||||||
|
receivers[i] = ccRecipients.getString(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
draft.putExtra(android.content.Intent.EXTRA_CC, receivers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die BCC-Empfänger der Mail.
|
||||||
|
*/
|
||||||
|
private void setBccRecipients (JSONArray bccRecipients, Intent draft) throws JSONException {
|
||||||
|
String[] receivers = new String[bccRecipients.length()];
|
||||||
|
|
||||||
|
for (int i = 0; i < bccRecipients.length(); i++) {
|
||||||
|
receivers[i] = bccRecipients.getString(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
draft.putExtra(android.content.Intent.EXTRA_BCC, receivers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt die Anhände zur Mail hinzu.
|
||||||
|
*/
|
||||||
|
private void setAttachments (JSONArray attachments, Intent draft) throws JSONException {
|
||||||
|
ArrayList<Uri> attachmentUris = new ArrayList<Uri>();
|
||||||
|
|
||||||
|
for (int i = 0; i < attachments.length(); i++) {
|
||||||
|
Uri attachmentUri = getUriForPath(attachments.getString(i));
|
||||||
|
|
||||||
|
attachmentUris.add(attachmentUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
draft.putParcelableArrayListExtra(Intent.EXTRA_STREAM, attachmentUris);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gibt an, ob es eine Anwendung gibt, welche E-Mails versenden kann.
|
||||||
|
*/
|
||||||
|
private Boolean isEmailAccountConfigured () {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto","max@mustermann.com", null));
|
||||||
|
Boolean available = cordova.getActivity().getPackageManager().queryIntentActivities(intent, 0).size() > 1;
|
||||||
|
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URI for an attachment path.
|
||||||
|
*
|
||||||
|
* @param {String} path
|
||||||
|
* The given path to the attachment
|
||||||
|
*
|
||||||
|
* @return The URI pointing to the given path
|
||||||
|
*/
|
||||||
|
private Uri getUriForPath (String path) {
|
||||||
|
if (path.startsWith("res:")) {
|
||||||
|
return getUriForResourcePath(path);
|
||||||
|
} else if (path.startsWith("file:")) {
|
||||||
|
return getUriForAbsolutePath(path);
|
||||||
|
} else if (path.startsWith("www:")) {
|
||||||
|
return getUriForAssetPath(path);
|
||||||
|
} else if (path.startsWith("base64:")) {
|
||||||
|
return getUriForBase64Content(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Uri.parse(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URI for a file.
|
||||||
|
*
|
||||||
|
* @param {String} path
|
||||||
|
* The given absolute path
|
||||||
|
*
|
||||||
|
* @return The URI pointing to the given path
|
||||||
|
*/
|
||||||
|
private Uri getUriForAbsolutePath (String path) {
|
||||||
|
String absPath = path.replaceFirst("file://", "");
|
||||||
|
File file = new File(absPath);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
System.err.println("Attachment path not found: " + file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Uri.fromFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URI for an asset.
|
||||||
|
*
|
||||||
|
* @param {String} path
|
||||||
|
* The given asset path
|
||||||
|
*
|
||||||
|
* @return The URI pointing to the given path
|
||||||
|
*/
|
||||||
|
private Uri getUriForAssetPath (String path) {
|
||||||
|
String resPath = path.replaceFirst("www:/", "www");
|
||||||
|
String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
|
||||||
|
String storage = cordova.getActivity().getExternalCacheDir().toString() + STORAGE_FOLDER;
|
||||||
|
|
||||||
|
File file = new File(storage, fileName);
|
||||||
|
|
||||||
|
new File(storage).mkdir();
|
||||||
|
|
||||||
|
try {
|
||||||
|
AssetManager assets = cordova.getActivity().getAssets();
|
||||||
|
|
||||||
|
FileOutputStream outStream = new FileOutputStream(file);
|
||||||
|
InputStream inputStream = assets.open(resPath);
|
||||||
|
|
||||||
|
copyFile(inputStream, outStream);
|
||||||
|
outStream.flush();
|
||||||
|
outStream.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Attachment asset not found: assets/" + resPath);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Uri.fromFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URI for a resource.
|
||||||
|
*
|
||||||
|
* @param {String} path
|
||||||
|
* The given relative path
|
||||||
|
*
|
||||||
|
* @return The URI pointing to the given path
|
||||||
|
*/
|
||||||
|
private Uri getUriForResourcePath (String path) {
|
||||||
|
String resPath = path.replaceFirst("res://", "");
|
||||||
|
String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
|
||||||
|
String resName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||||
|
String extension = resPath.substring(resPath.lastIndexOf('.'));
|
||||||
|
String storage = cordova.getActivity().getExternalCacheDir().toString() + STORAGE_FOLDER;
|
||||||
|
|
||||||
|
int resId = getResId(resPath);
|
||||||
|
File file = new File(storage, resName + extension);
|
||||||
|
|
||||||
|
if (resId == 0) {
|
||||||
|
System.err.println("Attachment resource not found: " + resPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
new File(storage).mkdir();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Resources res = cordova.getActivity().getResources();
|
||||||
|
FileOutputStream outStream = new FileOutputStream(file);
|
||||||
|
InputStream inputStream = res.openRawResource(resId);
|
||||||
|
|
||||||
|
copyFile(inputStream, outStream);
|
||||||
|
outStream.flush();
|
||||||
|
outStream.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Uri.fromFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The URI for a base64 encoded content.
|
||||||
|
*
|
||||||
|
* @param {String} content
|
||||||
|
* The given base64 encoded content
|
||||||
|
*
|
||||||
|
* @return The URI including the given content
|
||||||
|
*/
|
||||||
|
private Uri getUriForBase64Content (String content) {
|
||||||
|
String resName = content.substring(content.indexOf(":") + 1, content.indexOf("//"));
|
||||||
|
String resData = content.substring(content.indexOf("//") + 2);
|
||||||
|
byte[] bytes = Base64.decode(resData, 0);
|
||||||
|
String storage = this.cordova.getActivity().getCacheDir() + STORAGE_FOLDER;
|
||||||
|
File file = new File(storage, resName);
|
||||||
|
|
||||||
|
new File(storage).mkdir();
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileOutputStream outStream = new FileOutputStream(file);
|
||||||
|
|
||||||
|
outStream.write(bytes);
|
||||||
|
outStream.flush();
|
||||||
|
outStream.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
String pkgName = getPackageName();
|
||||||
|
String uriPath = pkgName + AttachmentProvider.AUTHORITY + "/" + resName;
|
||||||
|
|
||||||
|
return Uri.parse("content://" + uriPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an InputStream to an OutputStream
|
||||||
|
*
|
||||||
|
* @param {InputStream} in
|
||||||
|
* @param {OutputStream} out
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private void copyFile (InputStream in, OutputStream out) throws IOException {
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int read;
|
||||||
|
|
||||||
|
while((read = in.read(buffer)) != -1){
|
||||||
|
out.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* The resource ID for the given resource.
|
||||||
|
*/
|
||||||
|
private int getResId (String resPath) {
|
||||||
|
Resources res = cordova.getActivity().getResources();
|
||||||
|
|
||||||
|
String pkgName = getPackageName();
|
||||||
|
String dirName = resPath.substring(0, resPath.lastIndexOf('/'));
|
||||||
|
String fileName = resPath.substring(resPath.lastIndexOf('/') + 1);
|
||||||
|
String resName = fileName.substring(0, fileName.lastIndexOf('.'));
|
||||||
|
|
||||||
|
int resId = res.getIdentifier(resName, dirName, pkgName);
|
||||||
|
|
||||||
|
return resId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return
|
||||||
|
* The name for the package.
|
||||||
|
*/
|
||||||
|
private String getPackageName () {
|
||||||
|
return cordova.getActivity().getPackageName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, intent);
|
||||||
|
|
||||||
|
command.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
43
mobile/src/org/apache/cordova/splashscreen/SplashScreen.java
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.cordova.splashscreen;
|
||||||
|
|
||||||
|
import org.apache.cordova.CallbackContext;
|
||||||
|
import org.apache.cordova.CordovaPlugin;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
|
||||||
|
public class SplashScreen extends CordovaPlugin {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||||
|
if (action.equals("hide")) {
|
||||||
|
this.webView.postMessage("splashscreen", "hide");
|
||||||
|
} else if (action.equals("show")){
|
||||||
|
this.webView.postMessage("splashscreen", "show");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
callbackContext.success();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
124
mobile/src/org/apache/cordova/vibration/Vibration.java
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.cordova.vibration;
|
||||||
|
|
||||||
|
import org.apache.cordova.CallbackContext;
|
||||||
|
import org.apache.cordova.CordovaPlugin;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Vibrator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides access to vibration on the device.
|
||||||
|
*/
|
||||||
|
public class Vibration extends CordovaPlugin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
public Vibration() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the request and returns PluginResult.
|
||||||
|
*
|
||||||
|
* @param action The action to execute.
|
||||||
|
* @param args JSONArray of arguments for the plugin.
|
||||||
|
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||||
|
* @return True when the action was valid, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||||
|
if (action.equals("vibrate")) {
|
||||||
|
this.vibrate(args.getLong(0));
|
||||||
|
}
|
||||||
|
else if (action.equals("vibrateWithPattern")) {
|
||||||
|
JSONArray pattern = args.getJSONArray(0);
|
||||||
|
int repeat = args.getInt(1);
|
||||||
|
long[] patternArray = new long[pattern.length()];
|
||||||
|
for (int i = 0; i < pattern.length(); i++) {
|
||||||
|
patternArray[i] = pattern.getLong(i);
|
||||||
|
}
|
||||||
|
this.vibrateWithPattern(patternArray, repeat);
|
||||||
|
}
|
||||||
|
else if (action.equals("cancelVibration")) {
|
||||||
|
this.cancelVibration();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only alert and confirm are async.
|
||||||
|
callbackContext.success();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
// LOCAL METHODS
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vibrates the device for a given amount of time.
|
||||||
|
*
|
||||||
|
* @param time Time to vibrate in ms.
|
||||||
|
*/
|
||||||
|
public void vibrate(long time) {
|
||||||
|
// Start the vibration, 0 defaults to half a second.
|
||||||
|
if (time == 0) {
|
||||||
|
time = 500;
|
||||||
|
}
|
||||||
|
Vibrator vibrator = (Vibrator) this.cordova.getActivity().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
vibrator.vibrate(time);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vibrates the device with a given pattern.
|
||||||
|
*
|
||||||
|
* @param pattern Pattern with which to vibrate the device.
|
||||||
|
* Pass in an array of longs that
|
||||||
|
* are the durations for which to
|
||||||
|
* turn on or off the vibrator in
|
||||||
|
* milliseconds. The first value
|
||||||
|
* indicates the number of milliseconds
|
||||||
|
* to wait before turning the vibrator
|
||||||
|
* on. The next value indicates the
|
||||||
|
* number of milliseconds for which
|
||||||
|
* to keep the vibrator on before
|
||||||
|
* turning it off. Subsequent values
|
||||||
|
* alternate between durations in
|
||||||
|
* milliseconds to turn the vibrator
|
||||||
|
* off or to turn the vibrator on.
|
||||||
|
*
|
||||||
|
* @param repeat Optional index into the pattern array at which
|
||||||
|
* to start repeating, or -1 for no repetition (default).
|
||||||
|
*/
|
||||||
|
public void vibrateWithPattern(long[] pattern, int repeat) {
|
||||||
|
Vibrator vibrator = (Vibrator) this.cordova.getActivity().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
vibrator.vibrate(pattern, repeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Immediately cancels any currently running vibration.
|
||||||
|
*/
|
||||||
|
public void cancelVibration() {
|
||||||
|
Vibrator vibrator = (Vibrator) this.cordova.getActivity().getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
vibrator.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/bitpay/copay/issues"
|
"url": "https://github.com/bitpay/copay/issues"
|
||||||
},
|
},
|
||||||
"version": "0.3.2",
|
"version": "0.4.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"preconditions": "^1.0.7",
|
"preconditions": "^1.0.7",
|
||||||
"sinon": "1.9.1",
|
"sinon": "1.9.1",
|
||||||
|
|
@ -48,7 +48,7 @@
|
||||||
"github-releases": "0.2.0",
|
"github-releases": "0.2.0",
|
||||||
"grunt-markdown": "0.5.0",
|
"grunt-markdown": "0.5.0",
|
||||||
"browser-pack": "2.0.1",
|
"browser-pack": "2.0.1",
|
||||||
"bitcore": "0.1.25",
|
"bitcore": "0.1.34",
|
||||||
"node-cryptojs-aes": "0.4.0",
|
"node-cryptojs-aes": "0.4.0",
|
||||||
"blanket": "1.1.6",
|
"blanket": "1.1.6",
|
||||||
"express": "4.0.0",
|
"express": "4.0.0",
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ var WalletKey = bitcore.WalletKey;
|
||||||
var Key = bitcore.Key;
|
var Key = bitcore.Key;
|
||||||
var bignum = bitcore.Bignum;
|
var bignum = bitcore.Bignum;
|
||||||
var Script = bitcore.Script;
|
var Script = bitcore.Script;
|
||||||
var Builder = bitcore.TransactionBuilder;
|
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||||
var util = bitcore.util;
|
var util = bitcore.util;
|
||||||
var networks = bitcore.networks;
|
var networks = bitcore.networks;
|
||||||
try {
|
try {
|
||||||
|
|
@ -139,7 +139,6 @@ describe('TxProposals model', function() {
|
||||||
var createTx = function(toAddress, amountSatStr, utxos, opts, priv, pkr) {
|
var createTx = function(toAddress, amountSatStr, utxos, opts, priv, pkr) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
|
||||||
var amountSat = bitcore.Bignum(amountSatStr);
|
|
||||||
var pub = priv.publicHex;
|
var pub = priv.publicHex;
|
||||||
|
|
||||||
if (!pkr.isComplete()) {
|
if (!pkr.isComplete()) {
|
||||||
|
|
@ -152,11 +151,11 @@ describe('TxProposals model', function() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var b = new Builder(opts)
|
var b = new TransactionBuilder(opts)
|
||||||
.setUnspent(utxos)
|
.setUnspent(utxos)
|
||||||
.setOutputs([{
|
.setOutputs([{
|
||||||
address: toAddress,
|
address: toAddress,
|
||||||
amountSat: amountSat
|
amountSatStr: amountSatStr,
|
||||||
}]);
|
}]);
|
||||||
var selectedUtxos = b.getSelectedUnspent();
|
var selectedUtxos = b.getSelectedUnspent();
|
||||||
var inputChainPaths = selectedUtxos.map(function(utxo) {
|
var inputChainPaths = selectedUtxos.map(function(utxo) {
|
||||||
|
|
@ -177,11 +176,13 @@ describe('TxProposals model', function() {
|
||||||
var me = {};
|
var me = {};
|
||||||
if (priv) me[priv.getId()] = Date.now();
|
if (priv) me[priv.getId()] = Date.now();
|
||||||
|
|
||||||
|
var tx = b.build();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
inputChainPaths: inputChainPaths,
|
inputChainPaths: inputChainPaths,
|
||||||
creator: priv.getId(),
|
creator: priv.getId(),
|
||||||
createdTs: new Date(),
|
createdTs: new Date(),
|
||||||
signedBy: priv && b.signaturesAdded ? me : {},
|
signedBy: priv && tx.countInputSignatures(0) ? me : {},
|
||||||
seenBy: priv ? me : {},
|
seenBy: priv ? me : {},
|
||||||
builder: b,
|
builder: b,
|
||||||
};
|
};
|
||||||
|
|
@ -287,6 +288,7 @@ describe('TxProposals model', function() {
|
||||||
var ntxid = Object.keys(w.txps)[0];
|
var ntxid = Object.keys(w.txps)[0];
|
||||||
var tx = w.txps[ntxid].builder.build();
|
var tx = w.txps[ntxid].builder.build();
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
|
tx.countInputSignatures(0).should.equal(0);
|
||||||
tx.countInputMissingSignatures(0).should.equal(1);
|
tx.countInputMissingSignatures(0).should.equal(1);
|
||||||
|
|
||||||
Object.keys(w.txps[ntxid].signedBy).length.should.equal(0);
|
Object.keys(w.txps[ntxid].signedBy).length.should.equal(0);
|
||||||
|
|
@ -313,7 +315,8 @@ describe('TxProposals model', function() {
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
(w2.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true);
|
|
||||||
|
(w2.txps[ntxid].signedBy[priv.id] - ts > 0).should.equal(true, 'asdsd');
|
||||||
(w2.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].seenBy[priv.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
var info = w.merge(w2.txps[ntxid], pkr.getCopayerId(0));
|
var info = w.merge(w2.txps[ntxid], pkr.getCopayerId(0));
|
||||||
|
|
@ -390,7 +393,6 @@ describe('TxProposals model', function() {
|
||||||
|
|
||||||
var ntxid = Object.keys(w.txps)[0];
|
var ntxid = Object.keys(w.txps)[0];
|
||||||
txb = w.txps[ntxid].builder;
|
txb = w.txps[ntxid].builder;
|
||||||
txb.signaturesAdded.should.equal(0);
|
|
||||||
tx = txb.build();
|
tx = txb.build();
|
||||||
|
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
|
|
@ -417,7 +419,6 @@ describe('TxProposals model', function() {
|
||||||
txb = w2.txps[ntxid].builder;
|
txb = w2.txps[ntxid].builder;
|
||||||
tx = txb.build();
|
tx = txb.build();
|
||||||
|
|
||||||
txb.signaturesAdded.should.equal(1);
|
|
||||||
tx.isComplete().should.equal(false);
|
tx.isComplete().should.equal(false);
|
||||||
tx.countInputMissingSignatures(0).should.equal(2);
|
tx.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
||||||
|
|
@ -526,7 +527,6 @@ describe('TxProposals model', function() {
|
||||||
(w2.txps[ntxid].signedBy[priv2.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].signedBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
(w2.txps[ntxid].seenBy[priv2.id] - ts > 0).should.equal(true);
|
(w2.txps[ntxid].seenBy[priv2.id] - ts > 0).should.equal(true);
|
||||||
|
|
||||||
|
|
||||||
var w3 = new TxProposals({
|
var w3 = new TxProposals({
|
||||||
networkName: config.networkName,
|
networkName: config.networkName,
|
||||||
});
|
});
|
||||||
|
|
@ -573,6 +573,13 @@ describe('TxProposals model', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('#fromObj stored (hardcoded) data', function() {
|
||||||
|
var txp = TxProposals.TxProposal.fromObj(txpv1);
|
||||||
|
txp.getID().should.equal('5cae6e225335acd2725856c71ef1ca61c42f118967102c5d0ed6710343e4a19f');
|
||||||
|
var tx = txp.builder.build();
|
||||||
|
tx.countInputSignatures(0).should.equal(2);
|
||||||
|
tx.countInputMissingSignatures(0).should.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
it('#toObj #fromObj roundtrip', function() {
|
it('#toObj #fromObj roundtrip', function() {
|
||||||
|
|
||||||
|
|
@ -605,16 +612,17 @@ describe('TxProposals model', function() {
|
||||||
var o = w.toObj();
|
var o = w.toObj();
|
||||||
should.exist(o);
|
should.exist(o);
|
||||||
o.txps.length.should.equal(1);
|
o.txps.length.should.equal(1);
|
||||||
|
|
||||||
should.exist(o.txps[0]);
|
should.exist(o.txps[0]);
|
||||||
should.exist(o.txps[0].signedBy);
|
should.exist(o.txps[0].signedBy);
|
||||||
should.exist(o.txps[0].seenBy);
|
should.exist(o.txps[0].seenBy);
|
||||||
should.exist(o.txps[0].builderObj);
|
should.exist(o.txps[0].builderObj);
|
||||||
should.exist(o.txps[0].builderObj.valueInSat);
|
|
||||||
should.exist(o.txps[0].signedBy[priv.id]);
|
should.exist(o.txps[0].signedBy[priv.id]);
|
||||||
|
|
||||||
var o2 = JSON.parse(JSON.stringify(o));
|
var o2 = JSON.parse(JSON.stringify(o));
|
||||||
var w2 = TxProposals.fromObj(o2);
|
var w2 = TxProposals.fromObj(o2);
|
||||||
w2.walletId.should.equal(w.walletId);
|
w2.walletId.should.equal(w.walletId);
|
||||||
|
|
||||||
var tx2 = w2.txps[ntxid].builder.build();
|
var tx2 = w2.txps[ntxid].builder.build();
|
||||||
tx2.isComplete().should.equal(false);
|
tx2.isComplete().should.equal(false);
|
||||||
tx2.countInputMissingSignatures(0).should.equal(2);
|
tx2.countInputMissingSignatures(0).should.equal(2);
|
||||||
|
|
@ -627,5 +635,93 @@ describe('TxProposals model', function() {
|
||||||
Object.keys(w2.txps).length.should.equal(1);
|
Object.keys(w2.txps).length.should.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('TxProposal model', function() {
|
||||||
|
var createMockTxp = function(raw) {
|
||||||
|
var tx = new Transaction();
|
||||||
|
tx.parse(new Buffer(raw, 'hex'));
|
||||||
|
var txb = new TransactionBuilder();
|
||||||
|
var txp = new TxProposals.TxProposal({
|
||||||
|
builder: txb
|
||||||
|
});
|
||||||
|
txb.build = function() {
|
||||||
|
return tx;
|
||||||
|
};
|
||||||
|
return txp;
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should validate for no signatures yet in tx', function() {
|
||||||
|
// taken from https://gist.github.com/gavinandresen/3966071
|
||||||
|
var raw = '010000000189632848f99722915727c5c75da8db2dbf194342a0429828f66ff88fab2af7d60000000000ffffffff0140420f000000000017a914f815b036d9bbbce5e9f2a00abd1bf3dc91e955108700000000';
|
||||||
|
var txp = createMockTxp(raw);
|
||||||
|
txp.isValid().should.equal(true);
|
||||||
|
});
|
||||||
|
it('should validate for no signatures yet in copay generated tx', function() {
|
||||||
|
// taken from copay incomplete tx proposal
|
||||||
|
var raw = '0100000001e205297fd05e4504d72761dc7a16e5cc9f4ab89877f28aee97c1cc66b3f07d690100000000ffffffff01706f9800000000001976a91473707e88f79c9c616b44bc766a25efcb9f49346688ac00000000';
|
||||||
|
var txp = createMockTxp(raw);
|
||||||
|
txp.isValid().should.equal(true);
|
||||||
|
});
|
||||||
|
it('should validate for a SIGHASH_NONE tx in builder', function() {
|
||||||
|
var raw = '010000000145c3bf51ced6cefaea8c6578a645316270dbf8600f46969d31136e1e06829598000000007000483045022100877c715e0f3bd6377086c96d4757b2c983682a1934d9e3f894941f4f1e18d4710220272ed81758d7a391ee4c15a29246f3fe75efbddeaf1118e4c0d3bb14f57cdba601255121022f58491a833933a9bea80d8e820e66bee91bd8c71bfa972fe70482360b48129951aeffffffff01706f9800000000001976a91408328947f0caf8728729d740cbecdfe3c2327db588ac00000000';
|
||||||
|
var txp = createMockTxp(raw);
|
||||||
|
txp.isValid().should.equal(true);
|
||||||
|
});
|
||||||
|
it('should not validate for a non SIGHASH_NONE tx in builder with 1 input', function() {
|
||||||
|
var raw = '0100000001eaf08f93f895127fbf000128ac74f6e8c7f003854e5ee1f02a5fd820cb689beb00000000fdfe00004730440220778f3174393e9ee6b0bfa876b4150db6f12a4da9715044ead5e345c2781ceee002203aab31f1e1d3dcf77ca780d9af798139719891917c9a09123dba54483ef462bc02493046022100dd93b64b30580029605dbba09d7fa34194d9ff38fda0c4fa187c52bf7f79ae98022100dd7b056762087b9aa8ccfde328d7067fa1753b78c0ee25577122569ff9de1d57024c695221039f847c24f09d7299c10bba4e41b24dc78e47bbb05fd7c1d209c994899d6881062103d363476e634fc5cdc11e9330c05a141c1e0c7f8b616817bdb83e7579bbf870942103fb2072953ceab87c6da450ac661685a881ddb661002d2ec1d60bfd33e3ec807d53aeffffffff01d06bf5050000000017a914db682f579cf6ca483880460fcf4ab63e223dc07e8700000000';
|
||||||
|
var txp = createMockTxp(raw);
|
||||||
|
txp.isValid().should.equal(false);
|
||||||
|
});
|
||||||
|
it('should not validate for a non SIGHASH_NONE tx in builder with 1 input', function() {
|
||||||
|
var raw = '0100000002d903852d223b3100fcc01e0b02d73a76a0787cdff7d000e9cba0e931917f407501000000fdfe0000493046022100b232e994fdca7fd61fcf8ffe4a7f746ff8f8baf2667ac80841de0250f521c402022100862c0783ca7eafcbd2786b9444ed6e83ae941dcc2248bea4db12b7815d15de050247304402200189fe0cde9d1dd192553f4dddb6764df3eb643f9f71be8aa015f41f2d4fd11f02205513b8ca985c3b5b936f814c7eba92e2e2985c83927ca06c41081d264c0be7a7024c695221026fa1a3ed0c820c1053c8ba101f3c96f85c55624a902a82cf6b2896ed5f9b3d1521035a3383c13dd346a5784adfe3ec3026ab31d519fdfae2740497b10bdfb994e6442103c7477a6668d5bc250fe727e358d951b9e05f1d7c02059bf59ecbb335f1eeec7953aeffffffffd903852d223b3100fcc01e0b02d73a76a0787cdff7d000e9cba0e931917f407500000000fdfd0000483045022100bdb9d14569af66d84af63416d77296ace24a96f1720d30e74bc6e316a4b3727502206ed54d532467393488889d72edbb667d075de491a89e8e496fee8791b943fa37024730440220379c30c884a21a949d8ec32d6934ffa9faf86add4d839de0f5fbd2b90f8ef1e802204048df2ec0035ce5e4bf01e9d70fd93a45a41ce2630100d692cd908cdaa61fc0024c69522102203938ef947327edce2cf2997c55b433be3d3ffcf3284c10d6fcdf4b01c6221f21033b60c3363a226ce9b850af655c6e1470d9a0936d7f56ea4a07ab84005f91cd1b210385755bc813fe7f92577b93bf689bf0d9b2118e6bbb7fee5d3d16976f4f7271af53aeffffffff01c02d9a3b0000000017a914db682f579cf6ca483880460fcf4ab63e223dc07e8700000000';
|
||||||
|
var txp = createMockTxp(raw);
|
||||||
|
txp.isValid().should.equal(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var txpv1 = {
|
||||||
|
"creator": "0361fb4252367715405a0d27f99cc74a671133292e8d725e009536d7257c8c01b0",
|
||||||
|
"createdTs": 1406310417996,
|
||||||
|
"seenBy": {
|
||||||
|
"0361fb4252367715405a0d27f99cc74a671133292e8d725e009536d7257c8c01b0": 1406310417996,
|
||||||
|
"02ba1599c64da4d80e25985be46c50e944b65f02e2b48c930528ce763d6710158f": 1406310418162
|
||||||
|
},
|
||||||
|
"signedBy": {
|
||||||
|
"0361fb4252367715405a0d27f99cc74a671133292e8d725e009536d7257c8c01b0": 1406310417996,
|
||||||
|
"02ba1599c64da4d80e25985be46c50e944b65f02e2b48c930528ce763d6710158f": 1406310645549
|
||||||
|
},
|
||||||
|
"rejectedBy": {},
|
||||||
|
"sentTs": 1406310645873,
|
||||||
|
"sentTxid": "87296c50e8601437d63d556afb27c3b8e3819214be0a9d756d401a8286c0ec43",
|
||||||
|
"inputChainPaths": ["m/45'/0/1/1"],
|
||||||
|
"comment": "test 6",
|
||||||
|
"builderObj": {
|
||||||
|
"version": 1,
|
||||||
|
"outs": [{
|
||||||
|
"address": "mph66bnLvcn9KUSMrpikUBUZZkN2C1Z5tg",
|
||||||
|
"amountSatStr": 100
|
||||||
|
}],
|
||||||
|
"utxos": [{
|
||||||
|
"address": "2NEodmgBa4SH3VwE2asgW34vMYe8VThBZNo",
|
||||||
|
"txid": "8f8deda12dad6248e655054632a27f6891ebb37e8d2b3dd1bff87e71fd451ac7",
|
||||||
|
"vout": 1,
|
||||||
|
"ts": 1406312717,
|
||||||
|
"scriptPubKey": "a914ec7bce12d0e82a7d2b5431f6d89ca70af317f5a187",
|
||||||
|
"amount": 0.009798,
|
||||||
|
"confirmations": 0,
|
||||||
|
"confirmationsFromCache": false
|
||||||
|
}],
|
||||||
|
"opts": {
|
||||||
|
"spendUnconfirmed": true,
|
||||||
|
"remainderOut": {
|
||||||
|
"address": "2N74XAozMH3JB3XgeBkRvRw1J8TtfLTtvny"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scriptSig": ["00483045022100f167ad33b8bef4c65af8d19c1a849d1770cc8d1e35bffebe6b5459dcbe655c7802207b37370b308ba668fe19f8e8bc462c9fbdc6c67f79900670758d228d83ea96da014730440220038ad3f4cc7b0738b593454ec189913ae4b442bc83da153d68d9a0077bd1b09102202b5728a08f302e97de61ea37280b48ccdd575f0d235c22f5e0ecac6a4ab0f46401475221024739614847d5233a46913482c17c6860194ad78abb3bf47de46223047d8a0b5821024c6dc65a52c5eaaa080b96888091544f8ab8712caa7e0b69ea4b45f6f059557452ae"],
|
||||||
|
"hashToScriptMap": {
|
||||||
|
"2NEodmgBa4SH3VwE2asgW34vMYe8VThBZNo": "5221024739614847d5233a46913482c17c6860194ad78abb3bf47de46223047d8a0b5821024c6dc65a52c5eaaa080b96888091544f8ab8712caa7e0b69ea4b45f6f059557452ae"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ var Network = require('./mocks/FakeNetwork');
|
||||||
var Blockchain = require('./mocks/FakeBlockchain');
|
var Blockchain = require('./mocks/FakeBlockchain');
|
||||||
var bitcore = bitcore || require('bitcore');
|
var bitcore = bitcore || require('bitcore');
|
||||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||||
|
var Transaction = bitcore.Transaction;
|
||||||
|
var Address = bitcore.Address;
|
||||||
|
|
||||||
var addCopayers = function(w) {
|
var addCopayers = function(w) {
|
||||||
for (var i = 0; i < 4; i++) {
|
for (var i = 0; i < 4; i++) {
|
||||||
|
|
@ -424,46 +426,46 @@ describe('Wallet model', function() {
|
||||||
var w = createW();
|
var w = createW();
|
||||||
var txp = {
|
var txp = {
|
||||||
'txProposal': {
|
'txProposal': {
|
||||||
"seenBy": {
|
creator: '02c643ef43c14481fa8e81e61438c2cbc39a59024663f8cab575d28a248fe53d96',
|
||||||
"undefined": 1402337282806
|
createdTs: '2014-07-24T23:54:26.682Z',
|
||||||
|
seenBy: {
|
||||||
|
'02c643ef43c14481fa8e81e61438c2cbc39a59024663f8cab575d28a248fe53d96': 1406246066682
|
||||||
},
|
},
|
||||||
"signedBy": {
|
signedBy: {
|
||||||
"undefined": 1402337282841
|
'02c643ef43c14481fa8e81e61438c2cbc39a59024663f8cab575d28a248fe53d96': 1406246066682
|
||||||
},
|
},
|
||||||
"rejectedBy": {},
|
rejectedBy: {},
|
||||||
"sentTs": null,
|
sentTs: null,
|
||||||
"sentTxid": null,
|
sentTxid: null,
|
||||||
"inputChainPaths": [],
|
inputChainPaths: ['m/45\'/2/0/0'],
|
||||||
"builderObj": {
|
comment: null,
|
||||||
"valueInSat": "1000000000",
|
builderObj: {
|
||||||
"valueOutSat": "123456789",
|
version: 1,
|
||||||
"feeSat": "10000",
|
outs: [{
|
||||||
"remainderSat": "876533211",
|
address: '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
|
||||||
"hashToScriptMap": {
|
amountSatStr: '123456789'
|
||||||
"3QjgeBsNeiDkoVQxDAw4sSEe9BVaqhmpnd": "5321025c2951a7e94e39f2c3210fb2d71b7d79044f7daa6b007556a6feb4b2059091e921026c56eea4ef632b24c4efe9d5391ea0e15e306450e5b7db26cd1a7a33d9d4e82821026d06e532385186793121633fb365fa2f0c7246b30590db78cba795641c94734521035051d2b742263bc5257c5a4461e1a6fa32c3f1965f5ab5e8b0f0f80c9e28bc702103ddb0d39c1cd88295e7b81050dcab11ca4419bf64fba3ef9d9130ea64d6604ab055ae",
|
|
||||||
"365dPtmHPAdUUNvPBcnfidYt2Snyaka7AH": "5321020faaf739abda51e822621d42062470c5743a127186369a67fd3303f71727fb1d210237063d3b27c21767054dad203298bfed90cb08dca371950a08b88aa979c33bd22102a3780cb275b6dcb8b379514679387d4578068745f08c5799e4ee1a90aa3a70922102f4fea0cd5bc2418892278f480682690857f4d963deeb95d88febfa255e4ce0b821035d53fa74fee26873c3f5a4acf4dce53ff9d50db59e1ca4acddb82168dd429a3355ae",
|
|
||||||
"372DueknmmjVR3zcWfEfADp8FwJ3ARhGaN": "532102666c2873c9e6f58ca594cd6c8327a1515db32ebadbaff4fe75e63b917560b7d021031ca2d159ae8868a5eef6d67a5a8a5bcc6fb2e8b711669f1c1e8b05327236a3cf2103798a4ce34929cb450bf88557dd90cae538c67f9d0c76314ae18679200be9c17021039624dec23dc1bb628cea55e949ea26225949a3349346a0732fef9def6f1f75292103d1edaadb8555012b752dd7a5c6672c379ce827e4bc0d4e992ee1ab3488b445b255ae",
|
|
||||||
"3GLJbHv7RqPeFk2SqWapqJ3XibVibiKi8f": "5321021a33d48b9f5d3adc41004709313277d4c6969268cf41e3f5b695a934c676829a21031b87321307db7a0f6ea22847a538505188f1a2231eba68867e17c5f0e4434c0721035dee0a6e1df66a6c9c7592ef03aa02bba9b06742134d27bd08f356e33f21259c2103768a109d682a76c09f865912e9e64a8689b03c1a231c613d87ec9bd535fd74c22103911038638b9fc52b2d375ce207c8388bd5ee7f2d54ab9b4148bd406c6d7dcad355ae",
|
|
||||||
"3E1d1z7gJSFeZM2d3k12MJ1jGwrBRy1YTw": "5321027da98ce0407138461f4ad803a1fb67afa4246f06ad0e1256a087daeffd82a8642102e7f87f2b094ec322be2fb99d3ca81fd6ac0ab26ce081e51ab18c507f8b6d1d162102ed5d256036e10568c33e0a15021cc1806e04d7d24c8f020aaae23ec2deecb4302103b6f88231bb5a5f4629e4d097492321f86968c6aeb012196a1fe3b217fe4ae0ac2103f6b5e2c1db3124a5058a0e3e83d5d0c412b9a5b50e2ef97e2b7e1c0af57ab3e355ae",
|
|
||||||
"3QYueyPXq2QDRQCDo7agTimz9673NKV25E": "5321021543292c2942708ccc83354ebf507044b310ed0d33a19e2327a49be53e0f314221024a1a83f8c50f6cad7c134b9cded509dabf196ae49eca157a39ad95798943dc95210292698fbb97e8f6e67296b22b36367ba85c8101fcbc27bb4b00b43623639212ac2102d86980796027a00ba7aa9b53671762c908962654f969f5dec340071bb30e8d7621038fb3fa5e77dafd24c09d819dbdc7c11dca55b350511bf3bc499c09316a89286e55ae",
|
|
||||||
"372BzC1GGjziT8zGYbryja3kF2KaTeobRK": "53210214ec25e5cb42e51883d4e615316748feefe91133fcfc0f93f0b5a24a55e0a347210262336210b3173aa4ca90d292990f463e42bdeb2e73112925dc712c5a2e749bcb210277517855f512564f225e63c650dad7720565aa563901d50743be4b0f0267dcc72102d2777a9faf5d2e2b2363e1270d61021bc2e36e8cb19ca9d29dedbba9a0348b532103a57db80b6ae573e5cef2115e36e73e5ef41f8a099bfb5087d80320c04a7db72e55ae"
|
|
||||||
},
|
|
||||||
"selectedUtxos": [{
|
|
||||||
"address": "3E1d1z7gJSFeZM2d3k12MJ1jGwrBRy1YTw",
|
|
||||||
"scriptPubKey": "a91487264aa41e3df76f3156c7fa587fd7d5b1f7b96b87",
|
|
||||||
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
|
||||||
"vout": 1,
|
|
||||||
"amount": 10,
|
|
||||||
"confirmations": 7
|
|
||||||
}],
|
}],
|
||||||
"inputsSigned": 0,
|
utxos: [{
|
||||||
"signaturesAdded": 1,
|
address: '2N6fdPg2QL7V36XKe7a8wkkA5HCy7fNYmZF',
|
||||||
"signhash": 1,
|
scriptPubKey: 'a91493372782bab70f4eefdefefea8ece0df44f9596887',
|
||||||
"spendUnconfirmed": false,
|
txid: '2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1',
|
||||||
"tx": "0100000001c1cf12ab89729d19d3cdec8ae531b5038d56c741006a105d532b3a7afa65c12a01000000fd40010047304402201aad0ea92f929be21d60afb741b76bfcf2aa9614079cb9da76b15b3a1210f07b02205bc5c1895105da3ee682e532d2d49dfd1214fa6123fb9c9d552336f135c77eff0147304402203f02f15bb4ad4bc7d0ca4612fd9e9ae05a1331f3d62f302cf08dba56695f5dcf0220700110562fe228ebba324b120de64a7bbaddf049d67a313f749e9b32ee88787d014cad5321027da98ce0407138461f4ad803a1fb67afa4246f06ad0e1256a087daeffd82a8642102e7f87f2b094ec322be2fb99d3ca81fd6ac0ab26ce081e51ab18c507f8b6d1d162102ed5d256036e10568c33e0a15021cc1806e04d7d24c8f020aaae23ec2deecb4302103b6f88231bb5a5f4629e4d097492321f86968c6aeb012196a1fe3b217fe4ae0ac2103f6b5e2c1db3124a5058a0e3e83d5d0c412b9a5b50e2ef97e2b7e1c0af57ab3e355aeffffffff0215cd5b07000000001976a91434f8e0c5be216025a52addf18a987543cad23f7a88acdbd53e340000000017a914a09f10bc42b61ecf9d3d09550765c228f1cb808a8700000000"
|
vout: 1,
|
||||||
|
amount: 10,
|
||||||
|
confirmations: 7
|
||||||
|
}],
|
||||||
|
opts: {
|
||||||
|
remainderOut: {
|
||||||
|
address: '2N7BLvdrxJ4YzDtb3hfgt6CMY5rrw5kNT1H'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scriptSig: ['00493046022100b8249a4fc326c4c33882e9d5468a1c6faa01e8c6cef0a24970122e804abdd860022100dbf6ee3b07d3aad8f73997e62ad20654a08aa63a7609792d02f3d5d088e69ad9014cad5321027445ab3a935dce7aee1dadb0d103ed6147a0f83deb80474a04538b2c5bc4d5092102ab32ba51402a139873aeb919c738f5a945f3956f8f8c6ba296677bd29e85d7e821036f119b72e09f76c11ebe2cf754d64eac2cb42c9e623455d54aaa89d70c11f9c82103bcbd3f8ab2c849ea9eae434733cee8b75120d26233def56011b3682ca12081d72103f37f81dc534163b9f73ecf36b91e6c3fb8ae370c24618f91bb1d972e86ceeee255ae'],
|
||||||
|
hashToScriptMap: {
|
||||||
|
'2N6fdPg2QL7V36XKe7a8wkkA5HCy7fNYmZF': '5321027445ab3a935dce7aee1dadb0d103ed6147a0f83deb80474a04538b2c5bc4d5092102ab32ba51402a139873aeb919c738f5a945f3956f8f8c6ba296677bd29e85d7e821036f119b72e09f76c11ebe2cf754d64eac2cb42c9e623455d54aaa89d70c11f9c82103bcbd3f8ab2c849ea9eae434733cee8b75120d26233def56011b3682ca12081d72103f37f81dc534163b9f73ecf36b91e6c3fb8ae370c24618f91bb1d972e86ceeee255ae'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
w._handleTxProposal('senderID', txp, true);
|
w._handleTxProposal('senderID', txp, true);
|
||||||
Object.keys(w.txProposals.txps).length.should.equal(1);
|
Object.keys(w.txProposals.txps).length.should.equal(1);
|
||||||
w.getTxProposals().length.should.equal(1);
|
w.getTxProposals().length.should.equal(1);
|
||||||
|
|
@ -702,7 +704,7 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#send', function() {
|
describe('#send', function() {
|
||||||
it('should call this.network.send', function () {
|
it('should call this.network.send', function() {
|
||||||
var w = cachedCreateW2();
|
var w = cachedCreateW2();
|
||||||
var save = w.network.send;
|
var save = w.network.send;
|
||||||
w.network.send = sinon.spy();
|
w.network.send = sinon.spy();
|
||||||
|
|
@ -778,12 +780,14 @@ describe('Wallet model', function() {
|
||||||
return index <= 14 && index % 2 == 0;
|
return index <= 14 && index % 2 == 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
var updateIndex = sinon.stub(w, 'updateIndex', function(i, cb) { cb(); });
|
var updateIndex = sinon.stub(w, 'updateIndex', function(i, cb) {
|
||||||
|
cb();
|
||||||
|
});
|
||||||
|
|
||||||
w.updateIndexes(function(err) {
|
w.updateIndexes(function(err) {
|
||||||
// check updated all indexes
|
// check updated all indexes
|
||||||
var cosignersChecked = []
|
var cosignersChecked = []
|
||||||
updateIndex.args.forEach(function(i){
|
updateIndex.args.forEach(function(i) {
|
||||||
cosignersChecked.indexOf(i[0].cosigner).should.equal(-1);
|
cosignersChecked.indexOf(i[0].cosigner).should.equal(-1);
|
||||||
cosignersChecked.push(i[0].cosigner);
|
cosignersChecked.push(i[0].cosigner);
|
||||||
});
|
});
|
||||||
|
|
@ -800,7 +804,9 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
var indexDiscovery = sinon.stub(w, 'indexDiscovery', function(a, b, c, d, cb) { cb(null, 8); });
|
var indexDiscovery = sinon.stub(w, 'indexDiscovery', function(a, b, c, d, cb) {
|
||||||
|
cb(null, 8);
|
||||||
|
});
|
||||||
var index = {
|
var index = {
|
||||||
changeIndex: 1,
|
changeIndex: 1,
|
||||||
receiveIndex: 2,
|
receiveIndex: 2,
|
||||||
|
|
@ -820,7 +826,9 @@ describe('Wallet model', function() {
|
||||||
mockFakeActivity(function(index) {
|
mockFakeActivity(function(index) {
|
||||||
return index <= 14 && index % 2 == 0;
|
return index <= 14 && index % 2 == 0;
|
||||||
});
|
});
|
||||||
var indexDiscovery = sinon.stub(w, 'indexDiscovery', function(a, b, c, d, cb) { cb(null, 8); });
|
var indexDiscovery = sinon.stub(w, 'indexDiscovery', function(a, b, c, d, cb) {
|
||||||
|
cb(null, 8);
|
||||||
|
});
|
||||||
var spyStore = sinon.spy(w, 'store');
|
var spyStore = sinon.spy(w, 'store');
|
||||||
w.updateIndexes(function(err) {
|
w.updateIndexes(function(err) {
|
||||||
sinon.assert.callCount(spyStore, 1);
|
sinon.assert.callCount(spyStore, 1);
|
||||||
|
|
@ -875,7 +883,7 @@ describe('Wallet model', function() {
|
||||||
w.addressBook[key].hidden.should.equal(true);
|
w.addressBook[key].hidden.should.equal(true);
|
||||||
w.toggleAddressBookEntry(key);
|
w.toggleAddressBookEntry(key);
|
||||||
w.addressBook[key].hidden.should.equal(false);
|
w.addressBook[key].hidden.should.equal(false);
|
||||||
(function() {
|
(function() {
|
||||||
w.toggleAddressBookEntry();
|
w.toggleAddressBookEntry();
|
||||||
}).should.throw();
|
}).should.throw();
|
||||||
});
|
});
|
||||||
|
|
@ -884,26 +892,26 @@ describe('Wallet model', function() {
|
||||||
var w = createW();
|
var w = createW();
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
type: "addressbook",
|
type: "addressbook",
|
||||||
addressBook: {
|
addressBook: {
|
||||||
"3Ae1ieAYNXznm7NkowoFTu5MkzgrTfDz8Z" : {
|
"3Ae1ieAYNXznm7NkowoFTu5MkzgrTfDz8Z": {
|
||||||
copayerId: "03baa45498fee1045fa8f91a2913f638dc3979b455498924d3cf1a11303c679cdb",
|
copayerId: "03baa45498fee1045fa8f91a2913f638dc3979b455498924d3cf1a11303c679cdb",
|
||||||
createdTs: 1404769393509,
|
createdTs: 1404769393509,
|
||||||
hidden: false,
|
hidden: false,
|
||||||
label: "adsf",
|
label: "adsf",
|
||||||
signature: "3046022100d4cdefef66ab8cea26031d5df03a38fc9ec9b09b0fb31d3a26b6e204918e9e78022100ecdbbd889ec99ea1bfd471253487af07a7fa7c0ac6012ca56e10e66f335e4586"
|
signature: "3046022100d4cdefef66ab8cea26031d5df03a38fc9ec9b09b0fb31d3a26b6e204918e9e78022100ecdbbd889ec99ea1bfd471253487af07a7fa7c0ac6012ca56e10e66f335e4586"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
walletId: "11d23e638ed84c06",
|
walletId: "11d23e638ed84c06",
|
||||||
isBroadcast: 1
|
isBroadcast: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
var senderId = "03baa45498fee1045fa8f91a2913f638dc3979b455498924d3cf1a11303c679cdb";
|
var senderId = "03baa45498fee1045fa8f91a2913f638dc3979b455498924d3cf1a11303c679cdb";
|
||||||
|
|
||||||
Object.keys(w.addressBook).length.should.equal(2);
|
Object.keys(w.addressBook).length.should.equal(2);
|
||||||
w._handleAddressBook(senderId, data, true);
|
w._handleAddressBook(senderId, data, true);
|
||||||
Object.keys(w.addressBook).length.should.equal(3);
|
Object.keys(w.addressBook).length.should.equal(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return signed object', function() {
|
it('should return signed object', function() {
|
||||||
var w = createW();
|
var w = createW();
|
||||||
|
|
@ -950,7 +958,7 @@ describe('Wallet model', function() {
|
||||||
w.verifyAddressbookEntry(w.addressBook[key], pubKey, key).should.equal(true);
|
w.verifyAddressbookEntry(w.addressBook[key], pubKey, key).should.equal(true);
|
||||||
w.addressBook[key].label = 'Another';
|
w.addressBook[key].label = 'Another';
|
||||||
w.verifyAddressbookEntry(w.addressBook[key], pubKey, key).should.equal(false);
|
w.verifyAddressbookEntry(w.addressBook[key], pubKey, key).should.equal(false);
|
||||||
(function() {
|
(function() {
|
||||||
w.verifyAddressbookEntry();
|
w.verifyAddressbookEntry();
|
||||||
}).should.throw();
|
}).should.throw();
|
||||||
});
|
});
|
||||||
|
|
@ -1005,4 +1013,80 @@ describe('Wallet model', function() {
|
||||||
copayConfig.forceNetwork = backup;
|
copayConfig.forceNetwork = backup;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('validate txProposals', function() {
|
||||||
|
var a1 = 'n1pKARYYUnZwxBuGj3y7WqVDu6VLN7n971';
|
||||||
|
var a2 = 'mtxYYJXZJmQc2iJRHQ4RZkfxU5K7TE2qMJ';
|
||||||
|
var utxos = [{
|
||||||
|
address: a1,
|
||||||
|
txid: '2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1',
|
||||||
|
vout: 1,
|
||||||
|
scriptPubKey: Address.getScriptPubKeyFor(a1).serialize().toString('hex'),
|
||||||
|
amount: 0.5,
|
||||||
|
confirmations: 200
|
||||||
|
}, {
|
||||||
|
address: a2,
|
||||||
|
txid: '88c4520ffd97ea565578afe0b40919120be704b36561c71ba4e450e83cb3c9fd',
|
||||||
|
vout: 1,
|
||||||
|
scriptPubKey: Address.getScriptPubKeyFor(a2).serialize().toString('hex'),
|
||||||
|
amount: 0.5001,
|
||||||
|
confirmations: 200
|
||||||
|
}];
|
||||||
|
var destAddress = 'myuAQcCc1REUgXGsCTiYhZvPPc3XxZ36G1';
|
||||||
|
var outs = [{
|
||||||
|
address: destAddress,
|
||||||
|
amount: 1.0
|
||||||
|
}];
|
||||||
|
|
||||||
|
var testValidate = function(signhash, result, done) {
|
||||||
|
var w = cachedCreateW();
|
||||||
|
var spy = sinon.spy();
|
||||||
|
w.on('txProposalEvent', spy);
|
||||||
|
w.on('txProposalEvent', function(e) {
|
||||||
|
e.type.should.equal(result);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
var opts = {};
|
||||||
|
opts.signhash = signhash;
|
||||||
|
var txb = new TransactionBuilder(opts)
|
||||||
|
.setUnspent(utxos)
|
||||||
|
.setOutputs(outs)
|
||||||
|
.sign(['cVBtNonMyTydnS3NnZyipbduXo9KZfF1aUZ3uQHcvJB6UARZbiWG',
|
||||||
|
'cRVF68hhZp1PUQCdjr2k6aVYb2cn6uabbySDPBizAJ3PXF7vDXTL'
|
||||||
|
]);
|
||||||
|
var txp = {
|
||||||
|
'txProposal': {
|
||||||
|
'builderObj': txb.toObj()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
w._handleTxProposal('senderID', txp, true);
|
||||||
|
spy.callCount.should.equal(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should validate for undefined', function(done) {
|
||||||
|
var result = 'new';
|
||||||
|
var signhash;
|
||||||
|
testValidate(signhash, result, done);
|
||||||
|
});
|
||||||
|
it('should validate for SIGHASH_ALL', function(done) {
|
||||||
|
var result = 'new';
|
||||||
|
var signhash = Transaction.SIGHASH_ALL;
|
||||||
|
testValidate(signhash, result, done);
|
||||||
|
});
|
||||||
|
it('should not validate for different SIGHASH_NONE', function(done) {
|
||||||
|
var result = 'corrupt';
|
||||||
|
var signhash = Transaction.SIGHASH_NONE;
|
||||||
|
testValidate(signhash, result, done);
|
||||||
|
});
|
||||||
|
it('should not validate for different SIGHASH_SINGLE', function(done) {
|
||||||
|
var result = 'corrupt';
|
||||||
|
var signhash = Transaction.SIGHASH_SINGLE;
|
||||||
|
testValidate(signhash, result, done);
|
||||||
|
});
|
||||||
|
it('should not validate for different SIGHASH_ANYONECANPAY', function(done) {
|
||||||
|
var result = 'corrupt';
|
||||||
|
var signhash = Transaction.SIGHASH_ANYONECANPAY;
|
||||||
|
testValidate(signhash, result, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -180,25 +180,25 @@ describe("Unit: Testing Directives", function() {
|
||||||
it('should check very weak password', function() {
|
it('should check very weak password', function() {
|
||||||
$scope.password = 'asd';
|
$scope.password = 'asd';
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
expect($scope.passwordStrength).to.equal('very weak');
|
expect($scope.passwordStrength).to.equal('Very Weak, that\'s short');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check weak password', function() {
|
it('should check weak password', function() {
|
||||||
$scope.password = 'asdasdASDASD';
|
$scope.password = 'asdasdASDASD';
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
expect($scope.passwordStrength).to.equal('weak');
|
expect($scope.passwordStrength).to.equal('Weak, add numerals');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check medium password', function() {
|
it('should check medium password', function() {
|
||||||
$scope.password = 'asdasdASDASD1';
|
$scope.password = 'asdasdA1';
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
expect($scope.passwordStrength).to.equal('medium');
|
expect($scope.passwordStrength).to.equal('Medium, add punctuation');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check strong password', function() {
|
it('should check strong password', function() {
|
||||||
$scope.password = 'asdasdASDASD1{';
|
$scope.password = 'asdasdASDASD1{';
|
||||||
$scope.$digest();
|
$scope.$digest();
|
||||||
expect($scope.passwordStrength).to.equal('strong');
|
expect($scope.passwordStrength).to.equal('Strong, add punctuation');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -80,26 +80,26 @@ describe('Unit: Testing Filters', function() {
|
||||||
expect(removeEmpty(undefined).length).to.equal(0);
|
expect(removeEmpty(undefined).length).to.equal(0);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should filter empty addresses from other copayers', inject(function($filter) {
|
it('should filter empty change addresses from other copayers', inject(function($filter) {
|
||||||
var removeEmpty = $filter('removeEmpty');
|
var removeEmpty = $filter('removeEmpty');
|
||||||
var addresses = [{
|
var addresses = [{
|
||||||
owned: true,
|
owned: true,
|
||||||
|
isChange: false,
|
||||||
balance: 0
|
balance: 0
|
||||||
}, {
|
}, {
|
||||||
owned: false,
|
owned: false,
|
||||||
|
isChange: false,
|
||||||
balance: 0
|
balance: 0
|
||||||
}, {
|
}, {
|
||||||
owned: true,
|
owned: true,
|
||||||
|
isChange: true,
|
||||||
balance: 0
|
balance: 0
|
||||||
}, {
|
}, {
|
||||||
owned: false,
|
owned: false,
|
||||||
|
isChange: true,
|
||||||
balance: 0
|
balance: 0
|
||||||
}];
|
}];
|
||||||
expect(removeEmpty(addresses).length).to.equal(2);
|
expect(removeEmpty(addresses).length).to.equal(2);
|
||||||
addresses[1].owned = true;
|
|
||||||
expect(removeEmpty(addresses).length).to.equal(3);
|
|
||||||
addresses[3].balance = 10;
|
|
||||||
expect(removeEmpty(addresses).length).to.equal(4);
|
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||