mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-10 22:10:49 +02:00
Compare commits
84 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
af68c5e51f | ||
![]() |
5a2b48a087 | ||
![]() |
0776b7b6a3 | ||
![]() |
c2eed85a83 | ||
![]() |
d7c2b4a727 | ||
![]() |
085e41f5da | ||
![]() |
6c6b3061a8 | ||
![]() |
5fc15779b7 | ||
![]() |
520d151f3c | ||
![]() |
7aad941dab | ||
![]() |
01e7693425 | ||
![]() |
091538752b | ||
![]() |
d5b95dd976 | ||
![]() |
008f06959c | ||
![]() |
817816cd34 | ||
![]() |
9d41d5da52 | ||
![]() |
ad76a7ffc1 | ||
![]() |
475542c4f3 | ||
![]() |
b5d0659ca9 | ||
![]() |
781bfbc78b | ||
![]() |
8985511209 | ||
![]() |
3c8a4ce967 | ||
![]() |
fcfedbcfae | ||
![]() |
74279b135a | ||
![]() |
d6d2de8312 | ||
![]() |
af0ecb2894 | ||
![]() |
975cc4f43c | ||
![]() |
74ba36de26 | ||
![]() |
7627e15a48 | ||
![]() |
37244cb9e0 | ||
![]() |
843566b820 | ||
![]() |
0bcf156929 | ||
![]() |
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 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,8 +1,7 @@
|
|||||||
.gradle
|
.gradle
|
||||||
/build
|
/build
|
||||||
*.iml
|
*.iml
|
||||||
/.idea/libraries
|
/.idea
|
||||||
/.idea/workspace.xml
|
|
||||||
/local.properties
|
/local.properties
|
||||||
/captures
|
/captures
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
|
19
README.md
19
README.md
@@ -1,12 +1,23 @@
|
|||||||
# Monerujo
|
# 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
|
### QUICKSTART
|
||||||
- Download the APK for the most current release [here](https://github.com/m2049r/xmrwallet/releases) and install it
|
- 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
|
- 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)
|
- 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)
|
- 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
|
### Disclaimer
|
||||||
You may lose all your Moneroj if you use this App. Be cautious when spending on the mainnet.
|
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
|
- see taiga.getmonero.org & issues on github
|
||||||
|
|
||||||
### Issues / Pitfalls
|
### 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.
|
- 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)
|
- 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
|
- 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:
|
No need to build. Binaries are included:
|
||||||
|
|
||||||
- openssl-1.0.2l
|
- openssl-1.0.2l
|
||||||
- monero-v0.11.1.0
|
- monero-v0.12
|
||||||
- boost_1_64_0
|
- boost_1_58_0
|
||||||
|
|
||||||
If you want to build them yourself (recommended) check out [the instructions](doc/BUILDING-external-libs.md)
|
If you want to build them yourself (recommended) check out [the instructions](doc/BUILDING-external-libs.md)
|
||||||
|
|
||||||
|
@@ -1,15 +1,14 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'witness'
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion 25
|
||||||
buildToolsVersion '26.0.2'
|
buildToolsVersion '27.0.3'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.m2049r.xmrwallet"
|
applicationId "com.m2049r.xmrwallet"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
versionCode 86
|
versionCode 100
|
||||||
versionName "1.4.6 'Monero Spedner'"
|
versionName "1.5.10 'Maximum Nacho'"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
@@ -17,9 +16,6 @@ android {
|
|||||||
arguments '-DANDROID_STL=c++_shared'
|
arguments '-DANDROID_STL=c++_shared'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ndk {
|
|
||||||
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -52,59 +48,34 @@ android {
|
|||||||
// APKs for the same app that all have the same version information.
|
// APKs for the same app that all have the same version information.
|
||||||
android.applicationVariants.all { variant ->
|
android.applicationVariants.all { variant ->
|
||||||
// Assigns a different version code for each output APK.
|
// Assigns a different version code for each output APK.
|
||||||
variant.outputs.each {
|
variant.outputs.all {
|
||||||
output ->
|
output ->
|
||||||
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
|
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
|
||||||
output.versionCodeOverride = abiCodes.get(abiName, 0) + 10 * variant.versionCode
|
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 {
|
dependencies {
|
||||||
compile 'com.android.support:appcompat-v7:25.4.0'
|
implementation 'com.android.support:appcompat-v7:25.4.0'
|
||||||
compile 'com.android.support:design:25.4.0'
|
implementation 'com.android.support:design:25.4.0'
|
||||||
compile 'com.android.support:support-v4:25.4.0'
|
implementation 'com.android.support:support-v4:25.4.0'
|
||||||
compile 'com.android.support:recyclerview-v7:25.4.0'
|
implementation 'com.android.support:recyclerview-v7:25.4.0'
|
||||||
compile 'com.android.support:cardview-v7:25.4.0'
|
implementation 'com.android.support:cardview-v7:25.4.0'
|
||||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||||
compile 'me.dm7.barcodescanner:zxing:1.9.8'
|
|
||||||
|
|
||||||
compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
|
implementation "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
|
||||||
compile "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
|
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"
|
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||||
testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||||
testCompile "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
||||||
testCompile 'org.json:json:20140107'
|
testImplementation 'org.json:json:20140107'
|
||||||
testCompile 'net.jodah:concurrentunit:0.4.2'
|
testImplementation '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',
|
|
||||||
'com.jakewharton.timber:timber:35c22867f2673132e97e17857d36bb2fc25f5790f0425406833ed0254d62fc66',
|
|
||||||
'com.nulab-inc:zxcvbn:18d7862a6abd2705defec478d77dedadf8f3bb7cf811df22995494f05485785f',
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
|||||||
#define XMRWALLET_WALLET_LIB_H
|
#define XMRWALLET_WALLET_LIB_H
|
||||||
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
@@ -27,13 +28,13 @@
|
|||||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
#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);
|
jclass c = env->GetObjectClass(obj);
|
||||||
return env->GetFieldID(c, fieldName, "J"); // of type long
|
return env->GetFieldID(c, fieldName, "J"); // of type long
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
T *getHandle(JNIEnv *env, jobject obj, const char* fieldName = "handle") {
|
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||||
jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName));
|
jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName));
|
||||||
return reinterpret_cast<T *>(handle);
|
return reinterpret_cast<T *>(handle);
|
||||||
}
|
}
|
||||||
@@ -42,10 +43,35 @@ void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
|
|||||||
env->SetLongField(obj, getHandleField(env, obj), handle);
|
env->SetLongField(obj, getHandleField(env, obj), handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template<typename T>
|
||||||
void setHandle(JNIEnv *env, jobject obj, T *t) {
|
void setHandle(JNIEnv *env, jobject obj, T *t) {
|
||||||
jlong handle = reinterpret_cast<jlong>(t);
|
jlong handle = reinterpret_cast<jlong>(t);
|
||||||
setHandleFromLong(env, obj, handle);
|
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
|
#endif //XMRWALLET_WALLET_LIB_H
|
||||||
|
@@ -16,12 +16,15 @@
|
|||||||
|
|
||||||
package com.m2049r.xmrwallet;
|
package com.m2049r.xmrwallet;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.design.widget.TextInputLayout;
|
import android.support.design.widget.TextInputLayout;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
|
import android.text.Html;
|
||||||
import android.text.InputType;
|
import android.text.InputType;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
@@ -32,20 +35,23 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Switch;
|
||||||
import android.widget.TextView;
|
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.Wallet;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
|
import com.m2049r.xmrwallet.util.FingerprintHelper;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
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.Strength;
|
||||||
import com.nulabinc.zxcvbn.Zxcvbn;
|
import com.nulabinc.zxcvbn.Zxcvbn;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Calendar;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
@@ -59,6 +65,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
|
|
||||||
private TextInputLayout etWalletName;
|
private TextInputLayout etWalletName;
|
||||||
private TextInputLayout etWalletPassword;
|
private TextInputLayout etWalletPassword;
|
||||||
|
private LinearLayout llFingerprintAuth;
|
||||||
private TextInputLayout etWalletAddress;
|
private TextInputLayout etWalletAddress;
|
||||||
private TextInputLayout etWalletMnemonic;
|
private TextInputLayout etWalletMnemonic;
|
||||||
private TextInputLayout etWalletViewKey;
|
private TextInputLayout etWalletViewKey;
|
||||||
@@ -79,6 +86,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
|
|
||||||
etWalletName = (TextInputLayout) view.findViewById(R.id.etWalletName);
|
etWalletName = (TextInputLayout) view.findViewById(R.id.etWalletName);
|
||||||
etWalletPassword = (TextInputLayout) view.findViewById(R.id.etWalletPassword);
|
etWalletPassword = (TextInputLayout) view.findViewById(R.id.etWalletPassword);
|
||||||
|
llFingerprintAuth = (LinearLayout) view.findViewById(R.id.llFingerprintAuth);
|
||||||
etWalletMnemonic = (TextInputLayout) view.findViewById(R.id.etWalletMnemonic);
|
etWalletMnemonic = (TextInputLayout) view.findViewById(R.id.etWalletMnemonic);
|
||||||
etWalletAddress = (TextInputLayout) view.findViewById(R.id.etWalletAddress);
|
etWalletAddress = (TextInputLayout) view.findViewById(R.id.etWalletAddress);
|
||||||
etWalletViewKey = (TextInputLayout) view.findViewById(R.id.etWalletViewKey);
|
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)) {
|
if (type.equals(TYPE_NEW)) {
|
||||||
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
|
etWalletPassword.getEditText().setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||||
etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
etWalletPassword.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
@@ -423,18 +455,28 @@ public class GenerateFragment extends Fragment {
|
|||||||
|
|
||||||
String name = etWalletName.getEditText().getText().toString();
|
String name = etWalletName.getEditText().getText().toString();
|
||||||
String password = etWalletPassword.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();
|
long height = getHeight();
|
||||||
if (height < 0) height = 0;
|
if (height < 0) height = 0;
|
||||||
|
|
||||||
if (type.equals(TYPE_NEW)) {
|
if (type.equals(TYPE_NEW)) {
|
||||||
bGenerate.setEnabled(false);
|
bGenerate.setEnabled(false);
|
||||||
activityCallback.onGenerate(name, password);
|
if (fingerprintAuthAllowed) {
|
||||||
|
KeyStoreHelper.saveWalletUserPass(getActivity(), name, password);
|
||||||
|
}
|
||||||
|
activityCallback.onGenerate(name, crazyPass);
|
||||||
} else if (type.equals(TYPE_SEED)) {
|
} else if (type.equals(TYPE_SEED)) {
|
||||||
if (!checkMnemonic()) return;
|
if (!checkMnemonic()) return;
|
||||||
String seed = etWalletMnemonic.getEditText().getText().toString();
|
String seed = etWalletMnemonic.getEditText().getText().toString();
|
||||||
bGenerate.setEnabled(false);
|
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)) {
|
} else if (type.equals(TYPE_KEY) || type.equals(TYPE_VIEWONLY)) {
|
||||||
if (checkAddress() && checkViewKey() && checkSpendKey()) {
|
if (checkAddress() && checkViewKey() && checkSpendKey()) {
|
||||||
bGenerate.setEnabled(false);
|
bGenerate.setEnabled(false);
|
||||||
@@ -444,7 +486,10 @@ public class GenerateFragment extends Fragment {
|
|||||||
if (type.equals(TYPE_KEY)) {
|
if (type.equals(TYPE_KEY)) {
|
||||||
spendKey = etWalletSpendKey.getEditText().getText().toString();
|
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
@@ -43,18 +43,20 @@ import android.widget.RelativeLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.dialog.HelpFragment;
|
|
||||||
import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
|
import com.m2049r.xmrwallet.layout.WalletInfoAdapter;
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
import com.m2049r.xmrwallet.util.KeyStoreHelper;
|
||||||
import com.m2049r.xmrwallet.util.NodeList;
|
import com.m2049r.xmrwallet.util.NodeList;
|
||||||
|
import com.m2049r.xmrwallet.util.Notice;
|
||||||
import com.m2049r.xmrwallet.widget.DropDownEditText;
|
import com.m2049r.xmrwallet.widget.DropDownEditText;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
@@ -71,9 +73,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
private DropDownEditText etDaemonAddress;
|
private DropDownEditText etDaemonAddress;
|
||||||
private ArrayAdapter<String> nodeAdapter;
|
private ArrayAdapter<String> nodeAdapter;
|
||||||
|
|
||||||
private View llXmrToEnabled;
|
|
||||||
private View ibXmrToInfoClose;
|
|
||||||
|
|
||||||
private Listener activityCallback;
|
private Listener activityCallback;
|
||||||
|
|
||||||
// Container Activity must implement this interface
|
// Container Activity must implement this interface
|
||||||
@@ -173,23 +172,8 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
|
|
||||||
etDummy = (EditText) view.findViewById(R.id.etDummy);
|
etDummy = (EditText) view.findViewById(R.id.etDummy);
|
||||||
|
|
||||||
llXmrToEnabled = view.findViewById(R.id.llXmrToEnabled);
|
ViewGroup llNotice = (ViewGroup) view.findViewById(R.id.llNotice);
|
||||||
llXmrToEnabled.setOnClickListener(new View.OnClickListener() {
|
Notice.showAll(llNotice,".*_login");
|
||||||
@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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
etDaemonAddress = (DropDownEditText) view.findViewById(R.id.etDaemonAddress);
|
etDaemonAddress = (DropDownEditText) view.findViewById(R.id.etDaemonAddress);
|
||||||
nodeAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_dropdown_item_1line);
|
nodeAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_dropdown_item_1line);
|
||||||
@@ -237,9 +221,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
});
|
});
|
||||||
|
|
||||||
loadPrefs();
|
loadPrefs();
|
||||||
if (!showXmrtoEnabled) {
|
|
||||||
llXmrToEnabled.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@@ -326,6 +307,17 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
ivGunther.setImageDrawable(null);
|
ivGunther.setImageDrawable(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove information of non-existent wallet
|
||||||
|
Set<String> removedWallets = getActivity()
|
||||||
|
.getSharedPreferences(KeyStoreHelper.SecurityConstants.WALLET_PASS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||||
|
.getAll().keySet();
|
||||||
|
for (WalletManager.WalletInfo s : walletList) {
|
||||||
|
removedWallets.remove(s.name);
|
||||||
|
}
|
||||||
|
for (String name : removedWallets) {
|
||||||
|
KeyStoreHelper.removeWalletUserPass(getActivity(), name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showInfo(@NonNull String name) {
|
private void showInfo(@NonNull String name) {
|
||||||
@@ -345,81 +337,65 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.list_menu, menu);
|
inflater.inflate(R.menu.list_menu, menu);
|
||||||
menu.findItem(R.id.action_testnet).setChecked(testnetCheckMenu);
|
menu.findItem(R.id.action_stagenet).setChecked(stagenetCheckMenu);
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean testnetCheckMenu = BuildConfig.DEBUG;
|
private boolean stagenetCheckMenu = BuildConfig.DEBUG;
|
||||||
|
|
||||||
//boolean isTestnet() {
|
public boolean onStagenetMenuItem() {
|
||||||
// return testnet;
|
boolean lastState = stagenetCheckMenu;
|
||||||
//}
|
|
||||||
|
|
||||||
public boolean onTestnetMenuItem() {
|
|
||||||
boolean lastState = testnetCheckMenu;
|
|
||||||
setNet(!lastState, true); // set and save
|
setNet(!lastState, true); // set and save
|
||||||
return !lastState;
|
return !lastState;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNet(boolean testnetChecked, boolean save) {
|
public void setNet(boolean stagenetChecked, boolean save) {
|
||||||
this.testnetCheckMenu = testnetChecked;
|
this.stagenetCheckMenu = stagenetChecked;
|
||||||
NetworkType net = testnetChecked ? NetworkType.NetworkType_Testnet : NetworkType.NetworkType_Mainnet;
|
NetworkType net = stagenetChecked ? NetworkType.NetworkType_Stagenet : NetworkType.NetworkType_Mainnet;
|
||||||
activityCallback.setNetworkType(net);
|
activityCallback.setNetworkType(net);
|
||||||
activityCallback.showNet();
|
activityCallback.showNet();
|
||||||
if (save) {
|
if (save) {
|
||||||
savePrefs(true); // use previous state as we just clicked it
|
savePrefs(true); // use previous state as we just clicked it
|
||||||
}
|
}
|
||||||
if (testnetChecked) {
|
if (stagenetChecked) {
|
||||||
setDaemon(daemonTestNet);
|
setDaemon(daemonStageNet);
|
||||||
} else {
|
} else {
|
||||||
setDaemon(daemonMainNet);
|
setDaemon(daemonMainNet);
|
||||||
}
|
}
|
||||||
loadList();
|
loadList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String PREF_DAEMON_TESTNET = "daemon_testnet";
|
private static final String PREF_DAEMON_STAGENET = "daemon_stagenet";
|
||||||
private static final String PREF_DAEMON_MAINNET = "daemon_mainnet";
|
private static final String PREF_DAEMON_MAINNET = "daemon_mainnet";
|
||||||
private static final String PREF_SHOW_XMRTO_ENABLED = "info_xmrto_enabled_login";
|
|
||||||
|
|
||||||
private static final String PREF_DAEMONLIST_MAINNET =
|
private static final String PREF_DAEMONLIST_MAINNET =
|
||||||
"node.moneroworld.com:18089;node.xmrbackb.one;node.xmr.be";
|
"node.moneroworld.com:18089;node.xmrbackb.one;node.xmr.be";
|
||||||
|
|
||||||
private static final String PREF_DAEMONLIST_TESTNET =
|
private static final String PREF_DAEMONLIST_STAGENET =
|
||||||
"testnet.xmrchain.net";
|
"stagenet.monerujo.io;stagenet.xmr-tw.org";
|
||||||
|
|
||||||
private NodeList daemonTestNet;
|
private NodeList daemonStageNet;
|
||||||
private NodeList daemonMainNet;
|
private NodeList daemonMainNet;
|
||||||
|
|
||||||
boolean showXmrtoEnabled = true;
|
|
||||||
|
|
||||||
void loadPrefs() {
|
void loadPrefs() {
|
||||||
SharedPreferences sharedPref = activityCallback.getPrefs();
|
SharedPreferences sharedPref = activityCallback.getPrefs();
|
||||||
|
|
||||||
daemonMainNet = new NodeList(sharedPref.getString(PREF_DAEMON_MAINNET, PREF_DAEMONLIST_MAINNET));
|
daemonMainNet = new NodeList(sharedPref.getString(PREF_DAEMON_MAINNET, PREF_DAEMONLIST_MAINNET));
|
||||||
daemonTestNet = new NodeList(sharedPref.getString(PREF_DAEMON_TESTNET, PREF_DAEMONLIST_TESTNET));
|
daemonStageNet = new NodeList(sharedPref.getString(PREF_DAEMON_STAGENET, PREF_DAEMONLIST_STAGENET));
|
||||||
setNet(testnetCheckMenu, false);
|
setNet(stagenetCheckMenu, false);
|
||||||
|
|
||||||
showXmrtoEnabled = sharedPref.getBoolean(PREF_SHOW_XMRTO_ENABLED, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void saveXmrToPrefs() {
|
|
||||||
SharedPreferences sharedPref = activityCallback.getPrefs();
|
|
||||||
SharedPreferences.Editor editor = sharedPref.edit();
|
|
||||||
editor.putBoolean(PREF_SHOW_XMRTO_ENABLED, showXmrtoEnabled);
|
|
||||||
editor.apply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void savePrefs() {
|
void savePrefs() {
|
||||||
savePrefs(false);
|
savePrefs(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void savePrefs(boolean usePreviousTestnetState) {
|
void savePrefs(boolean usePreviousNetState) {
|
||||||
Timber.d("SAVE / %s", usePreviousTestnetState);
|
Timber.d("SAVE / %s", usePreviousNetState);
|
||||||
// save the daemon address for the net
|
// save the daemon address for the net
|
||||||
boolean testnet = testnetCheckMenu ^ usePreviousTestnetState;
|
boolean stagenet = stagenetCheckMenu ^ usePreviousNetState;
|
||||||
String daemon = getDaemon();
|
String daemon = getDaemon();
|
||||||
if (testnet) {
|
if (stagenet) {
|
||||||
daemonTestNet.setRecent(daemon);
|
daemonStageNet.setRecent(daemon);
|
||||||
} else {
|
} else {
|
||||||
daemonMainNet.setRecent(daemon);
|
daemonMainNet.setRecent(daemon);
|
||||||
}
|
}
|
||||||
@@ -427,8 +403,7 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
|
|||||||
SharedPreferences sharedPref = activityCallback.getPrefs();
|
SharedPreferences sharedPref = activityCallback.getPrefs();
|
||||||
SharedPreferences.Editor editor = sharedPref.edit();
|
SharedPreferences.Editor editor = sharedPref.edit();
|
||||||
editor.putString(PREF_DAEMON_MAINNET, daemonMainNet.toString());
|
editor.putString(PREF_DAEMON_MAINNET, daemonMainNet.toString());
|
||||||
editor.putString(PREF_DAEMON_TESTNET, daemonTestNet.toString());
|
editor.putString(PREF_DAEMON_STAGENET, daemonStageNet.toString());
|
||||||
editor.putBoolean(PREF_SHOW_XMRTO_ENABLED, showXmrtoEnabled);
|
|
||||||
editor.apply();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -62,6 +62,8 @@ import timber.log.Timber;
|
|||||||
public class ReceiveFragment extends Fragment {
|
public class ReceiveFragment extends Fragment {
|
||||||
|
|
||||||
private ProgressBar pbProgress;
|
private ProgressBar pbProgress;
|
||||||
|
private View llAddress;
|
||||||
|
private TextView tvAddressLabel;
|
||||||
private TextView tvAddress;
|
private TextView tvAddress;
|
||||||
private TextInputLayout etPaymentId;
|
private TextInputLayout etPaymentId;
|
||||||
private ExchangeView evAmount;
|
private ExchangeView evAmount;
|
||||||
@@ -71,6 +73,10 @@ public class ReceiveFragment extends Fragment {
|
|||||||
private ImageView qrCodeFull;
|
private ImageView qrCodeFull;
|
||||||
private EditText etDummy;
|
private EditText etDummy;
|
||||||
private ImageButton bCopyAddress;
|
private ImageButton bCopyAddress;
|
||||||
|
private Button bSubaddress;
|
||||||
|
|
||||||
|
private Wallet wallet = null;
|
||||||
|
private boolean isMyWallet = false;
|
||||||
|
|
||||||
public interface Listener {
|
public interface Listener {
|
||||||
void setToolbarButton(int type);
|
void setToolbarButton(int type);
|
||||||
@@ -87,6 +93,8 @@ public class ReceiveFragment extends Fragment {
|
|||||||
View view = inflater.inflate(R.layout.fragment_receive, container, false);
|
View view = inflater.inflate(R.layout.fragment_receive, container, false);
|
||||||
|
|
||||||
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
|
pbProgress = (ProgressBar) view.findViewById(R.id.pbProgress);
|
||||||
|
llAddress = view.findViewById(R.id.llAddress);
|
||||||
|
tvAddressLabel = (TextView) view.findViewById(R.id.tvAddressLabel);
|
||||||
tvAddress = (TextView) view.findViewById(R.id.tvAddress);
|
tvAddress = (TextView) view.findViewById(R.id.tvAddress);
|
||||||
etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId);
|
etPaymentId = (TextInputLayout) view.findViewById(R.id.etPaymentId);
|
||||||
evAmount = (ExchangeView) view.findViewById(R.id.evAmount);
|
evAmount = (ExchangeView) view.findViewById(R.id.evAmount);
|
||||||
@@ -96,6 +104,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
qrCodeFull = (ImageView) view.findViewById(R.id.qrCodeFull);
|
qrCodeFull = (ImageView) view.findViewById(R.id.qrCodeFull);
|
||||||
etDummy = (EditText) view.findViewById(R.id.etDummy);
|
etDummy = (EditText) view.findViewById(R.id.etDummy);
|
||||||
bCopyAddress = (ImageButton) view.findViewById(R.id.bCopyAddress);
|
bCopyAddress = (ImageButton) view.findViewById(R.id.bCopyAddress);
|
||||||
|
bSubaddress = (Button) view.findViewById(R.id.bSubaddress);
|
||||||
|
|
||||||
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||||
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||||
@@ -106,7 +115,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
copyAddress();
|
copyAddress();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
bCopyAddress.setClickable(false);
|
enableCopyAddress(false);
|
||||||
|
|
||||||
evAmount.setOnNewAmountListener(new ExchangeView.OnNewAmountListener() {
|
evAmount.setOnNewAmountListener(new ExchangeView.OnNewAmountListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -162,6 +171,37 @@ public class ReceiveFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bSubaddress.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
enableSubaddressButton(false);
|
||||||
|
enableCopyAddress(false);
|
||||||
|
|
||||||
|
final Runnable resetSize = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
tvAddress.animate().setDuration(125).scaleX(1).scaleY(1).start();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
final Runnable newAddress = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
tvAddress.setText(wallet.getNewSubaddress());
|
||||||
|
tvAddressLabel.setText(getString(R.string.generate_address_label_sub,
|
||||||
|
wallet.getNumSubaddresses() - 1));
|
||||||
|
storeWallet();
|
||||||
|
generateQr();
|
||||||
|
enableCopyAddress(true);
|
||||||
|
tvAddress.animate().alpha(1).setDuration(125)
|
||||||
|
.scaleX(1.2f).scaleY(1.2f)
|
||||||
|
.withEndAction(resetSize).start();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
tvAddress.animate().alpha(0).setDuration(250)
|
||||||
|
.withEndAction(newAddress).start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
qrCode.setOnClickListener(new View.OnClickListener() {
|
qrCode.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@@ -194,11 +234,25 @@ public class ReceiveFragment extends Fragment {
|
|||||||
String password = b.getString("password");
|
String password = b.getString("password");
|
||||||
loadAndShow(path, password);
|
loadAndShow(path, password);
|
||||||
} else {
|
} else {
|
||||||
show(walletName, address);
|
if (getActivity() instanceof GenerateReviewFragment.ListenerWithWallet) {
|
||||||
|
wallet = ((GenerateReviewFragment.ListenerWithWallet) getActivity()).getWallet();
|
||||||
|
show();
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("no wallet info");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enableSubaddressButton(boolean enable) {
|
||||||
|
bSubaddress.setEnabled(enable);
|
||||||
|
if (enable) {
|
||||||
|
bSubaddress.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_settings_orange_24dp, 0, 0);
|
||||||
|
} else {
|
||||||
|
bSubaddress.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.ic_settings_gray_24dp, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void copyAddress() {
|
void copyAddress() {
|
||||||
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address), tvAddress.getText().toString());
|
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address), tvAddress.getText().toString());
|
||||||
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
|
||||||
@@ -228,25 +282,38 @@ public class ReceiveFragment extends Fragment {
|
|||||||
super.onResume();
|
super.onResume();
|
||||||
Timber.d("onResume()");
|
Timber.d("onResume()");
|
||||||
listenerCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
listenerCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||||
listenerCallback.setSubtitle(getString(R.string.receive_title));
|
if (wallet != null) {
|
||||||
generateQr();
|
listenerCallback.setSubtitle(wallet.getAccountLabel());
|
||||||
|
generateQr();
|
||||||
|
} else {
|
||||||
|
listenerCallback.setSubtitle(getString(R.string.status_wallet_loading));
|
||||||
|
clearQR();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isLoaded = false;
|
private boolean isLoaded = false;
|
||||||
|
|
||||||
private void show(String name, String address) {
|
private void show() {
|
||||||
Timber.d("name=%s", name);
|
Timber.d("name=%s", wallet.getName());
|
||||||
isLoaded = true;
|
isLoaded = true;
|
||||||
listenerCallback.setTitle(name);
|
listenerCallback.setTitle(wallet.getName());
|
||||||
tvAddress.setText(address);
|
listenerCallback.setSubtitle(wallet.getAccountLabel());
|
||||||
|
tvAddress.setText(wallet.getAddress());
|
||||||
etPaymentId.setEnabled(true);
|
etPaymentId.setEnabled(true);
|
||||||
bPaymentId.setEnabled(true);
|
bPaymentId.setEnabled(true);
|
||||||
bCopyAddress.setClickable(true);
|
enableCopyAddress(true);
|
||||||
bCopyAddress.setImageResource(R.drawable.ic_content_copy_black_24dp);
|
|
||||||
hideProgress();
|
hideProgress();
|
||||||
generateQr();
|
generateQr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void enableCopyAddress(boolean enable) {
|
||||||
|
bCopyAddress.setClickable(enable);
|
||||||
|
if (enable)
|
||||||
|
bCopyAddress.setImageResource(R.drawable.ic_content_copy_black_24dp);
|
||||||
|
else
|
||||||
|
bCopyAddress.setImageResource(R.drawable.ic_content_nocopy_black_24dp);
|
||||||
|
}
|
||||||
|
|
||||||
private void loadAndShow(String walletPath, String password) {
|
private void loadAndShow(String walletPath, String password) {
|
||||||
new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR,
|
new AsyncShow().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR,
|
||||||
walletPath, password);
|
walletPath, password);
|
||||||
@@ -254,18 +321,14 @@ public class ReceiveFragment extends Fragment {
|
|||||||
|
|
||||||
private class AsyncShow extends AsyncTask<String, Void, Boolean> {
|
private class AsyncShow extends AsyncTask<String, Void, Boolean> {
|
||||||
String password;
|
String password;
|
||||||
String address;
|
|
||||||
String name;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(String... params) {
|
protected Boolean doInBackground(String... params) {
|
||||||
if (params.length != 2) return false;
|
if (params.length != 2) return false;
|
||||||
String walletPath = params[0];
|
String walletPath = params[0];
|
||||||
password = params[1];
|
password = params[1];
|
||||||
Wallet wallet = WalletManager.getInstance().openWallet(walletPath, password);
|
wallet = WalletManager.getInstance().openWallet(walletPath, password);
|
||||||
address = wallet.getAddress();
|
isMyWallet = true;
|
||||||
name = wallet.getName();
|
|
||||||
wallet.close();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +337,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
if (!isAdded()) return; // never mind
|
if (!isAdded()) return; // never mind
|
||||||
if (result) {
|
if (result) {
|
||||||
show(name, address);
|
show();
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(getActivity(), getString(R.string.receive_cannot_open), Toast.LENGTH_LONG).show();
|
Toast.makeText(getActivity(), getString(R.string.receive_cannot_open), Toast.LENGTH_LONG).show();
|
||||||
hideProgress();
|
hideProgress();
|
||||||
@@ -282,6 +345,27 @@ public class ReceiveFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void storeWallet() {
|
||||||
|
new AsyncStore().executeOnExecutor(MoneroThreadPoolExecutor.MONERO_THREAD_POOL_EXECUTOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class AsyncStore extends AsyncTask<String, Void, Boolean> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(String... params) {
|
||||||
|
if (params.length != 0) return false;
|
||||||
|
if (wallet != null) wallet.store();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
enableSubaddressButton(true);
|
||||||
|
super.onPostExecute(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private boolean checkPaymentId() {
|
private boolean checkPaymentId() {
|
||||||
String paymentId = etPaymentId.getEditText().getText().toString();
|
String paymentId = etPaymentId.getEditText().getText().toString();
|
||||||
boolean ok = paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId);
|
boolean ok = paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId);
|
||||||
@@ -335,6 +419,7 @@ public class ReceiveFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Bitmap generate(String text, int width, int height) {
|
public Bitmap generate(String text, int width, int height) {
|
||||||
|
if ((width <= 0) || (height <= 0)) return null;
|
||||||
Map<EncodeHintType, Object> hints = new HashMap<>();
|
Map<EncodeHintType, Object> hints = new HashMap<>();
|
||||||
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
|
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
|
||||||
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
|
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
|
||||||
@@ -417,4 +502,15 @@ public class ReceiveFragment extends Fragment {
|
|||||||
Timber.d("onPause()");
|
Timber.d("onPause()");
|
||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
Timber.d("onDetach()");
|
||||||
|
if ((wallet != null) && (isMyWallet)) {
|
||||||
|
wallet.close();
|
||||||
|
wallet = null;
|
||||||
|
isMyWallet = false;
|
||||||
|
}
|
||||||
|
super.onDetach();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -59,6 +59,7 @@ public class TxFragment extends Fragment {
|
|||||||
TS_FORMATTER.setTimeZone(tz);
|
TS_FORMATTER.setTimeZone(tz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TextView tvAccount;
|
||||||
private TextView tvTxTimestamp;
|
private TextView tvTxTimestamp;
|
||||||
private TextView tvTxId;
|
private TextView tvTxId;
|
||||||
private TextView tvTxKey;
|
private TextView tvTxKey;
|
||||||
@@ -88,6 +89,7 @@ public class TxFragment extends Fragment {
|
|||||||
tvDestinationBtc = (TextView) view.findViewById(R.id.tvDestinationBtc);
|
tvDestinationBtc = (TextView) view.findViewById(R.id.tvDestinationBtc);
|
||||||
tvTxAmountBtc = (TextView) view.findViewById(R.id.tvTxAmountBtc);
|
tvTxAmountBtc = (TextView) view.findViewById(R.id.tvTxAmountBtc);
|
||||||
|
|
||||||
|
tvAccount = (TextView) view.findViewById(R.id.tvAccount);
|
||||||
tvTxTimestamp = (TextView) view.findViewById(R.id.tvTxTimestamp);
|
tvTxTimestamp = (TextView) view.findViewById(R.id.tvTxTimestamp);
|
||||||
tvTxId = (TextView) view.findViewById(R.id.tvTxId);
|
tvTxId = (TextView) view.findViewById(R.id.tvTxId);
|
||||||
tvTxKey = (TextView) view.findViewById(R.id.tvTxKey);
|
tvTxKey = (TextView) view.findViewById(R.id.tvTxKey);
|
||||||
@@ -222,6 +224,8 @@ public class TxFragment extends Fragment {
|
|||||||
activityCallback.setSubtitle(getString(R.string.tx_title));
|
activityCallback.setSubtitle(getString(R.string.tx_title));
|
||||||
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
activityCallback.setToolbarButton(Toolbar.BUTTON_BACK);
|
||||||
|
|
||||||
|
tvAccount.setText(getString(R.string.tx_account_formatted, info.account, info.subaddress));
|
||||||
|
|
||||||
tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000)));
|
tvTxTimestamp.setText(TS_FORMATTER.format(new Date(info.timestamp * 1000)));
|
||||||
tvTxId.setText(info.hash);
|
tvTxId.setText(info.hash);
|
||||||
tvTxKey.setText(info.txKey.isEmpty() ? "-" : info.txKey);
|
tvTxKey.setText(info.txKey.isEmpty() ? "-" : info.txKey);
|
||||||
@@ -236,9 +240,6 @@ public class TxFragment extends Fragment {
|
|||||||
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
|
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
|
||||||
|
|
||||||
long realAmount = info.amount;
|
long realAmount = info.amount;
|
||||||
if (info.isPending) {
|
|
||||||
realAmount = realAmount - info.fee;
|
|
||||||
}
|
|
||||||
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount));
|
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount));
|
||||||
|
|
||||||
if ((info.fee > 0)) {
|
if ((info.fee > 0)) {
|
||||||
@@ -286,7 +287,10 @@ public class TxFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sb.append("-");
|
sb.append("-");
|
||||||
dstSb.append(info.direction == TransactionInfo.Direction.Direction_In ? activityCallback.getWalletAddress() : "-");
|
dstSb.append(info.direction ==
|
||||||
|
TransactionInfo.Direction.Direction_In ?
|
||||||
|
activityCallback.getWalletSubaddress(info.account, info.subaddress) :
|
||||||
|
"-");
|
||||||
}
|
}
|
||||||
tvTxTransfers.setText(sb.toString());
|
tvTxTransfers.setText(sb.toString());
|
||||||
tvDestination.setText(dstSb.toString());
|
tvDestination.setText(dstSb.toString());
|
||||||
@@ -321,7 +325,7 @@ public class TxFragment extends Fragment {
|
|||||||
Listener activityCallback;
|
Listener activityCallback;
|
||||||
|
|
||||||
public interface Listener {
|
public interface Listener {
|
||||||
String getWalletAddress();
|
String getWalletSubaddress(int accountIndex, int subaddressIndex);
|
||||||
|
|
||||||
String getTxKey(String hash);
|
String getTxKey(String hash);
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -43,9 +43,7 @@ import com.m2049r.xmrwallet.model.Wallet;
|
|||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
import com.m2049r.xmrwallet.service.exchange.kraken.ExchangeApiImpl;
|
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.OkHttpClientSingleton;
|
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
@@ -101,7 +99,9 @@ public class WalletFragment extends Fragment
|
|||||||
ivSynced = (ImageView) view.findViewById(R.id.ivSynced);
|
ivSynced = (ImageView) view.findViewById(R.id.ivSynced);
|
||||||
|
|
||||||
sCurrency = (Spinner) view.findViewById(R.id.sCurrency);
|
sCurrency = (Spinner) view.findViewById(R.id.sCurrency);
|
||||||
sCurrency.setAdapter(ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner_balance));
|
ArrayAdapter currencyAdapter = ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner_balance);
|
||||||
|
currencyAdapter.setDropDownViewResource(R.layout.item_spinner_dropdown_item);
|
||||||
|
sCurrency.setAdapter(currencyAdapter);
|
||||||
|
|
||||||
bSend = (Button) view.findViewById(R.id.bSend);
|
bSend = (Button) view.findViewById(R.id.bSend);
|
||||||
bReceive = (Button) view.findViewById(R.id.bReceive);
|
bReceive = (Button) view.findViewById(R.id.bReceive);
|
||||||
@@ -150,7 +150,7 @@ public class WalletFragment extends Fragment
|
|||||||
// at this point selection is XMR in case of error
|
// at this point selection is XMR in case of error
|
||||||
String displayB;
|
String displayB;
|
||||||
double amountA = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // crash if this fails!
|
double amountA = Double.parseDouble(Wallet.getDisplayAmount(unlockedBalance)); // crash if this fails!
|
||||||
if (!"XMR".equals(balanceCurrency)) { // not XMR
|
if (!Helper.CRYPTO.equals(balanceCurrency)) { // not XMR
|
||||||
double amountB = amountA * balanceRate;
|
double amountB = amountA * balanceRate;
|
||||||
displayB = Helper.getFormattedAmount(amountB, false);
|
displayB = Helper.getFormattedAmount(amountB, false);
|
||||||
} else { // XMR
|
} else { // XMR
|
||||||
@@ -159,10 +159,10 @@ public class WalletFragment extends Fragment
|
|||||||
tvBalance.setText(displayB);
|
tvBalance.setText(displayB);
|
||||||
}
|
}
|
||||||
|
|
||||||
String balanceCurrency = "XMR";
|
String balanceCurrency = Helper.CRYPTO;
|
||||||
double balanceRate = 1.0;
|
double balanceRate = 1.0;
|
||||||
|
|
||||||
private final ExchangeApi exchangeApi = new ExchangeApiImpl(OkHttpClientSingleton.getOkHttpClient());
|
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
|
||||||
|
|
||||||
void refreshBalance() {
|
void refreshBalance() {
|
||||||
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
||||||
@@ -170,9 +170,10 @@ public class WalletFragment extends Fragment
|
|||||||
tvBalance.setText(Helper.getFormattedAmount(amountXmr, true));
|
tvBalance.setText(Helper.getFormattedAmount(amountXmr, true));
|
||||||
} else { // not XMR
|
} else { // not XMR
|
||||||
String currency = (String) sCurrency.getSelectedItem();
|
String currency = (String) sCurrency.getSelectedItem();
|
||||||
|
Timber.d(currency);
|
||||||
if (!currency.equals(balanceCurrency) || (balanceRate <= 0)) {
|
if (!currency.equals(balanceCurrency) || (balanceRate <= 0)) {
|
||||||
showExchanging();
|
showExchanging();
|
||||||
exchangeApi.queryExchangeRate("XMR", currency,
|
exchangeApi.queryExchangeRate(Helper.CRYPTO, currency,
|
||||||
new ExchangeCallback() {
|
new ExchangeCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final ExchangeRate exchangeRate) {
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
@@ -228,10 +229,10 @@ public class WalletFragment extends Fragment
|
|||||||
|
|
||||||
public void exchange(final ExchangeRate exchangeRate) {
|
public void exchange(final ExchangeRate exchangeRate) {
|
||||||
hideExchanging();
|
hideExchanging();
|
||||||
if (!"XMR".equals(exchangeRate.getBaseCurrency())) {
|
if (!Helper.CRYPTO.equals(exchangeRate.getBaseCurrency())) {
|
||||||
Timber.e("Not XMR");
|
Timber.e("Not XMR");
|
||||||
sCurrency.setSelection(0, true);
|
sCurrency.setSelection(0, true);
|
||||||
balanceCurrency = "XMR";
|
balanceCurrency = Helper.CRYPTO;
|
||||||
balanceRate = 1.0;
|
balanceRate = 1.0;
|
||||||
} else {
|
} else {
|
||||||
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
|
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
|
||||||
@@ -256,7 +257,7 @@ public class WalletFragment extends Fragment
|
|||||||
// called from activity
|
// called from activity
|
||||||
|
|
||||||
public void onRefreshed(final Wallet wallet, final boolean full) {
|
public void onRefreshed(final Wallet wallet, final boolean full) {
|
||||||
Timber.d("onRefreshed()");
|
Timber.d("onRefreshed(%b)", full);
|
||||||
if (full) {
|
if (full) {
|
||||||
List<TransactionInfo> list = wallet.getHistory().getAll();
|
List<TransactionInfo> list = wallet.getHistory().getAll();
|
||||||
adapter.setInfos(list);
|
adapter.setInfos(list);
|
||||||
@@ -270,6 +271,7 @@ public class WalletFragment extends Fragment
|
|||||||
bSend.setVisibility(View.VISIBLE);
|
bSend.setVisibility(View.VISIBLE);
|
||||||
bSend.setEnabled(true);
|
bSend.setEnabled(true);
|
||||||
}
|
}
|
||||||
|
enableAccountsList(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean walletLoaded = false;
|
boolean walletLoaded = false;
|
||||||
@@ -313,7 +315,7 @@ public class WalletFragment extends Fragment
|
|||||||
if (wallet == null) return;
|
if (wallet == null) return;
|
||||||
walletTitle = wallet.getName();
|
walletTitle = wallet.getName();
|
||||||
String watchOnly = (wallet.isWatchOnly() ? getString(R.string.label_watchonly) : "");
|
String watchOnly = (wallet.isWatchOnly() ? getString(R.string.label_watchonly) : "");
|
||||||
walletSubtitle = wallet.getAddress().substring(0, 10) + "…" + watchOnly;
|
walletSubtitle = wallet.getAccountLabel();
|
||||||
activityCallback.setTitle(walletTitle, walletSubtitle);
|
activityCallback.setTitle(walletTitle, walletSubtitle);
|
||||||
Timber.d("wallet title is %s", walletTitle);
|
Timber.d("wallet title is %s", walletTitle);
|
||||||
}
|
}
|
||||||
@@ -323,10 +325,13 @@ public class WalletFragment extends Fragment
|
|||||||
private String walletSubtitle = null;
|
private String walletSubtitle = null;
|
||||||
private long unlockedBalance = 0;
|
private long unlockedBalance = 0;
|
||||||
|
|
||||||
|
private int accountIdx = -1;
|
||||||
|
|
||||||
private void updateStatus(Wallet wallet) {
|
private void updateStatus(Wallet wallet) {
|
||||||
if (!isAdded()) return;
|
if (!isAdded()) return;
|
||||||
Timber.d("updateStatus()");
|
Timber.d("updateStatus()");
|
||||||
if (walletTitle == null) {
|
if ((walletTitle == null) || (accountIdx != wallet.getAccountIndex())) {
|
||||||
|
accountIdx = wallet.getAccountIndex();
|
||||||
setActivityTitle(wallet);
|
setActivityTitle(wallet);
|
||||||
}
|
}
|
||||||
long balance = wallet.getBalance();
|
long balance = wallet.getBalance();
|
||||||
@@ -352,7 +357,7 @@ public class WalletFragment extends Fragment
|
|||||||
setProgress(x);
|
setProgress(x);
|
||||||
ivSynced.setVisibility(View.GONE);
|
ivSynced.setVisibility(View.GONE);
|
||||||
} else {
|
} 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);
|
ivSynced.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -412,9 +417,28 @@ public class WalletFragment extends Fragment
|
|||||||
super.onResume();
|
super.onResume();
|
||||||
Timber.d("onResume()");
|
Timber.d("onResume()");
|
||||||
activityCallback.setTitle(walletTitle, walletSubtitle);
|
activityCallback.setTitle(walletTitle, walletSubtitle);
|
||||||
activityCallback.setToolbarButton(Toolbar.BUTTON_CLOSE);
|
//activityCallback.setToolbarButton(Toolbar.BUTTON_CLOSE); // TODO: Close button somewhere else
|
||||||
|
activityCallback.setToolbarButton(Toolbar.BUTTON_NONE);
|
||||||
setProgress(syncProgress);
|
setProgress(syncProgress);
|
||||||
setProgress(syncText);
|
setProgress(syncText);
|
||||||
showReceive();
|
showReceive();
|
||||||
|
if (activityCallback.isSynced()) enableAccountsList(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
enableAccountsList(false);
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface DrawerLocker {
|
||||||
|
void setDrawerEnabled(boolean enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableAccountsList(boolean enable) {
|
||||||
|
if (activityCallback instanceof DrawerLocker) {
|
||||||
|
((DrawerLocker) activityCallback).setDrawerEnabled(enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -86,6 +86,10 @@ public class WalletNode {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NetworkType getNetworkType() {
|
||||||
|
return networkType;
|
||||||
|
}
|
||||||
|
|
||||||
public String getAddress() {
|
public String getAddress() {
|
||||||
return host + ":" + port;
|
return host + ":" + port;
|
||||||
}
|
}
|
||||||
|
@@ -233,7 +233,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
private boolean isBitcoinAddress() {
|
private boolean isBitcoinAddress() {
|
||||||
String address = etAddress.getEditText().getText().toString();
|
String address = etAddress.getEditText().getText().toString();
|
||||||
if ((address.length() >= 27) && (address.length() <= 34))
|
if ((address.length() >= 27) && (address.length() <= 35))
|
||||||
return BitcoinAddressValidator.validate(address);
|
return BitcoinAddressValidator.validate(address);
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
@@ -20,6 +20,9 @@ import android.os.Bundle;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
@@ -57,8 +60,9 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
private TextView tvFunds;
|
private TextView tvFunds;
|
||||||
private ExchangeTextView evAmount;
|
private ExchangeTextView evAmount;
|
||||||
//private Button bSendAll;
|
private View llAmount;
|
||||||
private NumberPadView numberPad;
|
private View rlSweep;
|
||||||
|
private ImageButton ibSweep;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
@@ -73,37 +77,64 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
|||||||
tvFunds = (TextView) view.findViewById(R.id.tvFunds);
|
tvFunds = (TextView) view.findViewById(R.id.tvFunds);
|
||||||
|
|
||||||
evAmount = (ExchangeTextView) view.findViewById(R.id.evAmount);
|
evAmount = (ExchangeTextView) view.findViewById(R.id.evAmount);
|
||||||
numberPad = (NumberPadView) view.findViewById(R.id.numberPad);
|
((NumberPadView) view.findViewById(R.id.numberPad)).setListener(evAmount);
|
||||||
numberPad.setListener(evAmount);
|
|
||||||
|
|
||||||
/*
|
rlSweep = view.findViewById(R.id.rlSweep);
|
||||||
bSendAll = (Button) view.findViewById(R.id.bSendAll);
|
llAmount = view.findViewById(R.id.llAmount);
|
||||||
bSendAll.setOnClickListener(new View.OnClickListener() {
|
view.findViewById(R.id.ivSweep).setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
// TODO: send all - figure out how to display this
|
sweepAll(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ibSweep = (ImageButton) view.findViewById(R.id.ibSweep);
|
||||||
|
|
||||||
|
ibSweep.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
sweepAll(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
Helper.hideKeyboard(getActivity());
|
Helper.hideKeyboard(getActivity());
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean spendAllMode = false;
|
||||||
|
|
||||||
|
private void sweepAll(boolean spendAllMode) {
|
||||||
|
if (spendAllMode) {
|
||||||
|
ibSweep.setVisibility(View.INVISIBLE);
|
||||||
|
llAmount.setVisibility(View.GONE);
|
||||||
|
rlSweep.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
ibSweep.setVisibility(View.VISIBLE);
|
||||||
|
llAmount.setVisibility(View.VISIBLE);
|
||||||
|
rlSweep.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
this.spendAllMode = spendAllMode;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onValidateFields() {
|
public boolean onValidateFields() {
|
||||||
if (!evAmount.validate(maxFunds)) {
|
if (spendAllMode) {
|
||||||
return false;
|
if (sendListener != null) {
|
||||||
}
|
sendListener.getTxData().setAmount(Wallet.SWEEP_ALL);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!evAmount.validate(maxFunds)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (sendListener != null) {
|
if (sendListener != null) {
|
||||||
String xmr = evAmount.getAmount();
|
String xmr = evAmount.getAmount();
|
||||||
if (xmr != null) {
|
if (xmr != null) {
|
||||||
sendListener.getTxData().setAmount(Wallet.getAmountFromString(xmr));
|
sendListener.getTxData().setAmount(Wallet.getAmountFromString(xmr));
|
||||||
} else {
|
} else {
|
||||||
sendListener.getTxData().setAmount(0L);
|
sendListener.getTxData().setAmount(0L);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@@ -247,7 +247,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
private XmrToApi xmrToApi = null;
|
private XmrToApi xmrToApi = null;
|
||||||
|
|
||||||
private final XmrToApi getXmrToApi() {
|
private XmrToApi getXmrToApi() {
|
||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (xmrToApi == null) {
|
if (xmrToApi == null) {
|
||||||
|
@@ -47,6 +47,7 @@ import com.m2049r.xmrwallet.layout.SpendViewPager;
|
|||||||
import com.m2049r.xmrwallet.model.PendingTransaction;
|
import com.m2049r.xmrwallet.model.PendingTransaction;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.NodeList;
|
import com.m2049r.xmrwallet.util.NodeList;
|
||||||
|
import com.m2049r.xmrwallet.util.Notice;
|
||||||
import com.m2049r.xmrwallet.util.UserNotes;
|
import com.m2049r.xmrwallet.util.UserNotes;
|
||||||
import com.m2049r.xmrwallet.widget.DotBar;
|
import com.m2049r.xmrwallet.widget.DotBar;
|
||||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
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);
|
arrowPrev = getResources().getDrawable(R.drawable.ic_navigate_prev_white_24dp);
|
||||||
arrowNext = getResources().getDrawable(R.drawable.ic_navigate_next_white_24dp);
|
arrowNext = getResources().getDrawable(R.drawable.ic_navigate_next_white_24dp);
|
||||||
|
|
||||||
llXmrToEnabled = view.findViewById(R.id.llXmrToEnabled);
|
ViewGroup llNotice = (ViewGroup) view.findViewById(R.id.llNotice);
|
||||||
llXmrToEnabled.setOnClickListener(new View.OnClickListener() {
|
Notice.showAll(llNotice,".*_send");
|
||||||
@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);
|
|
||||||
}
|
|
||||||
|
|
||||||
spendViewPager = (SpendViewPager) view.findViewById(R.id.pager);
|
spendViewPager = (SpendViewPager) view.findViewById(R.id.pager);
|
||||||
pagerAdapter = new SpendPagerAdapter(getChildFragmentManager());
|
pagerAdapter = new SpendPagerAdapter(getChildFragmentManager());
|
||||||
@@ -291,7 +273,7 @@ public class SendFragment extends Fragment
|
|||||||
pagerAdapter.notifyDataSetChanged();
|
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
|
@Override
|
||||||
public SendWizardFragment getItem(int position) {
|
public SendWizardFragment getItem(int position) {
|
||||||
Timber.d("getItem(%d) CREATE", position);
|
Timber.d("getItem(%d) CREATE", position);
|
||||||
Timber.d("Mode=" + mode.toString());
|
Timber.d("Mode=%s", mode.toString());
|
||||||
if (mode == Mode.XMR) {
|
if (mode == Mode.XMR) {
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case POS_ADDRESS:
|
case POS_ADDRESS:
|
||||||
|
@@ -36,6 +36,7 @@ import com.m2049r.xmrwallet.util.UserNotes;
|
|||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class SendSettingsWizardFragment extends SendWizardFragment {
|
public class SendSettingsWizardFragment extends SendWizardFragment {
|
||||||
|
final static public int MIXIN = 6;
|
||||||
|
|
||||||
public static SendSettingsWizardFragment newInstance(Listener listener) {
|
public static SendSettingsWizardFragment newInstance(Listener listener) {
|
||||||
SendSettingsWizardFragment instance = new SendSettingsWizardFragment();
|
SendSettingsWizardFragment instance = new SendSettingsWizardFragment();
|
||||||
@@ -54,15 +55,12 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
|
|||||||
TxData getTxData();
|
TxData getTxData();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mixin = Ringsize - 1
|
|
||||||
final static int Mixins[] = {6, 9, 12, 25}; // must match the layout XML / "@array/mixin"
|
|
||||||
final static PendingTransaction.Priority Priorities[] =
|
final static PendingTransaction.Priority Priorities[] =
|
||||||
{PendingTransaction.Priority.Priority_Default,
|
{PendingTransaction.Priority.Priority_Default,
|
||||||
PendingTransaction.Priority.Priority_Low,
|
PendingTransaction.Priority.Priority_Low,
|
||||||
PendingTransaction.Priority.Priority_Medium,
|
PendingTransaction.Priority.Priority_Medium,
|
||||||
PendingTransaction.Priority.Priority_High}; // must match the layout XML
|
PendingTransaction.Priority.Priority_High}; // must match the layout XML
|
||||||
|
|
||||||
private Spinner sMixin;
|
|
||||||
private Spinner sPriority;
|
private Spinner sPriority;
|
||||||
private EditText etNotes;
|
private EditText etNotes;
|
||||||
private EditText etDummy;
|
private EditText etDummy;
|
||||||
@@ -77,7 +75,6 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
|
|||||||
View view = inflater.inflate(
|
View view = inflater.inflate(
|
||||||
R.layout.fragment_send_settings, container, false);
|
R.layout.fragment_send_settings, container, false);
|
||||||
|
|
||||||
sMixin = (Spinner) view.findViewById(R.id.sMixin);
|
|
||||||
sPriority = (Spinner) view.findViewById(R.id.sPriority);
|
sPriority = (Spinner) view.findViewById(R.id.sPriority);
|
||||||
|
|
||||||
etNotes = (EditText) view.findViewById(R.id.etNotes);
|
etNotes = (EditText) view.findViewById(R.id.etNotes);
|
||||||
@@ -104,7 +101,7 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
|
|||||||
if (sendListener != null) {
|
if (sendListener != null) {
|
||||||
TxData txData = sendListener.getTxData();
|
TxData txData = sendListener.getTxData();
|
||||||
txData.setPriority(Priorities[sPriority.getSelectedItemPosition()]);
|
txData.setPriority(Priorities[sPriority.getSelectedItemPosition()]);
|
||||||
txData.setMixin(Mixins[sMixin.getSelectedItemPosition()]);
|
txData.setMixin(MIXIN);
|
||||||
txData.setUserNotes(new UserNotes(etNotes.getText().toString()));
|
txData.setUserNotes(new UserNotes(etNotes.getText().toString()));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@@ -166,7 +166,11 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((userNotes.note.isEmpty())) {
|
if ((userNotes.note.isEmpty())) {
|
||||||
this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ? "" : infoItem.paymentId);
|
this.tvPaymentId.setText(infoItem.paymentId.equals("0000000000000000") ?
|
||||||
|
(infoItem.subaddress != 0 ?
|
||||||
|
(context.getString(R.string.tx_subaddress, infoItem.subaddress)) :
|
||||||
|
"") :
|
||||||
|
infoItem.paymentId);
|
||||||
} else {
|
} else {
|
||||||
this.tvPaymentId.setText(userNotes.note);
|
this.tvPaymentId.setText(userNotes.note);
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,11 @@
|
|||||||
package com.m2049r.xmrwallet.model;
|
package com.m2049r.xmrwallet.model;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class TransactionHistory {
|
public class TransactionHistory {
|
||||||
static {
|
static {
|
||||||
System.loadLibrary("monerujo");
|
System.loadLibrary("monerujo");
|
||||||
@@ -26,8 +29,18 @@ public class TransactionHistory {
|
|||||||
|
|
||||||
private long handle;
|
private long handle;
|
||||||
|
|
||||||
public TransactionHistory(long handle) {
|
int accountIndex;
|
||||||
|
|
||||||
|
public void setAccountFor(Wallet wallet) {
|
||||||
|
if (accountIndex != wallet.getAccountIndex()) {
|
||||||
|
this.accountIndex = wallet.getAccountIndex();
|
||||||
|
refreshWithNotes(wallet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TransactionHistory(long handle, int accountIndex) {
|
||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
|
this.accountIndex = accountIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadNotes(Wallet wallet) {
|
public void loadNotes(Wallet wallet) {
|
||||||
@@ -36,7 +49,7 @@ public class TransactionHistory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public native int getCount();
|
public native int getCount(); // over all accounts/subaddresses
|
||||||
|
|
||||||
//private native long getTransactionByIndexJ(int i);
|
//private native long getTransactionByIndexJ(int i);
|
||||||
|
|
||||||
@@ -53,8 +66,23 @@ public class TransactionHistory {
|
|||||||
loadNotes(wallet);
|
loadNotes(wallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// public void refresh() {
|
||||||
|
// transactions = refreshJ();
|
||||||
|
// }
|
||||||
|
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
transactions = refreshJ();
|
List<TransactionInfo> t = refreshJ();
|
||||||
|
Timber.d("refreshed %d", t.size());
|
||||||
|
for (Iterator<TransactionInfo> iterator = t.iterator(); iterator.hasNext(); ) {
|
||||||
|
TransactionInfo info = iterator.next();
|
||||||
|
if (info.account != accountIndex) {
|
||||||
|
iterator.remove();
|
||||||
|
Timber.d("removed %s", info.hash);
|
||||||
|
} else {
|
||||||
|
Timber.d("kept %s", info.hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transactions = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
private native List<TransactionInfo> refreshJ();
|
private native List<TransactionInfo> refreshJ();
|
||||||
|
@@ -53,6 +53,11 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// virtual std::set<uint32_t> subaddrIndex() const = 0;
|
||||||
|
// virtual uint32_t subaddrAccount() const = 0;
|
||||||
|
// virtual std::string label() const = 0;
|
||||||
|
// virtual uint64_t confirmations() const = 0;
|
||||||
|
|
||||||
public Direction direction;
|
public Direction direction;
|
||||||
public boolean isPending;
|
public boolean isPending;
|
||||||
public boolean isFailed;
|
public boolean isFailed;
|
||||||
@@ -62,6 +67,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||||||
public String hash;
|
public String hash;
|
||||||
public long timestamp;
|
public long timestamp;
|
||||||
public String paymentId;
|
public String paymentId;
|
||||||
|
public int account;
|
||||||
|
public int subaddress;
|
||||||
public long confirmations;
|
public long confirmations;
|
||||||
public List<Transfer> transfers;
|
public List<Transfer> transfers;
|
||||||
|
|
||||||
@@ -78,6 +85,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||||||
String hash,
|
String hash,
|
||||||
long timestamp,
|
long timestamp,
|
||||||
String paymentId,
|
String paymentId,
|
||||||
|
int account,
|
||||||
|
int subaddress,
|
||||||
long confirmations,
|
long confirmations,
|
||||||
List<Transfer> transfers) {
|
List<Transfer> transfers) {
|
||||||
this.direction = Direction.values()[direction];
|
this.direction = Direction.values()[direction];
|
||||||
@@ -89,6 +98,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||||||
this.hash = hash;
|
this.hash = hash;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
this.paymentId = paymentId;
|
this.paymentId = paymentId;
|
||||||
|
this.account = account;
|
||||||
|
this.subaddress = subaddress;
|
||||||
this.confirmations = confirmations;
|
this.confirmations = confirmations;
|
||||||
this.transfers = transfers;
|
this.transfers = transfers;
|
||||||
}
|
}
|
||||||
@@ -108,6 +119,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||||||
out.writeString(hash);
|
out.writeString(hash);
|
||||||
out.writeLong(timestamp);
|
out.writeLong(timestamp);
|
||||||
out.writeString(paymentId);
|
out.writeString(paymentId);
|
||||||
|
out.writeInt(account);
|
||||||
|
out.writeInt(subaddress);
|
||||||
out.writeLong(confirmations);
|
out.writeLong(confirmations);
|
||||||
out.writeList(transfers);
|
out.writeList(transfers);
|
||||||
out.writeString(txKey);
|
out.writeString(txKey);
|
||||||
@@ -134,6 +147,8 @@ public class TransactionInfo implements Parcelable, Comparable<TransactionInfo>
|
|||||||
hash = in.readString();
|
hash = in.readString();
|
||||||
timestamp = in.readLong();
|
timestamp = in.readLong();
|
||||||
paymentId = in.readString();
|
paymentId = in.readString();
|
||||||
|
account = in.readInt();
|
||||||
|
subaddress = in.readInt();
|
||||||
confirmations = in.readLong();
|
confirmations = in.readLong();
|
||||||
transfers = in.readArrayList(Transfer.class.getClassLoader());
|
transfers = in.readArrayList(Transfer.class.getClassLoader());
|
||||||
txKey = in.readString();
|
txKey = in.readString();
|
||||||
|
@@ -19,13 +19,30 @@ package com.m2049r.xmrwallet.model;
|
|||||||
import com.m2049r.xmrwallet.data.TxData;
|
import com.m2049r.xmrwallet.data.TxData;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class Wallet {
|
public class Wallet {
|
||||||
|
final static public long SWEEP_ALL = Long.MAX_VALUE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
System.loadLibrary("monerujo");
|
System.loadLibrary("monerujo");
|
||||||
}
|
}
|
||||||
|
|
||||||
static final String TAG = "Wallet";
|
private int accountIndex = 0;
|
||||||
|
|
||||||
|
public int getAccountIndex() {
|
||||||
|
return accountIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccountIndex(int accountIndex) {
|
||||||
|
Timber.d("setAccountIndex(%d)", accountIndex);
|
||||||
|
this.accountIndex = accountIndex;
|
||||||
|
getHistory().setAccountFor(this);
|
||||||
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return new File(getPath()).getName();
|
return new File(getPath()).getName();
|
||||||
@@ -38,6 +55,11 @@ public class Wallet {
|
|||||||
this.handle = handle;
|
this.handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Wallet(long handle, int accountIndex) {
|
||||||
|
this.handle = handle;
|
||||||
|
this.accountIndex = accountIndex;
|
||||||
|
}
|
||||||
|
|
||||||
public enum Status {
|
public enum Status {
|
||||||
Status_Ok,
|
Status_Ok,
|
||||||
Status_Error,
|
Status_Error,
|
||||||
@@ -66,16 +88,23 @@ public class Wallet {
|
|||||||
|
|
||||||
public native boolean setPassword(String password);
|
public native boolean setPassword(String password);
|
||||||
|
|
||||||
private String address = null;
|
|
||||||
|
|
||||||
public String getAddress() {
|
public String getAddress() {
|
||||||
if (address == null) {
|
return getAddress(accountIndex);
|
||||||
address = getAddressJ();
|
|
||||||
}
|
|
||||||
return address;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private native String getAddressJ();
|
public String getAddress(int accountIndex) {
|
||||||
|
return getAddressJ(accountIndex, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubaddress(int addressIndex) {
|
||||||
|
return getAddressJ(accountIndex, addressIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubaddress(int accountIndex, int addressIndex) {
|
||||||
|
return getAddressJ(accountIndex, addressIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native String getAddressJ(int accountIndex, int addressIndex);
|
||||||
|
|
||||||
public native String getPath();
|
public native String getPath();
|
||||||
|
|
||||||
@@ -95,7 +124,9 @@ public class Wallet {
|
|||||||
public native String getSecretSpendKey();
|
public native String getSecretSpendKey();
|
||||||
|
|
||||||
public boolean store() {
|
public boolean store() {
|
||||||
return store("");
|
final boolean ok = store("");
|
||||||
|
Timber.d("stored");
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
public native boolean store(String path);
|
public native boolean store(String path);
|
||||||
@@ -132,9 +163,21 @@ public class Wallet {
|
|||||||
//TODO virtual void setTrustedDaemon(bool arg) = 0;
|
//TODO virtual void setTrustedDaemon(bool arg) = 0;
|
||||||
//TODO virtual bool trustedDaemon() const = 0;
|
//TODO virtual bool trustedDaemon() const = 0;
|
||||||
|
|
||||||
public native long getBalance();
|
public long getBalance() {
|
||||||
|
return getBalance(accountIndex);
|
||||||
|
}
|
||||||
|
|
||||||
public native long getUnlockedBalance();
|
public native long getBalance(int accountIndex);
|
||||||
|
|
||||||
|
public native long getBalanceAll();
|
||||||
|
|
||||||
|
public long getUnlockedBalance() {
|
||||||
|
return getUnlockedBalance(accountIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native long getUnlockedBalanceAll();
|
||||||
|
|
||||||
|
public native long getUnlockedBalance(int accountIndex);
|
||||||
|
|
||||||
public native boolean isWatchOnly();
|
public native boolean isWatchOnly();
|
||||||
|
|
||||||
@@ -164,9 +207,7 @@ public class Wallet {
|
|||||||
|
|
||||||
public static native boolean isAddressValid(String address, int networkType);
|
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, int networkType);
|
||||||
|
|
||||||
public static native String getPaymentIdFromAddress(String address, boolean isTestNet);
|
|
||||||
|
|
||||||
public static native long getMaximumAllowedAmount();
|
public static native long getMaximumAllowedAmount();
|
||||||
|
|
||||||
@@ -209,14 +250,23 @@ public class Wallet {
|
|||||||
PendingTransaction.Priority priority) {
|
PendingTransaction.Priority priority) {
|
||||||
disposePendingTransaction();
|
disposePendingTransaction();
|
||||||
int _priority = priority.getValue();
|
int _priority = priority.getValue();
|
||||||
long txHandle = createTransactionJ(dst_addr, payment_id, amount, mixin_count, _priority);
|
long txHandle =
|
||||||
|
(amount == SWEEP_ALL ?
|
||||||
|
createSweepTransaction(dst_addr, payment_id, mixin_count, _priority,
|
||||||
|
accountIndex) :
|
||||||
|
createTransactionJ(dst_addr, payment_id, amount, mixin_count, _priority,
|
||||||
|
accountIndex));
|
||||||
pendingTransaction = new PendingTransaction(txHandle);
|
pendingTransaction = new PendingTransaction(txHandle);
|
||||||
return pendingTransaction;
|
return pendingTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
private native long createTransactionJ(String dst_addr, String payment_id,
|
private native long createTransactionJ(String dst_addr, String payment_id,
|
||||||
long amount, int mixin_count,
|
long amount, int mixin_count,
|
||||||
int priority);
|
int priority, int accountIndex);
|
||||||
|
|
||||||
|
private native long createSweepTransaction(String dst_addr, String payment_id,
|
||||||
|
int mixin_count,
|
||||||
|
int priority, int accountIndex);
|
||||||
|
|
||||||
|
|
||||||
public PendingTransaction createSweepUnmixableTransaction() {
|
public PendingTransaction createSweepUnmixableTransaction() {
|
||||||
@@ -243,7 +293,7 @@ public class Wallet {
|
|||||||
|
|
||||||
public TransactionHistory getHistory() {
|
public TransactionHistory getHistory() {
|
||||||
if (history == null) {
|
if (history == null) {
|
||||||
history = new TransactionHistory(getHistoryJ());
|
history = new TransactionHistory(getHistoryJ(), accountIndex);
|
||||||
}
|
}
|
||||||
return history;
|
return history;
|
||||||
}
|
}
|
||||||
@@ -275,5 +325,68 @@ 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 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;
|
//virtual bool rescanSpent() = 0;
|
||||||
|
|
||||||
|
private static final String NEW_ACCOUNT_NAME = "Untitled account"; // src/wallet/wallet2.cpp:941
|
||||||
|
|
||||||
|
public void addAccount() {
|
||||||
|
addAccount(NEW_ACCOUNT_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native void addAccount(String label);
|
||||||
|
|
||||||
|
public String getAccountLabel() {
|
||||||
|
return getAccountLabel(accountIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccountLabel(int accountIndex) {
|
||||||
|
String label = getSubaddressLabel(accountIndex, 0);
|
||||||
|
if (label.equals(NEW_ACCOUNT_NAME)) {
|
||||||
|
String address = getAddress(accountIndex);
|
||||||
|
int len = address.length();
|
||||||
|
return address.substring(0, 6) +
|
||||||
|
"\u2026" + address.substring(len - 6, len);
|
||||||
|
} else return label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubaddressLabel(int addressIndex) {
|
||||||
|
return getSubaddressLabel(accountIndex, addressIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native String getSubaddressLabel(int accountIndex, int addressIndex);
|
||||||
|
|
||||||
|
public void setAccountLabel(String label) {
|
||||||
|
setAccountLabel(accountIndex, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccountLabel(int accountIndex, String label) {
|
||||||
|
setSubaddressLabel(accountIndex, 0, label);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native void setSubaddressLabel(int accountIndex, int addressIndex, String label);
|
||||||
|
|
||||||
|
public native int getNumAccounts();
|
||||||
|
|
||||||
|
public int getNumSubaddresses() {
|
||||||
|
return getNumSubaddresses(accountIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public native int getNumSubaddresses(int accountIndex);
|
||||||
|
|
||||||
|
public String getNewSubaddress() {
|
||||||
|
return getNewSubaddress(accountIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNewSubaddress(int accountIndex) {
|
||||||
|
String timeStamp = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss", Locale.US).format(new Date());
|
||||||
|
addSubaddress(accountIndex, timeStamp);
|
||||||
|
String subaddress = getLastSubaddress(accountIndex);
|
||||||
|
Timber.d("%d: %s", getNumSubaddresses(accountIndex) - 1, subaddress);
|
||||||
|
return subaddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public native void addSubaddress(int accountIndex, String label);
|
||||||
|
|
||||||
|
public String getLastSubaddress(int accountIndex) {
|
||||||
|
return getSubaddress(accountIndex, getNumSubaddresses(accountIndex) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -41,6 +41,7 @@ public class WalletManager {
|
|||||||
if (WalletManager.Instance == null) {
|
if (WalletManager.Instance == null) {
|
||||||
WalletManager.Instance = new WalletManager();
|
WalletManager.Instance = new WalletManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
return WalletManager.Instance;
|
return WalletManager.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,6 +80,13 @@ public class WalletManager {
|
|||||||
|
|
||||||
private native long createWalletJ(String path, String password, String language, int networkType);
|
private native long createWalletJ(String path, String password, String language, int networkType);
|
||||||
|
|
||||||
|
public Wallet openAccount(String path, int accountIndex, String password) {
|
||||||
|
long walletHandle = openWalletJ(path, password, getNetworkType().getValue());
|
||||||
|
Wallet wallet = new Wallet(walletHandle, accountIndex);
|
||||||
|
manageWallet(wallet);
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
public Wallet openWallet(String path, String password) {
|
public Wallet openWallet(String path, String password) {
|
||||||
long walletHandle = openWalletJ(path, password, getNetworkType().getValue());
|
long walletHandle = openWalletJ(path, password, getNetworkType().getValue());
|
||||||
Wallet wallet = new Wallet(walletHandle);
|
Wallet wallet = new Wallet(walletHandle);
|
||||||
@@ -215,7 +223,7 @@ public class WalletManager {
|
|||||||
//public void setDaemon(String address, NetworkType networkType, String username, String password) {
|
//public void setDaemon(String address, NetworkType networkType, String username, String password) {
|
||||||
public void setDaemon(WalletNode walletNode) {
|
public void setDaemon(WalletNode walletNode) {
|
||||||
this.daemonAddress = walletNode.getAddress();
|
this.daemonAddress = walletNode.getAddress();
|
||||||
this.networkType = networkType;
|
this.networkType = walletNode.getNetworkType();
|
||||||
this.daemonUsername = walletNode.getUsername();
|
this.daemonUsername = walletNode.getUsername();
|
||||||
this.daemonPassword = walletNode.getPassword();
|
this.daemonPassword = walletNode.getPassword();
|
||||||
setDaemonAddressJ(daemonAddress);
|
setDaemonAddressJ(daemonAddress);
|
||||||
@@ -227,7 +235,6 @@ public class WalletManager {
|
|||||||
|
|
||||||
public String getDaemonAddress() {
|
public String getDaemonAddress() {
|
||||||
if (daemonAddress == null) {
|
if (daemonAddress == null) {
|
||||||
// assume testnet not explicitly initialised
|
|
||||||
throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
|
throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
|
||||||
}
|
}
|
||||||
return this.daemonAddress;
|
return this.daemonAddress;
|
||||||
@@ -235,13 +242,13 @@ public class WalletManager {
|
|||||||
|
|
||||||
private native void setDaemonAddressJ(String address);
|
private native void setDaemonAddressJ(String address);
|
||||||
|
|
||||||
String daemonUsername = "";
|
private String daemonUsername = "";
|
||||||
|
|
||||||
public String getDaemonUsername() {
|
public String getDaemonUsername() {
|
||||||
return daemonUsername;
|
return daemonUsername;
|
||||||
}
|
}
|
||||||
|
|
||||||
String daemonPassword = "";
|
private String daemonPassword = "";
|
||||||
|
|
||||||
public String getDaemonPassword() {
|
public String getDaemonPassword() {
|
||||||
return daemonPassword;
|
return daemonPassword;
|
||||||
@@ -269,5 +276,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);
|
//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);
|
||||||
}
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 m2049r et al.
|
* Copyright (c) 2017-2018 m2049r et al.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.m2049r.xmrwallet.service.exchange.kraken;
|
package com.m2049r.xmrwallet.service.exchange.coinmarketcap;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
@@ -23,6 +23,7 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
|||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeException;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeException;
|
||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -37,6 +38,7 @@ import okhttp3.Request;
|
|||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
public class ExchangeApiImpl implements ExchangeApi {
|
public class ExchangeApiImpl implements ExchangeApi {
|
||||||
|
static final String CRYPTO_ID = "328";
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final OkHttpClient okHttpClient;
|
private final OkHttpClient okHttpClient;
|
||||||
@@ -52,7 +54,7 @@ public class ExchangeApiImpl implements ExchangeApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ExchangeApiImpl(@NonNull final OkHttpClient okHttpClient) {
|
public ExchangeApiImpl(@NonNull final OkHttpClient okHttpClient) {
|
||||||
this(okHttpClient, HttpUrl.parse("https://api.kraken.com/0/public/Ticker"));
|
this(okHttpClient, HttpUrl.parse("https://api.coinmarketcap.com/v2/ticker/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,12 +69,12 @@ public class ExchangeApiImpl implements ExchangeApi {
|
|||||||
boolean inverse = false;
|
boolean inverse = false;
|
||||||
String fiat = null;
|
String fiat = null;
|
||||||
|
|
||||||
if (baseCurrency.equals("XMR")) {
|
if (baseCurrency.equals(Helper.CRYPTO)) {
|
||||||
fiat = quoteCurrency;
|
fiat = quoteCurrency;
|
||||||
inverse = false;
|
inverse = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quoteCurrency.equals("XMR")) {
|
if (quoteCurrency.equals(Helper.CRYPTO)) {
|
||||||
fiat = baseCurrency;
|
fiat = baseCurrency;
|
||||||
inverse = true;
|
inverse = true;
|
||||||
}
|
}
|
||||||
@@ -85,7 +87,8 @@ public class ExchangeApiImpl implements ExchangeApi {
|
|||||||
final boolean swapAssets = inverse;
|
final boolean swapAssets = inverse;
|
||||||
|
|
||||||
final HttpUrl url = baseUrl.newBuilder()
|
final HttpUrl url = baseUrl.newBuilder()
|
||||||
.addQueryParameter("pair", "XMR" + fiat)
|
.addEncodedPathSegments(CRYPTO_ID + "/")
|
||||||
|
.addQueryParameter("convert", fiat)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final Request httpRequest = createHttpRequest(url);
|
final Request httpRequest = createHttpRequest(url);
|
||||||
@@ -101,12 +104,12 @@ public class ExchangeApiImpl implements ExchangeApi {
|
|||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
try {
|
try {
|
||||||
final JSONObject json = new JSONObject(response.body().string());
|
final JSONObject json = new JSONObject(response.body().string());
|
||||||
final JSONArray jsonError = json.getJSONArray("error");
|
final JSONObject metadata = json.getJSONObject("metadata");
|
||||||
if (jsonError.length() > 0) {
|
if (!metadata.isNull("error")) {
|
||||||
final String errorMsg = jsonError.getString(0);
|
final String errorMsg = metadata.getString("error");
|
||||||
callback.onError(new ExchangeException(response.code(), errorMsg));
|
callback.onError(new ExchangeException(response.code(), (String) errorMsg));
|
||||||
} else {
|
} else {
|
||||||
final JSONObject jsonResult = json.getJSONObject("result");
|
final JSONObject jsonResult = json.getJSONObject("data");
|
||||||
reportSuccess(jsonResult, swapAssets, callback);
|
reportSuccess(jsonResult, swapAssets, callback);
|
||||||
}
|
}
|
||||||
} catch (JSONException ex) {
|
} catch (JSONException ex) {
|
||||||
@@ -130,7 +133,6 @@ public class ExchangeApiImpl implements ExchangeApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Request createHttpRequest(final HttpUrl url) {
|
private Request createHttpRequest(final HttpUrl url) {
|
||||||
return new Request.Builder()
|
return new Request.Builder()
|
||||||
.url(url)
|
.url(url)
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017 m2049r et al.
|
* Copyright (c) 2017-2018 m2049r et al.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.m2049r.xmrwallet.service.exchange.kraken;
|
package com.m2049r.xmrwallet.service.exchange.coinmarketcap;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -37,7 +38,7 @@ class ExchangeRateImpl implements ExchangeRate {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getServiceName() {
|
public String getServiceName() {
|
||||||
return "kraken.com";
|
return "coinmarketcap.com";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -64,29 +65,21 @@ class ExchangeRateImpl implements ExchangeRate {
|
|||||||
|
|
||||||
ExchangeRateImpl(final JSONObject jsonObject, final boolean swapAssets) throws JSONException, ExchangeException {
|
ExchangeRateImpl(final JSONObject jsonObject, final boolean swapAssets) throws JSONException, ExchangeException {
|
||||||
try {
|
try {
|
||||||
final String key = jsonObject.keys().next(); // we expect only one
|
final String baseC = jsonObject.getString("symbol");
|
||||||
Pattern pattern = Pattern.compile("^X(.*?)Z(.*?)$");
|
final JSONObject quotes = jsonObject.getJSONObject("quotes");
|
||||||
Matcher matcher = pattern.matcher(key);
|
final Iterator<String> keys = quotes.keys();
|
||||||
if (matcher.find()) {
|
String key = null;
|
||||||
this.baseCurrency = swapAssets ? matcher.group(2) : matcher.group(1);
|
// get key which is not USD unless it is the only one
|
||||||
this.quoteCurrency = swapAssets ? matcher.group(1) : matcher.group(2);
|
while (keys.hasNext()) {
|
||||||
} else {
|
key = keys.next();
|
||||||
throw new ExchangeException("no pair returned!");
|
if (!key.equals("USD")) break;
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject pair = jsonObject.getJSONObject(key);
|
|
||||||
JSONArray close = pair.getJSONArray("c");
|
|
||||||
String closePrice = close.getString(0);
|
|
||||||
if (closePrice != null) {
|
|
||||||
try {
|
|
||||||
double rate = Double.parseDouble(closePrice);
|
|
||||||
this.rate = swapAssets ? (1 / rate) : rate;
|
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
throw new ExchangeException(ex.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new ExchangeException("no close price returned!");
|
|
||||||
}
|
}
|
||||||
|
final String quoteC = key;
|
||||||
|
baseCurrency = swapAssets ? quoteC : baseC;
|
||||||
|
quoteCurrency = swapAssets ? baseC : quoteC;
|
||||||
|
JSONObject quote = quotes.getJSONObject(key);
|
||||||
|
double price = quote.getDouble("price");
|
||||||
|
this.rate = swapAssets ? (1d / price) : price;
|
||||||
} catch (NoSuchElementException ex) {
|
} catch (NoSuchElementException ex) {
|
||||||
throw new ExchangeException(ex.getLocalizedMessage());
|
throw new ExchangeException(ex.getLocalizedMessage());
|
||||||
}
|
}
|
@@ -66,6 +66,8 @@ public class BitcoinAddressValidator {
|
|||||||
|
|
||||||
byte[] result = new byte[25];
|
byte[] result = new byte[25];
|
||||||
byte[] numBytes = num.toByteArray();
|
byte[] numBytes = num.toByteArray();
|
||||||
|
if (num.bitLength() > 200) return null;
|
||||||
|
|
||||||
if (num.bitLength() == 200) {
|
if (num.bitLength() == 200) {
|
||||||
System.arraycopy(numBytes, 1, result, 0, 25);
|
System.arraycopy(numBytes, 1, result, 0, 25);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,45 @@
|
|||||||
|
package com.m2049r.xmrwallet.util;
|
||||||
|
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
|
||||||
|
public class FingerprintHelper {
|
||||||
|
|
||||||
|
public static boolean isDeviceSupported(Context context) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FingerprintManager fingerprintManager = context.getSystemService(FingerprintManager.class);
|
||||||
|
KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
|
||||||
|
|
||||||
|
return (keyguardManager != null) && (fingerprintManager != 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,
|
||||||
|
FingerprintManager.AuthenticationCallback callback) {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FingerprintManager manager = context.getSystemService(FingerprintManager.class);
|
||||||
|
if (manager != null) {
|
||||||
|
manager.authenticate(null, cancelSignal, 0, callback, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
353
app/src/main/java/com/m2049r/xmrwallet/util/KeyStoreHelper.java
Normal file
353
app/src/main/java/com/m2049r/xmrwallet/util/KeyStoreHelper.java
Normal file
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
Reference in New Issue
Block a user