1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-12 07:10:49 +02:00

Compare commits

...

43 Commits

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

* Display a security warning

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

* and add CrAzYpass notice

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

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

* set networktype from node

* add monero logging for DEBUG builds

* do not reset timestamps in apk

* no witness

* new version & apk naming
2018-04-15 16:33:20 +02:00
m2049r
b37adb4546 Update README.md 2018-04-11 18:20:07 +02:00
erciccione
a4e99209be change suggested ringsize from 5 to 7 in help.xml (#223) 2018-04-07 16:44:18 +02:00
m2049r
45aa8f30e5 fix openssl links 2018-04-05 19:09:30 +02:00
m2049r
60a7b15700 new version for fixed malloc (83dcd65) 2018-04-03 11:51:14 +02:00
m2049r
df2ff8b3b7 correct toolbar colour for testnet (#218) 2018-04-02 13:02:00 +02:00
m2049r
da8c8f80f1 new version for m2049r/monero 7e97e11 (#217) 2018-04-02 12:14:55 +02:00
m2049r
eda3de11fe added zxcvbn4j license (#216) 2018-04-01 13:23:03 +02:00
82 changed files with 4670 additions and 738 deletions

3
.gitignore vendored
View File

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

View File

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

View File

@@ -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 83 versionCode 91
versionName "1.4.3 'Monero Spedner'" versionName "1.5.1 'CrAzY 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',
]
} }

View File

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

View File

@@ -695,6 +695,23 @@ Java_com_m2049r_xmrwallet_model_Wallet_isSynchronized(JNIEnv *env, jobject insta
return static_cast<jboolean>(wallet->synchronized()); return static_cast<jboolean>(wallet->synchronized());
} }
//void cn_slow_hash(const void *data, size_t length, char *hash); // from crypto/hash-ops.h
JNIEXPORT jbyteArray JNICALL
Java_com_m2049r_xmrwallet_util_KeyStoreHelper_cnSlowHash(JNIEnv *env, jobject clazz,
jbyteArray data) {
jbyte *buffer = env->GetByteArrayElements(data, NULL);
jsize size = env->GetArrayLength(data);
char hash[HASH_SIZE];
cn_slow_hash(buffer, (size_t) size, hash);
env->ReleaseByteArrayElements(data, buffer, JNI_ABORT); // do not update java byte[]
jbyteArray result = env->NewByteArray(HASH_SIZE);
env->SetByteArrayRegion(result, 0, HASH_SIZE, (jbyte *) hash);
return result;
}
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_com_m2049r_xmrwallet_model_Wallet_getDisplayAmount(JNIEnv *env, jobject clazz, Java_com_m2049r_xmrwallet_model_Wallet_getDisplayAmount(JNIEnv *env, jobject clazz,
jlong amount) { jlong amount) {
@@ -1075,6 +1092,85 @@ Java_com_m2049r_xmrwallet_model_PendingTransaction_getTxCount(JNIEnv *env, jobje
} }
// these are all in Bitmonero::Wallet - which I find wrong, so they are here!
//static void init(const char *argv0, const char *default_log_base_name);
//static void debug(const std::string &category, const std::string &str);
//static void info(const std::string &category, const std::string &str);
//static void warning(const std::string &category, const std::string &str);
//static void error(const std::string &category, const std::string &str);
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_initLogger(JNIEnv *env, jobject instance,
jstring argv0,
jstring default_log_base_name) {
const char *_argv0 = env->GetStringUTFChars(argv0, NULL);
const char *_default_log_base_name = env->GetStringUTFChars(default_log_base_name, NULL);
Bitmonero::Wallet::init(_argv0, _default_log_base_name);
env->ReleaseStringUTFChars(argv0, _argv0);
env->ReleaseStringUTFChars(default_log_base_name, _default_log_base_name);
}
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_logDebug(JNIEnv *env, jobject instance,
jstring category, jstring message) {
const char *_category = env->GetStringUTFChars(category, NULL);
const char *_message = env->GetStringUTFChars(message, NULL);
Bitmonero::Wallet::debug(_category, _message);
env->ReleaseStringUTFChars(category, _category);
env->ReleaseStringUTFChars(message, _message);
}
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_logInfo(JNIEnv *env, jobject instance,
jstring category, jstring message) {
const char *_category = env->GetStringUTFChars(category, NULL);
const char *_message = env->GetStringUTFChars(message, NULL);
Bitmonero::Wallet::info(_category, _message);
env->ReleaseStringUTFChars(category, _category);
env->ReleaseStringUTFChars(message, _message);
}
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_logWarning(JNIEnv *env, jobject instance,
jstring category, jstring message) {
const char *_category = env->GetStringUTFChars(category, NULL);
const char *_message = env->GetStringUTFChars(message, NULL);
Bitmonero::Wallet::warning(_category, _message);
env->ReleaseStringUTFChars(category, _category);
env->ReleaseStringUTFChars(message, _message);
}
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_logError(JNIEnv *env, jobject instance,
jstring category, jstring message) {
const char *_category = env->GetStringUTFChars(category, NULL);
const char *_message = env->GetStringUTFChars(message, NULL);
Bitmonero::Wallet::error(_category, _message);
env->ReleaseStringUTFChars(category, _category);
env->ReleaseStringUTFChars(message, _message);
}
JNIEXPORT void JNICALL
Java_com_m2049r_xmrwallet_model_WalletManager_setLogLevel(JNIEnv *env, jobject instance,
jint level) {
Bitmonero::WalletManagerFactory::setLogLevel(level);
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -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,27 @@ 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);
#ifdef __cplusplus
}
#endif
#endif //XMRWALLET_WALLET_LIB_H #endif //XMRWALLET_WALLET_LIB_H

