1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-04 00:53:36 +02:00

Compare commits

...

59 Commits

Author SHA1 Message Date
m2049r
b1d91e2671 new version 2018-05-27 10:59:10 +02:00
m2049r
271cd2d4a8 deal with all broken variants (#292)
* remove variant code for arm32

* deal with all broken variants
2018-05-25 23:44:37 +02:00
m2049r
22c5a543db upgrade to v0.12.1.0 (#291) 2018-05-25 22:38:05 +02:00
m2049r
cd986860c5 remove variant code for arm32 (#290) 2018-05-25 22:37:50 +02:00
m2049r
0cf5981eae Fixes "Invalid Password" although password correct (#289)
* don't log warning

* fix cn_slow_hash variant&prehash
cn_slow_hash signature was changed in monero-core but the linker didn't
notice - also added code to support wallets created with variant &
prehash enabled
2018-05-25 18:20:48 +02:00
m2049r
e109df34f0 remove if save fails (#281) 2018-05-25 18:20:27 +02:00
m2049r
5a7aa6cc77 testnet => stagenet (#288) 2018-05-25 18:19:29 +02:00
m2049r
f50629ff81 check for encoded pw (#280) 2018-05-10 15:26:44 +02:00
m2049r
cb12d64e5f Various Fixes (#279)
* load password only if it's passed

* cancel fingerprint if password entered

* rework fingerprint code

* cleanup unused params

* new version code
2018-05-10 13:48:11 +02:00
m2049r
a8f08fb9b9 new version 2018-05-06 17:46:48 +02:00
m2049r
3e9be418a8 removed removed strings (#276) 2018-05-06 12:30:01 +02:00
KillASIC.com
fa5dc9988d Monero translation to simplified chinese. (#263) 2018-05-06 12:17:09 +02:00
m2049r
857cf8d6d8 Random fixes (#275)
* reduce label length

* api fix

* show correct password after change
2018-05-06 12:12:53 +02:00
m2049r
63e0c265cb correct translation 2018-04-30 12:09:19 +02:00
m2049r
39d2c0a25f correct translation 2018-04-30 12:03:13 +02:00
m2049r
ff6e00e1a1 update monero version (#270) 2018-04-30 11:15:40 +02:00
m2049r
3a839a04d5 shorten de label for receive button (#269) 2018-04-30 11:14:56 +02:00
m2049r
42e4db5cc1 Spanish update (#268)
fingerprint strings translated to spanish
2018-04-29 11:26:50 +02:00
m2049r
d18999e731 don't try to generate 0-size qrcodes (#267) 2018-04-29 11:21:50 +02:00
m2049r
425246beb1 remove unused ids & strings (#266) 2018-04-29 11:21:20 +02:00
erciccione
e1a2572236 Translate new Italian strings in strings.xml and help.xml (#252) 2018-04-25 23:56:32 +02:00
m2049r
7fdae76f51 remove untranslatable strings (#259) 2018-04-25 19:49:55 +02:00
el00ruobuob
b4e1767a7b French (#248) 2018-04-25 19:30:19 +02:00
m2049r
9b9995437d added missing string for de translation (#258) 2018-04-25 19:24:06 +02:00
m2049r
94abc00422 fix notice layout (#257) 2018-04-25 19:22:02 +02:00
Flenst
d7d6601b60 Translation de (#253) 2018-04-25 19:21:43 +02:00
m2049r
bf95994c9e update gradle:3.1.2 2018-04-25 07:32:42 +02:00
0140454
a2d6ca0740 Implement fingerprint login method (#244)
* Implement fingerprint login method

* Display a security warning

* Verify the identity before sensitive operation
2018-04-24 22:34:39 +02:00
m2049r
1115bbb706 Popup notice for new CrAzYpass feature (#254)
* make add notices more flexible

* and add CrAzYpass notice

* added notice translations
2018-04-23 23:43:45 +02:00
anhdres
0b17ed4322 Update strings.xml (#236)
there was a tiny gender error with "monedero"
2018-04-22 20:39:47 +02:00
anhdres
bcc85a5b3f Update help.xml (#250)
crazypass section translated to spanish
2018-04-22 20:38:09 +02:00
anhdres
5b26f1a30b Update strings.xml (#251) 2018-04-22 20:37:48 +02:00
Codivorous
63677d5027 Added Norwegian-Bokmål translation (#238) 2018-04-22 15:37:39 +02:00
m2049r
96579c1be4 separate monero/monerujo folder for debug version (#245) 2018-04-22 11:27:35 +02:00
m2049r
af58b76f0c add blank (#247) 2018-04-22 11:26:46 +02:00
m2049r
7879f31f63 use CrAzYpass for send verification as well (#249) 2018-04-22 11:25:59 +02:00
Lafudoci
2ca7b41982 Update zh-rTW translation for CrAzYpass (#243)
* Update zh-rTW translation for CrAzYpass

* Remove extra space and wrap long lines
2018-04-22 10:30:43 +02:00
m2049r
8bdc0f8fde sgp suggestions (#242) 2018-04-21 10:31:43 +02:00
erciccione
0bb9e9cb6c add reference to the Localization Workgroup in the README (#235) 2018-04-21 10:24:40 +02:00
m2049r
bdc2a72790 march & april (#233) 2018-04-21 10:24:12 +02:00
m2049r
073bd96b17 CrAzYpass implementation (#234) 2018-04-21 10:23:47 +02:00
hrumag
37f22a9dc2 Added Italian Translation (#224) 2018-04-19 10:55:00 +02:00
Lafudoci
bc3c5b3f66 Add tranditional Chinese language (#226) 2018-04-19 10:54:13 +02:00
erciccione
c61a62cc85 edit gender-specific string (#231) 2018-04-19 10:53:35 +02:00
m2049r
66a6583ec4 info about being XMR-only 2018-04-16 14:26:50 +02:00
m2049r
d24b37e2f2 info about missing crypto on some arm64 devices 2018-04-16 14:21:03 +02:00
m2049r
161c155de6 update dependencies 2018-04-16 14:15:49 +02:00
m2049r
dd59233dd4 fix closing tag (#230) 2018-04-15 18:34:35 +02:00
m2049r
e9c74d4d9c Random fixes (#228)
* adapt for android studio 3.1 and remove witness

* set networktype from node

* add monero logging for DEBUG builds

* do not reset timestamps in apk

* no witness

* new version & apk naming
2018-04-15 16:33:20 +02:00
m2049r
b37adb4546 Update README.md 2018-04-11 18:20:07 +02:00
erciccione
a4e99209be change suggested ringsize from 5 to 7 in help.xml (#223) 2018-04-07 16:44:18 +02:00
m2049r
45aa8f30e5 fix openssl links 2018-04-05 19:09:30 +02:00
m2049r
60a7b15700 new version for fixed malloc (83dcd65) 2018-04-03 11:51:14 +02:00
m2049r
df2ff8b3b7 correct toolbar colour for testnet (#218) 2018-04-02 13:02:00 +02:00
m2049r
da8c8f80f1 new version for m2049r/monero 7e97e11 (#217) 2018-04-02 12:14:55 +02:00
m2049r
eda3de11fe added zxcvbn4j license (#216) 2018-04-01 13:23:03 +02:00
m2049r
7d7de14827 new version id for monero PR#3526 (#215) 2018-03-30 14:12:17 +02:00
m2049r
1c709da92c Update FAQ.md 2018-03-29 23:00:39 +02:00
m2049r
a9092497b2 changes for monero v0.12 (#214)
* new version id & name
* witness checksums
* build docs updated for v0.12
* remove binaries
* setenv HOME for ringdb to 'monero' in shared storage
* min ringsize 7
* remove boost_locale and zmq from build - don't need them for wallet_api
* splits for all archs
* throw IndexOutOfBounds in case the TX is empty
* donate, you ungrateful bastards! (removed donations to make google happy)
2018-03-29 22:35:31 +02:00
234 changed files with 6309 additions and 1320 deletions

3
.gitignore vendored
View File

@@ -1,8 +1,7 @@
.gradle
/build
*.iml
/.idea/libraries
/.idea/workspace.xml
/.idea
/local.properties
/captures
.externalNativeBuild

View File

@@ -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)

View File

@@ -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

View File

@@ -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 74
versionName "1.3.14 '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', 'arm64-v8a', 'x86', 'x86_64'
}
}
buildTypes {
@@ -42,7 +38,7 @@ android {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk false
universalApk true
}
}
@@ -52,57 +48,34 @@ android {
// 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.each {
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"
compile 'com.nulab-inc:zxcvbn:1.2.3'
implementation 'com.nulab-inc:zxcvbn:1.2.3'
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',
]
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'
}

View File

@@ -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

View File

@@ -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

View File

@@ -16,12 +16,15 @@
package com.m2049r.xmrwallet;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.Html;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.KeyEvent;
@@ -32,20 +35,23 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
import com.m2049r.xmrwallet.util.RestoreHeight;
import com.m2049r.xmrwallet.widget.Toolbar;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.util.FingerprintHelper;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.KeyStoreHelper;
import com.m2049r.xmrwallet.util.RestoreHeight;
import com.m2049r.xmrwallet.widget.Toolbar;
import com.nulabinc.zxcvbn.Strength;
import com.nulabinc.zxcvbn.Zxcvbn;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import timber.log.Timber;
@@ -59,6 +65,7 @@ public class GenerateFragment extends Fragment {
private TextInputLayout etWalletName;
private TextInputLayout etWalletPassword;
private LinearLayout llFingerprintAuth;
private TextInputLayout etWalletAddress;
private TextInputLayout etWalletMnemonic;
private TextInputLayout etWalletViewKey;
@@ -79,6 +86,7 @@ public class GenerateFragment extends Fragment {
etWalletName = (TextInputLayout) view.findViewById(R.id.etWalletName);
etWalletPassword = (TextInputLayout) view.findViewById(R.id.etWalletPassword);
llFingerprintAuth = (LinearLayout) view.findViewById(R.id.llFingerprintAuth);
etWalletMnemonic = (TextInputLayout) view.findViewById(R.id.etWalletMnemonic);
etWalletAddress = (TextInputLayout) view.findViewById(R.id.etWalletAddress);
etWalletViewKey = (TextInputLayout) view.findViewById(R.id.etWalletViewKey);
@@ -146,6 +154,30 @@ public class GenerateFragment extends Fragment {
}
});
if (FingerprintHelper.isDeviceSupported(getContext())) {
llFingerprintAuth.setVisibility(View.VISIBLE);
final Switch swFingerprintAllowed = (Switch) llFingerprintAuth.getChildAt(0);
swFingerprintAllowed.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!swFingerprintAllowed.isChecked()) return;
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(Html.fromHtml(getString(R.string.generate_fingerprint_warn)))
.setCancelable(false)
.setPositiveButton(getString(R.string.label_ok), null)
.setNegativeButton(getString(R.string.label_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
swFingerprintAllowed.setChecked(false);
}
})
.show();
}
});
}
if (type.equals(TYPE_NEW)) {
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
@@ -386,7 +418,7 @@ public class GenerateFragment extends Fragment {
private boolean checkAddress() {
String address = etWalletAddress.getEditText().getText().toString();
boolean ok = Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet());
boolean ok = Wallet.isAddressValid(address);
if (!ok) {
etWalletAddress.setError(getString(R.string.generate_check_address));
} else {
@@ -423,18 +455,28 @@ public class GenerateFragment extends Fragment {
String name = etWalletName.getEditText().getText().toString();
String password = etWalletPassword.getEditText().getText().toString();
boolean fingerprintAuthAllowed = ((Switch) llFingerprintAuth.getChildAt(0)).isChecked();
// create the real wallet password
String crazyPass = KeyStoreHelper.getCrazyPass(getActivity(), password);
long height = getHeight();
if (height < 0) height = 0;
if (type.equals(TYPE_NEW)) {
bGenerate.setEnabled(false);
activityCallback.onGenerate(name, password);
if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
}
activityCallback.onGenerate(name, crazyPass);
} else if (type.equals(TYPE_SEED)) {
if (!checkMnemonic()) return;
String seed = etWalletMnemonic.getEditText().getText().toString();
bGenerate.setEnabled(false);
activityCallback.onGenerate(name, password, seed, height);
if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
}
activityCallback.onGenerate(name, crazyPass, seed, height);
} else if (type.equals(TYPE_KEY) || type.equals(TYPE_VIEWONLY)) {
if (checkAddress() && checkViewKey() && checkSpendKey()) {
bGenerate.setEnabled(false);
@@ -444,7 +486,10 @@ public class GenerateFragment extends Fragment {
if (type.equals(TYPE_KEY)) {
spendKey = etWalletSpendKey.getEditText().getText().toString();
}
activityCallback.onGenerate(name, password, address, viewKey, spendKey, height);
if (fingerprintAuthAllowed) {
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
}
activityCallback.onGenerate(name, crazyPass, address, viewKey, spendKey, height);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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);

View File

@@ -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!");
@@ -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

View File

@@ -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 {

View File

@@ -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());
}

View File

@@ -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;
}

View 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();
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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:

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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,22 +72,22 @@ 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 password, String mnemonic) {
return recoveryWallet(aFile, password, mnemonic, 0);
@@ -92,28 +95,28 @@ public class WalletManager {
public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) {
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic,
isTestNet(), restoreHeight);
getNetworkType().getValue(), restoreHeight);
Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet);
return wallet;
}
private native long recoveryWalletJ(String path, String password, String mnemonic,
boolean isTestNet, long restoreHeight);
int networkType, long restoreHeight);
public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight,
String addressString, String viewKeyString, String spendKeyString) {
long walletHandle = createWalletWithKeysJ(aFile.getAbsolutePath(), password,
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 createWalletWithKeysJ(String path, String password,
private native long createWalletFromKeysJ(String path, String password,
String language,
boolean isTestNet,
int networkType,
long restoreHeight,
String addressString,
String viewKeyString,
@@ -204,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;
@@ -234,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;
@@ -268,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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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
}
}
}

View File

@@ -0,0 +1,36 @@
package com.m2049r.xmrwallet.util;
import android.app.KeyguardManager;
import android.content.Context;
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
import android.support.v4.os.CancellationSignal;
import timber.log.Timber;
public class FingerprintHelper {
public static boolean isDeviceSupported(Context context) {
FingerprintManagerCompat fingerprintManager = FingerprintManagerCompat.from(context);
KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
return keyguardManager != null &&
keyguardManager.isKeyguardSecure() &&
fingerprintManager.isHardwareDetected() &&
fingerprintManager.hasEnrolledFingerprints();
}
public static boolean isFingerPassValid(Context context, String wallet) {
try {
KeyStoreHelper.loadWalletUserPass(context, wallet);
return true;
} catch (KeyStoreHelper.BrokenPasswordStoreException ex) {
return false;
}
}
public static void authenticate(Context context, CancellationSignal cancelSignal,
FingerprintManagerCompat.AuthenticationCallback callback) {
FingerprintManagerCompat manager = FingerprintManagerCompat.from(context);
manager.authenticate(null, 0, cancelSignal, callback, null);
}
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More