1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-06 02:27:11 +02:00

Compare commits

..

71 Commits

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

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

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

* cancel fingerprint if password entered

* rework fingerprint code

* cleanup unused params

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

* api fix

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

* Display a security warning

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

* and add CrAzYpass notice

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

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

* set networktype from node

* add monero logging for DEBUG builds

* do not reset timestamps in apk

* no witness

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

* removed testcode

* fix issue #206
2018-03-06 18:15:23 +01:00
m2049r
c0fdfe87be build for x86 & arm in both 32 & 64 bits (#204) 2018-02-27 07:22:11 +01:00
m2049r
6cfd840283 fixed some bugs & reverted to boost 1.58 2018-02-26 09:46:20 +01:00
Miguel Botón
d80cde1136 Added build instructions for 64-bit (aarch64). (#172) 2018-02-26 08:42:22 +01:00
m2049r
bea4b06675 new version 2018-02-23 19:47:20 +01:00
m2049r
88bc33b5a4 added zxcvbn password score (#199)
* added zxcvbn password score

* changes messages & thresholds

change password label to passphrase
2018-02-23 19:32:21 +01:00
m2049r
2db36bb824 Enable Restore with Date instead of Height Only (#198)
* restore height class

* exact heights

* tweaks with DST

* some more spanish
2018-02-23 19:31:55 +01:00
m2049r
dd689b1883 fix for 2N9fzq66uZYQXp7uqrPBH6jKBhjrgTzpGCy (#190) 2018-01-24 19:59:13 +01:00
m2049r
641abd13f5 split apk (#191) 2018-01-23 21:35:26 +01:00
Stephan Hagios
a0b3a7fe5d used background borderless for nicer ripple effect (#189) 2018-01-22 21:36:13 +01:00
Stephan Hagios
a0486f581f Added circleci yml (#188)
* Adjusted layout files with correct sizes.

* Added password visibility toggle

* PR suggestions

* circle ci yml added

* different container with ndk
2018-01-21 18:53:45 +01:00
152 changed files with 6795 additions and 1361 deletions

27
.circleci/config.yml Normal file
View File

@@ -0,0 +1,27 @@
version: 2
jobs:
build:
working_directory: ~/code
docker:
- image: bitriseio/android-ndk
environment:
JVM_OPTS: -Xmx3200m
steps:
- checkout
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Download Dependencies
command: ./gradlew androidDependencies
- save_cache:
paths:
- ~/.gradle
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}
- run:
name: Run Tests
command: ./gradlew test
- store_artifacts:
path: app/build/reports
destination: reports
- store_test_results:
path: app/build/test-results

3
.gitignore vendored
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

@@ -60,9 +60,13 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a) ${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a)
############# #############
# Monero set(libs_to_merge wallet cryptonote_core cryptonote_basic mnemonics common cncrypto ringct) # Monero
############# #############
add_library(wallet_api STATIC IMPORTED)
set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet_api.a)
add_library(wallet STATIC IMPORTED) add_library(wallet STATIC IMPORTED)
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
@@ -91,11 +95,9 @@ add_library(ringct STATIC IMPORTED)
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
##### add_library(ringct_basic STATIC IMPORTED)
set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION
add_library(p2p STATIC IMPORTED) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct_basic.a)
set_target_properties(p2p PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libp2p.a)
add_library(blockchain_db STATIC IMPORTED) add_library(blockchain_db STATIC IMPORTED)
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
@@ -113,7 +115,6 @@ add_library(unbound STATIC IMPORTED)
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
####
add_library(epee STATIC IMPORTED) add_library(epee STATIC IMPORTED)
set_target_properties(epee PROPERTIES IMPORTED_LOCATION set_target_properties(epee PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a)
@@ -122,9 +123,21 @@ add_library(blocks STATIC IMPORTED)
set_target_properties(blocks PROPERTIES IMPORTED_LOCATION set_target_properties(blocks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a)
add_library(miniupnpc STATIC IMPORTED) add_library(checkpoints STATIC IMPORTED)
set_target_properties(miniupnpc PROPERTIES IMPORTED_LOCATION set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libminiupnpc.a) ${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcheckpoints.a)
add_library(device STATIC IMPORTED)
set_target_properties(device PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
add_library(multisig STATIC IMPORTED)
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmultisig.a)
add_library(version STATIC IMPORTED)
set_target_properties(version PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
############# #############
# System # System
@@ -137,23 +150,26 @@ include_directories( ${EXTERNAL_LIBS_DIR}/monero/include )
message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR}) message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR})
target_link_libraries( monerujo target_link_libraries( monerujo
wallet_api
wallet wallet
cryptonote_core cryptonote_core
cryptonote_basic cryptonote_basic
mnemonics mnemonics
ringct ringct
ringct_basic
common common
cncrypto cncrypto
blockchain_db blockchain_db
lmdb lmdb
easylogging easylogging
unbound unbound
p2p
epee epee
blocks blocks
miniupnpc checkpoints
device
multisig
version
boost_chrono boost_chrono
boost_date_time boost_date_time

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 71 versionCode 94
versionName "1.3.11 'Satoshis Dream'" versionName "1.5.4 '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'
}
} }
buildTypes { buildTypes {
@@ -36,49 +32,50 @@ android {
path "CMakeLists.txt" path "CMakeLists.txt"
} }
} }
splits {
abi {
enable true
reset()
include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
universalApk true
}
}
// Map for the version code that gives each ABI a value.
def abiCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
// APKs for the same app that all have the same version information.
android.applicationVariants.all { variant ->
// Assigns a different version code for each output APK.
variant.outputs.all {
output ->
def abiName = output.getFilter(com.android.build.OutputFile.ABI)
output.versionCodeOverride = abiCodes.get(abiName, 0) + 10 * variant.versionCode
if (abiName == null) abiName = "universal"
def v = "${variant.versionName}".replaceFirst(" .*\$", "").replace(".", "x")
outputFileName = "$rootProject.ext.apkName-" + v + "_" + abiName + ".apk"
}
}
} }
dependencies { 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"
testCompile "junit:junit:$rootProject.ext.junitVersion" implementation 'com.nulab-inc:zxcvbn:1.2.3'
testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
testCompile "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion" testImplementation "junit:junit:$rootProject.ext.junitVersion"
testCompile 'org.json:json:20140107' testImplementation "org.mockito:mockito-all:$rootProject.ext.mockitoVersion"
testCompile 'net.jodah:concurrentunit:0.4.2' testImplementation "com.squareup.okhttp3:mockwebserver:$rootProject.ext.okHttpVersion"
} testImplementation 'org.json:json:20140107'
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',
]
} }

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"