View File

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

View File

@@ -18,12 +18,15 @@ package com.m2049r.xmrwallet;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.ColorStateList;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.Gravity;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@@ -38,7 +41,9 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.EditText; import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
@@ -48,13 +53,16 @@ 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 +79,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 +178,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 +227,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
}); });
loadPrefs(); loadPrefs();
if (!showXmrtoEnabled) {
llXmrToEnabled.setVisibility(View.GONE);
}
return view; return view;
} }
@@ -326,6 +313,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) {
@@ -379,7 +377,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private static final String PREF_DAEMON_TESTNET = "daemon_testnet"; private static final String PREF_DAEMON_TESTNET = "daemon_testnet";
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";
@@ -390,23 +387,12 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
private NodeList daemonTestNet; private NodeList daemonTestNet;
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)); daemonTestNet = new NodeList(sharedPref.getString(PREF_DAEMON_TESTNET, PREF_DAEMONLIST_TESTNET));
setNet(testnetCheckMenu, false); setNet(testnetCheckMenu, 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() {
@@ -428,7 +414,6 @@ public class LoginFragment extends Fragment implements WalletInfoAdapter.OnInter
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_TESTNET, daemonTestNet.toString());
editor.putBoolean(PREF_SHOW_XMRTO_ENABLED, showXmrtoEnabled);
editor.apply(); editor.apply();
} }

View File

@@ -335,6 +335,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);

View File

