mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-03 08:23:04 +02:00
Compare commits
75 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b1d91e2671 | ||
![]() |
271cd2d4a8 | ||
![]() |
22c5a543db | ||
![]() |
cd986860c5 | ||
![]() |
0cf5981eae | ||
![]() |
e109df34f0 | ||
![]() |
5a7aa6cc77 | ||
![]() |
f50629ff81 | ||
![]() |
cb12d64e5f | ||
![]() |
a8f08fb9b9 | ||
![]() |
3e9be418a8 | ||
![]() |
fa5dc9988d | ||
![]() |
857cf8d6d8 | ||
![]() |
63e0c265cb | ||
![]() |
39d2c0a25f | ||
![]() |
ff6e00e1a1 | ||
![]() |
3a839a04d5 | ||
![]() |
42e4db5cc1 | ||
![]() |
d18999e731 | ||
![]() |
425246beb1 | ||
![]() |
e1a2572236 | ||
![]() |
7fdae76f51 | ||
![]() |
b4e1767a7b | ||
![]() |
9b9995437d | ||
![]() |
94abc00422 | ||
![]() |
d7d6601b60 | ||
![]() |
bf95994c9e | ||
![]() |
a2d6ca0740 | ||
![]() |
1115bbb706 | ||
![]() |
0b17ed4322 | ||
![]() |
bcc85a5b3f | ||
![]() |
5b26f1a30b | ||
![]() |
63677d5027 | ||
![]() |
96579c1be4 | ||
![]() |
af58b76f0c | ||
![]() |
7879f31f63 | ||
![]() |
2ca7b41982 | ||
![]() |
8bdc0f8fde | ||
![]() |
0bb9e9cb6c | ||
![]() |
bdc2a72790 | ||
![]() |
073bd96b17 | ||
![]() |
37f22a9dc2 | ||
![]() |
bc3c5b3f66 | ||
![]() |
c61a62cc85 | ||
![]() |
66a6583ec4 | ||
![]() |
d24b37e2f2 | ||
![]() |
161c155de6 | ||
![]() |
dd59233dd4 | ||
![]() |
e9c74d4d9c | ||
![]() |
b37adb4546 | ||
![]() |
a4e99209be | ||
![]() |
45aa8f30e5 | ||
![]() |
60a7b15700 | ||
![]() |
df2ff8b3b7 | ||
![]() |
da8c8f80f1 | ||
![]() |
eda3de11fe | ||
![]() |
7d7de14827 | ||
![]() |
1c709da92c | ||
![]() |
a9092497b2 | ||
![]() |
b1f530e64a | ||
![]() |
8c086b77d3 | ||
![]() |
c0fdfe87be | ||
![]() |
6cfd840283 | ||
![]() |
d80cde1136 | ||
![]() |
bea4b06675 | ||
![]() |
88bc33b5a4 | ||
![]() |
2db36bb824 | ||
![]() |
dd689b1883 | ||
![]() |
641abd13f5 | ||
![]() |
a0b3a7fe5d | ||
![]() |
a0486f581f | ||
![]() |
36161137ec | ||
![]() |
c9927edbd1 | ||
![]() |
f0a3c05a9a | ||
![]() |
d6eb82c457 |
27
.circleci/config.yml
Normal file
27
.circleci/config.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/code
|
||||
docker:
|
||||
- image: bitriseio/android-ndk
|
||||
environment:
|
||||
JVM_OPTS: -Xmx3200m
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
||||
- run:
|
||||
name: Download Dependencies
|
||||
command: ./gradlew androidDependencies
|
||||
- save_cache:
|
||||
paths:
|
||||
- ~/.gradle
|
||||
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
|
||||
- run:
|
||||
name: Run Tests
|
||||
command: ./gradlew test
|
||||
- store_artifacts:
|
||||
path: app/build/reports
|
||||
destination: reports
|
||||
- store_test_results:
|
||||
path: app/build/test-results
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,8 +1,7 @@
|
||||
.gradle
|
||||
/build
|
||||
*.iml
|
||||
/.idea/libraries
|
||||
/.idea/workspace.xml
|
||||
/.idea
|
||||
/local.properties
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
|
19
README.md
19
README.md
@@ -1,12 +1,23 @@
|
||||
# Monerujo
|
||||
Another Android Monero Wallet
|
||||
Another Android Monero Wallet for Monero
|
||||
**(not
|
||||
Monero Classic,
|
||||
Monero-Classic,
|
||||
Monero Zero,
|
||||
Monero Original,
|
||||
Monero C,
|
||||
Monero V)**
|
||||
|
||||
### QUICKSTART
|
||||
- Download the APK for the most current release [here](https://github.com/m2049r/xmrwallet/releases) and install it
|
||||
- Alternatively add our F-Droid repo https://f-droid.monerujo.io/fdroid/repo with fingerpint ```A8 2C 68 E1 4A F0 AA 6A 2E C2 0E 6B 27 2E FF 25 E5 A0 38 F3 F6 58 84 31 6E 0F 5E 0D 91 E7 B7 13``` to your F-Droid client
|
||||
- Run the App and select "Generate Wallet" to create a new wallet or recover a wallet
|
||||
- Advanced users can copy over synced wallet files (all files) onto sdcard in directory Monerujo (created first time App is started)
|
||||
- See the [FAQ](doc/FAQ.md)
|
||||
|
||||
## Translations
|
||||
Help us translate Monerujo! You can find instructions [On Taiga](https://taiga.getmonero.org/project/erciccione-monero-localization/wiki/monerujo), and if you need help/support, open an issue or contact the Localization Workgroup. You can find us on the freenode channel `#monero-translations`, also relayed on [MatterMost](https://mattermost.getmonero.org/monero/channels/monero-translations), and matrix/riot.
|
||||
|
||||
### Disclaimer
|
||||
You may lose all your Moneroj if you use this App. Be cautious when spending on the mainnet.
|
||||
|
||||
@@ -23,6 +34,8 @@ You may lose all your Moneroj if you use this App. Be cautious when spending on
|
||||
- see taiga.getmonero.org & issues on github
|
||||
|
||||
### Issues / Pitfalls
|
||||
- Users of Zenfone MAX & Zenfone 2 Laser (possibly others) **MUST** use the armeabi-v7a APK as the arm64-v8a build uses hardware AES
|
||||
functionality these models don't have.
|
||||
- You should backup your wallet files in the "monerujo" folder periodically.
|
||||
- Also note, that on some devices the backups will only be visible on a PC over USB after a reboot of the device (it's an Android bug/feature)
|
||||
- Created wallets on a private testnet are unusable because the restore height is set to that
|
||||
@@ -33,8 +46,8 @@ The official monero client shows the same behaviour.
|
||||
No need to build. Binaries are included:
|
||||
|
||||
- openssl-1.0.2l
|
||||
- monero-v0.11.1.0
|
||||
- boost_1_64_0
|
||||
- monero-v0.12
|
||||
- boost_1_58_0
|
||||
|
||||
If you want to build them yourself (recommended) check out [the instructions](doc/BUILDING-external-libs.md)
|
||||
|
||||
|
@@ -60,9 +60,13 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a)
|
||||
|
||||
#############
|
||||
# Monero set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common cncrypto ringct)
|
||||
# Monero
|
||||
#############
|
||||
|
||||
add_library(wallet_api STATIC IMPORTED)
|
||||
set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet_api.a)
|
||||
|
||||
add_library(wallet STATIC IMPORTED)
|
||||
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
|
||||
@@ -91,11 +95,9 @@ add_library(ringct STATIC IMPORTED)
|
||||
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
|
||||
|
||||
#####
|
||||
|
||||
add_library(p2p STATIC IMPORTED)
|
||||
set_target_properties(p2p PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libp2p.a)
|
||||
add_library(ringct_basic STATIC IMPORTED)
|
||||
set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct_basic.a)
|
||||
|
||||
add_library(blockchain_db STATIC IMPORTED)
|
||||
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
|
||||
@@ -113,7 +115,6 @@ add_library(unbound STATIC IMPORTED)
|
||||
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
|
||||
|
||||
####
|
||||
add_library(epee STATIC IMPORTED)
|
||||
set_target_properties(epee PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a)
|
||||
@@ -122,9 +123,21 @@ add_library(blocks STATIC IMPORTED)
|
||||
set_target_properties(blocks PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a)
|
||||
|
||||
add_library(miniupnpc STATIC IMPORTED)
|
||||
set_target_properties(miniupnpc PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libminiupnpc.a)
|
||||
add_library(checkpoints STATIC IMPORTED)
|
||||
set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcheckpoints.a)
|
||||
|
||||
add_library(device STATIC IMPORTED)
|
||||
set_target_properties(device PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
|
||||
|
||||
add_library(multisig STATIC IMPORTED)
|
||||
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmultisig.a)
|
||||
|
||||
add_library(version STATIC IMPORTED)
|
||||
set_target_properties(version PROPERTIES IMPORTED_LOCATION
|
||||
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
|
||||
|
||||
#############
|
||||
# System
|
||||
@@ -137,23 +150,26 @@ include_directories( ${EXTERNAL_LIBS_DIR}/monero/include )
|
||||
message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR})
|
||||
|
||||
target_link_libraries( monerujo
|
||||
|
||||
wallet_api
|
||||
wallet
|
||||
cryptonote_core
|
||||
cryptonote_basic
|
||||
mnemonics
|
||||
ringct
|
||||
ringct_basic
|
||||
common
|
||||
cncrypto
|
||||
|
||||
blockchain_db
|
||||
lmdb
|
||||
easylogging
|
||||
unbound
|
||||
p2p
|
||||
|
||||
epee
|
||||
blocks
|
||||
miniupnpc
|
||||
checkpoints
|
||||
device
|
||||
multisig
|
||||
version
|
||||
|
||||
boost_chrono
|
||||
boost_date_time
|
||||
|
@@ -1,15 +1,14 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'witness'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '26.0.2'
|
||||
buildToolsVersion '27.0.3'
|
||||
defaultConfig {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 67
|
||||
versionName "1.3.7 'Satoshis Dream'"
|
||||
versionCode 94
|
||||
versionName "1.5.4 'CrAzY Nacho'"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
@@ -17,9 +16,6 @@ android {
|
||||
arguments '-DANDROID_STL=c++_shared'
|
||||
}
|
||||
}
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a'
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -36,49 +32,50 @@ android {
|
||||
path "CMakeLists.txt"
|
||||
}
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable true
|
||||
reset()
|
||||
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
universalApk true
|
||||
}
|
||||
}
|
||||
|
||||
// Map for the version code that gives each ABI a value.
|
||||
def abiCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
|
||||
|
||||
// APKs for the same app that all have the same version information.
|
||||
android.applicationVariants.all { variant ->
|
||||
// Assigns a different version code for each output APK.
|
||||
variant.outputs.all {
|
||||
output ->
|
||||
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
|
||||
output.versionCodeOverride = abiCodes.get(abiName, 0) + 10 * variant.versionCode
|
||||
|
||||
if (abiName == null) abiName = "universal"
|
||||
def v = "${variant.versionName}".replaceFirst(" .*\$", "").replace(".", "x")
|
||||
outputFileName = "$rootProject.ext.apkName-" + v + "_" + abiName + ".apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:appcompat-v7:25.4.0'
|
||||
compile 'com.android.support:design:25.4.0'
|
||||
compile 'com.android.support:support-v4:25.4.0'
|
||||
compile 'com.android.support:recyclerview-v7:25.4.0'
|
||||
compile 'com.android.support:cardview-v7:25.4.0'
|
||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
compile 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||
implementation 'com.android.support:appcompat-v7:25.4.0'
|
||||
implementation 'com.android.support:design:25.4.0'
|
||||
implementation 'com.android.support:support-v4:25.4.0'
|
||||
implementation 'com.android.support:recyclerview-v7:25.4.0'
|
||||
implementation 'com.android.support:cardview-v7:25.4.0'
|
||||
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||
|
||||
compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
|
||||
compile "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
|
||||
implementation "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
|
||||
implementation "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
|
||||
|
||||
testCompile "junit:junit:$rootProject.ext.junitVersion"
|
||||
testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||
testCompile "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
||||
testCompile 'org.json:json:20140107'
|
||||
testCompile 'net.jodah:concurrentunit:0.4.2'
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'com.android.support:design:3f409bf2019967ffc344cfaf11e52131fac982468a1707aaeb25bf3c52838966',
|
||||
'com.android.support:appcompat-v7:70551e62660db15b790c5275f56b9de4dd9407d1494d07c8f3dd5698f3638677',
|
||||
'com.android.support:transition:848270144fb180efd2bf928a00ed176dbbc5290badfd638390ffba90088df8b3',
|
||||
'me.dm7.barcodescanner:zxing:d43973c9527c23fa8e6d338c6a2c458e373ce1ac6bcaa3bc41d11ae49116000d',
|
||||
'me.dm7.barcodescanner:core:a5c8a704089b58029db166172ed8e55d756877d010a85a0b1c94fdc96ffb8f9a',
|
||||
'com.android.support:support-v4:ee44c481a1f4d6978568e223e8125379b52b2ececdd53450e09ebae144bd377d',
|
||||
'com.android.support:recyclerview-v7:a2fe121f9d01ed8980e97095b4a3fe9700a0aa0a7d4b0f8c594f765ad8455a0d',
|
||||
'com.android.support:cardview-v7:f3fbbe1fcfdbec7333c6a2c516c5fd511a909d1975271818e268d6fe297d8c70',
|
||||
'com.android.support.constraint:constraint-layout:b0c688cc2b7172608f8153a689d746da40f71e52d7e2fe2bfd9df2f92db77085',
|
||||
'com.android.support:animated-vector-drawable:628ab1d56a6ee4cbedf32617af8b2a1fe02964ed0628e8f898cc09ddba6e1835',
|
||||
'com.android.support:support-vector-drawable:077009d13882ee96f061e4bc2dbe7cce7ae1762d8297592a787ff741afbfb1f2',
|
||||
'com.android.support:support-fragment:316d35d4d2d2902057efad104a73e4bdb50bee260a7075678185b8cd71170945',
|
||||
'com.android.support:support-core-ui:e72ae29b823889686cff6fcb948d6745c2baf6d4c2af4fdffa1ec1e42e3833a3',
|
||||
'com.android.support:support-media-compat:566a161d9cb0083ef62a53e46b71ce5b3d455b8635b1a0a4ae28d96d4b583de8',
|
||||
'com.android.support:support-core-utils:34b8437dfa95ff28d29cf57ffa3b1354a9fa9bfe4059f0fd5ce2f5e4326a1748',
|
||||
'com.android.support:support-compat:54019c63614ce08b02d7b9605490cd2b29ba5b2505f394a9517450b5f72b30ca',
|
||||
'com.android.support:support-annotations:a774272036941b4e912eb426d70c848bde7f06a3bf5fb491f75a427dc6595270',
|
||||
'com.android.support.constraint:constraint-layout-solver:8c62525a9bc5cff5633a96cb9b32fffeccaf41b8841aa87fc22607070dea9b8d',
|
||||
'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
|
||||
'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850',
|
||||
'com.squareup.okhttp3:okhttp:7265adbd6f028aade307f58569d814835cd02bc9beffb70c25f72c9de50d61c4',
|
||||
]
|
||||
implementation 'com.nulab-inc:zxcvbn:1.2.3'
|
||||
|
||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
||||
testImplementation 'org.json:json:20140107'
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
#define XMRWALLET_WALLET_LIB_H
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
/*
|
||||
#include <android/log.h>
|
||||
|
||||
@@ -27,13 +28,13 @@
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
*/
|
||||
|
||||
jfieldID getHandleField(JNIEnv *env, jobject obj, const char* fieldName = "handle") {
|
||||
jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||
jclass c = env->GetObjectClass(obj);
|
||||
return env->GetFieldID(c, fieldName, "J"); // of type long
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *getHandle(JNIEnv *env, jobject obj, const char* fieldName = "handle") {
|
||||
template<typename T>
|
||||
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||
jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName));
|
||||
return reinterpret_cast<T *>(handle);
|
||||
}
|
||||
@@ -42,10 +43,35 @@ void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
|
||||
env->SetLongField(obj, getHandleField(env, obj), handle);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
void setHandle(JNIEnv *env, jobject obj, T *t) {
|
||||
jlong handle = reinterpret_cast<jlong>(t);
|
||||
setHandleFromLong(env, obj, handle);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// from monero-core crypto/hash-ops.h - avoid #including monero code here
|
||||
enum {
|
||||
HASH_SIZE = 32,
|
||||
HASH_DATA_AREA = 136
|
||||
};
|
||||
|
||||
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed);
|
||||
|
||||
inline void slow_hash(const void *data, const size_t length, char *hash) {
|
||||
cn_slow_hash(data, length, hash, 0 /* variant */, 0/*prehashed*/);
|
||||
}
|
||||
|
||||
inline void slow_hash_broken(const void *data, char *hash, int variant) {
|
||||
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //XMRWALLET_WALLET_LIB_H
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -300,7 +300,7 @@ public class ReceiveFragment extends Fragment {
|
||||
String paymentId = etPaymentId.getEditText().getText().toString();
|
||||
String xmrAmount = evAmount.getAmount();
|
||||
Timber.d("%s/%s/%s", xmrAmount, paymentId, address);
|
||||
if ((xmrAmount == null) || !Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet())) {
|
||||
if ((xmrAmount == null) || !Wallet.isAddressValid(address)) {
|
||||
clearQR();
|
||||
Timber.d("CLEARQR");
|
||||
return;
|
||||
@@ -335,6 +335,7 @@ public class ReceiveFragment extends Fragment {
|
||||
}
|
||||
|
||||
public Bitmap generate(String text, int width, int height) {
|
||||
if ((width <= 0) || (height <= 0)) return null;
|
||||
Map<EncodeHintType, Object> hints = new HashMap<>();
|
||||
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
|
||||
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
|
||||
|
@@ -36,7 +36,7 @@ import android.widget.Toast;
|
||||
|
||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||
import com.m2049r.xmrwallet.data.TxData;
|
||||
import com.m2049r.xmrwallet.dialog.DonationFragment;
|
||||
import com.m2049r.xmrwallet.dialog.CreditsFragment;
|
||||
import com.m2049r.xmrwallet.dialog.HelpFragment;
|
||||
import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment;
|
||||
import com.m2049r.xmrwallet.fragment.send.SendFragment;
|
||||
@@ -49,21 +49,34 @@ import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.UserNotes;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class WalletActivity extends SecureActivity implements WalletFragment.Listener,
|
||||
WalletService.Observer, SendFragment.Listener, TxFragment.Listener,
|
||||
GenerateReviewFragment.ListenerWithWallet,
|
||||
GenerateReviewFragment.Listener,
|
||||
GenerateReviewFragment.PasswordChangedListener,
|
||||
ScannerFragment.OnScannedListener, ReceiveFragment.Listener,
|
||||
SendAddressWizardFragment.OnScanListener {
|
||||
|
||||
public static final String REQUEST_ID = "id";
|
||||
public static final String REQUEST_PW = "pw";
|
||||
public static final String REQUEST_FINGERPRINT_USED = "fingerprint";
|
||||
|
||||
private Toolbar toolbar;
|
||||
private boolean needVerifyIdentity;
|
||||
|
||||
private String password;
|
||||
|
||||
@Override
|
||||
public void onPasswordChanged(String newPassword) {
|
||||
password = newPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setToolbarButton(int type) {
|
||||
@@ -119,8 +132,9 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
if (extras != null) {
|
||||
acquireWakeLock();
|
||||
String walletId = extras.getString(REQUEST_ID);
|
||||
String walletPassword = extras.getString(REQUEST_PW);
|
||||
connectWalletService(walletId, walletPassword);
|
||||
needVerifyIdentity = extras.getBoolean(REQUEST_FINGERPRINT_USED);
|
||||
password = extras.getString(REQUEST_PW);
|
||||
connectWalletService(walletId, password);
|
||||
} else {
|
||||
finish();
|
||||
//throw new IllegalStateException("No extras passed! Panic!");
|
||||
@@ -141,7 +155,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
Timber.d("onDestroy()");
|
||||
if (!isSynced()) {
|
||||
if ((mBoundService != null) && !isSynced() && (getWallet() != null)) {
|
||||
saveWallet();
|
||||
}
|
||||
stopWalletService();
|
||||
@@ -159,8 +173,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
case R.id.action_info:
|
||||
onWalletDetails();
|
||||
return true;
|
||||
case R.id.action_donate:
|
||||
DonationFragment.display(getSupportFragmentManager());
|
||||
case R.id.action_credits:
|
||||
CreditsFragment.display(getSupportFragmentManager());
|
||||
return true;
|
||||
case R.id.action_share:
|
||||
onShareTxInfo();
|
||||
@@ -174,6 +188,9 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
case R.id.action_details_help:
|
||||
HelpFragment.display(getSupportFragmentManager(), R.string.help_details);
|
||||
return true;
|
||||
case R.id.action_details_changepw:
|
||||
onWalletChangePassword();
|
||||
return true;
|
||||
case R.id.action_help_send:
|
||||
HelpFragment.display(getSupportFragmentManager(), R.string.help_send);
|
||||
return true;
|
||||
@@ -182,6 +199,20 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
}
|
||||
}
|
||||
|
||||
public void onWalletChangePassword() {
|
||||
try {
|
||||
GenerateReviewFragment detailsFragment = (GenerateReviewFragment)
|
||||
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
AlertDialog dialog = detailsFragment.createChangePasswordDialog();
|
||||
if (dialog != null) {
|
||||
Helper.showKeyboard(dialog);
|
||||
dialog.show();
|
||||
}
|
||||
} catch (ClassCastException ex) {
|
||||
Timber.w("onWalletChangePassword() called, but no GenerateReviewFragment active");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Timber.d("onCreate()");
|
||||
@@ -213,8 +244,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
case Toolbar.BUTTON_CLOSE:
|
||||
finish();
|
||||
break;
|
||||
case Toolbar.BUTTON_DONATE:
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.label_donate), Toast.LENGTH_SHORT).show();
|
||||
case Toolbar.BUTTON_CREDITS:
|
||||
Toast.makeText(WalletActivity.this, getString(R.string.label_credits), Toast.LENGTH_SHORT).show();
|
||||
case Toolbar.BUTTON_NONE:
|
||||
default:
|
||||
Timber.e("Button " + type + "pressed - how can this be?");
|
||||
@@ -222,12 +253,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
}
|
||||
});
|
||||
|
||||
boolean testnet = WalletManager.getInstance().isTestNet();
|
||||
if (testnet) {
|
||||
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
|
||||
} else {
|
||||
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
|
||||
}
|
||||
showNet();
|
||||
|
||||
Fragment walletFragment = new WalletFragment();
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
@@ -238,6 +264,23 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
Timber.d("onCreate() done.");
|
||||
}
|
||||
|
||||
public void showNet() {
|
||||
switch (WalletManager.getInstance().getNetworkType()) {
|
||||
case NetworkType_Mainnet:
|
||||
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
|
||||
break;
|
||||
case NetworkType_Testnet:
|
||||
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
|
||||
break;
|
||||
case NetworkType_Stagenet:
|
||||
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported Network: " + WalletManager.getInstance().getNetworkType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Wallet getWallet() {
|
||||
if (mBoundService == null) throw new IllegalStateException("WalletService not bound.");
|
||||
return mBoundService.getWallet();
|
||||
@@ -368,7 +411,17 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
|
||||
@Override
|
||||
public void onSendRequest() {
|
||||
replaceFragment(new SendFragment(), null, null);
|
||||
if (needVerifyIdentity) {
|
||||
Helper.promptPassword(WalletActivity.this, getWallet().getName(), true, new Helper.PasswordAction() {
|
||||
@Override
|
||||
public void action(String walletName, String password, boolean fingerprintUsed) {
|
||||
replaceFragment(new SendFragment(), null, null);
|
||||
needVerifyIdentity = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
replaceFragment(new SendFragment(), null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -668,9 +721,21 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("type", GenerateReviewFragment.VIEW_TYPE_WALLET);
|
||||
replaceFragment(new GenerateReviewFragment(), null, extras);
|
||||
final Bundle extras = new Bundle();
|
||||
extras.putString(GenerateReviewFragment.REQUEST_TYPE, GenerateReviewFragment.VIEW_TYPE_WALLET);
|
||||
|
||||
if (needVerifyIdentity) {
|
||||
Helper.promptPassword(WalletActivity.this, getWallet().getName(), true, new Helper.PasswordAction() {
|
||||
@Override
|
||||
public void action(String walletName, String password, boolean fingerprintUsed) {
|
||||
replaceFragment(new GenerateReviewFragment(), null, extras);
|
||||
needVerifyIdentity = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
replaceFragment(new GenerateReviewFragment(), null, extras);
|
||||
}
|
||||
|
||||
break;
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
// do nothing
|
||||
@@ -798,9 +863,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
||||
|
||||
@Override
|
||||
public boolean verifyWalletPassword(String password) {
|
||||
String walletPath = new File(Helper.getStorageRoot(this),
|
||||
getWalletName() + ".keys").getAbsolutePath();
|
||||
return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true);
|
||||
String walletPassword = Helper.getWalletPassword(getApplicationContext(), getWalletName(), password);
|
||||
return walletPassword != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -352,7 +352,7 @@ public class WalletFragment extends Fragment
|
||||
setProgress(x);
|
||||
ivSynced.setVisibility(View.GONE);
|
||||
} else {
|
||||
sync = getString(R.string.status_synced) + formatter.format(wallet.getBlockChainHeight());
|
||||
sync = getString(R.string.status_synced) + " " + formatter.format(wallet.getBlockChainHeight());
|
||||
ivSynced.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
|
@@ -19,13 +19,15 @@ package com.m2049r.xmrwallet;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class XmrWalletApplication extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(new Timber.DebugTree());
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ package com.m2049r.xmrwallet.data;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.m2049r.xmrwallet.model.NetworkType;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.util.BitcoinAddressValidator;
|
||||
@@ -178,7 +179,7 @@ public class BarcodeData {
|
||||
return null; // we have an amount but its not a number!
|
||||
}
|
||||
}
|
||||
if (!BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet())) {
|
||||
if (!BitcoinAddressValidator.validate(address)) {
|
||||
Timber.d("address invalid");
|
||||
return null;
|
||||
}
|
||||
@@ -190,7 +191,7 @@ public class BarcodeData {
|
||||
|
||||
if (address == null) return null;
|
||||
|
||||
if (!BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet())) {
|
||||
if (!BitcoinAddressValidator.validate(address)) {
|
||||
Timber.d("address invalid");
|
||||
return null;
|
||||
}
|
||||
|
112
app/src/main/java/com/m2049r/xmrwallet/data/WalletNode.java
Normal file
112
app/src/main/java/com/m2049r/xmrwallet/data/WalletNode.java
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2018 m2049r
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.data;
|
||||
|
||||
import com.m2049r.xmrwallet.model.NetworkType;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
public class WalletNode {
|
||||
private final String name;
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final String user;
|
||||
private final String password;
|
||||
private final NetworkType networkType;
|
||||
|
||||
public WalletNode(String walletName, String daemon, NetworkType networkType) {
|
||||
if ((daemon == null) || daemon.isEmpty())
|
||||
throw new IllegalArgumentException("daemon is empty");
|
||||
this.name = walletName;
|
||||
String daemonAddress;
|
||||
String a[] = daemon.split("@");
|
||||
if (a.length == 1) { // no credentials
|
||||
daemonAddress = a[0];
|
||||
user = "";
|
||||
password = "";
|
||||
} else if (a.length == 2) { // credentials
|
||||
String userPassword[] = a[0].split(":");
|
||||
if (userPassword.length != 2)
|
||||
throw new IllegalArgumentException("User:Password invalid");
|
||||
user = userPassword[0];
|
||||
if (!user.isEmpty()) {
|
||||
password = userPassword[1];
|
||||
} else {
|
||||
password = "";
|
||||
}
|
||||
daemonAddress = a[1];
|
||||
} else {
|
||||
throw new IllegalArgumentException("Too many @");
|
||||
}
|
||||
|
||||
String da[] = daemonAddress.split(":");
|
||||
if ((da.length > 2) || (da.length < 1))
|
||||
throw new IllegalArgumentException("Too many ':' or too few");
|
||||
host = da[0];
|
||||
if (da.length == 2) {
|
||||
try {
|
||||
port = Integer.parseInt(da[1]);
|
||||
} catch (NumberFormatException ex) {
|
||||
throw new IllegalArgumentException("Port not numeric");
|
||||
}
|
||||
} else {
|
||||
switch (networkType) {
|
||||
case NetworkType_Mainnet:
|
||||
port = 18081;
|
||||
break;
|
||||
case NetworkType_Testnet:
|
||||
port = 28081;
|
||||
break;
|
||||
case NetworkType_Stagenet:
|
||||
port = 38081;
|
||||
break;
|
||||
default:
|
||||
port = 0;
|
||||
}
|
||||
}
|
||||
this.networkType = networkType;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public NetworkType getNetworkType() {
|
||||
return networkType;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return host + ":" + port;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public SocketAddress getSocketAddress() {
|
||||
return new InetSocketAddress(host, port);
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return !host.isEmpty();
|
||||
}
|
||||
}
|
@@ -28,16 +28,14 @@ import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
public class DonationFragment extends DialogFragment {
|
||||
public class CreditsFragment extends DialogFragment {
|
||||
static final String TAG = "DonationFragment";
|
||||
|
||||
public static DonationFragment newInstance() {
|
||||
return new DonationFragment();
|
||||
public static CreditsFragment newInstance() {
|
||||
return new CreditsFragment();
|
||||
}
|
||||
|
||||
public static void display(FragmentManager fm) {
|
||||
@@ -47,24 +45,14 @@ public class DonationFragment extends DialogFragment {
|
||||
ft.remove(prev);
|
||||
}
|
||||
|
||||
DonationFragment.newInstance().show(ft, TAG);
|
||||
CreditsFragment.newInstance().show(ft, TAG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_donation, null);
|
||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_credits, null);
|
||||
|
||||
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.donation_credits)));
|
||||
|
||||
(view.findViewById(R.id.bCopyAddress)).
|
||||
setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address),
|
||||
((TextView) view.findViewById(R.id.tvWalletAddress)).getText().toString());
|
||||
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.credits_text)));
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setView(view);
|
@@ -212,7 +212,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
private boolean checkAddressNoError() {
|
||||
String address = etAddress.getEditText().getText().toString();
|
||||
return Wallet.isAddressValid(address)
|
||||
|| BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet());
|
||||
|| BitcoinAddressValidator.validate(address);
|
||||
}
|
||||
|
||||
private boolean checkAddress() {
|
||||
@@ -228,13 +228,13 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
||||
private boolean isIntegratedAddress() {
|
||||
String address = etAddress.getEditText().getText().toString();
|
||||
return (address.length() == INTEGRATED_ADDRESS_LENGTH)
|
||||
&& Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet());
|
||||
&& Wallet.isAddressValid(address);
|
||||
}
|
||||
|
||||
private boolean isBitcoinAddress() {
|
||||
String address = etAddress.getEditText().getText().toString();
|
||||
if ((address.length() >= 27) && (address.length() <= 34))
|
||||
return BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet());
|
||||
if ((address.length() >= 27) && (address.length() <= 35))
|
||||
return BitcoinAddressValidator.validate(address);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
@@ -247,7 +247,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
|
||||
private XmrToApi xmrToApi = null;
|
||||
|
||||
private final XmrToApi getXmrToApi() {
|
||||
private XmrToApi getXmrToApi() {
|
||||
if (xmrToApi == null) {
|
||||
synchronized (this) {
|
||||
if (xmrToApi == null) {
|
||||
|
@@ -47,6 +47,7 @@ import com.m2049r.xmrwallet.layout.SpendViewPager;
|
||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.NodeList;
|
||||
import com.m2049r.xmrwallet.util.Notice;
|
||||
import com.m2049r.xmrwallet.util.UserNotes;
|
||||
import com.m2049r.xmrwallet.widget.DotBar;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
@@ -119,27 +120,8 @@ public class SendFragment extends Fragment
|
||||
arrowPrev = getResources().getDrawable(R.drawable.ic_navigate_prev_white_24dp);
|
||||
arrowNext = getResources().getDrawable(R.drawable.ic_navigate_next_white_24dp);
|
||||
|
||||
llXmrToEnabled = view.findViewById(R.id.llXmrToEnabled);
|
||||
llXmrToEnabled.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
HelpFragment.display(getChildFragmentManager(), R.string.help_xmrto);
|
||||
|
||||
}
|
||||
});
|
||||
ibXmrToInfoClose = view.findViewById(R.id.ibXmrToInfoClose);
|
||||
ibXmrToInfoClose.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
llXmrToEnabled.setVisibility(View.GONE);
|
||||
showXmrtoEnabled = false;
|
||||
saveXmrToPrefs();
|
||||
}
|
||||
});
|
||||
loadPrefs();
|
||||
if (!showXmrtoEnabled) {
|
||||
llXmrToEnabled.setVisibility(View.GONE);
|
||||
}
|
||||
ViewGroup llNotice = (ViewGroup) view.findViewById(R.id.llNotice);
|
||||
Notice.showAll(llNotice,".*_send");
|
||||
|
||||
spendViewPager = (SpendViewPager) view.findViewById(R.id.pager);
|
||||
pagerAdapter = new SpendPagerAdapter(getChildFragmentManager());
|
||||
@@ -291,7 +273,7 @@ public class SendFragment extends Fragment
|
||||
pagerAdapter.notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
Timber.d("New Mode = " + mode.toString());
|
||||
Timber.d("New Mode = %s", mode.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +332,7 @@ public class SendFragment extends Fragment
|
||||
@Override
|
||||
public SendWizardFragment getItem(int position) {
|
||||
Timber.d("getItem(%d) CREATE", position);
|
||||
Timber.d("Mode=" + mode.toString());
|
||||
Timber.d("Mode=%s", mode.toString());
|
||||
if (mode == Mode.XMR) {
|
||||
switch (position) {
|
||||
case POS_ADDRESS:
|
||||
|
@@ -54,7 +54,8 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
|
||||
TxData getTxData();
|
||||
}
|
||||
|
||||
final static int Mixins[] = {4, 7, 12, 25}; // must match the layout XML
|
||||
// Mixin = Ringsize - 1
|
||||
final static int Mixins[] = {6, 9, 12, 25}; // must match the layout XML / "@array/mixin"
|
||||
final static PendingTransaction.Priority Priorities[] =
|
||||
{PendingTransaction.Priority.Priority_Default,
|
||||
PendingTransaction.Priority.Priority_Low,
|
||||
|
@@ -77,7 +77,7 @@ public class SendSuccessWizardFragment extends SendWizardFragment {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_send_txid), tvTxId.getText().toString());
|
||||
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), getString(R.string.message_copy_txid), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -137,12 +137,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
||||
ivTxType.setVisibility(View.GONE); // gives us more space for the amount
|
||||
}
|
||||
|
||||
long realAmount = infoItem.amount;
|
||||
if (infoItem.isPending) {
|
||||
realAmount = realAmount - infoItem.fee;
|
||||
}
|
||||
|
||||
String displayAmount = Helper.getDisplayAmount(realAmount, Helper.DISPLAY_DIGITS_INFO);
|
||||
String displayAmount = Helper.getDisplayAmount(infoItem.amount, Helper.DISPLAY_DIGITS_INFO);
|
||||
if (infoItem.direction == TransactionInfo.Direction.Direction_Out) {
|
||||
tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount));
|
||||
} else {
|
||||
|
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2018 m2049r
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.m2049r.xmrwallet.model;
|
||||
|
||||
public enum NetworkType {
|
||||
NetworkType_Mainnet(0),
|
||||
NetworkType_Testnet(1),
|
||||
NetworkType_Stagenet(2);
|
||||
|
||||
public static NetworkType fromInteger(int n) {
|
||||
switch (n) {
|
||||
case 0:
|
||||
return NetworkType_Mainnet;
|
||||
case 1:
|
||||
return NetworkType_Testnet;
|
||||
case 2:
|
||||
return NetworkType_Stagenet;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
private int value;
|
||||
|
||||
NetworkType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
@@ -84,7 +84,14 @@ public class PendingTransaction {
|
||||
|
||||
public native long getFee();
|
||||
|
||||
public native String getFirstTxId();
|
||||
public String getFirstTxId() {
|
||||
String id = getFirstTxIdJ();
|
||||
if (id == null)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return id;
|
||||
}
|
||||
|
||||
public native String getFirstTxIdJ();
|
||||
|
||||
public native long getTxCount();
|
||||
|
||||
|
@@ -79,7 +79,11 @@ public class Wallet {
|
||||
|
||||
public native String getPath();
|
||||
|
||||
public native boolean isTestNet();
|
||||
public NetworkType getNetworkType() {
|
||||
return NetworkType.fromInteger(nettype());
|
||||
}
|
||||
|
||||
public native int nettype();
|
||||
|
||||
//TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
|
||||
//TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
|
||||
@@ -155,14 +159,12 @@ public class Wallet {
|
||||
public static native boolean isPaymentIdValid(String payment_id);
|
||||
|
||||
public static boolean isAddressValid(String address) {
|
||||
return isAddressValid(address, WalletManager.getInstance().isTestNet());
|
||||
return isAddressValid(address, WalletManager.getInstance().getNetworkType().getValue());
|
||||
}
|
||||
|
||||
public static native boolean isAddressValid(String address, boolean isTestNet);
|
||||
public static native boolean isAddressValid(String address, int networkType);
|
||||
|
||||
//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error);
|
||||
|
||||
public static native String getPaymentIdFromAddress(String address, boolean isTestNet);
|
||||
public static native String getPaymentIdFromAddress(String address, int networkType);
|
||||
|
||||
public static native long getMaximumAllowedAmount();
|
||||
|
||||
@@ -271,5 +273,4 @@ public class Wallet {
|
||||
//virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &tvAmount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0;
|
||||
//virtual bool rescanSpent() = 0;
|
||||
|
||||
|
||||
}
|
||||
|
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.m2049r.xmrwallet.model;
|
||||
|
||||
import com.m2049r.xmrwallet.data.WalletNode;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
@@ -39,6 +41,7 @@ public class WalletManager {
|
||||
if (WalletManager.Instance == null) {
|
||||
WalletManager.Instance = new WalletManager();
|
||||
}
|
||||
|
||||
return WalletManager.Instance;
|
||||
}
|
||||
|
||||
@@ -69,49 +72,51 @@ public class WalletManager {
|
||||
}
|
||||
|
||||
public Wallet createWallet(File aFile, String password, String language) {
|
||||
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, isTestNet());
|
||||
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, getNetworkType().getValue());
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private native long createWalletJ(String path, String password, String language, boolean isTestNet);
|
||||
private native long createWalletJ(String path, String password, String language, int networkType);
|
||||
|
||||
public Wallet openWallet(String path, String password) {
|
||||
long walletHandle = openWalletJ(path, password, isTestNet());
|
||||
long walletHandle = openWalletJ(path, password, getNetworkType().getValue());
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private native long openWalletJ(String path, String password, boolean isTestNet);
|
||||
private native long openWalletJ(String path, String password, int networkType);
|
||||
|
||||
public Wallet recoveryWallet(File aFile, String mnemonic) {
|
||||
Wallet wallet = recoveryWallet(aFile, mnemonic, 0);
|
||||
manageWallet(wallet);
|
||||
return wallet;
|
||||
public Wallet recoveryWallet(File aFile, String password, String mnemonic) {
|
||||
return recoveryWallet(aFile, password, mnemonic, 0);
|
||||
}
|
||||
|
||||
public Wallet recoveryWallet(File aFile, String mnemonic, long restoreHeight) {
|
||||
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), mnemonic, isTestNet(), restoreHeight);
|
||||
public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) {
|
||||
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic,
|
||||
getNetworkType().getValue(), restoreHeight);
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private native long recoveryWalletJ(String path, String mnemonic, boolean isTestNet, long restoreHeight);
|
||||
private native long recoveryWalletJ(String path, String password, String mnemonic,
|
||||
int networkType, long restoreHeight);
|
||||
|
||||
public Wallet createWalletFromKeys(File aFile, String language, long restoreHeight,
|
||||
public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight,
|
||||
String addressString, String viewKeyString, String spendKeyString) {
|
||||
long walletHandle = createWalletFromKeysJ(aFile.getAbsolutePath(), language, isTestNet(), restoreHeight,
|
||||
long walletHandle = createWalletFromKeysJ(aFile.getAbsolutePath(), password,
|
||||
language, getNetworkType().getValue(), restoreHeight,
|
||||
addressString, viewKeyString, spendKeyString);
|
||||
Wallet wallet = new Wallet(walletHandle);
|
||||
manageWallet(wallet);
|
||||
return wallet;
|
||||
}
|
||||
|
||||
private native long createWalletFromKeysJ(String path, String language,
|
||||
boolean isTestNet,
|
||||
private native long createWalletFromKeysJ(String path, String password,
|
||||
String language,
|
||||
int networkType,
|
||||
long restoreHeight,
|
||||
String addressString,
|
||||
String viewKeyString,
|
||||
@@ -202,29 +207,27 @@ public class WalletManager {
|
||||
//TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
|
||||
|
||||
private String daemonAddress = null;
|
||||
private boolean testnet = true;
|
||||
private NetworkType networkType = null;
|
||||
|
||||
public boolean isTestNet() {
|
||||
if (daemonAddress == null) {
|
||||
return true;
|
||||
// assume testnet not explicitly initialised
|
||||
//throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
|
||||
}
|
||||
return testnet;
|
||||
public NetworkType getNetworkType() {
|
||||
return networkType;
|
||||
}
|
||||
|
||||
public void setDaemon(String address, boolean testnet, String username, String password) {
|
||||
//Timber.d("SETDAEMON " + username + "/" + password + "/" + address);
|
||||
this.daemonAddress = address;
|
||||
this.testnet = testnet;
|
||||
this.daemonUsername = username;
|
||||
this.daemonPassword = password;
|
||||
setDaemonAddressJ(address);
|
||||
//public void setDaemon(String address, NetworkType networkType, String username, String password) {
|
||||
public void setDaemon(WalletNode walletNode) {
|
||||
this.daemonAddress = walletNode.getAddress();
|
||||
this.networkType = walletNode.getNetworkType();
|
||||
this.daemonUsername = walletNode.getUsername();
|
||||
this.daemonPassword = walletNode.getPassword();
|
||||
setDaemonAddressJ(daemonAddress);
|
||||
}
|
||||
|
||||
public void setNetworkType(NetworkType networkType) {
|
||||
this.networkType = networkType;
|
||||
}
|
||||
|
||||
public String getDaemonAddress() {
|
||||
if (daemonAddress == null) {
|
||||
// assume testnet not explicitly initialised
|
||||
throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
|
||||
}
|
||||
return this.daemonAddress;
|
||||
@@ -232,13 +235,13 @@ public class WalletManager {
|
||||
|
||||
private native void setDaemonAddressJ(String address);
|
||||
|
||||
String daemonUsername = "";
|
||||
private String daemonUsername = "";
|
||||
|
||||
public String getDaemonUsername() {
|
||||
return daemonUsername;
|
||||
}
|
||||
|
||||
String daemonPassword = "";
|
||||
private String daemonPassword = "";
|
||||
|
||||
public String getDaemonPassword() {
|
||||
return daemonPassword;
|
||||
@@ -266,5 +269,23 @@ public class WalletManager {
|
||||
|
||||
//TODO static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir);
|
||||
|
||||
static public native void initLogger(String argv0, String defaultLogBaseName);
|
||||
|
||||
//TODO: maybe put these in an enum like in monero core - but why?
|
||||
static public int LOGLEVEL_SILENT = -1;
|
||||
static public int LOGLEVEL_WARN = 0;
|
||||
static public int LOGLEVEL_INFO = 1;
|
||||
static public int LOGLEVEL_DEBUG = 2;
|
||||
static public int LOGLEVEL_TRACE = 3;
|
||||
static public int LOGLEVEL_MAX = 4;
|
||||
|
||||
static public native void setLogLevel(int level);
|
||||
|
||||
static public native void logDebug(String category, String message);
|
||||
|
||||
static public native void logInfo(String category, String message);
|
||||
|
||||
static public native void logWarning(String category, String message);
|
||||
|
||||
static public native void logError(String category, String message);
|
||||
}
|
@@ -524,7 +524,7 @@ public class WalletService extends Service {
|
||||
showProgress(20);
|
||||
Wallet wallet = null;
|
||||
WalletManager walletMgr = WalletManager.getInstance();
|
||||
Timber.d("WalletManager testnet=%s", walletMgr.isTestNet());
|
||||
Timber.d("WalletManager network=%s", walletMgr.getNetworkType().name());
|
||||
showProgress(30);
|
||||
if (walletMgr.walletExists(path)) {
|
||||
Timber.d("open wallet %s", path);
|
||||
|
@@ -18,6 +18,9 @@ package com.m2049r.xmrwallet.util;
|
||||
|
||||
// based on https://rosettacode.org/wiki/Bitcoin/address_validation#Java
|
||||
|
||||
import com.m2049r.xmrwallet.model.NetworkType;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -27,6 +30,11 @@ public class BitcoinAddressValidator {
|
||||
|
||||
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
|
||||
public static boolean validate(String addrress) {
|
||||
return validate(addrress,
|
||||
WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet);
|
||||
}
|
||||
|
||||
public static boolean validate(String addrress, boolean testnet) {
|
||||
if (addrress.length() < 26 || addrress.length() > 35)
|
||||
return false;
|
||||
@@ -34,10 +42,11 @@ public class BitcoinAddressValidator {
|
||||
if (decoded == null)
|
||||
return false;
|
||||
|
||||
int v = decoded[0] & 0xFF;
|
||||
if (!testnet) {
|
||||
if ((decoded[0] != 0x00) && (decoded[0] != 0x05)) return false;
|
||||
if ((v != 0x00) && (v != 0x05)) return false;
|
||||
} else {
|
||||
if ((decoded[0] != 0x6f) && (decoded[0] != 0xc4)) return false;
|
||||
if ((v != 0x6f) && (v != 0xc4)) return false;
|
||||
}
|
||||
|
||||
byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21));
|
||||
@@ -57,7 +66,11 @@ public class BitcoinAddressValidator {
|
||||
|
||||
byte[] result = new byte[25];
|
||||
byte[] numBytes = num.toByteArray();
|
||||
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length);
|
||||
if (num.bitLength() == 200) {
|
||||
System.arraycopy(numBytes, 1, result, 0, 25);
|
||||
} else {
|
||||
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,54 @@
|
||||
package com.m2049r.xmrwallet.util;
|
||||
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
public class CrazyPassEncoder {
|
||||
static final String BASE = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
|
||||
static final int PW_CHARS = 52;
|
||||
|
||||
// this takes a 32 byte buffer and converts it to 52 alphnumeric characters
|
||||
// separated by blanks every 4 characters = 13 groups of 4
|
||||
// always (padding by Xs if need be
|
||||
static public String encode(byte[] data) {
|
||||
if (data.length != 32) throw new IllegalArgumentException("data[] is not 32 bytes long");
|
||||
BigInteger rest = new BigInteger(1, data);
|
||||
BigInteger remainder;
|
||||
final StringBuilder result = new StringBuilder();
|
||||
final BigInteger base = BigInteger.valueOf(BASE.length());
|
||||
int i = 0;
|
||||
do {
|
||||
if ((i > 0) && (i % 4 == 0)) result.append(' ');
|
||||
i++;
|
||||
remainder = rest.remainder(base);
|
||||
rest = rest.divide(base);
|
||||
result.append(BASE.charAt(remainder.intValue()));
|
||||
} while (!BigInteger.ZERO.equals(rest));
|
||||
// pad it
|
||||
while (i < PW_CHARS) {
|
||||
if ((i > 0) && (i % 4 == 0)) result.append(' ');
|
||||
result.append('2');
|
||||
i++;
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
static public String reformat(String password) {
|
||||
// maybe this is a CrAzYpass without blanks? or lowercase letters
|
||||
String noBlanks = password.toUpperCase().replaceAll(" ", "");
|
||||
if (noBlanks.length() == PW_CHARS) { // looks like a CrAzYpass
|
||||
// insert blanks every 4 characters
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < PW_CHARS; i++) {
|
||||
if ((i > 0) && (i % 4 == 0)) sb.append(' ');
|
||||
char c = noBlanks.charAt(i);
|
||||
if (BASE.indexOf(c) < 0) return null; // invalid character found
|
||||
sb.append(c);
|
||||
}
|
||||
return sb.toString();
|
||||
} else {
|
||||
return null; // not a CrAzYpass
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user