File diff suppressed because it is too large Load Diff

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,35 @@ void setHandleFromLong(JNIEnv *env, jobject obj, jlong handle) {
env->SetLongField(obj, getHandleField(env, obj), handle); env->SetLongField(obj, getHandleField(env, obj), handle);
} }
template <typename T> template<typename T>
void setHandle(JNIEnv *env, jobject obj, T *t) { void setHandle(JNIEnv *env, jobject obj, T *t) {
jlong handle = reinterpret_cast<jlong>(t); jlong handle = reinterpret_cast<jlong>(t);
setHandleFromLong(env, obj, handle); setHandleFromLong(env, obj, handle);
} }
#ifdef __cplusplus
extern "C"
{
#endif
// from monero-core crypto/hash-ops.h - avoid #including monero code here
enum {
HASH_SIZE = 32,
HASH_DATA_AREA = 136
};
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed);
inline void slow_hash(const void *data, const size_t length, char *hash) {
cn_slow_hash(data, length, hash, 0 /* variant */, 0/*prehashed*/);
}
inline void slow_hash_broken(const void *data, char *hash, int variant) {
cn_slow_hash(data, 200 /*sizeof(union hash_state)*/, hash, variant, 1 /*prehashed*/);
}
#ifdef __cplusplus
}
#endif
#endif //XMRWALLET_WALLET_LIB_H #endif //XMRWALLET_WALLET_LIB_H

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

View File