@@ -49,8 +49,6 @@ import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.UserNotes; import com.m2049r.xmrwallet.util.UserNotes;
import com.m2049r.xmrwallet.widget.Toolbar; import com.m2049r.xmrwallet.widget.Toolbar;
import java.io.File;
import timber.log.Timber; import timber.log.Timber;
public class WalletActivity extends SecureActivity implements WalletFragment.Listener, public class WalletActivity extends SecureActivity implements WalletFragment.Listener,
@@ -62,8 +60,10 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
public static final String REQUEST_ID = "id"; public static final String REQUEST_ID = "id";
public static final String REQUEST_PW = "pw"; public static final String REQUEST_PW = "pw";
public static final String REQUEST_FINGERPRINT_USED = "fingerprint";
private Toolbar toolbar; private Toolbar toolbar;
private boolean needVerifyIdentity;
@Override @Override
public void setToolbarButton(int type) { public void setToolbarButton(int type) {
@@ -120,6 +120,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
acquireWakeLock(); acquireWakeLock();
String walletId = extras.getString(REQUEST_ID); String walletId = extras.getString(REQUEST_ID);
String walletPassword = extras.getString(REQUEST_PW); String walletPassword = extras.getString(REQUEST_PW);
needVerifyIdentity = extras.getBoolean(REQUEST_FINGERPRINT_USED);
connectWalletService(walletId, walletPassword); connectWalletService(walletId, walletPassword);
} else { } else {
finish(); finish();
@@ -174,6 +175,9 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
case R.id.action_details_help: case R.id.action_details_help:
HelpFragment.display(getSupportFragmentManager(), R.string.help_details); HelpFragment.display(getSupportFragmentManager(), R.string.help_details);
return true; return true;
case R.id.action_details_changepw:
onWalletChangePassword();
return true;
case R.id.action_help_send: case R.id.action_help_send:
HelpFragment.display(getSupportFragmentManager(), R.string.help_send); HelpFragment.display(getSupportFragmentManager(), R.string.help_send);
return true; return true;
@@ -182,6 +186,20 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
} }
} }
public void onWalletChangePassword() {//final String walletName, final String walletPassword) {
try {
GenerateReviewFragment detailsFragment = (GenerateReviewFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
AlertDialog dialog = detailsFragment.createChangePasswordDialog();
if (dialog != null) {
Helper.showKeyboard(dialog);
dialog.show();
}
} catch (ClassCastException ex) {
Timber.w("onWalletChangePassword() called, but no GenerateReviewFragment active");
}
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
Timber.d("onCreate()"); Timber.d("onCreate()");
@@ -222,6 +240,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
} }
}); });
showNet();
Fragment walletFragment = new WalletFragment(); Fragment walletFragment = new WalletFragment();
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, walletFragment, WalletFragment.class.getName()).commit(); .add(R.id.fragment_container, walletFragment, WalletFragment.class.getName()).commit();
@@ -378,7 +398,17 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
@Override @Override
public void onSendRequest() { public void onSendRequest() {
replaceFragment(new SendFragment(), null, null); if (needVerifyIdentity) {
Helper.promptPassword(WalletActivity.this, getWallet().getName(), true, new Helper.PasswordAction() {
@Override
public void action(String walletName, String password, boolean fingerprintUsed) {
replaceFragment(new SendFragment(), null, null);
needVerifyIdentity = false;
}
});
} else {
replaceFragment(new SendFragment(), null, null);
}
} }
@Override @Override
@@ -678,9 +708,22 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
switch (which) { switch (which) {
case DialogInterface.BUTTON_POSITIVE: case DialogInterface.BUTTON_POSITIVE:
Bundle extras = new Bundle(); final Bundle extras = new Bundle();
extras.putString("type", GenerateReviewFragment.VIEW_TYPE_WALLET); extras.putString("type", GenerateReviewFragment.VIEW_TYPE_WALLET);
replaceFragment(new GenerateReviewFragment(), null, extras); extras.putString("password", getIntent().getExtras().getString(REQUEST_PW));
if (needVerifyIdentity) {
Helper.promptPassword(WalletActivity.this, getWallet().getName(), true, new Helper.PasswordAction() {
@Override
public void action(String walletName, String password, boolean fingerprintUsed) {
replaceFragment(new GenerateReviewFragment(), null, extras);
needVerifyIdentity = false;
}
});
} else {
replaceFragment(new GenerateReviewFragment(), null, extras);
}
break; break;
case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEGATIVE:
// do nothing // do nothing
@@ -808,9 +851,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
@Override @Override
public boolean verifyWalletPassword(String password) { public boolean verifyWalletPassword(String password) {
String walletPath = new File(Helper.getWalletRoot(this), String walletPassword = Helper.getWalletPassword(getApplicationContext(), getWalletName(), password);
getWalletName() + ".keys").getAbsolutePath(); return walletPassword != null;
return WalletManager.getInstance().verifyWalletPassword(walletPath, password, true);
} }
@Override @Override

View File

@@ -352,7 +352,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 {

View File

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

View File

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

View File

@@ -275,5 +275,4 @@ public class Wallet {
//virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &tvAmount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0; //virtual bool 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;
} }

View File

@@ -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;
} }
@@ -215,7 +216,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);
@@ -269,5 +270,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);
} }

View File

@@ -0,0 +1,54 @@
package com.m2049r.xmrwallet.util;
import com.m2049r.xmrwallet.model.WalletManager;
import java.math.BigInteger;
public class CrazyPassEncoder {
static final String BASE = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
static final int PW_CHARS = 52;
// this takes a 32 byte buffer and converts it to 52 alphnumeric characters
// separated by blanks every 4 characters = 13 groups of 4
// always (padding by Xs if need be
static public String encode(byte[] data) {
if (data.length != 32) throw new IllegalArgumentException("data[] is not 32 bytes long");
BigInteger rest = new BigInteger(1, data);
BigInteger remainder;
final StringBuilder result = new StringBuilder();
final BigInteger base = BigInteger.valueOf(BASE.length());
int i = 0;
do {
if ((i > 0) && (i % 4 == 0)) result.append(' ');
i++;
remainder = rest.remainder(base);
rest = rest.divide(base);
result.append(BASE.charAt(remainder.intValue()));
} while (!BigInteger.ZERO.equals(rest));
// pad it
while (i < PW_CHARS) {
if ((i > 0) && (i % 4 == 0)) result.append(' ');
result.append('2');
i++;
}
return result.toString();
}
static public String reformat(String password) {
// maybe this is a CrAzYpass without blanks? or lowercase letters
String noBlanks = password.toUpperCase().replaceAll(" ", "");
if (noBlanks.length() == PW_CHARS) { // looks like a CrAzYpass
// insert blanks every 4 characters
StringBuilder sb = new StringBuilder();
for (int i = 0; i < PW_CHARS; i++) {
if ((i > 0) && (i % 4 == 0)) sb.append(' ');
char c = noBlanks.charAt(i);
if (BASE.indexOf(c) < 0) return null; // invalid character found
sb.append(c);
}
return sb.toString();
} else {
return null; // not a CrAzYpass
}
}
}

