mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-03 08:23:04 +02:00
Compare commits
142 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5e6d3f3032 | ||
![]() |
65991ff554 | ||
![]() |
b0629e46e8 | ||
![]() |
45ec3198a0 | ||
![]() |
9b66c466f2 | ||
![]() |
d257e183ad | ||
![]() |
10d8e441fe | ||
![]() |
9f9bc4793d | ||
![]() |
8b28e3ea1e | ||
![]() |
e394394538 | ||
![]() |
7424ef07f7 | ||
![]() |
f5ce6ec824 | ||
![]() |
4215a8bf9e | ||
![]() |
8b016f93dc | ||
![]() |
b239a5094b | ||
![]() |
6a2de36578 | ||
![]() |
5aded68c53 | ||
![]() |
d78a2be120 | ||
![]() |
e6c7800911 | ||
![]() |
cbbe079f67 | ||
![]() |
7fc2dc3ba1 | ||
![]() |
43204d64ef | ||
![]() |
1433143a39 | ||
![]() |
a9a78393a9 | ||
![]() |
fe7ab31050 | ||
![]() |
bf5ed793b3 | ||
![]() |
679bae5f42 | ||
![]() |
e3ccda910e | ||
![]() |
ae75a34977 | ||
![]() |
03efedf35c | ||
![]() |
403dbdf14f | ||
![]() |
0bf3c6f099 | ||
![]() |
1b0ac1c481 | ||
![]() |
dc95539fc1 | ||
![]() |
023fb9e215 | ||
![]() |
92728026c7 | ||
![]() |
8fd9598c6c | ||
![]() |
6633261ba2 | ||
![]() |
dc8c8634cb | ||
![]() |
dcf9b6db15 | ||
![]() |
654d63c32e | ||
![]() |
caf91fccfd | ||
![]() |
41f1f3dec0 | ||
![]() |
bb66d1f68d | ||
![]() |
310548b031 | ||
![]() |
ad7737475f | ||
![]() |
b2d07a65b7 | ||
![]() |
5dfcaae5b9 | ||
![]() |
3c91fc060c | ||
![]() |
f8f113faab | ||
![]() |
268a00cb3e | ||
![]() |
878500ae71 | ||
![]() |
a0debb0f7e | ||
![]() |
3a71e8d352 | ||
![]() |
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 | ||
![]() |
60a7b15700 | ||
![]() |
df2ff8b3b7 | ||
![]() |
da8c8f80f1 | ||
![]() |
eda3de11fe |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,8 +1,7 @@
|
||||
.gradle
|
||||
/build
|
||||
*.iml
|
||||
/.idea/libraries
|
||||
/.idea/workspace.xml
|
||||
/.idea
|
||||
/local.properties
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
|
19
README.md
19
README.md
@@ -1,12 +1,23 @@
|
||||
# Monerujo
|
||||
Another Android Monero Wallet
|
||||
Another Android Monero Wallet for Monero
|
||||
**(not
|
||||
Monero Classic,
|
||||
Monero-Classic,
|
||||
Monero Zero,
|
||||
Monero Original,
|
||||
Monero C,
|
||||
Monero V)**
|
||||
|
||||
### QUICKSTART
|
||||
- Download the APK for the most current release [here](https://github.com/m2049r/xmrwallet/releases) and install it
|
||||
- Alternatively add our F-Droid repo https://f-droid.monerujo.io/fdroid/repo with fingerpint ```A8 2C 68 E1 4A F0 AA 6A 2E C2 0E 6B 27 2E FF 25 E5 A0 38 F3 F6 58 84 31 6E 0F 5E 0D 91 E7 B7 13``` to your F-Droid client
|
||||
- Run the App and select "Generate Wallet" to create a new wallet or recover a wallet
|
||||
- Advanced users can copy over synced wallet files (all files) onto sdcard in directory Monerujo (created first time App is started)
|
||||
- See the [FAQ](doc/FAQ.md)
|
||||
|
||||
## Translations
|
||||
Help us translate Monerujo! You can find instructions [On Taiga](https://taiga.getmonero.org/project/erciccione-monero-localization/wiki/monerujo), and if you need help/support, open an issue or contact the Localization Workgroup. You can find us on the freenode channel `#monero-translations`, also relayed on [MatterMost](https://mattermost.getmonero.org/monero/channels/monero-translations), and matrix/riot.
|
||||
|
||||
### Disclaimer
|
||||
You may lose all your Moneroj if you use this App. Be cautious when spending on the mainnet.
|
||||
|
||||
@@ -23,6 +34,8 @@ You may lose all your Moneroj if you use this App. Be cautious when spending on
|
||||
- see taiga.getmonero.org & issues on github
|
||||
|
||||
### Issues / Pitfalls
|
||||
- Users of Zenfone MAX & Zenfone 2 Laser (possibly others) **MUST** use the armeabi-v7a APK as the arm64-v8a build uses hardware AES
|
||||
functionality these models don't have.
|
||||
- You should backup your wallet files in the "monerujo" folder periodically.
|
||||
- Also note, that on some devices the backups will only be visible on a PC over USB after a reboot of the device (it's an Android bug/feature)
|
||||
- Created wallets on a private testnet are unusable because the restore height is set to that
|
||||
@@ -33,8 +46,8 @@ The official monero client shows the same behaviour.
|
||||
No need to build. Binaries are included:
|
||||
|
||||
- openssl-1.0.2l
|
||||
- monero-v0.11.1.0
|
||||
- boost_1_64_0
|
||||
- monero-v0.12
|
||||
- boost_1_58_0
|
||||
|
||||
If you want to build them yourself (recommended) check out [the instructions](doc/BUILDING-external-libs.md)
|
||||
|
||||
|
@@ -1,15 +1,14 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'witness'
|
||||
|
||||
android {
|
||||
compileSdkVersion 25
|
||||
buildToolsVersion '26.0.2'
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '28.0.2'
|
||||
defaultConfig {
|
||||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 83
|
||||
versionName "1.4.3 'Monero Spedner'"
|
||||
targetSdkVersion 27
|
||||
versionCode 123
|
||||
versionName "1.7.3 'OpenAlias'"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
@@ -17,9 +16,6 @@ android {
|
||||
arguments '-DANDROID_STL=c++_shared'
|
||||
}
|
||||
}
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -49,62 +45,50 @@ android {
|
||||
// Map for the version code that gives each ABI a value.
|
||||
def abiCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
|
||||
|
||||
// Enumerate translated locales
|
||||
def availableLocales = ["en"]
|
||||
new File("app/src/main/res/").eachFileMatch(~/^values-.*/) { file ->
|
||||
def languageTag = file.name.substring(7).replace("-r", "-")
|
||||
availableLocales.add(languageTag)
|
||||
}
|
||||
|
||||
// APKs for the same app that all have the same version information.
|
||||
android.applicationVariants.all { variant ->
|
||||
// Update string resource: available_locales
|
||||
variant.resValue("string", "available_locales", availableLocales.join(","))
|
||||
// Assigns a different version code for each output APK.
|
||||
variant.outputs.each {
|
||||
variant.outputs.all {
|
||||
output ->
|
||||
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
|
||||
output.versionCodeOverride = abiCodes.get(abiName, 0) + 10 * variant.versionCode
|
||||
|
||||
if (abiName == null) abiName = "universal"
|
||||
def v = "${variant.versionName}".replaceFirst(" .*\$", "").replace(".", "x")
|
||||
outputFileName = "$rootProject.ext.apkName-" + v + "_" + abiName + ".apk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:appcompat-v7:25.4.0'
|
||||
compile 'com.android.support:design:25.4.0'
|
||||
compile 'com.android.support:support-v4:25.4.0'
|
||||
compile 'com.android.support:recyclerview-v7:25.4.0'
|
||||
compile 'com.android.support:cardview-v7:25.4.0'
|
||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
compile 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||
implementation "com.android.support:appcompat-v7:$rootProject.ext.supportVersion"
|
||||
implementation "com.android.support:design:$rootProject.ext.supportVersion"
|
||||
implementation "com.android.support:support-v4:$rootProject.ext.supportVersion"
|
||||
implementation "com.android.support:recyclerview-v7:$rootProject.ext.supportVersion"
|
||||
implementation "com.android.support:cardview-v7:$rootProject.ext.supportVersion"
|
||||
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
|
||||
|
||||
compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
|
||||
compile "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
|
||||
implementation "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
|
||||
implementation "com.jakewharton.timber:timber:$rootProject.ext.timberVersion"
|
||||
|
||||
compile 'com.nulab-inc:zxcvbn:1.2.3'
|
||||
implementation 'com.nulab-inc:zxcvbn:1.2.3'
|
||||
|
||||
testCompile "junit:junit:$rootProject.ext.junitVersion"
|
||||
testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||
testCompile "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
||||
testCompile 'org.json:json:20140107'
|
||||
testCompile 'net.jodah:concurrentunit:0.4.2'
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'com.android.support:design:3f409bf2019967ffc344cfaf11e52131fac982468a1707aaeb25bf3c52838966',
|
||||
'com.android.support:appcompat-v7:70551e62660db15b790c5275f56b9de4dd9407d1494d07c8f3dd5698f3638677',
|
||||
'com.android.support:transition:848270144fb180efd2bf928a00ed176dbbc5290badfd638390ffba90088df8b3',
|
||||
'me.dm7.barcodescanner:zxing:d43973c9527c23fa8e6d338c6a2c458e373ce1ac6bcaa3bc41d11ae49116000d',
|
||||
'me.dm7.barcodescanner:core:a5c8a704089b58029db166172ed8e55d756877d010a85a0b1c94fdc96ffb8f9a',
|
||||
'com.android.support:support-v4:ee44c481a1f4d6978568e223e8125379b52b2ececdd53450e09ebae144bd377d',
|
||||
'com.android.support:recyclerview-v7:a2fe121f9d01ed8980e97095b4a3fe9700a0aa0a7d4b0f8c594f765ad8455a0d',
|
||||
'com.android.support:cardview-v7:f3fbbe1fcfdbec7333c6a2c516c5fd511a909d1975271818e268d6fe297d8c70',
|
||||
'com.android.support.constraint:constraint-layout:b0c688cc2b7172608f8153a689d746da40f71e52d7e2fe2bfd9df2f92db77085',
|
||||
'com.android.support:animated-vector-drawable:628ab1d56a6ee4cbedf32617af8b2a1fe02964ed0628e8f898cc09ddba6e1835',
|
||||
'com.android.support:support-vector-drawable:077009d13882ee96f061e4bc2dbe7cce7ae1762d8297592a787ff741afbfb1f2',
|
||||
'com.android.support:support-fragment:316d35d4d2d2902057efad104a73e4bdb50bee260a7075678185b8cd71170945',
|
||||
'com.android.support:support-core-ui:e72ae29b823889686cff6fcb948d6745c2baf6d4c2af4fdffa1ec1e42e3833a3',
|
||||
'com.android.support:support-media-compat:566a161d9cb0083ef62a53e46b71ce5b3d455b8635b1a0a4ae28d96d4b583de8',
|
||||
'com.android.support:support-core-utils:34b8437dfa95ff28d29cf57ffa3b1354a9fa9bfe4059f0fd5ce2f5e4326a1748',
|
||||
'com.android.support:support-compat:54019c63614ce08b02d7b9605490cd2b29ba5b2505f394a9517450b5f72b30ca',
|
||||
'com.android.support:support-annotations:a774272036941b4e912eb426d70c848bde7f06a3bf5fb491f75a427dc6595270',
|
||||
'com.android.support.constraint:constraint-layout-solver:8c62525a9bc5cff5633a96cb9b32fffeccaf41b8841aa87fc22607070dea9b8d',
|
||||
'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
|
||||
'com.squareup.okio:okio:734269c3ebc5090e3b23566db558f421f0b4027277c79ad5d176b8ec168bb850',
|
||||
'com.squareup.okhttp3:okhttp:7265adbd6f028aade307f58569d814835cd02bc9beffb70c25f72c9de50d61c4',
|
||||
'com.jakewharton.timber:timber:35c22867f2673132e97e17857d36bb2fc25f5790f0425406833ed0254d62fc66',
|
||||
'com.nulab-inc:zxcvbn:18d7862a6abd2705defec478d77dedadf8f3bb7cf811df22995494f05485785f',
|
||||
]
|
||||
implementation 'dnsjava:dnsjava:2.1.8'
|
||||
implementation 'org.jitsi:dnssecjava:1.1.3'
|
||||
implementation 'org.slf4j:slf4j-nop:1.7.25'
|
||||
|
||||
testImplementation "junit:junit:$rootProject.ext.junitVersion"
|
||||
testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
|
||||
testImplementation 'org.json:json:20140107'
|
||||
testImplementation 'net.jodah:concurrentunit:0.4.2'
|
||||
}
|
||||
|
@@ -7,13 +7,15 @@
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:name=".XmrWalletApplication"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:name=".XmrWalletApplication"
|
||||
android:theme="@style/MyMaterialTheme">
|
||||
|
||||
<activity
|
||||
@@ -27,19 +29,25 @@
|
||||
android:name=".LoginActivity"
|
||||
android:configChanges="orientation|keyboardHidden"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<intent-filter>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
||||
android:resource="@xml/usb_device_filter" />
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".service.WalletService"
|
||||
android:description="@string/service_description"
|
||||
android:exported="false"
|
||||
android:label="Monero Wallet Service" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
826
app/src/main/assets/licenses.html
Normal file
826
app/src/main/assets/licenses.html
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
#define XMRWALLET_WALLET_LIB_H
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
/*
|
||||
#include <android/log.h>
|
||||
|
||||
@@ -27,13 +28,13 @@
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
|
||||
*/
|
||||
|
||||
jfieldID getHandleField(JNIEnv *env, jobject obj, const char* fieldName = "handle") {
|
||||
jfieldID getHandleField(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||
jclass c = env->GetObjectClass(obj);
|
||||
return env->GetFieldID(c, fieldName, "J"); // of type long
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *getHandle(JNIEnv *env, jobject obj, const char* fieldName = "handle") {
|
||||
template<typename T>
|
||||
T *getHandle(JNIEnv *env, jobject obj, const char *fieldName = "handle") {
|
||||
jlong handle = env->GetLongField(obj, getHandleField(env, obj, fieldName));
|
||||
return reinterpret_cast<T *>(handle);
|
||||
}
|
||||
@@ -42,10 +43,35 @@ void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
|
||||
env->SetLongField(obj, getHandleField(env, obj), handle);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template<typename T>
|
||||
void setHandle(JNIEnv *env, jobject obj, T *t) {
|
||||
jlong handle = reinterpret_cast<jlong>(t);
|
||||
setHandleFromLong(env, obj, handle);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
// from monero-core crypto/hash-ops.h - avoid #including monero code here
|
||||
enum {
|
||||
HASH_SIZE = 32,
|
||||
HASH_DATA_AREA = 136
|
||||
};
|
||||
|
||||
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed);
|
||||
|
||||
inline void slow_hash(const void *data, const size_t length, char *hash) {
|
||||
cn_slow_hash(data, length, hash, 0 /* variant */, 0/*prehashed*/);
|
||||
}
|
||||
|
||||
inline void slow_hash_broken(const void *data, char *hash, int variant) {
|
||||
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //XMRWALLET_WALLET_LIB_H
|
||||
|
46
app/src/main/cpp/monerujo_ledger.h
Normal file
46
app/src/main/cpp/monerujo_ledger.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (c) 2018 m2049r
|
||||
* <p>
|
||||
* 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
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef XMRWALLET_LEDGER_H
|
||||
#define XMRWALLET_LEDGER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#define SCARD_S_SUCCESS ((LONG)0x00000000) /**< No error was encountered. */
|
||||
#define SCARD_E_INSUFFICIENT_BUFFER ((LONG)0x80100008) /**< The data buffer to receive returned data is too small for the returned data. */
|
||||
#define SCARD_E_NO_READERS_AVAILABLE ((LONG)0x8010002E) /**< Cannot find a smart card reader. */
|
||||
|
||||
typedef long LONG;
|
||||
typedef unsigned long DWORD;
|
||||
typedef DWORD *LPDWORD;
|
||||
typedef unsigned char BYTE;
|
||||
typedef BYTE *LPBYTE;
|
||||
typedef const BYTE *LPCBYTE;
|
||||
|
||||
typedef char CHAR;
|
||||
typedef CHAR *LPSTR;
|
||||
|
||||
int LedgerFind(char *buffer, size_t len);
|
||||
LONG LedgerExchange(LPCBYTE pbSendBuffer, DWORD cbSendLength, LPBYTE pbRecvBuffer, LPDWORD pcbRecvLength);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //XMRWALLET_LEDGER_H
|
53
app/src/main/java/com/btchip/BTChipException.java
Normal file
53
app/src/main/java/com/btchip/BTChipException.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* BTChip Bitcoin Hardware Wallet Java API
|
||||
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||
*
|
||||
* 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.btchip;
|
||||
|
||||
public class BTChipException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 5512803003827126405L;
|
||||
|
||||
public BTChipException(String reason) {
|
||||
super(reason);
|
||||
}
|
||||
|
||||
public BTChipException(String reason, Throwable cause) {
|
||||
super(reason, cause);
|
||||
}
|
||||
|
||||
public BTChipException(String reason, int sw) {
|
||||
super(reason);
|
||||
this.sw = sw;
|
||||
}
|
||||
|
||||
public int getSW() {
|
||||
return sw;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (sw == 0) {
|
||||
return "BTChip Exception : " + getMessage();
|
||||
} else {
|
||||
return "BTChip Exception : " + getMessage() + " " + Integer.toHexString(sw);
|
||||
}
|
||||
}
|
||||
|
||||
private int sw;
|
||||
|
||||
}
|
31
app/src/main/java/com/btchip/comm/BTChipTransport.java
Normal file
31
app/src/main/java/com/btchip/comm/BTChipTransport.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* BTChip Bitcoin Hardware Wallet Java API
|
||||
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||
* (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.btchip.comm;
|
||||
|
||||
import com.btchip.BTChipException;
|
||||
|
||||
public interface BTChipTransport {
|
||||
public byte[] exchange(byte[] command);
|
||||
|
||||
public void close();
|
||||
|
||||
public void setDebug(boolean debugFlag);
|
||||
}
|
126
app/src/main/java/com/btchip/comm/LedgerHelper.java
Normal file
126
app/src/main/java/com/btchip/comm/LedgerHelper.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* BTChip Bitcoin Hardware Wallet Java API
|
||||
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||
* (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.btchip.comm;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
public class LedgerHelper {
|
||||
|
||||
private static final int TAG_APDU = 0x05;
|
||||
|
||||
public static byte[] wrapCommandAPDU(int channel, byte[] command, int packetSize) {
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
if (packetSize < 3) {
|
||||
throw new IllegalArgumentException("Can't handle Ledger framing with less than 3 bytes for the report");
|
||||
}
|
||||
int sequenceIdx = 0;
|
||||
int offset = 0;
|
||||
output.write(channel >> 8);
|
||||
output.write(channel);
|
||||
output.write(TAG_APDU);
|
||||
output.write(sequenceIdx >> 8);
|
||||
output.write(sequenceIdx);
|
||||
sequenceIdx++;
|
||||
output.write(command.length >> 8);
|
||||
output.write(command.length);
|
||||
int blockSize = (command.length > packetSize - 7 ? packetSize - 7 : command.length);
|
||||
output.write(command, offset, blockSize);
|
||||
offset += blockSize;
|
||||
while (offset != command.length) {
|
||||
output.write(channel >> 8);
|
||||
output.write(channel);
|
||||
output.write(TAG_APDU);
|
||||
output.write(sequenceIdx >> 8);
|
||||
output.write(sequenceIdx);
|
||||
sequenceIdx++;
|
||||
blockSize = (command.length - offset > packetSize - 5 ? packetSize - 5 : command.length - offset);
|
||||
output.write(command, offset, blockSize);
|
||||
offset += blockSize;
|
||||
}
|
||||
if ((output.size() % packetSize) != 0) {
|
||||
byte[] padding = new byte[packetSize - (output.size() % packetSize)];
|
||||
output.write(padding, 0, padding.length);
|
||||
}
|
||||
return output.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] unwrapResponseAPDU(int channel, byte[] data, int packetSize) {
|
||||
ByteArrayOutputStream response = new ByteArrayOutputStream();
|
||||
int offset = 0;
|
||||
int responseLength;
|
||||
int sequenceIdx = 0;
|
||||
if ((data == null) || (data.length < 7 + 5)) {
|
||||
return null;
|
||||
}
|
||||
if (data[offset++] != (channel >> 8)) {
|
||||
throw new IllegalArgumentException("Invalid channel");
|
||||
}
|
||||
if (data[offset++] != (channel & 0xff)) {
|
||||
throw new IllegalArgumentException("Invalid channel");
|
||||
}
|
||||
if (data[offset++] != TAG_APDU) {
|
||||
throw new IllegalArgumentException("Invalid tag");
|
||||
}
|
||||
if (data[offset++] != 0x00) {
|
||||
throw new IllegalArgumentException("Invalid sequence");
|
||||
}
|
||||
if (data[offset++] != 0x00) {
|
||||
throw new IllegalArgumentException("Invalid sequence");
|
||||
}
|
||||
responseLength = ((data[offset++] & 0xff) << 8);
|
||||
responseLength |= (data[offset++] & 0xff);
|
||||
if (data.length < 7 + responseLength) {
|
||||
return null;
|
||||
}
|
||||
int blockSize = (responseLength > packetSize - 7 ? packetSize - 7 : responseLength);
|
||||
response.write(data, offset, blockSize);
|
||||
offset += blockSize;
|
||||
while (response.size() != responseLength) {
|
||||
sequenceIdx++;
|
||||
if (offset == data.length) {
|
||||
return null;
|
||||
}
|
||||
if (data[offset++] != (channel >> 8)) {
|
||||
throw new IllegalArgumentException("Invalid channel");
|
||||
}
|
||||
if (data[offset++] != (channel & 0xff)) {
|
||||
throw new IllegalArgumentException("Invalid channel");
|
||||
}
|
||||
if (data[offset++] != TAG_APDU) {
|
||||
throw new IllegalArgumentException("Invalid tag");
|
||||
}
|
||||
if (data[offset++] != (sequenceIdx >> 8)) {
|
||||
throw new IllegalArgumentException("Invalid sequence");
|
||||
}
|
||||
if (data[offset++] != (sequenceIdx & 0xff)) {
|
||||
throw new IllegalArgumentException("Invalid sequence");
|
||||
}
|
||||
blockSize = (responseLength - response.size() > packetSize - 5 ? packetSize - 5 : responseLength - response.size());
|
||||
if (blockSize > data.length - offset) {
|
||||
return null;
|
||||
}
|
||||
response.write(data, offset, blockSize);
|
||||
offset += blockSize;
|
||||
}
|
||||
return response.toByteArray();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* BTChip Bitcoin Hardware Wallet Java API
|
||||
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||
* (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.btchip.comm.android;
|
||||
|
||||
import android.hardware.usb.UsbConstants;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbEndpoint;
|
||||
import android.hardware.usb.UsbInterface;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.hardware.usb.UsbRequest;
|
||||
|
||||
import com.btchip.BTChipException;
|
||||
import com.btchip.comm.BTChipTransport;
|
||||
import com.btchip.comm.LedgerHelper;
|
||||
import com.btchip.utils.Dump;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BTChipTransportAndroidHID implements BTChipTransport {
|
||||
|
||||
public static UsbDevice getDevice(UsbManager manager) {
|
||||
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
|
||||
for (UsbDevice device : deviceList.values()) {
|
||||
Timber.d("%04X:%04X %s, %s", device.getVendorId(), device.getProductId(), device.getManufacturerName(), device.getProductName());
|
||||
if ((device.getVendorId() == VID) && (device.getProductId() == PID_HID)) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static BTChipTransport open(UsbManager manager, UsbDevice device) throws IOException {
|
||||
UsbDeviceConnection connection = manager.openDevice(device);
|
||||
if (connection == null) throw new IOException("Device not connected");
|
||||
// Must only be called once permission is granted (see http://developer.android.com/reference/android/hardware/usb/UsbManager.html)
|
||||
// Important if enumerating, rather than being awaken by the intent notification
|
||||
UsbInterface dongleInterface = device.getInterface(0);
|
||||
UsbEndpoint in = null;
|
||||
UsbEndpoint out = null;
|
||||
for (int i = 0; i < dongleInterface.getEndpointCount(); i++) {
|
||||
UsbEndpoint tmpEndpoint = dongleInterface.getEndpoint(i);
|
||||
if (tmpEndpoint.getDirection() == UsbConstants.USB_DIR_IN) {
|
||||
in = tmpEndpoint;
|
||||
} else {
|
||||
out = tmpEndpoint;
|
||||
}
|
||||
}
|
||||
connection.claimInterface(dongleInterface, true);
|
||||
return new BTChipTransportAndroidHID(connection, dongleInterface, in, out);
|
||||
}
|
||||
|
||||
private static final int VID = 0x2C97;
|
||||
private static final int PID_HID = 0x0001;
|
||||
|
||||
private UsbDeviceConnection connection;
|
||||
private UsbInterface dongleInterface;
|
||||
private UsbEndpoint in;
|
||||
private UsbEndpoint out;
|
||||
private byte transferBuffer[];
|
||||
private boolean debug;
|
||||
|
||||
public BTChipTransportAndroidHID(UsbDeviceConnection connection, UsbInterface dongleInterface, UsbEndpoint in, UsbEndpoint out) {
|
||||
this.connection = connection;
|
||||
this.dongleInterface = dongleInterface;
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
transferBuffer = new byte[HID_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] exchange(byte[] command) {
|
||||
ByteArrayOutputStream response = new ByteArrayOutputStream();
|
||||
byte[] responseData = null;
|
||||
int offset = 0;
|
||||
if (debug) {
|
||||
Timber.d("=> %s", Dump.dump(command));
|
||||
}
|
||||
command = LedgerHelper.wrapCommandAPDU(LEDGER_DEFAULT_CHANNEL, command, HID_BUFFER_SIZE);
|
||||
UsbRequest requestOut = new UsbRequest();
|
||||
requestOut.initialize(connection, out);
|
||||
while (offset != command.length) {
|
||||
int blockSize = (command.length - offset > HID_BUFFER_SIZE ? HID_BUFFER_SIZE : command.length - offset);
|
||||
System.arraycopy(command, offset, transferBuffer, 0, blockSize);
|
||||
requestOut.queue(ByteBuffer.wrap(transferBuffer), HID_BUFFER_SIZE);
|
||||
connection.requestWait();
|
||||
offset += blockSize;
|
||||
}
|
||||
requestOut.close();
|
||||
ByteBuffer responseBuffer = ByteBuffer.allocate(HID_BUFFER_SIZE);
|
||||
UsbRequest requestIn = new UsbRequest();
|
||||
requestIn.initialize(connection, in);
|
||||
while ((responseData = LedgerHelper.unwrapResponseAPDU(LEDGER_DEFAULT_CHANNEL, response.toByteArray(), HID_BUFFER_SIZE)) == null) {
|
||||
responseBuffer.clear();
|
||||
requestIn.queue(responseBuffer, HID_BUFFER_SIZE);
|
||||
connection.requestWait();
|
||||
responseBuffer.rewind();
|
||||
responseBuffer.get(transferBuffer, 0, HID_BUFFER_SIZE);
|
||||
response.write(transferBuffer, 0, HID_BUFFER_SIZE);
|
||||
}
|
||||
requestIn.close();
|
||||
if (debug) {
|
||||
Timber.d("<= %s", Dump.dump(responseData));
|
||||
}
|
||||
return responseData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
connection.releaseInterface(dongleInterface);
|
||||
connection.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debugFlag) {
|
||||
this.debug = debugFlag;
|
||||
}
|
||||
|
||||
private static final int HID_BUFFER_SIZE = 64;
|
||||
private static final int LEDGER_DEFAULT_CHANNEL = 1;
|
||||
private static final int SW1_DATA_AVAILABLE = 0x61;
|
||||
}
|
62
app/src/main/java/com/btchip/utils/Dump.java
Normal file
62
app/src/main/java/com/btchip/utils/Dump.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
* BTChip Bitcoin Hardware Wallet Java API
|
||||
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
|
||||
*
|
||||
* 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.btchip.utils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
public class Dump {
|
||||
|
||||
public static String dump(byte[] buffer, int offset, int length) {
|
||||
String result = "";
|
||||
for (int i = 0; i < length; i++) {
|
||||
String temp = Integer.toHexString((buffer[offset + i]) & 0xff);
|
||||
if (temp.length() < 2) {
|
||||
temp = "0" + temp;
|
||||
}
|
||||
result += temp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String dump(byte[] buffer) {
|
||||
return dump(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
public static byte[] hexToBin(String src) {
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
int i = 0;
|
||||
while (i < src.length()) {
|
||||
char x = src.charAt(i);
|
||||
if (!((x >= '0' && x <= '9') || (x >= 'A' && x <= 'F') || (x >= 'a' && x <= 'f'))) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
result.write(Integer.valueOf("" + src.charAt(i) + src.charAt(i + 1), 16));
|
||||
i += 2;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return result.toByteArray();
|
||||
}
|
||||
|
||||
|
||||
}
|
294
app/src/main/java/com/m2049r/xmrwallet/BaseActivity.java
Normal file
294
app/src/main/java/com/m2049r/xmrwallet/BaseActivity.java
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2017 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;
|
||||
|
||||
import com.m2049r.xmrwallet.data.BarcodeData;
|
||||
|
||||
public interface OnUriScannedListener {
|
||||
boolean onUriScanned(BarcodeData barcodeData);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -16,13 +16,12 @@
|
||||
|
||||
package com.m2049r.xmrwallet;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
|
||||
import java.io.File;
|
||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams;
|
||||
|
||||
@@ -36,4 +35,9 @@ public abstract class SecureActivity extends AppCompatActivity {
|
||||
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context context) {
|
||||
super.attachBaseContext(LocaleHelper.setLocale(context, LocaleHelper.getLocale(context)));
|
||||
}
|
||||
}
|
||||
|
@@ -59,6 +59,7 @@ public class TxFragment extends Fragment {
|
||||
TS_FORMATTER.setTimeZone(tz);
|
||||
}
|
||||
|
||||
private TextView tvAccount;
|
||||
private TextView tvTxTimestamp;
|
||||
private TextView tvTxId;
|
||||
private TextView tvTxKey;
|
||||
@@ -88,6 +89,7 @@ public class TxFragment extends Fragment {
|
||||
tvDestinationBtc = (TextView) view.findViewById(R.id.tvDestinationBtc);
|
||||
tvTxAmountBtc = (TextView) view.findViewById(R.id.tvTxAmountBtc);
|
||||
|
||||
tvAccount = (TextView) view.findViewById(R.id.tvAccount);
|
||||
tvTxTimestamp = (TextView) view.findViewById(R.id.tvTxTimestamp);
|
||||
tvTxId = (TextView) view.findViewById(R.id.tvTxId);
|
||||
tvTxKey = (TextView) view.findViewById(R.id.tvTxKey);
|
||||
@@ -222,6 +224,8 @@ public class TxFragment extends Fragment {
|
||||
activityCallback.setSubtitle(getString(R.string.tx_title));
|
||||
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)));
|
||||
tvTxId.setText(info.hash);
|
||||
tvTxKey.setText(info.txKey.isEmpty() ? "-" : info.txKey);
|
||||
@@ -236,9 +240,6 @@ public class TxFragment extends Fragment {
|
||||
String sign = (info.direction == TransactionInfo.Direction.Direction_In ? "+" : "-");
|
||||
|
||||
long realAmount = info.amount;
|
||||
if (info.isPending) {
|
||||
realAmount = realAmount - info.fee;
|
||||
}
|
||||
tvTxAmount.setText(sign + Wallet.getDisplayAmount(realAmount));
|
||||
|
||||
if ((info.fee > 0)) {
|
||||
@@ -286,7 +287,10 @@ public class TxFragment extends Fragment {
|
||||
}
|
||||
} else {
|
||||
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());
|
||||
tvDestination.setText(dstSb.toString());
|
||||
@@ -321,7 +325,7 @@ public class TxFragment extends Fragment {
|
||||
Listener activityCallback;
|
||||
|
||||
public interface Listener {
|
||||
String getWalletAddress();
|
||||
String getWalletSubaddress(int accountIndex, int subaddressIndex);
|
||||
|
||||
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.ExchangeCallback;
|
||||
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.OkHttpClientSingleton;
|
||||
import com.m2049r.xmrwallet.widget.Toolbar;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
@@ -101,7 +99,9 @@ public class WalletFragment extends Fragment
|
||||
ivSynced = (ImageView) view.findViewById(R.id.ivSynced);
|
||||
|
||||
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);
|
||||
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
|
||||
String displayB;
|
||||
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;
|
||||
displayB = Helper.getFormattedAmount(amountB, false);
|
||||
} else { // XMR
|
||||
@@ -159,10 +159,10 @@ public class WalletFragment extends Fragment
|
||||
tvBalance.setText(displayB);
|
||||
}
|
||||
|
||||
String balanceCurrency = "XMR";
|
||||
String balanceCurrency = Helper.CRYPTO;
|
||||
double balanceRate = 1.0;
|
||||
|
||||
private final ExchangeApi exchangeApi = new ExchangeApiImpl(OkHttpClientSingleton.getOkHttpClient());
|
||||
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
|
||||
|
||||
void refreshBalance() {
|
||||
if (sCurrency.getSelectedItemPosition() == 0) { // XMR
|
||||
@@ -170,9 +170,10 @@ public class WalletFragment extends Fragment
|
||||
tvBalance.setText(Helper.getFormattedAmount(amountXmr, true));
|
||||
} else { // not XMR
|
||||
String currency = (String) sCurrency.getSelectedItem();
|
||||
Timber.d(currency);
|
||||
if (!currency.equals(balanceCurrency) || (balanceRate <= 0)) {
|
||||
showExchanging();
|
||||
exchangeApi.queryExchangeRate("XMR", currency,
|
||||
exchangeApi.queryExchangeRate(Helper.CRYPTO, currency,
|
||||
new ExchangeCallback() {
|
||||
@Override
|
||||
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||
@@ -228,10 +229,10 @@ public class WalletFragment extends Fragment
|
||||
|
||||
public void exchange(final ExchangeRate exchangeRate) {
|
||||
hideExchanging();
|
||||
if (!"XMR".equals(exchangeRate.getBaseCurrency())) {
|
||||
if (!Helper.CRYPTO.equals(exchangeRate.getBaseCurrency())) {
|
||||
Timber.e("Not XMR");
|
||||
sCurrency.setSelection(0, true);
|
||||
balanceCurrency = "XMR";
|
||||
balanceCurrency = Helper.CRYPTO;
|
||||
balanceRate = 1.0;
|
||||
} else {
|
||||
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
|
||||
@@ -256,7 +257,7 @@ public class WalletFragment extends Fragment
|
||||
// called from activity
|
||||
|
||||
public void onRefreshed(final Wallet wallet, final boolean full) {
|
||||
Timber.d("onRefreshed()");
|
||||
Timber.d("onRefreshed(%b)", full);
|
||||
if (full) {
|
||||
List<TransactionInfo> list = wallet.getHistory().getAll();
|
||||
adapter.setInfos(list);
|
||||
@@ -270,6 +271,7 @@ public class WalletFragment extends Fragment
|
||||
bSend.setVisibility(View.VISIBLE);
|
||||
bSend.setEnabled(true);
|
||||
}
|
||||
if (isVisible()) enableAccountsList(true); //otherwise it is enabled in onResume()
|
||||
}
|
||||
|
||||
boolean walletLoaded = false;
|
||||
@@ -313,7 +315,7 @@ public class WalletFragment extends Fragment
|
||||
if (wallet == null) return;
|
||||
walletTitle = wallet.getName();
|
||||
String watchOnly = (wallet.isWatchOnly() ? getString(R.string.label_watchonly) : "");
|
||||
walletSubtitle = wallet.getAddress().substring(0, 10) + "…" + watchOnly;
|
||||
walletSubtitle = wallet.getAccountLabel();
|
||||
activityCallback.setTitle(walletTitle, walletSubtitle);
|
||||
Timber.d("wallet title is %s", walletTitle);
|
||||
}
|
||||
@@ -323,10 +325,13 @@ public class WalletFragment extends Fragment
|
||||
private String walletSubtitle = null;
|
||||
private long unlockedBalance = 0;
|
||||
|
||||
private int accountIdx = -1;
|
||||
|
||||
private void updateStatus(Wallet wallet) {
|
||||
if (!isAdded()) return;
|
||||
Timber.d("updateStatus()");
|
||||
if (walletTitle == null) {
|
||||
if ((walletTitle == null) || (accountIdx != wallet.getAccountIndex())) {
|
||||
accountIdx = wallet.getAccountIndex();
|
||||
setActivityTitle(wallet);
|
||||
}
|
||||
long balance = wallet.getBalance();
|
||||
@@ -352,7 +357,7 @@ public class WalletFragment extends Fragment
|
||||
setProgress(x);
|
||||
ivSynced.setVisibility(View.GONE);
|
||||
} else {
|
||||
sync = getString(R.string.status_synced) + formatter.format(wallet.getBlockChainHeight());
|
||||
sync = getString(R.string.status_synced) + " " + formatter.format(wallet.getBlockChainHeight());
|
||||
ivSynced.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
@@ -412,9 +417,28 @@ public class WalletFragment extends Fragment
|
||||
super.onResume();
|
||||
Timber.d("onResume()");
|
||||
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(syncText);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -18,8 +18,10 @@ package com.m2049r.xmrwallet;
|
||||
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
|
||||
import com.m2049r.xmrwallet.util.Helper;
|
||||
import com.m2049r.xmrwallet.util.LocaleHelper;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
@@ -27,9 +29,21 @@ public class XmrWalletApplication extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Timber.plant(new Timber.DebugTree());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context context) {
|
||||
super.attachBaseContext(LocaleHelper.setLocale(context, LocaleHelper.getLocale(context)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration configuration) {
|
||||
super.onConfigurationChanged(configuration);
|
||||
LocaleHelper.updateSystemDefaultLocale(configuration.locale);
|
||||
LocaleHelper.setLocale(XmrWalletApplication.this, LocaleHelper.getLocale(XmrWalletApplication.this));
|
||||
}
|
||||
}
|
||||
|
@@ -18,10 +18,9 @@ package com.m2049r.xmrwallet.data;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.m2049r.xmrwallet.model.NetworkType;
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
import com.m2049r.xmrwallet.model.WalletManager;
|
||||
import com.m2049r.xmrwallet.util.BitcoinAddressValidator;
|
||||
import com.m2049r.xmrwallet.util.OpenAliasHelper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -29,9 +28,14 @@ import java.util.Map;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BarcodeData {
|
||||
|
||||
public static final String XMR_SCHEME = "monero:";
|
||||
public static final String XMR_PAYMENTID = "tx_payment_id";
|
||||
public static final String XMR_AMOUNT = "tx_amount";
|
||||
public static final String XMR_DESCRIPTION = "tx_description";
|
||||
|
||||
public static final String OA_XMR_ASSET = "xmr";
|
||||
public static final String OA_BTC_ASSET = "btc";
|
||||
|
||||
static final String BTC_SCHEME = "bitcoin:";
|
||||
static final String BTC_AMOUNT = "amount";
|
||||
@@ -40,10 +44,24 @@ public class BarcodeData {
|
||||
XMR, BTC
|
||||
}
|
||||
|
||||
public enum Security {
|
||||
NORMAL,
|
||||
OA_NO_DNSSEC,
|
||||
OA_DNSSEC
|
||||
}
|
||||
|
||||
public Asset asset = null;
|
||||
public String addressName = null;
|
||||
public String address = null;
|
||||
public String paymentId = null;
|
||||
public String amount = null;
|
||||
public String description = null;
|
||||
public Security security = Security.NORMAL;
|
||||
|
||||
public BarcodeData(String uri) {
|
||||
this.asset = asset;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public BarcodeData(Asset asset, String address) {
|
||||
this.asset = asset;
|
||||
@@ -63,6 +81,48 @@ public class BarcodeData {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public BarcodeData(Asset asset, String address, String paymentId, String description, String amount) {
|
||||
this.asset = asset;
|
||||
this.address = address;
|
||||
this.paymentId = paymentId;
|
||||
this.description = description;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public void setAddressName(String name) {
|
||||
addressName = name;
|
||||
}
|
||||
|
||||
public void setSecurity(Security security) {
|
||||
this.security = security;
|
||||
}
|
||||
|
||||
public Uri getUri() {
|
||||
return Uri.parse(getUriString());
|
||||
}
|
||||
|
||||
public String getUriString() {
|
||||
if (asset != Asset.XMR) throw new IllegalStateException("We can only do XMR stuff!");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(BarcodeData.XMR_SCHEME).append(address);
|
||||
boolean first = true;
|
||||
if ((paymentId != null) && !paymentId.isEmpty()) {
|
||||
sb.append("?");
|
||||
first = false;
|
||||
sb.append(BarcodeData.XMR_PAYMENTID).append('=').append(paymentId);
|
||||
}
|
||||
if ((description != null) && !description.isEmpty()) {
|
||||
sb.append(first ? "?" : "&");
|
||||
first = false;
|
||||
sb.append(BarcodeData.XMR_DESCRIPTION).append('=').append(Uri.encode(description));
|
||||
}
|
||||
if ((amount != null) && !amount.isEmpty()) {
|
||||
sb.append(first ? "?" : "&");
|
||||
sb.append(BarcodeData.XMR_AMOUNT).append('=').append(amount);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
static public BarcodeData fromQrCode(String qrCode) {
|
||||
// check for monero uri
|
||||
BarcodeData bcData = parseMoneroUri(qrCode);
|
||||
@@ -74,10 +134,14 @@ public class BarcodeData {
|
||||
if (bcData == null) {
|
||||
bcData = parseBitcoinUri(qrCode);
|
||||
}
|
||||
// check for naked btc addres
|
||||
// check for naked btc address
|
||||
if (bcData == null) {
|
||||
bcData = parseBitcoinNaked(qrCode);
|
||||
}
|
||||
// check for OpenAlias
|
||||
if (bcData == null) {
|
||||
bcData = parseOpenAlias(qrCode);
|
||||
}
|
||||
return bcData;
|
||||
}
|
||||
|
||||
@@ -112,6 +176,7 @@ public class BarcodeData {
|
||||
}
|
||||
String address = monero.getPath();
|
||||
String paymentId = parms.get(XMR_PAYMENTID);
|
||||
String description = parms.get(XMR_DESCRIPTION);
|
||||
String amount = parms.get(XMR_AMOUNT);
|
||||
if (amount != null) {
|
||||
try {
|
||||
@@ -130,7 +195,7 @@ public class BarcodeData {
|
||||
Timber.d("address invalid");
|
||||
return null;
|
||||
}
|
||||
return new BarcodeData(Asset.XMR, address, paymentId, amount);
|
||||
return new BarcodeData(Asset.XMR, address, paymentId, description, amount);
|
||||
}
|
||||
|
||||
static public BarcodeData parseMoneroNaked(String address) {
|
||||
@@ -198,4 +263,61 @@ public class BarcodeData {
|
||||
|
||||
return new BarcodeData(BarcodeData.Asset.BTC, address);
|
||||
}
|
||||
|
||||
static public BarcodeData parseOpenAlias(String oaString) {
|
||||
Timber.d("parseOpenAlias=%s", oaString);
|
||||
if (oaString == null) return null;
|
||||
|
||||
Map<String, String> oaAttrs = OpenAliasHelper.parse(oaString);
|
||||
if (oaAttrs == null) return null;
|
||||
|
||||
String oaAsset = oaAttrs.get(OpenAliasHelper.OA1_ASSET);
|
||||
if (oaAsset == null) return null;
|
||||
|
||||
String address = oaAttrs.get(OpenAliasHelper.OA1_ADDRESS);
|
||||
if (address == null) return null;
|
||||
|
||||
Asset asset;
|
||||
if (OA_XMR_ASSET.equals(oaAsset)) {
|
||||
if (!Wallet.isAddressValid(address)) {
|
||||
Timber.d("XMR address invalid");
|
||||
return null;
|
||||
}
|
||||
asset = Asset.XMR;
|
||||
} else if (OA_BTC_ASSET.equals(oaAsset)) {
|
||||
if (!BitcoinAddressValidator.validate(address)) {
|
||||
Timber.d("BTC address invalid");
|
||||
return null;
|
||||
}
|
||||
asset = Asset.BTC;
|
||||
} else {
|
||||
Timber.i("Unsupported OpenAlias asset %s", oaAsset);
|
||||
return null;
|
||||
}
|
||||
|
||||
String paymentId = oaAttrs.get(OpenAliasHelper.OA1_PAYMENTID);
|
||||
String description = oaAttrs.get(OpenAliasHelper.OA1_DESCRIPTION);
|
||||
if (description == null) {
|
||||
description = oaAttrs.get(OpenAliasHelper.OA1_NAME);
|
||||
}
|
||||
String amount = oaAttrs.get(OpenAliasHelper.OA1_AMOUNT);
|
||||
String addressName = oaAttrs.get(OpenAliasHelper.OA1_NAME);
|
||||
|
||||
if (amount != null) {
|
||||
try {
|
||||
Double.parseDouble(amount);
|
||||
} catch (NumberFormatException ex) {
|
||||
Timber.d(ex.getLocalizedMessage());
|
||||
return null; // we have an amount but its not a number!
|
||||
}
|
||||
}
|
||||
if ((paymentId != null) && !Wallet.isPaymentIdValid(paymentId)) {
|
||||
Timber.d("paymentId invalid");
|
||||
return null;
|
||||
}
|
||||
|
||||
BarcodeData bc = new BarcodeData(asset, address, paymentId, description, amount);
|
||||
bc.setAddressName(addressName);
|
||||
return bc;
|
||||
}
|
||||
}
|
@@ -86,6 +86,10 @@ public class WalletNode {
|
||||
return name;
|
||||
}
|
||||
|
||||
public NetworkType getNetworkType() {
|
||||
return networkType;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return host + ":" + port;
|
||||
}
|
||||
|
@@ -32,6 +32,14 @@ import android.widget.TextView;
|
||||
import com.m2049r.xmrwallet.BuildConfig;
|
||||
import com.m2049r.xmrwallet.R;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class AboutFragment extends DialogFragment {
|
||||
static final String TAG = "AboutFragment";
|
||||
|
||||
@@ -52,7 +60,7 @@ public class AboutFragment extends DialogFragment {
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_about, null);
|
||||
((TextView) view.findViewById(R.id.tvHelp)).setText(Html.fromHtml(getString(R.string.about_licenses)));
|
||||
((TextView) view.findViewById(R.id.tvHelp)).setText(Html.fromHtml(getLicencesHtml()));
|
||||
((TextView) view.findViewById(R.id.tvVersion)).setText(getString(R.string.about_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
@@ -66,4 +74,18 @@ public class AboutFragment extends DialogFragment {
|
||||
});
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
private String getLicencesHtml() {
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(getContext().getAssets().open("licenses.html"), StandardCharsets.UTF_8))) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null)
|
||||
sb.append(line);
|
||||
return sb.toString();
|
||||
} catch (IOException ex) {
|
||||
Timber.e(ex);
|
||||
return ex.getLocalizedMessage();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,130 @@
|
||||
package com.m2049r.xmrwallet.dialog;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ProgressDialog extends AlertDialog {
|
||||
|
||||
private ProgressBar pbBar;
|
||||
|
||||
private TextView tvMessage;
|
||||
|
||||
private TextView tvProgress;
|
||||
|
||||
private View rlProgressBar, pbCircle;
|
||||
|
||||
static private final String PROGRESS_FORMAT = "%1d/%2d";
|
||||
|
||||
private CharSequence message;
|
||||
private int maxValue, progressValue;
|
||||
private boolean indeterminate = true;
|
||||
|
||||
public ProgressDialog(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
final View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_ledger_progress, null);
|
||||
pbCircle = view.findViewById(R.id.pbCircle);
|
||||
tvMessage = (TextView) view.findViewById(R.id.tvMessage);
|
||||
rlProgressBar = view.findViewById(R.id.rlProgressBar);
|
||||
pbBar = (ProgressBar) view.findViewById(R.id.pbBar);
|
||||
tvProgress = (TextView) view.findViewById(R.id.tvProgress);
|
||||
setView(view);
|
||||
//setTitle("blabla");
|
||||
//super.setMessage("bubbu");
|
||||
// view.invalidate();
|
||||
setIndeterminate(indeterminate);
|
||||
if (maxValue > 0) {
|
||||
setMax(maxValue);
|
||||
}
|
||||
if (progressValue > 0) {
|
||||
setProgress(progressValue);
|
||||
}
|
||||
if (message != null) {
|
||||
Timber.d("msg=%s", message);
|
||||
setMessage(message);
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
public void setProgress(int value, int max) {
|
||||
progressValue = value;
|
||||
maxValue = max;
|
||||
if (pbBar != null) {
|
||||
pbBar.setProgress(value);
|
||||
pbBar.setMax(max);
|
||||
tvProgress.setText(String.format(Locale.getDefault(), PROGRESS_FORMAT, value, maxValue));
|
||||
}
|
||||
}
|
||||
|
||||
public void setProgress(int value) {
|
||||
progressValue = value;
|
||||
if (pbBar != null) {
|
||||
pbBar.setProgress(value);
|
||||
tvProgress.setText(String.format(Locale.getDefault(), PROGRESS_FORMAT, value, maxValue));
|
||||
}
|
||||
}
|
||||
|
||||
public void setMax(int max) {
|
||||
maxValue = max;
|
||||
if (pbBar != null) {
|
||||
pbBar.setMax(max);
|
||||
}
|
||||
}
|
||||
|
||||
public void setIndeterminate(boolean indeterminate) {
|
||||
if (this.indeterminate != indeterminate) {
|
||||
if (rlProgressBar != null) {
|
||||
if (indeterminate) {
|
||||
pbCircle.setVisibility(View.VISIBLE);
|
||||
rlProgressBar.setVisibility(View.GONE);
|
||||
} else {
|
||||
pbCircle.setVisibility(View.GONE);
|
||||
rlProgressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
this.indeterminate = indeterminate;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessage(CharSequence message) {
|
||||
this.message = message;
|
||||
if (tvMessage != null) {
|
||||
tvMessage.setText(message);
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,9 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.m2049r.xmrwallet.R;
|
||||
@@ -57,8 +60,9 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
||||
|
||||
private TextView tvFunds;
|
||||
private ExchangeTextView evAmount;
|
||||
//private Button bSendAll;
|
||||
private NumberPadView numberPad;
|
||||
private View llAmount;
|
||||
private View rlSweep;
|
||||
private ImageButton ibSweep;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
@@ -73,37 +77,64 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
||||
tvFunds = (TextView) view.findViewById(R.id.tvFunds);
|
||||
|
||||
evAmount = (ExchangeTextView) view.findViewById(R.id.evAmount);
|
||||
numberPad = (NumberPadView) view.findViewById(R.id.numberPad);
|
||||
numberPad.setListener(evAmount);
|
||||
((NumberPadView) view.findViewById(R.id.numberPad)).setListener(evAmount);
|
||||
|
||||
/*
|
||||
bSendAll = (Button) view.findViewById(R.id.bSendAll);
|
||||
bSendAll.setOnClickListener(new View.OnClickListener() {
|
||||
rlSweep = view.findViewById(R.id.rlSweep);
|
||||
llAmount = view.findViewById(R.id.llAmount);
|
||||
view.findViewById(R.id.ivSweep).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
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());
|
||||
|
||||
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
|
||||
public boolean onValidateFields() {
|
||||
if (!evAmount.validate(maxFunds)) {
|
||||
return false;
|
||||
}
|
||||
if (spendAllMode) {
|
||||
if (sendListener != null) {
|
||||
sendListener.getTxData().setAmount(Wallet.SWEEP_ALL);
|
||||
}
|
||||
} else {
|
||||
if (!evAmount.validate(maxFunds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sendListener != null) {
|
||||
String xmr = evAmount.getAmount();
|
||||
if (xmr != null) {
|
||||
sendListener.getTxData().setAmount(Wallet.getAmountFromString(xmr));
|
||||
} else {
|
||||
sendListener.getTxData().setAmount(0L);
|
||||
if (sendListener != null) {
|
||||
String xmr = evAmount.getAmount();
|
||||
if (xmr != null) {
|
||||
sendListener.getTxData().setAmount(Wallet.getAmountFromString(xmr));
|
||||
} else {
|
||||
sendListener.getTxData().setAmount(0L);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@@ -247,7 +247,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
||||
|
||||
private XmrToApi xmrToApi = null;
|
||||
|
||||
private final XmrToApi getXmrToApi() {
|
||||
private XmrToApi getXmrToApi() {
|
||||
if (xmrToApi == null) {
|
||||
synchronized (this) {
|
||||
if (xmrToApi == null) {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user