@@ -300,7 +300,7 @@ public class ReceiveFragment extends Fragment {
String paymentId = etPaymentId.getEditText().getText().toString(); String paymentId = etPaymentId.getEditText().getText().toString();
String xmrAmount = evAmount.getAmount(); String xmrAmount = evAmount.getAmount();
Timber.d("%s/%s/%s", xmrAmount, paymentId, address); Timber.d("%s/%s/%s", xmrAmount, paymentId, address);
if ((xmrAmount == null) || !Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet())) { if ((xmrAmount == null) || !Wallet.isAddressValid(address)) {
clearQR(); clearQR();
Timber.d("CLEARQR"); Timber.d("CLEARQR");
return; return;
@@ -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

@@ -36,7 +36,7 @@ import android.widget.Toast;
import com.m2049r.xmrwallet.data.BarcodeData; import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.TxData; import com.m2049r.xmrwallet.data.TxData;
import com.m2049r.xmrwallet.dialog.DonationFragment; import com.m2049r.xmrwallet.dialog.CreditsFragment;
import com.m2049r.xmrwallet.dialog.HelpFragment; import com.m2049r.xmrwallet.dialog.HelpFragment;
import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment; import com.m2049r.xmrwallet.fragment.send.SendAddressWizardFragment;
import com.m2049r.xmrwallet.fragment.send.SendFragment; import com.m2049r.xmrwallet.fragment.send.SendFragment;
@@ -49,21 +49,34 @@ import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.UserNotes; import com.m2049r.xmrwallet.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,
WalletService.Observer, SendFragment.Listener, TxFragment.Listener, WalletService.Observer, SendFragment.Listener, TxFragment.Listener,
GenerateReviewFragment.ListenerWithWallet, GenerateReviewFragment.ListenerWithWallet,
GenerateReviewFragment.Listener, GenerateReviewFragment.Listener,
GenerateReviewFragment.PasswordChangedListener,
ScannerFragment.OnScannedListener, ReceiveFragment.Listener, ScannerFragment.OnScannedListener, ReceiveFragment.Listener,
SendAddressWizardFragment.OnScanListener { SendAddressWizardFragment.OnScanListener {
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;
private String password;
@Override
public void onPasswordChanged(String newPassword) {
password = newPassword;
}
@Override
public String getPassword() {
return password;
}
@Override @Override
public void setToolbarButton(int type) { public void setToolbarButton(int type) {
@@ -119,8 +132,9 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
if (extras != null) { if (extras != null) {
acquireWakeLock(); acquireWakeLock();
String walletId = extras.getString(REQUEST_ID); String walletId = extras.getString(REQUEST_ID);
String walletPassword = extras.getString(REQUEST_PW); needVerifyIdentity = extras.getBoolean(REQUEST_FINGERPRINT_USED);
connectWalletService(walletId, walletPassword); password = extras.getString(REQUEST_PW);
connectWalletService(walletId, password);
} else { } else {
finish(); finish();
//throw new IllegalStateException("No extras passed! Panic!"); //throw new IllegalStateException("No extras passed! Panic!");
@@ -159,8 +173,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
case R.id.action_info: case R.id.action_info:
onWalletDetails(); onWalletDetails();
return true; return true;
case R.id.action_donate: case R.id.action_credits:
DonationFragment.display(getSupportFragmentManager()); CreditsFragment.display(getSupportFragmentManager());
return true; return true;
case R.id.action_share: case R.id.action_share:
onShareTxInfo(); onShareTxInfo();
@@ -174,6 +188,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 +199,20 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
} }
} }
public void onWalletChangePassword() {
try {
GenerateReviewFragment detailsFragment = (GenerateReviewFragment)
getSupportFragmentManager().findFragmentById(R.id.fragment_container);
AlertDialog dialog = detailsFragment.createChangePasswordDialog();
if (dialog != null) {
Helper.showKeyboard(dialog);
dialog.show();
}
} catch (ClassCastException ex) {
Timber.w("onWalletChangePassword() called, but no GenerateReviewFragment active");
}
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
Timber.d("onCreate()"); Timber.d("onCreate()");
@@ -213,8 +244,8 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
case Toolbar.BUTTON_CLOSE: case Toolbar.BUTTON_CLOSE:
finish(); finish();
break; break;
case Toolbar.BUTTON_DONATE: case Toolbar.BUTTON_CREDITS:
Toast.makeText(WalletActivity.this, getString(R.string.label_donate), Toast.LENGTH_SHORT).show(); Toast.makeText(WalletActivity.this, getString(R.string.label_credits), Toast.LENGTH_SHORT).show();
case Toolbar.BUTTON_NONE: case Toolbar.BUTTON_NONE:
default: default:
Timber.e("Button " + type + "pressed - how can this be?"); Timber.e("Button " + type + "pressed - how can this be?");
@@ -222,12 +253,7 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
} }
}); });
boolean testnet = WalletManager.getInstance().isTestNet(); showNet();
if (testnet) {
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
} else {
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
}
Fragment walletFragment = new WalletFragment(); Fragment walletFragment = new WalletFragment();
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
@@ -238,6 +264,23 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
Timber.d("onCreate() done."); Timber.d("onCreate() done.");
} }
public void showNet() {
switch (WalletManager.getInstance().getNetworkType()) {
case NetworkType_Mainnet:
toolbar.setBackgroundResource(R.drawable.backgound_toolbar_mainnet);
break;
case NetworkType_Testnet:
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
break;
case NetworkType_Stagenet:
toolbar.setBackgroundResource(R.color.colorPrimaryDark);
break;
default:
throw new IllegalStateException("Unsupported Network: " + WalletManager.getInstance().getNetworkType());
}
}
public Wallet getWallet() { public Wallet getWallet() {
if (mBoundService == null) throw new IllegalStateException("WalletService not bound."); if (mBoundService == null) throw new IllegalStateException("WalletService not bound.");
return mBoundService.getWallet(); return mBoundService.getWallet();
@@ -368,7 +411,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
@@ -668,9 +721,21 @@ 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(GenerateReviewFragment.REQUEST_TYPE, GenerateReviewFragment.VIEW_TYPE_WALLET);
replaceFragment(new GenerateReviewFragment(), null, extras);
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
@@ -798,9 +863,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.getStorageRoot(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

@@ -19,13 +19,15 @@ package com.m2049r.xmrwallet;
import android.app.Application; import android.app.Application;
import com.m2049r.xmrwallet.util.Helper;
import timber.log.Timber; import timber.log.Timber;
public class XmrWalletApplication extends Application { public class XmrWalletApplication extends Application {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree()); Timber.plant(new Timber.DebugTree());
} }

View File

@@ -18,6 +18,7 @@ package com.m2049r.xmrwallet.data;
import android.net.Uri; import android.net.Uri;
import com.m2049r.xmrwallet.model.NetworkType;
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.BitcoinAddressValidator; import com.m2049r.xmrwallet.util.BitcoinAddressValidator;
@@ -178,7 +179,7 @@ public class BarcodeData {
return null; // we have an amount but its not a number! return null; // we have an amount but its not a number!
} }
} }
if (!BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet())) { if (!BitcoinAddressValidator.validate(address)) {
Timber.d("address invalid"); Timber.d("address invalid");
return null; return null;
} }
@@ -190,7 +191,7 @@ public class BarcodeData {
if (address == null) return null; if (address == null) return null;
if (!BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet())) { if (!BitcoinAddressValidator.validate(address)) {
Timber.d("address invalid"); Timber.d("address invalid");
return null; return null;
} }

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.data;
import com.m2049r.xmrwallet.model.NetworkType;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
public class WalletNode {
private final String name;
private final String host;
private final int port;
private final String user;
private final String password;
private final NetworkType networkType;
public WalletNode(String walletName, String daemon, NetworkType networkType) {
if ((daemon == null) || daemon.isEmpty())
throw new IllegalArgumentException("daemon is empty");
this.name = walletName;
String daemonAddress;
String a[] = daemon.split("@");
if (a.length == 1) { // no credentials
daemonAddress = a[0];
user = "";
password = "";
} else if (a.length == 2) { // credentials
String userPassword[] = a[0].split(":");
if (userPassword.length != 2)
throw new IllegalArgumentException("User:Password invalid");
user = userPassword[0];
if (!user.isEmpty()) {
password = userPassword[1];
} else {
password = "";
}
daemonAddress = a[1];
} else {
throw new IllegalArgumentException("Too many @");
}
String da[] = daemonAddress.split(":");
if ((da.length > 2) || (da.length < 1))
throw new IllegalArgumentException("Too many ':' or too few");
host = da[0];
if (da.length == 2) {
try {
port = Integer.parseInt(da[1]);
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("Port not numeric");
}
} else {
switch (networkType) {
case NetworkType_Mainnet:
port = 18081;
break;
case NetworkType_Testnet:
port = 28081;
break;
case NetworkType_Stagenet:
port = 38081;
break;
default:
port = 0;
}
}
this.networkType = networkType;
}
public String getName() {
return name;
}
public NetworkType getNetworkType() {
return networkType;
}
public String getAddress() {
return host + ":" + port;
}
public String getUsername() {
return user;
}
public String getPassword() {
return password;
}
public SocketAddress getSocketAddress() {
return new InetSocketAddress(host, port);
}
public boolean isValid() {
return !host.isEmpty();
}
}