View File

@@ -0,0 +1,40 @@
package com.m2049r.xmrwallet.util;
import android.app.KeyguardManager;
import android.content.Context;
import android.support.v4.hardware.fingerprint.FingerprintManagerCompat;
import android.support.v4.os.CancellationSignal;
import java.security.KeyStore;
import java.security.KeyStoreException;
public class FingerprintHelper {
public static boolean isDeviceSupported(Context context) {
FingerprintManagerCompat fingerprintManager = FingerprintManagerCompat.from(context);
KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
return keyguardManager != null &&
keyguardManager.isKeyguardSecure() &&
fingerprintManager.isHardwareDetected() &&
fingerprintManager.hasEnrolledFingerprints();
}
public static boolean isFingerprintAuthAllowed(String wallet) throws KeyStoreException {
KeyStore keyStore = KeyStore.getInstance(KeyStoreHelper.SecurityConstants.KEYSTORE_PROVIDER_ANDROID_KEYSTORE);
try {
keyStore.load(null);
} catch (Exception ex) {
throw new IllegalStateException("Could not load KeyStore", ex);
}
return keyStore.containsAlias(KeyStoreHelper.SecurityConstants.WALLET_PASS_KEY_PREFIX + wallet);
}
public static void authenticate(Context context, CancellationSignal cancelSignal,
FingerprintManagerCompat.AuthenticationCallback callback) {
FingerprintManagerCompat manager = FingerprintManagerCompat.from(context);
manager.authenticate(null, 0, cancelSignal, callback, null);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,135 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.dialog.HelpFragment;
import java.util.ArrayList;
import java.util.List;
public class Notice {
private static final String PREFS_NAME = "notice";
private static List<Notice> notices = null;
private static final String NOTICE_SHOW_XMRTO_ENABLED_LOGIN = "notice_xmrto_enabled_login";
private static final String NOTICE_SHOW_XMRTO_ENABLED_SEND = "notice_xmrto_enabled_send";
private static final String NOTICE_SHOW_CRAZYPASS = "notice_crazypass_enabled_login";
private static void init() {
synchronized (Notice.class) {
if (notices != null) return;
notices = new ArrayList<>();
notices.add(
new Notice(NOTICE_SHOW_XMRTO_ENABLED_SEND,
R.string.info_xmrto_enabled,
R.string.help_xmrto,
1)
);
notices.add(
new Notice(NOTICE_SHOW_XMRTO_ENABLED_LOGIN,
R.string.info_xmrto_enabled,
R.string.help_xmrto,
1)
);
notices.add(
new Notice(NOTICE_SHOW_CRAZYPASS,
R.string.info_crazypass_enabled,
R.string.help_details,
2)
);
}
}
public static void showAll(ViewGroup parent, String selector) {
if (notices == null) init();
for (Notice notice : notices) {
if (notice.id.matches(selector))
notice.show(parent);
}
}
private final String id;
private final int textResId;
private final int helpResId;
private final int defaultCount;
private transient int count = -1;
private Notice(final String id, final int textResId, final int helpResId, final int defaultCount) {
this.id = id;
this.textResId = textResId;
this.helpResId = helpResId;
this.defaultCount = defaultCount;
}
// show this notice as a child of the given parent view
// NB: it assumes the parent is in a Fragment
private void show(final ViewGroup parent) {
final Context context = parent.getContext();
if (getCount(context) <= 0) return; // don't add it
final LinearLayout ll =
(LinearLayout) LayoutInflater.from(context)
.inflate(R.layout.template_notice, parent, false);
((TextView) ll.findViewById(R.id.tvNotice)).setText(textResId);
final FragmentManager fragmentManager =
((FragmentActivity) context).getSupportFragmentManager();
ll.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
HelpFragment.display(fragmentManager, helpResId);
}
});
ImageButton ib = (ImageButton) ll.findViewById(R.id.ibClose);
ib.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ll.setVisibility(View.GONE);
decCount(context);
}
});
parent.addView(ll);
}
private int getCount(final Context context) {
count = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
.getInt(id, defaultCount);
return count;
}
private void decCount(final Context context) {
final SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
if (count < 0) // not initialized yet
count = prefs.getInt(id, defaultCount);
if (count > 0)
prefs.edit().putInt(id, count - 1).apply();
}
}

View File

@@ -87,6 +87,8 @@ public class RestoreHeight {
blockheight.put("2017-12-01", 1454639L); blockheight.put("2017-12-01", 1454639L);
blockheight.put("2018-01-01", 1477201L); blockheight.put("2018-01-01", 1477201L);
blockheight.put("2018-02-01", 1499599L); blockheight.put("2018-02-01", 1499599L);
blockheight.put("2018-03-01", 1519796L);
blockheight.put("2018-04-01", 1542067L);
} }
public long getHeight(String date) { public long getHeight(String date) {

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white" />
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp" />
</shape>

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<padding
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp" />
<corners android:radius="3dp" />
<stroke
android:width="2dp"
android:color="@color/gradientOrange"
android:dashGap="16dp"
android:dashWidth="16dp" />
</shape>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="@color/gradientOrange"
android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z" />
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFffffff"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z" />
</vector>

File diff suppressed because one or more lines are too long

View File

@@ -1,9 +0,0 @@
<vector android:height="24dp" android:viewportHeight="25.0"
android:viewportWidth="25.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF6105" android:fillType="evenOdd"
android:pathData="M3,0L22,0A3,3 0,0 1,25 3L25,22A3,3 0,0 1,22 25L3,25A3,3 0,0 1,0 22L0,3A3,3 0,0 1,3 0z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
<path android:fillColor="#FFFFFF" android:fillType="evenOdd"
android:pathData="M13.556,13.214L13.556,5L11.968,5L11.968,13.214L9.111,10L8,11.25L12.762,16.607L17.524,11.25L16.413,10L13.556,13.214ZM17.524,17.5L8,17.5L8,19.286L17.524,19.286L17.524,17.5L17.524,17.5Z"
android:strokeColor="#00000000" android:strokeWidth="1"/>
</vector>

View File

@@ -1,14 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportHeight="75.0"
android:viewportWidth="75.0">
<path
android:fillColor="#ff6600"
android:pathData="M 37.3,0.35329395 c -20.377,0 -36.903,16.524 -36.903,36.902 0,4.074 0.66,7.992 1.88,11.657 l 11.036,0 0,-31.049 23.987,23.987 23.987,-23.987 0,31.049 11.037,0 c 1.22,-3.665 1.88,-7.583 1.88,-11.657 0,-20.378 -16.526,-36.902 -36.904,-36.902" />
<path
android:fillColor="#4c4c4c"
android:pathData="M 21.3164,36.895994 l 0,19.537 -15.55,0 c 6.478,10.628 18.178,17.726 31.533,17.726 13.355,0 25.056,-7.098 31.533,-17.726 l -15.549,0 0,-19.537 -15.984,15.984 z" />
</vector>

View File

@@ -1,21 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="100dp"
android:height="100dp"
android:viewportHeight="75.0"
android:viewportWidth="75.0">
<path
android:fillColor="#ffffff"
android:pathData=" M 37.3, 37.3
m -36.9, 0
a 36.9,36.9 0 1,0 73.8,0
a 36.9,36.9 0 1,0 -73.8,0" />
<path
android:fillColor="#ff6600"
android:pathData="M 37.3,0.35329395 c -20.377,0 -36.903,16.524 -36.903,36.902 0,4.074 0.66,7.992 1.88,11.657 l 11.036,0 0,-31.049 23.987,23.987 23.987,-23.987 0,31.049 11.037,0 c 1.22,-3.665 1.88,-7.583 1.88,-11.657 0,-20.378 -16.526,-36.902 -36.904,-36.902" />
<path
android:fillColor="#4c4c4c"
android:pathData="M 21.3164,36.895994 l 0,19.537 -15.55,0 c 6.478,10.628 18.178,17.726 31.533,17.726 13.355,0 25.056,-7.098 31.533,-17.726 l -15.549,0 0,-19.537 -15.984,15.984 z" />
</vector>

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