View File

@@ -28,16 +28,14 @@ import android.text.Html;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.R; import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.util.Helper;
public class DonationFragment extends DialogFragment { public class CreditsFragment extends DialogFragment {
static final String TAG = "DonationFragment"; static final String TAG = "DonationFragment";
public static DonationFragment newInstance() { public static CreditsFragment newInstance() {
return new DonationFragment(); return new CreditsFragment();
} }
public static void display(FragmentManager fm) { public static void display(FragmentManager fm) {
@@ -47,24 +45,14 @@ public class DonationFragment extends DialogFragment {
ft.remove(prev); ft.remove(prev);
} }
DonationFragment.newInstance().show(ft, TAG); CreditsFragment.newInstance().show(ft, TAG);
} }
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) { public Dialog onCreateDialog(Bundle savedInstanceState) {
final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_donation, null); final View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_credits, null);
((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.donation_credits))); ((TextView) view.findViewById(R.id.tvCredits)).setText(Html.fromHtml(getString(R.string.credits_text)));
(view.findViewById(R.id.bCopyAddress)).
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_copy_address),
((TextView) view.findViewById(R.id.tvWalletAddress)).getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show();
}
});
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view); builder.setView(view);

View File

@@ -212,7 +212,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private boolean checkAddressNoError() { private boolean checkAddressNoError() {
String address = etAddress.getEditText().getText().toString(); String address = etAddress.getEditText().getText().toString();
return Wallet.isAddressValid(address) return Wallet.isAddressValid(address)
|| BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet()); || BitcoinAddressValidator.validate(address);
} }
private boolean checkAddress() { private boolean checkAddress() {
@@ -228,13 +228,13 @@ public class SendAddressWizardFragment extends SendWizardFragment {
private boolean isIntegratedAddress() { private boolean isIntegratedAddress() {
String address = etAddress.getEditText().getText().toString(); String address = etAddress.getEditText().getText().toString();
return (address.length() == INTEGRATED_ADDRESS_LENGTH) return (address.length() == INTEGRATED_ADDRESS_LENGTH)
&& Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet()); && Wallet.isAddressValid(address);
} }
private boolean isBitcoinAddress() { private boolean isBitcoinAddress() {
String address = etAddress.getEditText().getText().toString(); String address = etAddress.getEditText().getText().toString();
if ((address.length() >= 27) && (address.length() <= 34)) if ((address.length() >= 27) && (address.length() <= 35))
return BitcoinAddressValidator.validate(address, WalletManager.getInstance().isTestNet()); return BitcoinAddressValidator.validate(address);
else else
return false; return false;
} }

View File

@@ -247,7 +247,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
private XmrToApi xmrToApi = null; private XmrToApi xmrToApi = null;
private final XmrToApi getXmrToApi() { private XmrToApi getXmrToApi() {
if (xmrToApi == null) { if (xmrToApi == null) {
synchronized (this) { synchronized (this) {
if (xmrToApi == null) { if (xmrToApi == null) {

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

@@ -54,7 +54,8 @@ public class SendSettingsWizardFragment extends SendWizardFragment {
TxData getTxData(); TxData getTxData();
} }
final static int Mixins[] = {4, 7, 12, 25}; // must match the layout XML // Mixin = Ringsize - 1
final static int Mixins[] = {6, 9, 12, 25}; // must match the layout XML / "@array/mixin"
final static PendingTransaction.Priority Priorities[] = final static PendingTransaction.Priority Priorities[] =
{PendingTransaction.Priority.Priority_Default, {PendingTransaction.Priority.Priority_Default,
PendingTransaction.Priority.Priority_Low, PendingTransaction.Priority.Priority_Low,

View File

@@ -77,7 +77,7 @@ public class SendSuccessWizardFragment extends SendWizardFragment {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Helper.clipBoardCopy(getActivity(), getString(R.string.label_send_txid), tvTxId.getText().toString()); Helper.clipBoardCopy(getActivity(), getString(R.string.label_send_txid), tvTxId.getText().toString());
Toast.makeText(getActivity(), getString(R.string.message_copy_address), Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), getString(R.string.message_copy_txid), Toast.LENGTH_SHORT).show();
} }
}); });

View File

@@ -137,12 +137,7 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
ivTxType.setVisibility(View.GONE); // gives us more space for the amount ivTxType.setVisibility(View.GONE); // gives us more space for the amount
} }
long realAmount = infoItem.amount; String displayAmount = Helper.getDisplayAmount(infoItem.amount, Helper.DISPLAY_DIGITS_INFO);
if (infoItem.isPending) {
realAmount = realAmount - infoItem.fee;
}
String displayAmount = Helper.getDisplayAmount(realAmount, Helper.DISPLAY_DIGITS_INFO);
if (infoItem.direction == TransactionInfo.Direction.Direction_Out) { if (infoItem.direction == TransactionInfo.Direction.Direction_Out) {
tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount)); tvAmount.setText(context.getString(R.string.tx_list_amount_negative, displayAmount));
} else { } else {

View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2018 m2049r
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.m2049r.xmrwallet.model;
public enum NetworkType {
NetworkType_Mainnet(0),
NetworkType_Testnet(1),
NetworkType_Stagenet(2);
public static NetworkType fromInteger(int n) {
switch (n) {
case 0:
return NetworkType_Mainnet;
case 1:
return NetworkType_Testnet;
case 2:
return NetworkType_Stagenet;
}
return null;
}
public int getValue() {
return value;
}
private int value;
NetworkType(int value) {
this.value = value;
}
}

View File

@@ -84,7 +84,14 @@ public class PendingTransaction {
public native long getFee(); public native long getFee();
public native String getFirstTxId(); public String getFirstTxId() {
String id = getFirstTxIdJ();
if (id == null)
throw new IndexOutOfBoundsException();
return id;
}
public native String getFirstTxIdJ();
public native long getTxCount(); public native long getTxCount();

View File

@@ -79,7 +79,11 @@ public class Wallet {
public native String getPath(); public native String getPath();
public native boolean isTestNet(); public NetworkType getNetworkType() {
return NetworkType.fromInteger(nettype());
}
public native int nettype();
//TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0; //TODO virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
//TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0; //TODO virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
@@ -155,14 +159,12 @@ public class Wallet {
public static native boolean isPaymentIdValid(String payment_id); public static native boolean isPaymentIdValid(String payment_id);
public static boolean isAddressValid(String address) { public static boolean isAddressValid(String address) {
return isAddressValid(address, WalletManager.getInstance().isTestNet()); return isAddressValid(address, WalletManager.getInstance().getNetworkType().getValue());
} }
public static native boolean isAddressValid(String address, boolean isTestNet); public static native boolean isAddressValid(String address, int networkType);
//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error); public static native String getPaymentIdFromAddress(String address, int networkType);
public static native String getPaymentIdFromAddress(String address, boolean isTestNet);
public static native long getMaximumAllowedAmount(); public static native long getMaximumAllowedAmount();
@@ -271,5 +273,4 @@ public class Wallet {
//virtual bool parse_uri(const std::string &uri, std::string &address, std::string &payment_id, uint64_t &tvAmount, std::string &tx_description, std::string &recipient_name, std::vector<std::string> &unknown_parameters, std::string &error) = 0; //virtual bool 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

@@ -16,6 +16,8 @@
package com.m2049r.xmrwallet.model; package com.m2049r.xmrwallet.model;
import com.m2049r.xmrwallet.data.WalletNode;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
@@ -39,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;
} }
@@ -69,22 +72,22 @@ public class WalletManager {
} }
public Wallet createWallet(File aFile, String password, String language) { public Wallet createWallet(File aFile, String password, String language) {
long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, isTestNet()); long walletHandle = createWalletJ(aFile.getAbsolutePath(), password, language, getNetworkType().getValue());
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long createWalletJ(String path, String password, String language, boolean isTestNet); private native long createWalletJ(String path, String password, String language, int networkType);
public Wallet openWallet(String path, String password) { public Wallet openWallet(String path, String password) {
long walletHandle = openWalletJ(path, password, isTestNet()); long walletHandle = openWalletJ(path, password, getNetworkType().getValue());
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long openWalletJ(String path, String password, boolean isTestNet); private native long openWalletJ(String path, String password, int networkType);
public Wallet recoveryWallet(File aFile, String password, String mnemonic) { public Wallet recoveryWallet(File aFile, String password, String mnemonic) {
return recoveryWallet(aFile, password, mnemonic, 0); return recoveryWallet(aFile, password, mnemonic, 0);
@@ -92,28 +95,28 @@ public class WalletManager {
public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) { public Wallet recoveryWallet(File aFile, String password, String mnemonic, long restoreHeight) {
long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic, long walletHandle = recoveryWalletJ(aFile.getAbsolutePath(), password, mnemonic,
isTestNet(), restoreHeight); getNetworkType().getValue(), restoreHeight);
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long recoveryWalletJ(String path, String password, String mnemonic, private native long recoveryWalletJ(String path, String password, String mnemonic,
boolean isTestNet, long restoreHeight); int networkType, long restoreHeight);
public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight, public Wallet createWalletWithKeys(File aFile, String password, String language, long restoreHeight,
String addressString, String viewKeyString, String spendKeyString) { String addressString, String viewKeyString, String spendKeyString) {
long walletHandle = createWalletWithKeysJ(aFile.getAbsolutePath(), password, long walletHandle = createWalletFromKeysJ(aFile.getAbsolutePath(), password,
language, isTestNet(), restoreHeight, language, getNetworkType().getValue(), restoreHeight,
addressString, viewKeyString, spendKeyString); addressString, viewKeyString, spendKeyString);
Wallet wallet = new Wallet(walletHandle); Wallet wallet = new Wallet(walletHandle);
manageWallet(wallet); manageWallet(wallet);
return wallet; return wallet;
} }
private native long createWalletWithKeysJ(String path, String password, private native long createWalletFromKeysJ(String path, String password,
String language, String language,
boolean isTestNet, int networkType,
long restoreHeight, long restoreHeight,
String addressString, String addressString,
String viewKeyString, String viewKeyString,
@@ -204,29 +207,27 @@ public class WalletManager {
//TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0; //TODO virtual bool checkPayment(const std::string &address, const std::string &txid, const std::string &txkey, const std::string &daemon_address, uint64_t &received, uint64_t &height, std::string &error) const = 0;
private String daemonAddress = null; private String daemonAddress = null;
private boolean testnet = true; private NetworkType networkType = null;
public boolean isTestNet() { public NetworkType getNetworkType() {
if (daemonAddress == null) { return networkType;
return true;
// assume testnet not explicitly initialised
//throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
}
return testnet;
} }
public void setDaemon(String address, boolean testnet, String username, String password) { //public void setDaemon(String address, NetworkType networkType, String username, String password) {
//Timber.d("SETDAEMON " + username + "/" + password + "/" + address); public void setDaemon(WalletNode walletNode) {
this.daemonAddress = address; this.daemonAddress = walletNode.getAddress();
this.testnet = testnet; this.networkType = walletNode.getNetworkType();
this.daemonUsername = username; this.daemonUsername = walletNode.getUsername();
this.daemonPassword = password; this.daemonPassword = walletNode.getPassword();
setDaemonAddressJ(address); setDaemonAddressJ(daemonAddress);
}
public void setNetworkType(NetworkType networkType) {
this.networkType = networkType;
} }
public String getDaemonAddress() { public String getDaemonAddress() {
if (daemonAddress == null) { if (daemonAddress == null) {
// assume testnet not explicitly initialised
throw new IllegalStateException("use setDaemon() to initialise daemon and net first!"); throw new IllegalStateException("use setDaemon() to initialise daemon and net first!");
} }
return this.daemonAddress; return this.daemonAddress;
@@ -234,13 +235,13 @@ public class WalletManager {
private native void setDaemonAddressJ(String address); private native void setDaemonAddressJ(String address);
String daemonUsername = ""; private String daemonUsername = "";
public String getDaemonUsername() { public String getDaemonUsername() {
return daemonUsername; return daemonUsername;
} }
String daemonPassword = ""; private String daemonPassword = "";
public String getDaemonPassword() { public String getDaemonPassword() {
return daemonPassword; return daemonPassword;
@@ -268,5 +269,23 @@ public class WalletManager {
//TODO static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir); //TODO static std::tuple<bool, std::string, std::string, std::string, std::string> checkUpdates(const std::string &software, const std::string &subdir);
static public native void initLogger(String argv0, String defaultLogBaseName);
//TODO: maybe put these in an enum like in monero core - but why?
static public int LOGLEVEL_SILENT = -1;
static public int LOGLEVEL_WARN = 0;
static public int LOGLEVEL_INFO = 1;
static public int LOGLEVEL_DEBUG = 2;
static public int LOGLEVEL_TRACE = 3;
static public int LOGLEVEL_MAX = 4;
static public native void setLogLevel(int level);
static public native void logDebug(String category, String message);
static public native void logInfo(String category, String message);
static public native void logWarning(String category, String message);
static public native void logError(String category, String message);
} }

View File

@@ -524,7 +524,7 @@ public class WalletService extends Service {
showProgress(20); showProgress(20);
Wallet wallet = null; Wallet wallet = null;
WalletManager walletMgr = WalletManager.getInstance(); WalletManager walletMgr = WalletManager.getInstance();
Timber.d("WalletManager testnet=%s", walletMgr.isTestNet()); Timber.d("WalletManager network=%s", walletMgr.getNetworkType().name());
showProgress(30); showProgress(30);
if (walletMgr.walletExists(path)) { if (walletMgr.walletExists(path)) {
Timber.d("open wallet %s", path); Timber.d("open wallet %s", path);

View File

@@ -18,6 +18,9 @@ package com.m2049r.xmrwallet.util;
// based on https://rosettacode.org/wiki/Bitcoin/address_validation#Java // based on https://rosettacode.org/wiki/Bitcoin/address_validation#Java
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@@ -27,6 +30,11 @@ public class BitcoinAddressValidator {
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static boolean validate(String addrress) {
return validate(addrress,
WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet);
}
public static boolean validate(String addrress, boolean testnet) { public static boolean validate(String addrress, boolean testnet) {
if (addrress.length() < 26 || addrress.length() > 35) if (addrress.length() < 26 || addrress.length() > 35)
return false; return false;
@@ -34,10 +42,11 @@ public class BitcoinAddressValidator {
if (decoded == null) if (decoded == null)
return false; return false;
int v = decoded[0] & 0xFF;
if (!testnet) { if (!testnet) {
if ((decoded[0] != 0x00) && (decoded[0] != 0x05)) return false; if ((v != 0x00) && (v != 0x05)) return false;
} else { } else {
if ((decoded[0] != 0x6f) && (decoded[0] != 0xc4)) return false; if ((v != 0x6f) && (v != 0xc4)) return false;
} }
byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21)); byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21));
@@ -57,7 +66,11 @@ public class BitcoinAddressValidator {
byte[] result = new byte[25]; byte[] result = new byte[25];
byte[] numBytes = num.toByteArray(); byte[] numBytes = num.toByteArray();
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length); if (num.bitLength() == 200) {
System.arraycopy(numBytes, 1, result, 0, 25);
} else {
System.arraycopy(numBytes, 0, result, result.length - numBytes.length, numBytes.length);
}
return result; return result;
} }

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

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